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