Add a compatibility option for upgrading realtime extensions
[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                                         tmp = alloca(strlen(v->value) * 2 + 1);
185                                         for (ptr = tmp; *v->value; v->value++) {
186                                                 if (*v->value == ',') {
187                                                         *ptr++ = '\\';
188                                                         *ptr++ = ',';
189                                                 } else if (*v->value == '|') {
190                                                         *ptr++ = ',';
191                                                 } else {
192                                                         *ptr++ = *v->value;
193                                                 }
194                                         }
195                                 } else {
196                                         tmp = ast_strdupa(v->value);
197                                 }
198                         }
199                 }
200                 ast_variables_destroy(var);
201                 if (!ast_strlen_zero(app)) {
202                         struct ast_app *a = pbx_findapp(app);
203                         if (a) {
204                                 char appdata[512];
205                                 char tmp1[80];
206                                 char tmp2[80];
207                                 char tmp3[EXT_DATA_SIZE];
208
209                                 appdata[0] = 0; /* just in case the substitute var func isn't called */
210                                 if(!ast_strlen_zero(tmp))
211                                         pbx_substitute_variables_helper(chan, tmp, appdata, sizeof(appdata) - 1);
212                                 ast_verb(3, "Executing %s(\"%s\", \"%s\")\n",
213                                                  term_color(tmp1, app, COLOR_BRCYAN, 0, sizeof(tmp1)),
214                                                  term_color(tmp2, chan->name, COLOR_BRMAGENTA, 0, sizeof(tmp2)),
215                                                  term_color(tmp3, S_OR(appdata, ""), COLOR_BRMAGENTA, 0, sizeof(tmp3)));
216                                 manager_event(EVENT_FLAG_DIALPLAN, "Newexten",
217                                                           "Channel: %s\r\n"
218                                                           "Context: %s\r\n"
219                                                           "Extension: %s\r\n"
220                                                           "Priority: %d\r\n"
221                                                           "Application: %s\r\n"
222                                                           "AppData: %s\r\n"
223                                                           "Uniqueid: %s\r\n",
224                                                           chan->name, chan->context, chan->exten, chan->priority, app, !ast_strlen_zero(appdata) ? appdata : "(NULL)", chan->uniqueid);
225                                 
226                                 res = pbx_exec(chan, a, appdata);
227                         } else
228                                 ast_log(LOG_NOTICE, "No such application '%s' for extension '%s' in context '%s'\n", app, exten, context);
229                 } else {
230                         ast_log(LOG_WARNING, "No application specified for realtime extension '%s' in context '%s'\n", exten, context);
231                 }
232         }
233         return res;
234 }
235
236 static int realtime_matchmore(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
237 {
238         struct ast_variable *var = realtime_common(context, exten, priority, data, MODE_MATCHMORE);
239         if (var) {
240                 ast_variables_destroy(var);
241                 return 1;
242         }
243         return 0;
244 }
245
246 static struct ast_switch realtime_switch =
247 {
248         name:                   "Realtime",
249         description:            "Realtime Dialplan Switch",
250         exists:                 realtime_exists,
251         canmatch:               realtime_canmatch,
252         exec:                   realtime_exec,
253         matchmore:              realtime_matchmore,
254 };
255
256 static int unload_module(void)
257 {
258         ast_unregister_switch(&realtime_switch);
259         return 0;
260 }
261
262 static int load_module(void)
263 {
264         struct ast_flags flags = { 0 };
265         struct ast_config *cfg = ast_config_load("pbx_realtime.conf", flags);
266         if (cfg) {
267                 const char *tmp = ast_variable_retrieve(cfg, "general", "compat");
268                 if (tmp && strncmp(tmp, "1.6", 3)) {
269                         compat16 = 0;
270                 } else {
271                         compat16 = 1;
272                 }
273                 ast_config_destroy(cfg);
274         }
275
276         if (ast_register_switch(&realtime_switch))
277                 return AST_MODULE_LOAD_FAILURE;
278         return AST_MODULE_LOAD_SUCCESS;
279 }
280
281 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Realtime Switch");