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