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