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