Merged revisions 271399 via svnmerge from
[asterisk/asterisk.git] / pbx / pbx_ael.c
1 /* 
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2006, Digium, Inc.
5  *
6  * Steve Murphy <murf@parsetree.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19 /*! \file
20  *
21  * \brief Compile symbolic Asterisk Extension Logic into Asterisk extensions, version 2.
22  * 
23  */
24
25 /*** MODULEINFO
26         <depend>res_ael_share</depend>
27  ***/
28
29 #include "asterisk.h"
30
31 #if !defined(STANDALONE)
32 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
33 #endif
34
35 #include <ctype.h>
36 #include <regex.h>
37 #include <sys/stat.h>
38
39 #ifdef STANDALONE
40 #ifdef HAVE_MTX_PROFILE
41 static int mtx_prof = -1; /* helps the standalone compile with the mtx_prof flag on */
42 #endif
43 #endif
44 #include "asterisk/pbx.h"
45 #include "asterisk/config.h"
46 #include "asterisk/module.h"
47 #include "asterisk/logger.h"
48 #include "asterisk/cli.h"
49 #include "asterisk/app.h"
50 #include "asterisk/callerid.h"
51 #include "asterisk/hashtab.h"
52 #include "asterisk/ael_structs.h"
53 #include "asterisk/pval.h"
54 #ifdef AAL_ARGCHECK
55 #include "asterisk/argdesc.h"
56 #endif
57
58 /* these functions are in ../ast_expr2.fl */
59
60 #define DEBUG_READ   (1 << 0)
61 #define DEBUG_TOKENS (1 << 1)
62 #define DEBUG_MACROS (1 << 2)
63 #define DEBUG_CONTEXTS (1 << 3)
64
65 static char *config = "extensions.ael";
66 static char *registrar = "pbx_ael";
67 static int pbx_load_module(void);
68
69 #ifndef AAL_ARGCHECK
70 /* for the time being, short circuit all the AAL related structures
71    without permanently removing the code; after/during the AAL 
72    development, this code can be properly re-instated 
73 */
74
75 #endif
76
77 #ifdef AAL_ARGCHECK
78 int option_matches_j( struct argdesc *should, pval *is, struct argapp *app);
79 int option_matches( struct argdesc *should, pval *is, struct argapp *app);
80 int ael_is_funcname(char *name);
81 #endif
82
83 int check_app_args(pval *appcall, pval *arglist, struct argapp *app);
84 void check_pval(pval *item, struct argapp *apps, int in_globals);
85 void check_pval_item(pval *item, struct argapp *apps, int in_globals);
86 void check_switch_expr(pval *item, struct argapp *apps);
87 void ast_expr_register_extra_error_info(char *errmsg);
88 void ast_expr_clear_extra_error_info(void);
89 struct pval *find_macro(char *name);
90 struct pval *find_context(char *name);
91 struct pval *find_context(char *name);
92 struct pval *find_macro(char *name);
93 struct ael_priority *new_prio(void);
94 struct ael_extension *new_exten(void);
95 void destroy_extensions(struct ael_extension *exten);
96 void set_priorities(struct ael_extension *exten);
97 void add_extensions(struct ael_extension *exten);
98 int ast_compile_ael2(struct ast_context **local_contexts, struct ast_hashtab *local_table, struct pval *root);
99 void destroy_pval(pval *item);
100 void destroy_pval_item(pval *item);
101 int is_float(char *arg );
102 int is_int(char *arg );
103 int is_empty(char *arg);
104
105 /* static void substitute_commas(char *str); */
106
107 static int aeldebug = 0;
108
109 /* interface stuff */
110
111 /* if all the below are static, who cares if they are present? */
112
113 static int pbx_load_module(void)
114 {
115         int errs=0, sem_err=0, sem_warn=0, sem_note=0;
116         char *rfilename;
117         struct ast_context *local_contexts=NULL, *con;
118         struct ast_hashtab *local_table=NULL;
119         
120         struct pval *parse_tree;
121
122         ast_log(LOG_NOTICE, "Starting AEL load process.\n");
123         if (config[0] == '/')
124                 rfilename = (char *)config;
125         else {
126                 rfilename = alloca(strlen(config) + strlen(ast_config_AST_CONFIG_DIR) + 2);
127                 sprintf(rfilename, "%s/%s", ast_config_AST_CONFIG_DIR, config);
128         }
129         if (access(rfilename,R_OK) != 0) {
130                 ast_log(LOG_NOTICE, "File %s not found; AEL declining load\n", rfilename);
131                 return AST_MODULE_LOAD_DECLINE;
132         }
133         
134         parse_tree = ael2_parse(rfilename, &errs);
135         ast_log(LOG_NOTICE, "AEL load process: parsed config file name '%s'.\n", rfilename);
136         ael2_semantic_check(parse_tree, &sem_err, &sem_warn, &sem_note);
137         if (errs == 0 && sem_err == 0) {
138                 ast_log(LOG_NOTICE, "AEL load process: checked config file name '%s'.\n", rfilename);
139                 local_table = ast_hashtab_create(11, ast_hashtab_compare_contexts, ast_hashtab_resize_java, ast_hashtab_newsize_java, ast_hashtab_hash_contexts, 0);
140                 if (ast_compile_ael2(&local_contexts, local_table, parse_tree)) {
141                         ast_log(LOG_ERROR, "AEL compile failed! Aborting.\n");
142                         destroy_pval(parse_tree); /* free up the memory */
143                         return AST_MODULE_LOAD_DECLINE;
144                 }
145                 ast_log(LOG_NOTICE, "AEL load process: compiled config file name '%s'.\n", rfilename);
146                 
147                 ast_merge_contexts_and_delete(&local_contexts, local_table, registrar);
148                 local_table = NULL; /* it's the dialplan global now */
149                 local_contexts = NULL;
150                 ast_log(LOG_NOTICE, "AEL load process: merged config file name '%s'.\n", rfilename);
151                 for (con = ast_walk_contexts(NULL); con; con = ast_walk_contexts(con))
152                         ast_context_verify_includes(con);
153                 ast_log(LOG_NOTICE, "AEL load process: verified config file name '%s'.\n", rfilename);
154         } else {
155                 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);
156                 destroy_pval(parse_tree); /* free up the memory */
157                 return AST_MODULE_LOAD_DECLINE;
158         }
159         destroy_pval(parse_tree); /* free up the memory */
160         
161         return AST_MODULE_LOAD_SUCCESS;
162 }
163
164 /* CLI interface */
165 static char *handle_cli_ael_set_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
166 {
167         switch (cmd) {
168         case CLI_INIT:
169                 e->command = "ael set debug {read|tokens|macros|contexts|off}";
170                 e->usage =
171                         "Usage: ael set debug {read|tokens|macros|contexts|off}\n"
172                         "       Enable AEL read, token, macro, or context debugging,\n"
173                         "       or disable all AEL debugging messages.  Note: this\n"
174                         "       currently does nothing.\n";
175                 return NULL;
176         case CLI_GENERATE:
177                 return NULL;
178         }
179
180         if (a->argc != e->args)
181                 return CLI_SHOWUSAGE;
182
183         if (!strcasecmp(a->argv[3], "read"))
184                 aeldebug |= DEBUG_READ;
185         else if (!strcasecmp(a->argv[3], "tokens"))
186                 aeldebug |= DEBUG_TOKENS;
187         else if (!strcasecmp(a->argv[3], "macros"))
188                 aeldebug |= DEBUG_MACROS;
189         else if (!strcasecmp(a->argv[3], "contexts"))
190                 aeldebug |= DEBUG_CONTEXTS;
191         else if (!strcasecmp(a->argv[3], "off"))
192                 aeldebug = 0;
193         else
194                 return CLI_SHOWUSAGE;
195
196         return CLI_SUCCESS;
197 }
198
199 static char *handle_cli_ael_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
200 {
201         switch (cmd) {
202         case CLI_INIT:
203                 e->command = "ael reload";
204                 e->usage =
205                         "Usage: ael reload\n"
206                         "       Reloads AEL configuration.\n";
207                 return NULL;
208         case CLI_GENERATE:
209                 return NULL;
210         }
211
212         if (a->argc != 2)
213                 return CLI_SHOWUSAGE;
214
215         return (pbx_load_module() ? CLI_FAILURE : CLI_SUCCESS);
216 }
217
218 static struct ast_cli_entry cli_ael[] = {
219         AST_CLI_DEFINE(handle_cli_ael_reload,    "Reload AEL configuration"),
220         AST_CLI_DEFINE(handle_cli_ael_set_debug, "Enable AEL debugging flags")
221 };
222
223 static int unload_module(void)
224 {
225         ast_context_destroy(NULL, registrar);
226         ast_cli_unregister_multiple(cli_ael, ARRAY_LEN(cli_ael));
227         return 0;
228 }
229
230 static int load_module(void)
231 {
232         ast_cli_register_multiple(cli_ael, ARRAY_LEN(cli_ael));
233         return (pbx_load_module());
234 }
235
236 static int reload(void)
237 {
238         return pbx_load_module();
239 }
240
241 #ifdef STANDALONE
242 #define AST_MODULE "ael"
243 int ael_external_load_module(void);
244 int ael_external_load_module(void)
245 {
246         pbx_load_module();
247         return 1;
248 }
249 #endif
250
251 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Asterisk Extension Language Compiler",
252                 .load = load_module,
253                 .unload = unload_module,
254                 .reload = reload,
255                );
256
257 #ifdef AAL_ARGCHECK
258 static const char * const ael_funclist[] =
259 {
260         "AGENT",
261         "ARRAY",
262         "BASE64_DECODE",
263         "BASE64_ENCODE",
264         "CALLERID",
265         "CDR",
266         "CHANNEL",
267         "CHECKSIPDOMAIN",
268         "CHECK_MD5",
269         "CURL",
270         "CUT",
271         "DB",
272         "DB_EXISTS",
273         "DUNDILOOKUP",
274         "ENUMLOOKUP",
275         "ENV",
276         "EVAL",
277         "EXISTS",
278         "FIELDQTY",
279         "FILTER",
280         "GROUP",
281         "GROUP_COUNT",
282         "GROUP_LIST",
283         "GROUP_MATCH_COUNT",
284         "IAXPEER",
285         "IF",
286         "IFTIME",
287         "ISNULL",
288         "KEYPADHASH",
289         "LANGUAGE",
290         "LEN",
291         "MATH",
292         "MD5",
293         "MUSICCLASS",
294         "QUEUEAGENTCOUNT",
295         "QUEUE_MEMBER_COUNT",
296         "QUEUE_MEMBER_LIST",
297         "QUOTE",
298         "RAND",
299         "REGEX",
300         "SET",
301         "SHA1",
302         "SIPCHANINFO",
303         "SIPPEER",
304         "SIP_HEADER",
305         "SORT",
306         "STAT",
307         "STRFTIME",
308         "STRPTIME",
309         "TIMEOUT",
310         "TXTCIDNAME",
311         "URIDECODE",
312         "URIENCODE",
313         "VMCOUNT"
314 };
315
316
317 int ael_is_funcname(char *name)
318 {
319         int s,t;
320         t = sizeof(ael_funclist)/sizeof(char*);
321         s = 0;
322         while ((s < t) && strcasecmp(name, ael_funclist[s])) 
323                 s++;
324         if ( s < t )
325                 return 1;
326         else
327                 return 0;
328 }
329 #endif