Fix misuses of asprintf throughout the code.
[asterisk/asterisk.git] / pbx / pbx_realtime.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2005, Digium, Inc.
5  *
6  * Mark Spencer <markster@digium.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 Realtime PBX Module
22  *
23  * \arg See also: \ref AstARA
24  */
25
26 /*** MODULEINFO
27         <support_level>extended</support_level>
28  ***/
29
30 #include "asterisk.h"
31
32 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
33
34 #include <signal.h>
35
36 #include "asterisk/file.h"
37 #include "asterisk/logger.h"
38 #include "asterisk/channel.h"
39 #include "asterisk/config.h"
40 #include "asterisk/pbx.h"
41 #include "asterisk/module.h"
42 #include "asterisk/frame.h"
43 #include "asterisk/term.h"
44 #include "asterisk/manager.h"
45 #include "asterisk/cli.h"
46 #include "asterisk/lock.h"
47 #include "asterisk/linkedlists.h"
48 #include "asterisk/chanvars.h"
49 #include "asterisk/sched.h"
50 #include "asterisk/io.h"
51 #include "asterisk/utils.h"
52 #include "asterisk/astdb.h"
53 #include "asterisk/app.h"
54 #include "asterisk/astobj2.h"
55
56 #define MODE_MATCH              0
57 #define MODE_MATCHMORE  1
58 #define MODE_CANMATCH   2
59
60 #define EXT_DATA_SIZE 256
61
62 enum option_flags {
63         OPTION_PATTERNS_DISABLED = (1 << 0),
64 };
65
66 AST_APP_OPTIONS(switch_opts, {
67         AST_APP_OPTION('p', OPTION_PATTERNS_DISABLED),
68 });
69
70 struct cache_entry {
71         struct timeval when;
72         struct ast_variable *var;
73         int priority;
74         char *context;
75         char exten[2];
76 };
77
78 struct ao2_container *cache;
79 pthread_t cleanup_thread = 0;
80
81 static int cache_hash(const void *obj, const int flags)
82 {
83         const struct cache_entry *e = obj;
84         return ast_str_case_hash(e->exten) + e->priority;
85 }
86
87 static int cache_cmp(void *obj, void *arg, int flags)
88 {
89         struct cache_entry *e = obj, *f = arg;
90         return e->priority != f->priority ? 0 :
91                 strcmp(e->exten, f->exten) ? 0 :
92                 strcmp(e->context, f->context) ? 0 :
93                 CMP_MATCH;
94 }
95
96 static struct ast_variable *dup_vars(struct ast_variable *v)
97 {
98         struct ast_variable *new, *list = NULL;
99         for (; v; v = v->next) {
100                 if (!(new = ast_variable_new(v->name, v->value, v->file))) {
101                         ast_variables_destroy(list);
102                         return NULL;
103                 }
104                 /* Reversed list in cache, but when we duplicate out of the cache,
105                  * it's back to correct order. */
106                 new->next = list;
107                 list = new;
108         }
109         return list;
110 }
111
112 static void free_entry(void *obj)
113 {
114         struct cache_entry *e = obj;
115         ast_variables_destroy(e->var);
116 }
117
118 static int purge_old_fn(void *obj, void *arg, int flags)
119 {
120         struct cache_entry *e = obj;
121         struct timeval *now = arg;
122         return ast_tvdiff_ms(*now, e->when) >= 1000 ? CMP_MATCH : 0;
123 }
124
125 static void *cleanup(void *unused)
126 {
127         struct timespec forever = { 999999999, 0 }, one_second = { 1, 0 };
128         struct timeval now;
129
130         for (;;) {
131                 pthread_testcancel();
132                 if (ao2_container_count(cache) == 0) {
133                         nanosleep(&forever, NULL);
134                 }
135                 pthread_testcancel();
136                 now = ast_tvnow();
137                 ao2_callback(cache, OBJ_MULTIPLE | OBJ_UNLINK | OBJ_NODATA, purge_old_fn, &now);
138                 pthread_testcancel();
139                 nanosleep(&one_second, NULL);
140         }
141
142         return NULL;
143 }
144
145
146 /* Realtime switch looks up extensions in the supplied realtime table.
147
148         [context@][realtimetable][/options]
149
150         If the realtimetable is omitted it is assumed to be "extensions".  If no context is 
151         specified the context is assumed to be whatever is the container.
152
153         The realtime table should have entries for context,exten,priority,app,args
154         
155         The realtime table currently does not support callerid fields.
156
157 */
158
159
160 static struct ast_variable *realtime_switch_common(const char *table, const char *context, const char *exten, int priority, int mode, struct ast_flags flags)
161 {
162         struct ast_variable *var;
163         struct ast_config *cfg;
164         char pri[20];
165         char *ematch;
166         char rexten[AST_MAX_EXTENSION + 20]="";
167         int match;
168         /* Optimization: since we don't support hints in realtime, it's silly to
169          * query for a hint here, since we won't actually do anything with it.
170          * This just wastes CPU time and resources. */
171         if (priority < 0) {
172                 return NULL;
173         }
174         snprintf(pri, sizeof(pri), "%d", priority);
175         switch(mode) {
176         case MODE_MATCHMORE:
177                 ematch = "exten LIKE";
178                 snprintf(rexten, sizeof(rexten), "%s_%%", exten);
179                 break;
180         case MODE_CANMATCH:
181                 ematch = "exten LIKE";
182                 snprintf(rexten, sizeof(rexten), "%s%%", exten);
183                 break;
184         case MODE_MATCH:
185         default:
186                 ematch = "exten";
187                 ast_copy_string(rexten, exten, sizeof(rexten));
188         }
189         var = ast_load_realtime(table, ematch, rexten, "context", context, "priority", pri, SENTINEL);
190         if (!var && !ast_test_flag(&flags, OPTION_PATTERNS_DISABLED)) {
191                 cfg = ast_load_realtime_multientry(table, "exten LIKE", "\\_%", "context", context, "priority", pri, SENTINEL); 
192                 if (cfg) {
193                         char *cat = ast_category_browse(cfg, NULL);
194
195                         while(cat) {
196                                 switch(mode) {
197                                 case MODE_MATCHMORE:
198                                         match = ast_extension_close(cat, exten, 1);
199                                         break;
200                                 case MODE_CANMATCH:
201                                         match = ast_extension_close(cat, exten, 0);
202                                         break;
203                                 case MODE_MATCH:
204                                 default:
205                                         match = ast_extension_match(cat, exten);
206                                 }
207                                 if (match) {
208                                         var = ast_category_detach_variables(ast_category_get(cfg, cat));
209                                         break;
210                                 }
211                                 cat = ast_category_browse(cfg, cat);
212                         }
213                         ast_config_destroy(cfg);
214                 }
215         }
216         return var;
217 }
218
219 static struct ast_variable *realtime_common(const char *context, const char *exten, int priority, const char *data, int mode)
220 {
221         const char *ctx = NULL;
222         char *table;
223         struct ast_variable *var=NULL;
224         struct ast_flags flags = { 0, };
225         struct cache_entry *ce;
226         struct {
227                 struct cache_entry ce;
228                 char exten[AST_MAX_EXTENSION];
229         } cache_search = { { .priority = priority, .context = (char *) context }, };
230         char *buf = ast_strdupa(data);
231         /* "Realtime" prefix is stripped off in the parent engine.  The
232          * remaining string is: [[context@]table][/opts] */
233         char *opts = strchr(buf, '/');
234         if (opts)
235                 *opts++ = '\0';
236         table = strchr(buf, '@');
237         if (table) {
238                 *table++ = '\0';
239                 ctx = buf;
240         }
241         ctx = S_OR(ctx, context);
242         table = S_OR(table, "extensions");
243         if (!ast_strlen_zero(opts)) {
244                 ast_app_parse_options(switch_opts, &flags, NULL, opts);
245         }
246         ast_copy_string(cache_search.exten, exten, sizeof(cache_search.exten));
247         if (mode == MODE_MATCH && (ce = ao2_find(cache, &cache_search, OBJ_POINTER))) {
248                 var = dup_vars(ce->var);
249                 ao2_ref(ce, -1);
250         } else {
251                 var = realtime_switch_common(table, ctx, exten, priority, mode, flags);
252                 do {
253                         struct ast_variable *new;
254                         /* Only cache matches */
255                         if (mode != MODE_MATCH) {
256                                 break;
257                         }
258                         if (!(new = dup_vars(var))) {
259                                 break;
260                         }
261                         if (!(ce = ao2_alloc(sizeof(*ce) + strlen(exten) + strlen(context), free_entry))) {
262                                 ast_variables_destroy(new);
263                                 break;
264                         }
265                         ce->context = ce->exten + strlen(exten) + 1;
266                         strcpy(ce->exten, exten); /* SAFE */
267                         strcpy(ce->context, context); /* SAFE */
268                         ce->priority = priority;
269                         ce->var = new;
270                         ce->when = ast_tvnow();
271                         ao2_link(cache, ce);
272                         pthread_kill(cleanup_thread, SIGURG);
273                         ao2_ref(ce, -1);
274                 } while (0);
275         }
276         return var;
277 }
278
279 static int realtime_exists(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
280 {
281         struct ast_variable *var = realtime_common(context, exten, priority, data, MODE_MATCH);
282         if (var) {
283                 ast_variables_destroy(var);
284                 return 1;
285         }
286         return 0;
287 }
288
289 static int realtime_canmatch(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
290 {
291         struct ast_variable *var = realtime_common(context, exten, priority, data, MODE_CANMATCH);
292         if (var) {
293                 ast_variables_destroy(var);
294                 return 1;
295         }
296         return 0;
297 }
298
299 static int realtime_exec(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
300 {
301         int res = -1;
302         struct ast_variable *var = realtime_common(context, exten, priority, data, MODE_MATCH);
303
304         if (var) {
305                 char *tmp="";
306                 char *app = NULL;
307                 struct ast_variable *v;
308
309                 for (v = var; v ; v = v->next) {
310                         if (!strcasecmp(v->name, "app"))
311                                 app = ast_strdupa(v->value);
312                         else if (!strcasecmp(v->name, "appdata")) {
313                                 if (ast_compat_pbx_realtime) {
314                                         char *ptr;
315                                         int in = 0;
316                                         tmp = ast_alloca(strlen(v->value) * 2 + 1);
317                                         for (ptr = tmp; *v->value; v->value++) {
318                                                 if (*v->value == ',') {
319                                                         *ptr++ = '\\';
320                                                         *ptr++ = ',';
321                                                 } else if (*v->value == '|' && !in) {
322                                                         *ptr++ = ',';
323                                                 } else {
324                                                         *ptr++ = *v->value;
325                                                 }
326
327                                                 /* Don't escape '|', meaning 'or', inside expressions ($[ ]) */
328                                                 if (v->value[0] == '[' && v->value[-1] == '$') {
329                                                         in++;
330                                                 } else if (v->value[0] == ']' && in) {
331                                                         in--;
332                                                 }
333                                         }
334                                         *ptr = '\0';
335                                 } else {
336                                         tmp = ast_strdupa(v->value);
337                                 }
338                         }
339                 }
340                 ast_variables_destroy(var);
341                 if (!ast_strlen_zero(app)) {
342                         struct ast_app *a = pbx_findapp(app);
343                         if (a) {
344                                 char appdata[512];
345                                 char tmp1[80];
346                                 char tmp2[80];
347                                 char tmp3[EXT_DATA_SIZE];
348
349                                 appdata[0] = 0; /* just in case the substitute var func isn't called */
350                                 if(!ast_strlen_zero(tmp))
351                                         pbx_substitute_variables_helper(chan, tmp, appdata, sizeof(appdata) - 1);
352                                 ast_verb(3, "Executing [%s@%s:%d] %s(\"%s\", \"%s\")\n",
353                                                 ast_channel_exten(chan), ast_channel_context(chan), ast_channel_priority(chan),
354                                                  term_color(tmp1, app, COLOR_BRCYAN, 0, sizeof(tmp1)),
355                                                  term_color(tmp2, ast_channel_name(chan), COLOR_BRMAGENTA, 0, sizeof(tmp2)),
356                                                  term_color(tmp3, S_OR(appdata, ""), COLOR_BRMAGENTA, 0, sizeof(tmp3)));
357                                 manager_event(EVENT_FLAG_DIALPLAN, "Newexten",
358                                                           "Channel: %s\r\n"
359                                                           "Context: %s\r\n"
360                                                           "Extension: %s\r\n"
361                                                           "Priority: %d\r\n"
362                                                           "Application: %s\r\n"
363                                                           "AppData: %s\r\n"
364                                                           "Uniqueid: %s\r\n",
365                                                           ast_channel_name(chan), ast_channel_context(chan), ast_channel_exten(chan), ast_channel_priority(chan), app, !ast_strlen_zero(appdata) ? appdata : "(NULL)", ast_channel_uniqueid(chan));
366                                 
367                                 res = pbx_exec(chan, a, appdata);
368                         } else
369                                 ast_log(LOG_NOTICE, "No such application '%s' for extension '%s' in context '%s'\n", app, exten, context);
370                 } else {
371                         ast_log(LOG_WARNING, "No application specified for realtime extension '%s' in context '%s'\n", exten, context);
372                 }
373         }
374         return res;
375 }
376
377 static int realtime_matchmore(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
378 {
379         struct ast_variable *var = realtime_common(context, exten, priority, data, MODE_MATCHMORE);
380         if (var) {
381                 ast_variables_destroy(var);
382                 return 1;
383         }
384         return 0;
385 }
386
387 static struct ast_switch realtime_switch =
388 {
389         .name                   = "Realtime",
390         .description            = "Realtime Dialplan Switch",
391         .exists                 = realtime_exists,
392         .canmatch               = realtime_canmatch,
393         .exec                   = realtime_exec,
394         .matchmore              = realtime_matchmore,
395 };
396
397 static int unload_module(void)
398 {
399         ast_unregister_switch(&realtime_switch);
400         pthread_cancel(cleanup_thread);
401         pthread_kill(cleanup_thread, SIGURG);
402         pthread_join(cleanup_thread, NULL);
403         /* Destroy all remaining entries */
404         ao2_ref(cache, -1);
405         return 0;
406 }
407
408 static int load_module(void)
409 {
410         if (!(cache = ao2_container_alloc(573, cache_hash, cache_cmp))) {
411                 return AST_MODULE_LOAD_FAILURE;
412         }
413
414         if (ast_pthread_create(&cleanup_thread, NULL, cleanup, NULL)) {
415                 return AST_MODULE_LOAD_FAILURE;
416         }
417
418         if (ast_register_switch(&realtime_switch))
419                 return AST_MODULE_LOAD_FAILURE;
420         return AST_MODULE_LOAD_SUCCESS;
421 }
422
423 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Realtime Switch");