don't use '%i' at all, since we have no current use cases that need non base-10 parsi...
[asterisk/asterisk.git] / pbx / pbx_loopback.c
1 /*
2  * Loopback 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/file.h"
21 #include "asterisk/cli.h"
22 #include "asterisk/lock.h"
23 #include "asterisk/md5.h"
24 #include "asterisk/linkedlists.h"
25 #include "asterisk/chanvars.h"
26 #include "asterisk/sched.h"
27 #include "asterisk/io.h"
28 #include "asterisk/utils.h"
29 #include "asterisk/crypto.h"
30 #include "asterisk/astdb.h"
31
32 #include <stdlib.h>
33 #include <unistd.h>
34 #include <string.h>
35 #include <errno.h>
36
37 static char *tdesc = "Loopback Switch";
38
39 /* Loopback switch substitutes ${EXTEN}, ${CONTEXT}, and ${PRIORITY} into
40    the data passed to it to try to get a string of the form:
41
42         [exten]@context[:priority][/extramatch]
43    
44    Where exten, context, and priority are another extension, context, and priority
45    to lookup and "extramatch" is an extra match restriction the *original* number 
46    must fit if  specified.  The "extramatch" begins with _ like an exten pattern
47    if it is specified.  Note that the search context MUST be a different context
48    from the current context or the search will not succeed in an effort to reduce
49    the likelihood of loops (they're still possible if you try hard, so be careful!)
50
51 */
52
53
54 #define LOOPBACK_COMMON \
55         char buf[1024]; \
56         int res; \
57         char *newexten=(char *)exten, *newcontext=(char *)context; \
58         int newpriority=priority; \
59         char *newpattern=NULL; \
60         loopback_helper(buf, sizeof(buf), exten, context, priority, data); \
61         loopback_subst(&newexten, &newcontext, &newpriority, &newpattern, buf); \
62         ast_log(LOG_DEBUG, "Parsed into %s @ %s priority %d\n", newexten, newcontext, newpriority); \
63         if (!strcasecmp(newcontext, context)) return -1
64
65
66 static char *loopback_helper(char *buf, int buflen, const char *exten, const char *context, int priority, const char *data)
67 {
68         struct ast_var_t *newvariable;
69         struct varshead headp;
70         char tmp[80];
71
72         snprintf(tmp, sizeof(tmp), "%d", priority);
73         memset(buf, 0, buflen);
74         AST_LIST_HEAD_INIT(&headp);
75         newvariable = ast_var_assign("EXTEN", exten);
76         AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
77         newvariable = ast_var_assign("CONTEXT", context);
78         AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
79         newvariable = ast_var_assign("PRIORITY", tmp);
80         AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
81         pbx_substitute_variables_varshead(&headp, data, buf, buflen);
82         /* Substitute variables */
83         while (!AST_LIST_EMPTY(&headp)) {           /* List Deletion. */
84                 newvariable = AST_LIST_REMOVE_HEAD(&headp, entries);
85                 ast_var_delete(newvariable);
86         }
87         return buf;
88 }
89
90 static void loopback_subst(char **newexten, char **newcontext, int *priority, char **newpattern, char *buf)
91 {
92         char *con;
93         char *pri;
94         *newpattern = strchr(buf, '/');
95         if (*newpattern) {
96                 *(*newpattern) = '\0';
97                 (*newpattern)++;
98         }
99         con = strchr(buf, '@');
100         if (con) {
101                 *con = '\0';
102                 con++;
103                 pri = strchr(con, ':');
104         } else
105                 pri = strchr(buf, ':');
106         if (!ast_strlen_zero(buf))
107                 *newexten = buf;
108         if (con && !ast_strlen_zero(con))
109                 *newcontext = con;
110         if (pri && !ast_strlen_zero(pri))
111                 sscanf(pri, "%d", priority);
112 }
113
114 static int loopback_exists(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
115 {
116         LOOPBACK_COMMON;
117         res = ast_exists_extension(chan, newcontext, newexten, newpriority, callerid);
118         if (newpattern && !ast_extension_match(newpattern, exten))
119                 res = 0;
120         return res;
121 }
122
123 static int loopback_canmatch(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
124 {
125         LOOPBACK_COMMON;
126         res = ast_canmatch_extension(chan, newcontext, newexten, newpriority, callerid);
127         if (newpattern && !ast_extension_match(newpattern, exten))
128                 res = 0;
129         return res;
130 }
131
132 static int loopback_exec(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, int newstack, const char *data)
133 {
134         LOOPBACK_COMMON;
135         if (newstack)
136                 res = ast_spawn_extension(chan, newcontext, newexten, newpriority, callerid);
137         else
138                 res = ast_exec_extension(chan, newcontext, newexten, newpriority, callerid);
139         if (newpattern && !ast_extension_match(newpattern, exten))
140                 res = -1;
141         return res;
142 }
143
144 static int loopback_matchmore(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
145 {
146         LOOPBACK_COMMON;
147         res = ast_matchmore_extension(chan, newcontext, newexten, newpriority, callerid);
148         if (newpattern && !ast_extension_match(newpattern, exten))
149                 res = 0;
150         return res;
151 }
152
153 static struct ast_switch loopback_switch =
154 {
155         name:                   "Loopback",
156         description:                    "Loopback Dialplan Switch",
157         exists:                 loopback_exists,
158         canmatch:               loopback_canmatch,
159         exec:                   loopback_exec,
160         matchmore:              loopback_matchmore,
161 };
162
163 char *description(void)
164 {
165         return tdesc;
166 }
167
168 int usecount(void)
169 {
170         return 1;
171 }
172
173 char *key()
174 {
175         return ASTERISK_GPL_KEY;
176 }
177
178 int unload_module(void)
179 {
180         ast_unregister_switch(&loopback_switch);
181         return 0;
182 }
183
184 int load_module(void)
185 {
186         ast_register_switch(&loopback_switch);
187         return 0;
188 }
189