Fixes Park() argument handling
[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 "asterisk/file.h"
31 #include "asterisk/logger.h"
32 #include "asterisk/channel.h"
33 #include "asterisk/config.h"
34 #include "asterisk/pbx.h"
35 #include "asterisk/module.h"
36 #include "asterisk/frame.h"
37 #include "asterisk/term.h"
38 #include "asterisk/manager.h"
39 #include "asterisk/cli.h"
40 #include "asterisk/lock.h"
41 #include "asterisk/md5.h"
42 #include "asterisk/linkedlists.h"
43 #include "asterisk/chanvars.h"
44 #include "asterisk/sched.h"
45 #include "asterisk/io.h"
46 #include "asterisk/utils.h"
47 #include "asterisk/crypto.h"
48 #include "asterisk/astdb.h"
49 #include "asterisk/app.h"
50
51 #define MODE_MATCH              0
52 #define MODE_MATCHMORE  1
53 #define MODE_CANMATCH   2
54
55 #define EXT_DATA_SIZE 256
56
57 enum option_flags {
58         OPTION_PATTERNS_DISABLED = (1 << 0),
59 };
60
61 AST_APP_OPTIONS(switch_opts, {
62         AST_APP_OPTION('p', OPTION_PATTERNS_DISABLED),
63 });
64
65 /* Realtime switch looks up extensions in the supplied realtime table.
66
67         [context@][realtimetable][/options]
68
69         If the realtimetable is omitted it is assumed to be "extensions".  If no context is 
70         specified the context is assumed to be whatever is the container.
71
72         The realtime table should have entries for context,exten,priority,app,args
73         
74         The realtime table currently does not support callerid fields.
75
76 */
77
78
79 static struct ast_variable *realtime_switch_common(const char *table, const char *context, const char *exten, int priority, int mode, struct ast_flags flags)
80 {
81         struct ast_variable *var;
82         struct ast_config *cfg;
83         char pri[20];
84         char *ematch;
85         char rexten[AST_MAX_EXTENSION + 20]="";
86         int match;
87         /* Optimization: since we don't support hints in realtime, it's silly to
88          * query for a hint here, since we won't actually do anything with it.
89          * This just wastes CPU time and resources. */
90         if (priority < 0) {
91                 return NULL;
92         }
93         snprintf(pri, sizeof(pri), "%d", priority);
94         switch(mode) {
95         case MODE_MATCHMORE:
96                 ematch = "exten LIKE";
97                 snprintf(rexten, sizeof(rexten), "%s_%%", exten);
98                 break;
99         case MODE_CANMATCH:
100                 ematch = "exten LIKE";
101                 snprintf(rexten, sizeof(rexten), "%s%%", exten);
102                 break;
103         case MODE_MATCH:
104         default:
105                 ematch = "exten";
106                 ast_copy_string(rexten, exten, sizeof(rexten));
107         }
108         var = ast_load_realtime(table, ematch, rexten, "context", context, "priority", pri, SENTINEL);
109         if (!var && !ast_test_flag(&flags, OPTION_PATTERNS_DISABLED)) {
110                 cfg = ast_load_realtime_multientry(table, "exten LIKE", "\\_%", "context", context, "priority", pri, SENTINEL); 
111                 if (cfg) {
112                         char *cat = ast_category_browse(cfg, NULL);
113
114                         while(cat) {
115                                 switch(mode) {
116                                 case MODE_MATCHMORE:
117                                         match = ast_extension_close(cat, exten, 1);
118                                         break;
119                                 case MODE_CANMATCH:
120                                         match = ast_extension_close(cat, exten, 0);
121                                         break;
122                                 case MODE_MATCH:
123                                 default:
124                                         match = ast_extension_match(cat, exten);
125                                 }
126                                 if (match) {
127                                         var = ast_category_detach_variables(ast_category_get(cfg, cat));
128                                         break;
129                                 }
130                                 cat = ast_category_browse(cfg, cat);
131                         }
132                         ast_config_destroy(cfg);
133                 }
134         }
135         return var;
136 }
137
138 static struct ast_variable *realtime_common(const char *context, const char *exten, int priority, const char *data, int mode)
139 {
140         const char *ctx = NULL;
141         char *table;
142         struct ast_variable *var=NULL;
143         struct ast_flags flags = { 0, };
144         char *buf = ast_strdupa(data);
145         if (buf) {
146                 /* "Realtime" prefix is stripped off in the parent engine.  The
147                  * remaining string is: [[context@]table][/opts] */
148                 char *opts = strchr(buf, '/');
149                 if (opts)
150                         *opts++ = '\0';
151                 table = strchr(buf, '@');
152                 if (table) {
153                         *table++ = '\0';
154                         ctx = buf;
155                 }
156                 ctx = S_OR(ctx, context);
157                 table = S_OR(table, "extensions");
158                 if (!ast_strlen_zero(opts)) {
159                         ast_app_parse_options(switch_opts, &flags, NULL, opts);
160                 }
161                 var = realtime_switch_common(table, ctx, exten, priority, mode, flags);
162         }
163         return var;
164 }
165
166 static int realtime_exists(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
167 {
168         struct ast_variable *var = realtime_common(context, exten, priority, data, MODE_MATCH);
169         if (var) {
170                 ast_variables_destroy(var);
171                 return 1;
172         }
173         return 0;
174 }
175
176 static int realtime_canmatch(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
177 {
178         struct ast_variable *var = realtime_common(context, exten, priority, data, MODE_CANMATCH);
179         if (var) {
180                 ast_variables_destroy(var);
181                 return 1;
182         }
183         return 0;
184 }
185
186 static int realtime_exec(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
187 {
188         int res = -1;
189         struct ast_variable *var = realtime_common(context, exten, priority, data, MODE_MATCH);
190
191         if (var) {
192                 char *tmp="";
193                 char *app = NULL;
194                 struct ast_variable *v;
195
196                 for (v = var; v ; v = v->next) {
197                         if (!strcasecmp(v->name, "app"))
198                                 app = ast_strdupa(v->value);
199                         else if (!strcasecmp(v->name, "appdata")) {
200                                 if (ast_compat_pbx_realtime) {
201                                         char *ptr;
202                                         int in = 0;
203                                         tmp = alloca(strlen(v->value) * 2 + 1);
204                                         for (ptr = tmp; *v->value; v->value++) {
205                                                 if (*v->value == ',') {
206                                                         *ptr++ = '\\';
207                                                         *ptr++ = ',';
208                                                 } else if (*v->value == '|' && !in) {
209                                                         *ptr++ = ',';
210                                                 } else {
211                                                         *ptr++ = *v->value;
212                                                 }
213
214                                                 /* Don't escape '|', meaning 'or', inside expressions ($[ ]) */
215                                                 if (v->value[0] == '[' && v->value[-1] == '$') {
216                                                         in++;
217                                                 } else if (v->value[0] == ']' && in) {
218                                                         in--;
219                                                 }
220                                         }
221                                         *ptr = '\0';
222                                 } else {
223                                         tmp = ast_strdupa(v->value);
224                                 }
225                         }
226                 }
227                 ast_variables_destroy(var);
228                 if (!ast_strlen_zero(app)) {
229                         struct ast_app *a = pbx_findapp(app);
230                         if (a) {
231                                 char appdata[512];
232                                 char tmp1[80];
233                                 char tmp2[80];
234                                 char tmp3[EXT_DATA_SIZE];
235
236                                 appdata[0] = 0; /* just in case the substitute var func isn't called */
237                                 if(!ast_strlen_zero(tmp))
238                                         pbx_substitute_variables_helper(chan, tmp, appdata, sizeof(appdata) - 1);
239                                 ast_verb(3, "Executing %s(\"%s\", \"%s\")\n",
240                                                  term_color(tmp1, app, COLOR_BRCYAN, 0, sizeof(tmp1)),
241                                                  term_color(tmp2, chan->name, COLOR_BRMAGENTA, 0, sizeof(tmp2)),
242                                                  term_color(tmp3, S_OR(appdata, ""), COLOR_BRMAGENTA, 0, sizeof(tmp3)));
243                                 manager_event(EVENT_FLAG_DIALPLAN, "Newexten",
244                                                           "Channel: %s\r\n"
245                                                           "Context: %s\r\n"
246                                                           "Extension: %s\r\n"
247                                                           "Priority: %d\r\n"
248                                                           "Application: %s\r\n"
249                                                           "AppData: %s\r\n"
250                                                           "Uniqueid: %s\r\n",
251                                                           chan->name, chan->context, chan->exten, chan->priority, app, !ast_strlen_zero(appdata) ? appdata : "(NULL)", chan->uniqueid);
252                                 
253                                 res = pbx_exec(chan, a, appdata);
254                         } else
255                                 ast_log(LOG_NOTICE, "No such application '%s' for extension '%s' in context '%s'\n", app, exten, context);
256                 } else {
257                         ast_log(LOG_WARNING, "No application specified for realtime extension '%s' in context '%s'\n", exten, context);
258                 }
259         }
260         return res;
261 }
262
263 static int realtime_matchmore(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
264 {
265         struct ast_variable *var = realtime_common(context, exten, priority, data, MODE_MATCHMORE);
266         if (var) {
267                 ast_variables_destroy(var);
268                 return 1;
269         }
270         return 0;
271 }
272
273 static struct ast_switch realtime_switch =
274 {
275         name:                   "Realtime",
276         description:            "Realtime Dialplan Switch",
277         exists:                 realtime_exists,
278         canmatch:               realtime_canmatch,
279         exec:                   realtime_exec,
280         matchmore:              realtime_matchmore,
281 };
282
283 static int unload_module(void)
284 {
285         ast_unregister_switch(&realtime_switch);
286         return 0;
287 }
288
289 static int load_module(void)
290 {
291         if (ast_register_switch(&realtime_switch))
292                 return AST_MODULE_LOAD_FAILURE;
293         return AST_MODULE_LOAD_SUCCESS;
294 }
295
296 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Realtime Switch");