Merged revisions 144924-144925 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 #include "asterisk/pbx.h"
40 #include "asterisk/config.h"
41 #include "asterisk/module.h"
42 #include "asterisk/logger.h"
43 #include "asterisk/cli.h"
44 #include "asterisk/app.h"
45 #include "asterisk/callerid.h"
46 #include "asterisk/hashtab.h"
47 #include "asterisk/ael_structs.h"
48 #include "asterisk/pval.h"
49 #ifdef AAL_ARGCHECK
50 #include "asterisk/argdesc.h"
51 #endif
52
53 /* these functions are in ../ast_expr2.fl */
54
55 #define DEBUG_READ   (1 << 0)
56 #define DEBUG_TOKENS (1 << 1)
57 #define DEBUG_MACROS (1 << 2)
58 #define DEBUG_CONTEXTS (1 << 3)
59
60 static char *config = "extensions.ael";
61 static char *registrar = "pbx_ael";
62 static int pbx_load_module(void);
63
64 #ifndef AAL_ARGCHECK
65 /* for the time being, short circuit all the AAL related structures
66    without permanently removing the code; after/during the AAL 
67    development, this code can be properly re-instated 
68 */
69
70 #endif
71
72 #ifdef AAL_ARGCHECK
73 int option_matches_j( struct argdesc *should, pval *is, struct argapp *app);
74 int option_matches( struct argdesc *should, pval *is, struct argapp *app);
75 int ael_is_funcname(char *name);
76 #endif
77
78 int check_app_args(pval *appcall, pval *arglist, struct argapp *app);
79 void check_pval(pval *item, struct argapp *apps, int in_globals);
80 void check_pval_item(pval *item, struct argapp *apps, int in_globals);
81 void check_switch_expr(pval *item, struct argapp *apps);
82 void ast_expr_register_extra_error_info(char *errmsg);
83 void ast_expr_clear_extra_error_info(void);
84 struct pval *find_macro(char *name);
85 struct pval *find_context(char *name);
86 struct pval *find_context(char *name);
87 struct pval *find_macro(char *name);
88 struct ael_priority *new_prio(void);
89 struct ael_extension *new_exten(void);
90 void destroy_extensions(struct ael_extension *exten);
91 void set_priorities(struct ael_extension *exten);
92 void add_extensions(struct ael_extension *exten);
93 void ast_compile_ael2(struct ast_context **local_contexts, struct ast_hashtab *local_table, struct pval *root);
94 void destroy_pval(pval *item);
95 void destroy_pval_item(pval *item);
96 int is_float(char *arg );
97 int is_int(char *arg );
98 int is_empty(char *arg);
99
100 /* static void substitute_commas(char *str); */
101
102 static int aeldebug = 0;
103
104 /* interface stuff */
105
106 /* if all the below are static, who cares if they are present? */
107
108 static int pbx_load_module(void)
109 {
110         int errs=0, sem_err=0, sem_warn=0, sem_note=0;
111         char *rfilename;
112         struct ast_context *local_contexts=NULL, *con;
113         struct ast_hashtab *local_table=NULL;
114         
115         struct pval *parse_tree;
116
117         ast_log(LOG_NOTICE, "Starting AEL load process.\n");
118         if (config[0] == '/')
119                 rfilename = (char *)config;
120         else {
121                 rfilename = alloca(strlen(config) + strlen(ast_config_AST_CONFIG_DIR) + 2);
122                 sprintf(rfilename, "%s/%s", ast_config_AST_CONFIG_DIR, config);
123         }
124         if (access(rfilename,R_OK) != 0) {
125                 ast_log(LOG_NOTICE, "File %s not found; AEL declining load\n", rfilename);
126                 return AST_MODULE_LOAD_DECLINE;
127         }
128         
129         parse_tree = ael2_parse(rfilename, &errs);
130         ast_log(LOG_NOTICE, "AEL load process: parsed config file name '%s'.\n", rfilename);
131         ael2_semantic_check(parse_tree, &sem_err, &sem_warn, &sem_note);
132         if (errs == 0 && sem_err == 0) {
133                 ast_log(LOG_NOTICE, "AEL load process: checked config file name '%s'.\n", rfilename);
134                 local_table = ast_hashtab_create(11, ast_hashtab_compare_contexts, ast_hashtab_resize_java, ast_hashtab_newsize_java, ast_hashtab_hash_contexts, 0);
135                 ast_compile_ael2(&local_contexts, local_table, parse_tree);
136                 ast_log(LOG_NOTICE, "AEL load process: compiled config file name '%s'.\n", rfilename);
137                 
138                 ast_merge_contexts_and_delete(&local_contexts, local_table, registrar);
139                 local_table = NULL; /* it's the dialplan global now */
140                 local_contexts = NULL;
141                 ast_log(LOG_NOTICE, "AEL load process: merged config file name '%s'.\n", rfilename);
142                 for (con = ast_walk_contexts(NULL); con; con = ast_walk_contexts(con))
143                         ast_context_verify_includes(con);
144                 ast_log(LOG_NOTICE, "AEL load process: verified config file name '%s'.\n", rfilename);
145         } else {
146                 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);
147                 destroy_pval(parse_tree); /* free up the memory */
148                 return AST_MODULE_LOAD_DECLINE;
149         }
150         destroy_pval(parse_tree); /* free up the memory */
151         
152         return AST_MODULE_LOAD_SUCCESS;
153 }
154
155 /* CLI interface */
156 static char *handle_cli_ael_debug_multiple_deprecated(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
157 {
158         switch (cmd) {
159         case CLI_INIT:
160                 e->command = "ael debug [read|tokens|macros|contexts|off]";
161                 e->usage =
162                         "Usage: ael debug [read|tokens|macros|contexts|off]\n"
163                         "       Enable AEL read, token, macro, or context debugging,\n"
164                         "       or disable all AEL debugging messages.  Note: this\n"
165                         "       currently does nothing.\n";
166                 return NULL;
167         case CLI_GENERATE:
168                 return NULL;
169         }
170
171         if (a->argc != 3)
172                 return CLI_SHOWUSAGE;
173
174         if (!strcasecmp(a->argv[2], "read"))
175                 aeldebug |= DEBUG_READ;
176         else if (!strcasecmp(a->argv[2], "tokens"))
177                 aeldebug |= DEBUG_TOKENS;
178         else if (!strcasecmp(a->argv[2], "macros"))
179                 aeldebug |= DEBUG_MACROS;
180         else if (!strcasecmp(a->argv[2], "contexts"))
181                 aeldebug |= DEBUG_CONTEXTS;
182         else if (!strcasecmp(a->argv[2], "off"))
183                 aeldebug = 0;
184         else
185                 return CLI_SHOWUSAGE;
186
187         return CLI_SUCCESS;
188 }
189
190 static char *handle_cli_ael_set_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
191 {
192         switch (cmd) {
193         case CLI_INIT:
194                 e->command = "ael set debug {read|tokens|macros|contexts|off}";
195                 e->usage =
196                         "Usage: ael debug {read|tokens|macros|contexts|off}\n"
197                         "       Enable AEL read, token, macro, or context debugging,\n"
198                         "       or disable all AEL debugging messages.  Note: this\n"
199                         "       currently does nothing.\n";
200                 return NULL;
201         case CLI_GENERATE:
202                 return NULL;
203         }
204
205         if (a->argc != e->args)
206                 return CLI_SHOWUSAGE;
207
208         if (!strcasecmp(a->argv[3], "read"))
209                 aeldebug |= DEBUG_READ;
210         else if (!strcasecmp(a->argv[3], "tokens"))
211                 aeldebug |= DEBUG_TOKENS;
212         else if (!strcasecmp(a->argv[3], "macros"))
213                 aeldebug |= DEBUG_MACROS;
214         else if (!strcasecmp(a->argv[3], "contexts"))
215                 aeldebug |= DEBUG_CONTEXTS;
216         else if (!strcasecmp(a->argv[3], "off"))
217                 aeldebug = 0;
218         else
219                 return CLI_SHOWUSAGE;
220
221         return CLI_SUCCESS;
222 }
223
224 static char *handle_cli_ael_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
225 {
226         switch (cmd) {
227         case CLI_INIT:
228                 e->command = "ael reload";
229                 e->usage =
230                         "Usage: ael reload\n"
231                         "       Reloads AEL configuration.\n";
232                 return NULL;
233         case CLI_GENERATE:
234                 return NULL;
235         }
236
237         if (a->argc != 2)
238                 return CLI_SHOWUSAGE;
239
240         return (pbx_load_module() ? CLI_FAILURE : CLI_SUCCESS);
241 }
242
243 static struct ast_cli_entry cli_ael_debug_multiple_deprecated = AST_CLI_DEFINE(handle_cli_ael_debug_multiple_deprecated, "Enable AEL debugging flags");
244 static struct ast_cli_entry cli_ael[] = {
245         AST_CLI_DEFINE(handle_cli_ael_reload,    "Reload AEL configuration"),
246         AST_CLI_DEFINE(handle_cli_ael_set_debug, "Enable AEL debugging flags", .deprecate_cmd = &cli_ael_debug_multiple_deprecated)
247 };
248
249 static int unload_module(void)
250 {
251         ast_context_destroy(NULL, registrar);
252         ast_cli_unregister_multiple(cli_ael, sizeof(cli_ael) / sizeof(struct ast_cli_entry));
253         return 0;
254 }
255
256 static int load_module(void)
257 {
258         ast_cli_register_multiple(cli_ael, sizeof(cli_ael) / sizeof(struct ast_cli_entry));
259         return (pbx_load_module());
260 }
261
262 static int reload(void)
263 {
264         return pbx_load_module();
265 }
266
267 #ifdef STANDALONE
268 #define AST_MODULE "ael"
269 int ael_external_load_module(void);
270 int ael_external_load_module(void)
271 {
272         pbx_load_module();
273         return 1;
274 }
275 #endif
276
277 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Asterisk Extension Language Compiler",
278                 .load = load_module,
279                 .unload = unload_module,
280                 .reload = reload,
281                );
282
283 #ifdef AAL_ARGCHECK
284 static char *ael_funclist[] =
285 {
286         "AGENT",
287         "ARRAY",
288         "BASE64_DECODE",
289         "BASE64_ENCODE",
290         "CALLERID",
291         "CDR",
292         "CHANNEL",
293         "CHECKSIPDOMAIN",
294         "CHECK_MD5",
295         "CURL",
296         "CUT",
297         "DB",
298         "DB_EXISTS",
299         "DUNDILOOKUP",
300         "ENUMLOOKUP",
301         "ENV",
302         "EVAL",
303         "EXISTS",
304         "FIELDQTY",
305         "FILTER",
306         "GROUP",
307         "GROUP_COUNT",
308         "GROUP_LIST",
309         "GROUP_MATCH_COUNT",
310         "IAXPEER",
311         "IF",
312         "IFTIME",
313         "ISNULL",
314         "KEYPADHASH",
315         "LANGUAGE",
316         "LEN",
317         "MATH",
318         "MD5",
319         "MUSICCLASS",
320         "QUEUEAGENTCOUNT",
321         "QUEUE_MEMBER_COUNT",
322         "QUEUE_MEMBER_LIST",
323         "QUOTE",
324         "RAND",
325         "REGEX",
326         "SET",
327         "SHA1",
328         "SIPCHANINFO",
329         "SIPPEER",
330         "SIP_HEADER",
331         "SORT",
332         "STAT",
333         "STRFTIME",
334         "STRPTIME",
335         "TIMEOUT",
336         "TXTCIDNAME",
337         "URIDECODE",
338         "URIENCODE",
339         "VMCOUNT"
340 };
341
342
343 int ael_is_funcname(char *name)
344 {
345         int s,t;
346         t = sizeof(ael_funclist)/sizeof(char*);
347         s = 0;
348         while ((s < t) && strcasecmp(name, ael_funclist[s])) 
349                 s++;
350         if ( s < t )
351                 return 1;
352         else
353                 return 0;
354 }
355 #endif