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