Fix Realtime to work with MySQL and Postgres both (bug #3029)
[asterisk/asterisk.git] / pbx / pbx_realtime.c
1 /*
2  * Realtime PBX Module
3  *
4  * Copyright (C) 2004, Digium Inc.
5  *
6  * Written by Mark Spencer <markster@digium.com>
7  *
8  * This program is Free Software distributed under the terms of
9  * of the GNU General Public License.
10  */
11
12 #include <asterisk/file.h>
13 #include <asterisk/logger.h>
14 #include <asterisk/channel.h>
15 #include <asterisk/config.h>
16 #include <asterisk/config_pvt.h>
17 #include <asterisk/options.h>
18 #include <asterisk/pbx.h>
19 #include <asterisk/module.h>
20 #include <asterisk/frame.h>
21 #include <asterisk/term.h>
22 #include <asterisk/manager.h>
23 #include <asterisk/file.h>
24 #include <asterisk/cli.h>
25 #include <asterisk/lock.h>
26 #include <asterisk/md5.h>
27 #include <asterisk/linkedlists.h>
28 #include <asterisk/chanvars.h>
29 #include <asterisk/sched.h>
30 #include <asterisk/io.h>
31 #include <asterisk/utils.h>
32 #include <asterisk/crypto.h>
33 #include <asterisk/astdb.h>
34
35 #include <stdlib.h>
36 #include <unistd.h>
37 #include <string.h>
38 #include <errno.h>
39
40 #define MODE_MATCH              0
41 #define MODE_MATCHMORE  1
42 #define MODE_CANMATCH   2
43
44 #define EXT_DATA_SIZE 256
45
46 static char *tdesc = "Realtime Switch";
47
48 /* Realtime switch looks up extensions in the supplied realtime table.
49
50         [context@][realtimetable][/options]
51
52         If the realtimetable is omitted it is assumed to be "extensions".  If no context is 
53         specified the context is assumed to be whatever is the container.
54
55         The realtime table should have entries for context,exten,priority,app,args
56         
57         The realtime table currently does not support callerid fields.
58
59 */
60
61
62 #define REALTIME_COMMON(mode) \
63         char *buf; \
64         char *opts; \
65         const char *cxt; \
66         char *table; \
67         int res=-1; \
68         struct ast_variable *var=NULL; \
69         buf = ast_strdupa(data); \
70         if (buf) { \
71                 opts = strchr(buf, '/'); \
72                 if (opts) { \
73                         *opts='\0'; \
74                         opts++; \
75                 } else \
76                         opts=""; \
77                 table = strchr(buf, '@'); \
78                 if (table) { \
79                         *table = '\0'; \
80                         table++;\
81                         cxt = buf; \
82                 } else cxt = NULL; \
83                 if (!cxt || ast_strlen_zero(cxt)) \
84                         cxt = context;\
85                 if (!table || ast_strlen_zero(table)) \
86                         table = "extensions"; \
87                 var = realtime_switch_common(table, cxt, exten, priority, mode); \
88         } else \
89                 res = -1; 
90
91 static struct ast_variable *realtime_switch_common(const char *table, const char *context, const char *exten, int priority, int mode)
92 {
93         struct ast_variable *var;
94         struct ast_config *cfg;
95         struct ast_category *cat;
96         char pri[20];
97         char *ematch;
98         char rexten[AST_MAX_EXTENSION + 20]="";
99         int match;
100         snprintf(pri, sizeof(pri), "%d", priority);
101         switch(mode) {
102         case MODE_MATCHMORE:
103                 ematch = "exten LIKE";
104                 snprintf(rexten, sizeof(rexten), "%s_%%", exten);
105                 break;
106         case MODE_CANMATCH:
107                 ematch = "exten LIKE";
108                 snprintf(rexten, sizeof(rexten), "%s%%", exten);
109                 break;
110         case MODE_MATCH:
111         default:
112                 ematch = "exten";
113                 strncpy(rexten, exten, sizeof(rexten) - 1);
114         }
115         var = ast_load_realtime(table, ematch, rexten, "context", context, "priority", pri, NULL);
116         if (!var) {
117                 cfg = ast_load_realtime_multientry(table, "exten LIKE", "\\_%", "context", context, "priority", pri, NULL);     
118                 if (cfg) {
119                         cat = cfg->root;
120                         while(cat) {
121                                 switch(mode) {
122                                 case MODE_MATCHMORE:
123                                         match = ast_extension_close(cat->name, exten, 1);
124                                         break;
125                                 case MODE_CANMATCH:
126                                         match = ast_extension_close(cat->name, exten, 0);
127                                         break;
128                                 case MODE_MATCH:
129                                 default:
130                                         match = ast_extension_match(cat->name, exten);
131                                 }
132                                 if (match) {
133                                         var = cat->root;
134                                         cat->root = NULL;
135                                         break;
136                                 }
137                                 cat = cat->next;
138                         }
139                         ast_destroy(cfg);
140                 }
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         REALTIME_COMMON(MODE_MATCH);
148         if (var) ast_destroy_realtime(var);
149         if (var)
150                 res = 1;
151         return res > 0 ? res : 0;
152 }
153
154 static int realtime_canmatch(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
155 {
156         REALTIME_COMMON(MODE_CANMATCH);
157         if (var) ast_destroy_realtime(var);
158         if (var)
159                 res = 1;
160         return res > 0 ? res : 0;
161 }
162
163 static int realtime_exec(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, int newstack, const char *data)
164 {
165         char app[256];
166         char appdata[512]="";
167         char *tmp="";
168     char tmp1[80];
169     char tmp2[80];
170     char tmp3[EXT_DATA_SIZE];
171         struct ast_app *a;
172         struct ast_variable *v;
173         REALTIME_COMMON(MODE_MATCH);
174         if (var) {
175                 v = var;
176                 while(v) {
177                         if (!strcasecmp(v->name, "app"))
178                                 strncpy(app, v->value, sizeof(app) -1 );
179                         else if (!strcasecmp(v->name, "appdata"))
180                                 tmp = ast_strdupa(v->value);
181                         v = v->next;
182                 }
183                 ast_destroy_realtime(var);
184                 if (!ast_strlen_zero(app)) {
185                         a = pbx_findapp(app);
186                         if (a) {
187                                 if(!ast_strlen_zero(tmp))
188                                    pbx_substitute_variables_helper(chan, tmp, appdata, sizeof(appdata) - 1);
189                 if (option_verbose > 2)
190                                         ast_verbose( VERBOSE_PREFIX_3 "Executing %s(\"%s\", \"%s\")\n",
191                                                                  term_color(tmp1, app, COLOR_BRCYAN, 0, sizeof(tmp1)),
192                                                                  term_color(tmp2, chan->name, COLOR_BRMAGENTA, 0, sizeof(tmp2)),
193                                                                  term_color(tmp3, (!ast_strlen_zero(appdata) ? (char *)appdata : ""), COLOR_BRMAGENTA, 0, sizeof(tmp3)));
194                 manager_event(EVENT_FLAG_CALL, "Newexten",
195                                                           "Channel: %s\r\n"
196                                                           "Context: %s\r\n"
197                                                           "Extension: %s\r\n"
198                                                           "Priority: %d\r\n"
199                                                           "Application: %s\r\n"
200                                                           "AppData: %s\r\n"
201                                                           "Uniqueid: %s\r\n",
202                                                           chan->name, chan->context, chan->exten, chan->priority, app, appdata ? appdata : "(NULL)", chan->uniqueid);
203                                 
204                                 res = pbx_exec(chan, a, appdata, newstack);
205                         } else
206                                 ast_log(LOG_NOTICE, "No such application '%s' for extension '%s' in context '%s'\n", app, exten, context);
207                 }
208         }
209         return res;
210 }
211
212 static int realtime_matchmore(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
213 {
214         REALTIME_COMMON(MODE_MATCHMORE);
215         if (var) ast_destroy_realtime(var);
216         if (var)
217                 res = 1;
218         return res > 0 ? res : 0;
219 }
220
221 static struct ast_switch realtime_switch =
222 {
223         name:                   "Realtime",
224         description:                    "Realtime Dialplan Switch",
225         exists:                 realtime_exists,
226         canmatch:               realtime_canmatch,
227         exec:                   realtime_exec,
228         matchmore:              realtime_matchmore,
229 };
230
231 char *description(void)
232 {
233         return tdesc;
234 }
235
236 int usecount(void)
237 {
238         return 1;
239 }
240
241 char *key()
242 {
243         return ASTERISK_GPL_KEY;
244 }
245
246 int unload_module(void)
247 {
248         ast_unregister_switch(&realtime_switch);
249         return 0;
250 }
251
252 int load_module(void)
253 {
254         ast_register_switch(&realtime_switch);
255         return 0;
256 }
257