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