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