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