diff options
Diffstat (limited to '1.2-netsec/pbx/pbx_ael.c')
-rw-r--r-- | 1.2-netsec/pbx/pbx_ael.c | 1284 |
1 files changed, 1284 insertions, 0 deletions
diff --git a/1.2-netsec/pbx/pbx_ael.c b/1.2-netsec/pbx/pbx_ael.c new file mode 100644 index 000000000..4b758a61b --- /dev/null +++ b/1.2-netsec/pbx/pbx_ael.c @@ -0,0 +1,1284 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 1999 - 2005, Digium, Inc. + * + * Mark Spencer <markster@digium.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 + * + */ + +#include <sys/types.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <errno.h> + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + +#include "asterisk/pbx.h" +#include "asterisk/config.h" +#include "asterisk/module.h" +#include "asterisk/logger.h" +#include "asterisk/cli.h" +#include "asterisk/callerid.h" + +struct stringlink { + struct stringlink *next; + char data[0]; +}; + +#define FILLIN_BREAK 1 +#define FILLIN_CONTINUE 2 + +struct fillin { + struct fillin *next; + char exten[AST_MAX_EXTENSION]; + int priority; + int type; +}; + +#ifdef __AST_DEBUG_MALLOC +static void FREE(void *ptr) +{ + free(ptr); +} +#else +#define FREE free +#endif + +#define DEBUG_READ (1 << 0) +#define DEBUG_TOKENS (1 << 1) +#define DEBUG_MACROS (1 << 2) +#define DEBUG_CONTEXTS (1 << 3) + +static int aeldebug = 0; + +static char *dtext = "Asterisk Extension Language Compiler"; +static char *config = "extensions.ael"; +static char *registrar = "pbx_ael"; + +static char *__grab_token(char *src, const char *filename, int lineno, int link) +{ + char *c; + char *b; + char *a; + int level = 0; + char *ret; +#if 0 + if (aeldebug || DEBUG_TOKENS) + ast_verbose("Searching for token in '%s'!\n", src); +#endif + c = src; + while(*c) { + if ((*c == '\\')) { + c++; + if (!*c) + c--; + } else { + if ((*c == '{') || (*c == '(')) { + level++; + } else if ((*c == '}') || (*c == ')')) { + if (level) + level--; + else + ast_log(LOG_WARNING, "Syntax error at line %d of '%s', too many closing braces!\n", lineno, filename); + } else if ((*c == ';') && !level) { + /* Got a token! */ + *c = '\0'; + b = c; + b--; + c++; + while((b > src) && (*b < 33)) { + *b = '\0'; + b--; + } + a = ast_skip_blanks(src); + if (link) { + ret = malloc(strlen(a) + sizeof(struct stringlink) + 1); + if (ret) + strcpy(ret + sizeof(struct stringlink), a); + } else + ret = strdup(a); + /* Save remainder */ + memmove(src, c, strlen(c) + 1); + return ret; + } + } + c++; + } + return NULL; +} + +static char *grab_token(char *src, const char *filename, int lineno) +{ + return __grab_token(src, filename, lineno, 0); +} + +static struct stringlink *arg_parse(char *args, const char *filename, int lineno) +{ + struct stringlink *cur, *prev=NULL, *root=NULL; + if (args) { + if (aeldebug & DEBUG_TOKENS) + ast_verbose("Parsing args '%s'!\n", args); + if (args[0] == '{') { + /* Strip mandatory '}' from end */ + args[strlen(args) - 1] = '\0'; + while ((cur = (struct stringlink *)__grab_token(args + 1, filename, lineno, 1))) { + cur->next = NULL; + if (prev) + prev->next = cur; + else + root = cur; + prev = cur; + } + } else if (*args) { + root = malloc(sizeof(struct stringlink) + strlen(args) + 1); + if (root) { + strcpy(root->data, args); + root->next = NULL; + } + } + } + return root; +} + +static char *grab_else(char *args, const char *filename, int lineno) +{ + char *ret = NULL; + int level=0; + char *c; + if (args) { + if (args[0] == '{') { + c = args; + while(*c) { + if (*c == '{') + level++; + else if (*c == '}') { + level--; + if (!level) { + c++; + while(*c && (*c < 33)) { *c = '\0'; c++; }; + if (!strncasecmp(c, "else", 4) && + ((c[4] == '{') || (c[4] < 33))) { + /* Ladies and gentlemen, we have an else clause */ + *c = '\0'; + c += 4; + c = ast_skip_blanks(c); + ret = c; + if (aeldebug & DEBUG_TOKENS) + ast_verbose("Returning else clause '%s'\n", c); + } + break; + } + } + c++; + } + } + } + return ret; +} + +static struct stringlink *param_parse(char *parms, const char *macro, const char *filename, int lineno) +{ + char *s, *e; + struct stringlink *root = NULL, *prev=NULL, *cur; + if (!parms || !*parms) + return NULL; + if (*parms != '(') { + ast_log(LOG_NOTICE, "Syntax error in parameter list for macro '%s' at about line %d of %s: Expecting '(' but got '%c'\n", macro, lineno, filename, *parms); + return NULL; + } + s = parms + 1; + while(*s) { + s = ast_skip_blanks(s); + e = s; + while(*e && (*e != ')') && (*e != ',')) { + if (*e < 33) + *e = '\0'; + e++; + } + if (*e) { + /* Strip token */ + *e = '\0'; + e++; + /* Skip over whitespace */ + e = ast_skip_blanks(e); + /* Link */ + cur = malloc(strlen(s) + sizeof(struct stringlink) + 1); + if (cur) { + cur->next = NULL; + strcpy(cur->data, s); + if (prev) + prev->next = cur; + else + root = cur; + prev = cur; + } + s = e; + } + } + return root; +} + +static void arg_free(struct stringlink *cur) +{ + struct stringlink *last; + while(cur) { + last = cur; + cur = cur->next; + free(last); + } +} + +static void handle_globals(struct stringlink *vars) +{ + while(vars) { + pbx_builtin_setvar(NULL, vars->data); + vars = vars->next; + } +} + +static struct stringlink *split_token(char *token, const char *filename, int lineno) +{ + char *args, *p; + struct stringlink *argv; + args = token; + while (*args && (*args > 32) && (*args != '{') && (*args != '(')) args++; + if (*args) { + p = args; + args = ast_skip_blanks(args); + if (*args != '(') { + *p = '\0'; + } else { + while (*args && (*args != ')')) args++; + if (*args == ')') { + args++; + args = ast_skip_blanks(args); + } + } + if (!*args) + args = NULL; + } else args = NULL; + argv = arg_parse(args, filename, lineno); + if (args) + *args = '\0'; + return argv; +} + +static int matches_keyword(const char *data, const char *keyword) +{ + char c; + if (!strncasecmp(data, keyword, strlen(keyword))) { + c = data[strlen(keyword)]; + if ((c < 33) || (c == '(') || (c == '{')) + return 1; + } + return 0; +} + +static struct stringlink *split_params(char *token, const char *filename, int lineno) +{ + char *params; + struct stringlink *paramv; + params = token; + while(*params && (*params > 32) && (*params != '(')) params++; + if (*params) { + if (*params != '(') { + *params = '\0'; + params++; + params = ast_skip_blanks(params); + } + if (!*params) + params = NULL; + } else params = NULL; + paramv = param_parse(params, token, filename, lineno); + if (params) + *params = '\0'; + return paramv; +} + +static const char *get_case(char *s, char **restout, int *pattern) +{ + char *newcase=NULL; + char *rest=NULL; + if (!strncasecmp(s, "case", 4) && s[4] && ((s[4] < 33) || (s[4] == ':'))) { + newcase = s + 4; + newcase = ast_skip_blanks(newcase); + rest = newcase; + *pattern = 0; + } else if (!strncasecmp(s, "pattern", 7) && s[7] && ((s[7] < 33) || (s[7] == ':'))) { + newcase = s + 8; + newcase = ast_skip_blanks(newcase); + rest = newcase; + *pattern = 1; + } else if (!strncasecmp(s, "default", 7) && ((s[7] < 33) || (s[7] == ':'))) { + newcase = "."; + rest = s + 7; + rest = ast_skip_blanks(rest); + *pattern = 1; + } + + if (rest) { + while (*rest && (*rest > 32) && (*rest != ':')) rest++; + if (*rest) { + *rest = 0; + rest++; + while (*rest && ((*rest == ':') || (*rest < 33))) rest++; + *restout = rest; + } else { + *restout = ""; + } + } else + *restout = s; + if (aeldebug & DEBUG_TOKENS) + ast_verbose("GETCASE: newcase is '%s', rest = '%s'\n", newcase, *restout); + return newcase; +} + +static void fillin_free(struct fillin *fillin) +{ + struct fillin *cur, *next; + cur = fillin; + while(cur) { + next = cur->next; + free(cur); + cur = next; + } +} + +static void fillin_process(struct ast_context *con, struct fillin *fillin, const char *filename, int lineno, const char *breakexten, int breakprio, const char *contexten, int contprio) +{ + struct fillin *cur; + char *app; + char mdata[AST_MAX_EXTENSION + 20]; + cur = fillin; + while(cur) { + if (cur->type == FILLIN_BREAK) { + if (breakexten && breakprio) { + app = "Goto"; + snprintf(mdata, sizeof(mdata), "%s|%d", breakexten, breakprio); + } else { + app = "NoOp"; + snprintf(mdata, sizeof(mdata), "Invalid break"); + ast_log(LOG_NOTICE, "Ignoring inappropriate break around line %d of %s\n", lineno, filename); + } + if (ast_add_extension2(con, 0, cur->exten, cur->priority, NULL, NULL, app, strdup(mdata), FREE, registrar)) + ast_log(LOG_WARNING, "Unable to add step at priority '%d' of break '%s'\n", cur->priority, cur->exten); + } else if (cur->type == FILLIN_CONTINUE) { + if (contexten && contprio) { + app = "Goto"; + snprintf(mdata, sizeof(mdata), "%s|%d", contexten, contprio); + } else { + app = "NoOp"; + snprintf(mdata, sizeof(mdata), "Invalid continue"); + ast_log(LOG_NOTICE, "Ignoring inappropriate continue around line %d of %s\n", lineno, filename); + } + if (ast_add_extension2(con, 0, cur->exten, cur->priority, NULL, NULL, app, strdup(mdata), FREE, registrar)) + ast_log(LOG_WARNING, "Unable to add step at priority '%d' of continue '%s'\n", cur->priority, cur->exten); + } else { + ast_log(LOG_WARNING, "Whoa, unknown fillin type '%d'\n", cur->type); + } + cur = cur->next; + } +} + +static int match_assignment(char *variable, char **value) +{ + char *c; + char *ws; + int inpar = 0; + c = variable; + + while (*c) { + if(*c == ')' && (inpar > 0)) { + inpar--; + } else if(*c == '(' && (inpar >= 0)) { + inpar++; + } else if(*c == '=' && (inpar == 0)) { + break; + } + c++; + } + ws = c; + c = ast_skip_blanks(c); + if (*c == '=') { + *ws = '\0'; + *c = '\0'; + c++; + c = ast_skip_blanks(c); + *value = c; + return 1; + } + return 0; +} + +static int matches_label(char *data, char **rest) +{ + char last = 0; + char *start = data; + while (*data > 32) { + last = *data; + data++; + } + if (last != ':') { + data = ast_skip_blanks(data); + last = *data; + data++; + } + if (last == ':') { + *rest = data; + /* Go back and trim up the label */ + while(*start && ((*start > 32) && (*start != ':'))) start++; + *start = '\0'; + return 1; + } + return 0; +} + +static char *argument_end(char *str) +{ + int level=0; + while(*++str) { + switch(*str) { + case '(': + level++; + break; + case ')': + if(level) + level--; + else + return str; + break; + default: + break; + } + } + return NULL; +} + +static int build_step(const char *what, const char *name, const char *filename, int lineno, struct ast_context *con, char *exten, int *pos, char *data, struct fillin **fillout, char **label); +static int __build_step(const char *what, const char *name, const char *filename, int lineno, struct ast_context *con, char *exten, int *pos, char *data, struct fillin **fillout, char **label) +{ + char *app; + char *args; + char *c; + char *margs=NULL; + char *oargs; + char *rest; + const char *curcase, *newcase; + struct stringlink *swargs, *cur; + int cpos; + int mlen; + int pattern = 0; + struct fillin *fillin; + + data = ast_skip_blanks(data); + if (matches_label(data, &c)) { + *label = data; + data = c; + data = ast_skip_blanks(data); + } + if (ast_strlen_zero(data)) + return 0; + if (matches_keyword(data, "switch")) { + fillin = NULL; + /* Switch */ + args = data + strlen("switch"); + while ((*args < 33) && (*args != '(')) args++; + if ((*args == '(') && (c = argument_end(args))) { + args++; + *c = '\0'; + c++; + if (aeldebug & DEBUG_TOKENS) + ast_verbose("--SWITCH on : %s\n", args); + mlen = strlen(exten) + 128 + strlen(args) + strlen(name); + margs = alloca(mlen); + app = "Goto"; + sprintf(margs, "sw-%d-%s|1", *pos, args); + ast_process_quotes_and_slashes(margs, ',', '|'); + oargs = args; + args = margs; + if (ast_add_extension2(con, 0, exten, *pos, *label, NULL, app, strdup(args), FREE, registrar)) + ast_log(LOG_WARNING, "Unable to add step at priority '%d' of %s '%s'\n", *pos, what, name); + else { + *label = NULL; + (*pos)++; + } + app = "NoOp"; + sprintf(margs, "Finish switch-%d", *pos - 1); + if (ast_add_extension2(con, 0, exten, *pos, *label, NULL, app, strdup(args), FREE, registrar)) + ast_log(LOG_WARNING, "Unable to add step at priority '%d' of %s '%s'\n", *pos, what, name); + else { + *label = NULL; + (*pos)++; + } + c = ast_skip_blanks(c); + if (aeldebug & DEBUG_TOKENS) + ast_verbose("ARG Parsing '%s'\n", c); + swargs = arg_parse(c, filename, lineno); + cur = swargs; + curcase = NULL; + while(cur) { + if ((newcase = get_case(cur->data, &rest, &pattern))) { + if (aeldebug & DEBUG_TOKENS) + ast_verbose("--NEWCASE: '%s'!\n", newcase); + if (curcase) { + /* Handle fall through */ + char tmp[strlen(newcase) + strlen(name) + 40]; + sprintf(tmp, "sw-%d-%s|%d", *pos - 2, newcase, 1); + ast_add_extension2(con, 0, margs, cpos, NULL, NULL, "Goto", strdup(tmp), FREE, registrar); + } + curcase = newcase; + cpos = 1; + if (pattern) + snprintf(margs, mlen, "_sw-%d-%s", *pos - 2, curcase); + else + snprintf(margs, mlen, "sw-%d-%s", *pos - 2, curcase); + if (!strcasecmp(rest, "break")) { + char tmp[strlen(exten) + 10]; + sprintf(tmp, "%s|%d", exten, *pos - 1); + ast_add_extension2(con, 0, exten, cpos, *label, NULL, "Goto", strdup(tmp), FREE, registrar); + curcase = NULL; + *label = NULL; + } else + build_step("switch", margs, filename, lineno, con, margs, &cpos, rest, &fillin, label); + } else if (curcase) { + if (aeldebug & DEBUG_TOKENS) + ast_verbose("Building statement from '%s'\n", rest); + if (!strcasecmp(rest, "break")) { + char tmp[strlen(exten) + 10]; + sprintf(tmp, "%s|%d", exten, *pos - 1); + ast_add_extension2(con, 0, margs, cpos, *label, NULL, "Goto", strdup(tmp), FREE, registrar); + curcase = NULL; + *label = NULL; + } else + build_step("switch", margs, filename, lineno, con, margs, &cpos, rest, &fillin, label); + } else + ast_log(LOG_WARNING, "Unreachable code in switch at about line %d of %s\n", lineno, filename); + if (aeldebug & DEBUG_TOKENS) + ast_verbose("--SWARG: %s\n", cur->data); + cur = cur->next; + } + /* Can't do anything with these */ + fillin_process(con, fillin, filename, lineno, NULL, 0, NULL, 0); + fillin_free(fillin); + arg_free(swargs); + } else + ast_log(LOG_WARNING, "Syntax error in switch declaration in %s around line %d!\n", filename, lineno); + + } else if (matches_keyword(data, "if")) { + /* If... */ + args = data + strlen("if"); + while ((*args < 33) && (*args != '(')) args++; + if ((*args == '(') && (c = argument_end(args))) { + int ifblock; + int ifstart; + int elsestart; + int ifend; + int ifskip; + char *elses; + char *iflabel; + args++; + *c = '\0'; + c++; + c = ast_skip_blanks(c); + if (aeldebug & DEBUG_TOKENS) + ast_verbose("--IF on : '%s' : '%s'\n", args, c); + mlen = strlen(exten) + 128 + strlen(args) + strlen(name); + margs = alloca(mlen); + /* Remember where the ifblock starts, and skip over */ + ifblock = (*pos)++; + iflabel = *label; + *label = NULL; + /* Remember where the start of the ifblock is */ + ifstart = *pos; + snprintf(margs, mlen, "if-%s-%d", name, ifblock); + /* Now process the block of the if */ + if (aeldebug & DEBUG_TOKENS) + ast_verbose("Searching for elses in '%s'\n", c); + elses = grab_else(c, filename, lineno); + build_step("if", margs, filename, lineno, con, exten, pos, c, fillout, label); + if (elses) { + /* Reserve a goto to exit the if */ + ifskip = *pos; + (*pos)++; + elsestart = *pos; + build_step("else", margs, filename, lineno, con, exten, pos, elses, fillout, label); + } else { + elsestart = *pos; + ifskip = 0; + } + ifend = *pos; + (*pos)++; + app = "NoOp"; + snprintf(margs, mlen, "Finish if-%s-%d", name, ifblock); + if (ast_add_extension2(con, 0, exten, ifend, *label, NULL, app, strdup(margs), FREE, registrar)) + ast_log(LOG_WARNING, "Unable to add step at priority '%d' of %s '%s'\n", *pos, what, name); + *label = NULL; + app = "GotoIf"; + snprintf(margs, mlen, "$[ %s ]?%d:%d", args, ifstart, elsestart); + if (ast_add_extension2(con, 0, exten, ifblock, iflabel, NULL, app, strdup(margs), FREE, registrar)) + ast_log(LOG_WARNING, "Unable to add step at priority '%d' of %s '%s'\n", *pos, what, name); + if (ifskip) { + /* Skip as appropriate around else clause */ + snprintf(margs, mlen, "%d", ifend); + if (ast_add_extension2(con, 0, exten, ifskip, NULL, NULL, "Goto", strdup(margs), FREE, registrar)) + ast_log(LOG_WARNING, "Unable to add step at priority '%d' of %s '%s'\n", *pos, what, name); + } + } else + ast_log(LOG_WARNING, "Syntax error in if declaration in %s around line %d!\n", filename, lineno); + } else if (matches_keyword(data, "while")) { + /* While... */ + fillin = NULL; + args = data + strlen("while"); + while ((*args < 33) && (*args != '(')) args++; + if ((*args == '(') && (c = argument_end(args))) { + int whileblock; + int whilestart; + int whileend; + char *whilelabel; + args++; + *c = '\0'; + c++; + c = ast_skip_blanks(c); + if (aeldebug & DEBUG_TOKENS) + ast_verbose("--WHILE on : '%s' : '%s'\n", args, c); + mlen = strlen(exten) + 128 + strlen(args) + strlen(name); + margs = alloca(mlen); + /* Remember where to put the conditional, and keep its position */ + whilestart = (*pos); + whilelabel = *label; + *label = NULL; + (*pos)++; + /* Remember where the whileblock starts */ + whileblock = (*pos); + snprintf(margs, mlen, "while-%s-%d", name, whilestart); + build_step("while", margs, filename, lineno, con, exten, pos, c, &fillin, label); + /* Close the loop */ + app = "Goto"; + snprintf(margs, mlen, "%d", whilestart); + if (ast_add_extension2(con, 0, exten, (*pos)++, *label, NULL, app, strdup(margs), FREE, registrar)) + ast_log(LOG_WARNING, "Unable to add step at priority '%d' of %s '%s'\n", *pos, what, name); + *label = NULL; + whileend = (*pos); + /* Place trailer */ + app = "NoOp"; + snprintf(margs, mlen, "Finish while-%s-%d", name, whilestart); + if (ast_add_extension2(con, 0, exten, (*pos)++, *label, NULL, app, strdup(margs), FREE, registrar)) + ast_log(LOG_WARNING, "Unable to add step at priority '%d' of %s '%s'\n", *pos, what, name); + *label = NULL; + app = "GotoIf"; + snprintf(margs, mlen, "$[ %s ]?%d:%d", args, whileblock, whileend); + if (ast_add_extension2(con, 0, exten, whilestart, whilelabel, NULL, app, strdup(margs), FREE, registrar)) + ast_log(LOG_WARNING, "Unable to add step at priority '%d' of %s '%s'\n", *pos, what, name); + fillin_process(con, fillin, filename, lineno, exten, whileend, exten, whilestart); + fillin_free(fillin); + } else + ast_log(LOG_WARNING, "Syntax error in while declaration in %s around line %d!\n", filename, lineno); + } else if (matches_keyword(data, "jump")) { + char *p; + /* Jump... */ + fillin = NULL; + args = data + strlen("jump"); + args = ast_skip_blanks(args); + if (aeldebug & DEBUG_TOKENS) + ast_verbose("--JUMP to : '%s'\n", args); + p = strchr(args, ','); + if (p) { + *p = '\0'; + p++; + } else + p = "1"; + c = strchr(args, '@'); + if (c) { + *c = '\0'; + c++; + } + mlen = strlen(exten) + 128 + strlen(args) + strlen(name) + (c ? strlen(c) : 0); + margs = alloca(mlen); + if (c) + snprintf(margs, mlen, "%s|%s|%s", c,args, p); + else + snprintf(margs, mlen, "%s|%s", args, p); + app = "Goto"; + if (ast_add_extension2(con, 0, exten, (*pos)++, *label, NULL, app, strdup(margs), FREE, registrar)) + ast_log(LOG_WARNING, "Unable to add step at priority '%d' of %s '%s'\n", *pos, what, name); + *label = NULL; + } else if (matches_keyword(data, "goto")) { + /* Jump... */ + fillin = NULL; + args = data + strlen("goto"); + args = ast_skip_blanks(args); + if (aeldebug & DEBUG_TOKENS) + ast_verbose("--GOTO to : '%s'\n", args); + app = "Goto"; + if (args[0] == '(' && args[strlen(args) - 1] == ')') { + args[0] = '\0'; + args++; + args[strlen(args) - 1] = '\0'; + } + if (ast_add_extension2(con, 0, exten, (*pos)++, *label, NULL, app, strdup(args), FREE, registrar)) + ast_log(LOG_WARNING, "Unable to add step at priority '%d' of %s '%s'\n", *pos, what, name); + *label = NULL; + } else if (matches_keyword(data, "for")) { + /* While... */ + fillin = NULL; + args = data + strlen("for"); + while ((*args < 33) && (*args != '(')) args++; + if ((*args == '(') && (c = argument_end(args))) { + int forblock; + int forprep; + int forstart; + int forend; + struct stringlink *fields; + char *tmp; + char *forlabel = NULL; + args++; + *c = '\0'; + c++; + c = ast_skip_blanks(c); + /* Parse arguments first */ + tmp = alloca(strlen(args) + 10); + if (tmp) { + snprintf(tmp, strlen(args) + 10, "{%s;}", args); + fields = arg_parse(tmp, filename, lineno); + } else + fields = NULL; + if (fields && fields->next && fields->next->next) { + if (aeldebug & DEBUG_TOKENS) + ast_verbose("--FOR ('%s' ; '%s' ; '%s') : '%s'\n", fields->data, fields->next->data, fields->next->next->data, c); + mlen = strlen(exten) + 128 + strlen(args) + strlen(name); + margs = alloca(mlen); + forprep = *pos; + snprintf(margs, mlen, "for-%s-%d", name, forprep); + fillin = NULL; + build_step("while", margs, filename, lineno, con, exten, pos, fields->data, &fillin, label); + /* Remember where to put the conditional, and keep its position */ + forstart = (*pos); + forlabel = *label; + (*pos)++; + *label = NULL; + /* Remember where the whileblock starts */ + forblock = (*pos); + build_step("for", margs, filename, lineno, con, exten, pos, fields->next->next->data, &fillin, label); + build_step("for", margs, filename, lineno, con, exten, pos, c, &fillin, label); + /* Close the loop */ + app = "Goto"; + snprintf(margs, mlen, "%d", forstart); + if (ast_add_extension2(con, 0, exten, (*pos)++, *label, NULL, app, strdup(margs), FREE, registrar)) + ast_log(LOG_WARNING, "Unable to add step at priority '%d' of %s '%s'\n", *pos, what, name); + *label = NULL; + forend = (*pos); + /* Place trailer */ + app = "NoOp"; + snprintf(margs, mlen, "Finish for-%s-%d", name, forprep); + if (ast_add_extension2(con, 0, exten, (*pos)++, *label, NULL, app, strdup(margs), FREE, registrar)) + ast_log(LOG_WARNING, "Unable to add step at priority '%d' of %s '%s'\n", *pos, what, name); + *label = NULL; + app = "GotoIf"; + snprintf(margs, mlen, "$[ %s ]?%d:%d", fields->next->data, forblock, forend); + if (ast_add_extension2(con, 0, exten, forstart, forlabel, NULL, app, strdup(margs), FREE, registrar)) + ast_log(LOG_WARNING, "Unable to add step at priority '%d' of %s '%s'\n", forstart, what, name); + fillin_process(con, fillin, filename, lineno, exten, forend, exten, forstart); + fillin_free(fillin); + } else + ast_log(LOG_NOTICE, "Improper for declaration in %s around line %d!\n", filename, lineno); + arg_free(fields); + } else + ast_log(LOG_WARNING, "Syntax error in for declaration in %s around line %d!\n", filename, lineno); + + } else if (!strcasecmp(data, "break") || !strcasecmp(data, "continue")) { + struct fillin *fi; + fi = malloc(sizeof(struct fillin)); + if (fi) { + memset(fi, 0, sizeof(struct fillin)); + if (!strcasecmp(data, "break")) + fi->type = FILLIN_BREAK; + else + fi->type = FILLIN_CONTINUE; + ast_copy_string(fi->exten, exten, sizeof(fi->exten)); + fi->priority = (*pos)++; + fi->next = *fillout; + *fillout = fi; + } + } else if (match_assignment(data, &rest)) { + if (aeldebug & DEBUG_TOKENS) + ast_verbose("ASSIGN '%s' = '%s'\n", data, rest); + mlen = strlen(rest) + strlen(data) + 20; + margs = alloca(mlen); + snprintf(margs, mlen, "%s=$[ %s ]", data, rest); + app = "Set"; + if (ast_add_extension2(con, 0, exten, *pos, *label, NULL, app, strdup(margs), FREE, registrar)) + ast_log(LOG_WARNING, "Unable to add assignment at priority '%d' of %s '%s'\n", *pos, what, name); + else { + *label = NULL; + (*pos)++; + } + } else { + app = data; + args = app; + while (*args && (*args > 32) && (*args != '(')) args++; + if (*args != '(') { + while(*args && (*args != '(')) { *args = '\0'; args++; }; + } + if (*args == '(') { + *args = '\0'; + args++; + /* Got arguments, trim trailing ')' */ + c = args + strlen(args) - 1; + while((c >= args) && (*c < 33) && (*c != ')')) { *c = '\0'; c--; }; + if ((c >= args) && (*c == ')')) *c = '\0'; + } else + args = ""; + ast_process_quotes_and_slashes(args, ',', '|'); + if (app[0] == '&') { + app++; + margs = alloca(strlen(args) + strlen(app) + 10); + sprintf(margs, "%s|%s", app, args); + args = margs; + app = "Macro"; + } + if (aeldebug & DEBUG_TOKENS) + ast_verbose("-- APP: '%s', ARGS: '%s'\n", app, args); + if (ast_add_extension2(con, 0, exten, *pos, *label, NULL, app, strdup(args), FREE, registrar)) + ast_log(LOG_WARNING, "Unable to add step at priority '%d' of %s '%s'\n", *pos, what, name); + else { + (*pos)++; + *label = NULL; + } + } + return 0; +} + +static int build_step(const char *what, const char *name, const char *filename, int lineno, struct ast_context *con, char *exten, int *pos, char *data, struct fillin **fillout, char **label) +{ + struct stringlink *args, *cur; + int res=0; + struct fillin *fillin=NULL; + int dropfill = 0; + char *labelin = NULL; + if (!fillout) { + fillout = &fillin; + dropfill = 1; + } + if (!label) { + label = &labelin; + }; + args = arg_parse(data, filename, lineno); + cur = args; + while(cur) { + res |= __build_step(what, name, filename, lineno, con, exten, pos, cur->data, fillout, label); + cur = cur->next; + } + arg_free(args); + if (dropfill) { + fillin_process(con, fillin, filename, lineno, NULL, 0, NULL, 0); + fillin_free(fillin); + } + return res; +} + +static int parse_catch(char *data, char **catch, char **rest) +{ + /* Skip the word 'catch' */ + data += 5; + data = ast_skip_blanks(data); + /* Here's the extension */ + *catch = data; + if (!*data) + return 0; + while (*data && (*data > 32)) data++; + if (!*data) + return 0; + /* Trim any trailing spaces */ + *data = '\0'; + data++; + data = ast_skip_blanks(data); + if (!*data) + return 0; + *rest = data; + return 1; +} + +static void handle_macro(struct ast_context **local_contexts, struct stringlink *vars, const char *filename, int lineno) +{ + struct stringlink *argv; + struct stringlink *paramv; + struct stringlink *cur; + struct ast_context *con; + struct fillin *fillin; + char *catch, *rest; + char name[256]; + int pos; + int cpos; + + if (aeldebug & DEBUG_MACROS) + ast_verbose("Root macro def is '%s'\n", vars->data); + argv = split_token(vars->data, filename, lineno); + paramv = split_params(vars->data, filename, lineno); + if (aeldebug & DEBUG_MACROS) + ast_verbose("Found macro '%s'\n", vars->data); + snprintf(name, sizeof(name), "macro-%s", vars->data); + con = ast_context_create(local_contexts, name, registrar); + if (con) { + pos = 1; + cur = paramv; + while(cur) { + if (aeldebug & DEBUG_MACROS) + ast_verbose(" PARAM => '%s'\n", cur->data); + snprintf(name, sizeof(name), "%s=${ARG%d}", cur->data, pos); + if (ast_add_extension2(con, 0, "s", pos, NULL, NULL, "Set", strdup(name), FREE, registrar)) + ast_log(LOG_WARNING, "Unable to add step at priority '%d' of macro '%s'\n", pos, vars->data); + else + pos++; + cur = cur->next; + } + cur = argv; + while(cur) { + if (aeldebug & DEBUG_MACROS) + ast_verbose(" STEP => '%s'\n", cur->data); + if (matches_keyword(cur->data, "catch")) { + if (aeldebug & DEBUG_MACROS) + ast_verbose("--CATCH: '%s'\n", cur->data); + if (parse_catch(cur->data, &catch, &rest)) { + cpos = 1; + build_step("catch", catch, filename, lineno, con, catch, &cpos, rest, NULL, NULL); + } else + ast_log(LOG_NOTICE, "Parse error for catch at about line %d of %s\n", lineno, filename); + } else { + fillin = NULL; + build_step("macro", vars->data, filename, lineno, con, "s", &pos, cur->data, NULL, NULL); + } + cur = cur->next; + } + } else + ast_log(LOG_WARNING, "Unable to create context '%s'\n", name); + arg_free(paramv); + arg_free(argv); + if (vars->next) + ast_log(LOG_NOTICE, "Ignoring excess tokens in macro definition around line %d of %s!\n", lineno, filename); +} + +static int matches_extension(char *exten, char **extout) +{ + char *c; + *extout = NULL; + c = exten; + while(*c && (*c > 32)) c++; + if (*c) { + *c = '\0'; + c++; + c = ast_skip_blanks(c); + if (*c) { + if (*c == '=') { + *c = '\0'; + c++; + if (*c == '>') + c++; + c = ast_skip_blanks(c); + *extout = c; + return 1; + } + } + } + return 0; +} + +static void parse_keyword(char *s, char **o) +{ + char *c; + c = s; + while((*c) && (*c > 32)) c++; + if (*c) { + *c = '\0'; + c++; + c = ast_skip_blanks(c); + *o = c; + } else + *o = NULL; +} + +static void handle_context(struct ast_context **local_contexts, struct stringlink *vars, const char *filename, int lineno) +{ + struct stringlink *argv; + struct stringlink *cur2; + struct stringlink *argv2; + struct stringlink *cur; + struct ast_context *con; + char *rest; + char *c; + char name[256]; + int pos; + + if (aeldebug & DEBUG_CONTEXTS) + ast_verbose("Root context def is '%s'\n", vars->data); + argv = split_token(vars->data, filename, lineno); + if (aeldebug & DEBUG_CONTEXTS) + ast_verbose("Found context '%s'\n", vars->data); + snprintf(name, sizeof(name), "%s", vars->data); + con = ast_context_create(local_contexts, name, registrar); + if (con) { + cur = argv; + while(cur) { + if (matches_keyword(cur->data, "includes")) { + if (aeldebug & DEBUG_CONTEXTS) + ast_verbose("--INCLUDES: '%s'\n", cur->data); + parse_keyword(cur->data, &rest); + if (rest) { + argv2 = arg_parse(rest, filename, lineno); + cur2 = argv2; + while(cur2) { + ast_context_add_include2(con, cur2->data, registrar); + cur2 = cur2->next; + } + arg_free(argv2); + } + } else if (matches_keyword(cur->data, "ignorepat")) { + if (aeldebug & DEBUG_CONTEXTS) + ast_verbose("--IGNOREPAT: '%s'\n", cur->data); + parse_keyword(cur->data, &rest); + if (rest) { + argv2 = arg_parse(rest, filename, lineno); + cur2 = argv2; + while(cur2) { + ast_context_add_ignorepat2(con, cur2->data, registrar); + cur2 = cur2->next; + } + arg_free(argv2); + } + } else if (matches_keyword(cur->data, "switches") || matches_keyword(cur->data, "eswitches")) { + if (aeldebug & DEBUG_CONTEXTS) + ast_verbose("--[E]SWITCH: '%s'\n", cur->data); + parse_keyword(cur->data, &rest); + if (rest) { + argv2 = arg_parse(rest, filename, lineno); + cur2 = argv2; + while(cur2) { + c = strchr(cur2->data, '/'); + if (c) { + *c = '\0'; + c++; + } else + c = ""; + ast_context_add_switch2(con, cur2->data, c, (cur->data[0] == 'e'), registrar); + cur2 = cur2->next; + } + arg_free(argv2); + } + } else if (matches_extension(cur->data, &rest)) { + if (aeldebug & DEBUG_CONTEXTS) + ast_verbose("Extension: '%s' => '%s'\n", cur->data, rest); + pos = 1; + build_step("extension", cur->data, filename, lineno, con, cur->data, &pos, rest, NULL, NULL); + } + cur = cur->next; + } + } else + ast_log(LOG_WARNING, "Unable to create context '%s'\n", name); + arg_free(argv); + if (vars->next) + ast_log(LOG_NOTICE, "Ignoring excess tokens in macro definition around line %d of %s!\n", lineno, filename); +} + +static int handle_root_token(struct ast_context **local_contexts, char *token, int level, const char *filename, int lineno) +{ + struct stringlink *argv, *cur; + argv = split_token(token, filename, lineno); + if (aeldebug & DEBUG_TOKENS) { + ast_verbose("Found root token '%s' at level %d (%s:%d)!\n", token, level, filename, lineno); + cur = argv; + while(cur) { + ast_verbose(" ARG => '%s'\n", cur->data); + cur = cur->next; + } + } + if (!strcasecmp(token, "globals")) { + handle_globals(argv); + } else if (!strcasecmp(token, "macro")) { + handle_macro(local_contexts, argv, filename, lineno); + } else if (!strcasecmp(token, "context")) { + handle_context(local_contexts, argv, filename, lineno); + } else { + ast_log(LOG_NOTICE, "Unknown root token '%s'\n", token); + } + arg_free(argv); + return 0; +} + + +static int ast_ael_compile(struct ast_context **local_contexts, const char *filename) +{ + char *rfilename; + char *buf, *tbuf; + int bufsiz; + FILE *f; + char *c; + char *token; + int lineno=0; + + if (filename[0] == '/') + rfilename = (char *)filename; + else { + rfilename = alloca(strlen(filename) + strlen(ast_config_AST_CONFIG_DIR) + 2); + sprintf(rfilename, "%s/%s", ast_config_AST_CONFIG_DIR, filename); + } + + f = fopen(rfilename, "r"); + if (!f) { + ast_log(LOG_WARNING, "Unable to open '%s': %s\n", rfilename, strerror(errno)); + return -1; + } + buf = malloc(4096); + if (!buf) { + ast_log(LOG_WARNING, "Out of memory!\n"); + fclose(f); + return -1; + } + buf[0] = 0; + bufsiz = 4096; + while(!feof(f)) { + if (bufsiz - strlen(buf) < 2048) { + bufsiz += 4096; + tbuf = realloc(buf, bufsiz); + if (tbuf) { + buf = tbuf; + } else { + free(buf); + ast_log(LOG_WARNING, "Out of memory!\n"); + fclose(f); + } + } + if (fgets(buf + strlen(buf), bufsiz - strlen(buf), f)) { + lineno++; + while(*buf && buf[strlen(buf) - 1] < 33) + buf[strlen(buf) - 1] = '\0'; + c = strstr(buf, "//"); + if (c) + *c = '\0'; + if (*buf) { + if (aeldebug & DEBUG_READ) + ast_verbose("Newly composed line '%s'\n", buf); + while((token = grab_token(buf, filename, lineno))) { + handle_root_token(local_contexts, token, 0, filename, lineno); + free(token); + } + } + } + }; + free(buf); + fclose(f); + return 0; +} + +static int pbx_load_module(void) +{ + struct ast_context *local_contexts=NULL, *con; + ast_ael_compile(&local_contexts, config); + ast_merge_contexts_and_delete(&local_contexts, registrar); + for (con = ast_walk_contexts(NULL); con; con = ast_walk_contexts(con)) + ast_context_verify_includes(con); + + return 0; +} + +/* CLI interface */ +static int ael_debug_read(int fd, int argc, char *argv[]) +{ + aeldebug |= DEBUG_READ; + return 0; +} + +static int ael_debug_tokens(int fd, int argc, char *argv[]) +{ + aeldebug |= DEBUG_TOKENS; + return 0; +} + +static int ael_debug_macros(int fd, int argc, char *argv[]) +{ + aeldebug |= DEBUG_MACROS; + return 0; +} + +static int ael_debug_contexts(int fd, int argc, char *argv[]) +{ + aeldebug |= DEBUG_CONTEXTS; + return 0; +} + +static int ael_no_debug(int fd, int argc, char *argv[]) +{ + aeldebug = 0; + return 0; +} + +static int ael_reload(int fd, int argc, char *argv[]) +{ + ast_context_destroy(NULL, registrar); + return (pbx_load_module()); +} + +static struct ast_cli_entry ael_cli[] = { + { { "ael", "reload", NULL }, ael_reload, "Reload AEL configuration"}, + { { "ael", "debug", "read", NULL }, ael_debug_read, "Enable AEL read debug"}, + { { "ael", "debug", "tokens", NULL }, ael_debug_tokens, "Enable AEL tokens debug"}, + { { "ael", "debug", "macros", NULL }, ael_debug_macros, "Enable AEL macros debug"}, + { { "ael", "debug", "contexts", NULL }, ael_debug_contexts, "Enable AEL contexts debug"}, + { { "ael", "no", "debug", NULL }, ael_no_debug, "Disable AEL debug messages"}, +}; + +/* + * Standard module functions ... + */ +int unload_module(void) +{ + ast_context_destroy(NULL, registrar); + ast_cli_unregister_multiple(ael_cli, sizeof(ael_cli)/ sizeof(ael_cli[0])); + return 0; +} + + +int load_module(void) +{ + ast_cli_register_multiple(ael_cli, sizeof(ael_cli)/ sizeof(ael_cli[0])); + return (pbx_load_module()); +} + +int reload(void) +{ + ast_context_destroy(NULL, registrar); + return pbx_load_module(); +} + +int usecount(void) +{ + return 0; +} + +char *description(void) +{ + return dtext; +} + +char *key(void) +{ + return ASTERISK_GPL_KEY; +} |