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