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