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