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