Merged revisions 325673 via svnmerge from
[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 "asterisk.h"
26
27 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
28
29 #include "asterisk/file.h"
30 #include "asterisk/logger.h"
31 #include "asterisk/channel.h"
32 #include "asterisk/config.h"
33 #include "asterisk/pbx.h"
34 #include "asterisk/module.h"
35 #include "asterisk/frame.h"
36 #include "asterisk/cli.h"
37 #include "asterisk/lock.h"
38 #include "asterisk/md5.h"
39 #include "asterisk/linkedlists.h"
40 #include "asterisk/chanvars.h"
41 #include "asterisk/sched.h"
42 #include "asterisk/io.h"
43 #include "asterisk/utils.h"
44 #include "asterisk/astdb.h"
45
46
47 /* Loopback switch creates a 'tunnel' to another context.  When extension
48    lookups pass through the 'tunnel', Asterisk expressions can be used
49    to modify the target extension, context, and priority in any way desired.
50    If there is a match at the far end, execution jumps through the 'tunnel'
51    to the matched context, extension, and priority.
52  
53    Global variables as well as ${CONTEXT}, ${EXTEN}, and ${PRIORITY} are 
54    available for substitution.  After substitution Loopback expects to get
55    a string of the form:
56
57         [exten]@context[:priority][/extramatch]
58    
59    Where exten, context, and priority are another extension, context, and priority
60    to lookup and "extramatch" is a dialplan extension pattern which the *original*
61    number must match.  If exten or priority are empty, the original values are 
62    used.
63
64    Note that the search context MUST be a different context from the current
65    context or the search will not succeed.  This is intended to reduce the
66    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_subst(buf, sizeof(buf), exten, context, priority, data); \
78         loopback_parse(&newexten, &newcontext, &newpriority, &newpattern, buf); \
79         ast_debug(1, "Parsed into %s @ %s priority %d\n", newexten, newcontext, newpriority); \
80         if (!strcasecmp(newcontext, context)) return -1
81
82 static char *loopback_subst(char *buf, int buflen, const char *exten, const char *context, int priority, const char *data)
83 {
84         struct ast_var_t *newvariable;
85         struct varshead headp;
86         char tmp[80];
87
88         snprintf(tmp, sizeof(tmp), "%d", priority);
89         AST_LIST_HEAD_INIT_NOLOCK(&headp);
90         newvariable = ast_var_assign("EXTEN", exten);
91         AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
92         newvariable = ast_var_assign("CONTEXT", context);
93         AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
94         newvariable = ast_var_assign("PRIORITY", tmp);
95         AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
96         /* Substitute variables */
97         pbx_substitute_variables_varshead(&headp, data, buf, buflen);
98         /* free the list */
99         while ((newvariable = AST_LIST_REMOVE_HEAD(&headp, entries)))
100                 ast_var_delete(newvariable);
101         return buf;
102 }
103
104 static void loopback_parse(char **newexten, char **newcontext, int *priority, char **newpattern, char *buf)
105 {
106         char *con;
107         char *pri;
108         *newpattern = strchr(buf, '/');
109         if (*newpattern)
110                 *(*newpattern)++ = '\0';
111         con = strchr(buf, '@');
112         if (con) {
113                 *con++ = '\0';
114                 pri = strchr(con, ':');
115         } else
116                 pri = strchr(buf, ':');
117         if (!ast_strlen_zero(buf))
118                 *newexten = buf;
119         if (!ast_strlen_zero(con))
120                 *newcontext = con;
121         if (!ast_strlen_zero(pri))
122                 sscanf(pri, "%30d", priority);
123 }
124
125 static int loopback_exists(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
126 {
127         LOOPBACK_COMMON;
128         res = ast_exists_extension(chan, newcontext, newexten, newpriority, callerid);
129         if (newpattern && !ast_extension_match(newpattern, exten))
130                 res = 0;
131         return res;
132 }
133
134 static int loopback_canmatch(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
135 {
136         LOOPBACK_COMMON;
137         res = ast_canmatch_extension(chan, newcontext, newexten, newpriority, callerid);
138         if (newpattern && !ast_extension_match(newpattern, exten))
139                 res = 0;
140         return res;
141 }
142
143 static int loopback_exec(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
144 {
145         int found;
146         LOOPBACK_COMMON;
147         res = ast_spawn_extension(chan, newcontext, newexten, newpriority, callerid, &found, 0);
148         return res;
149 }
150
151 static int loopback_matchmore(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
152 {
153         LOOPBACK_COMMON;
154         res = ast_matchmore_extension(chan, newcontext, newexten, newpriority, callerid);
155         if (newpattern && !ast_extension_match(newpattern, exten))
156                 res = 0;
157         return res;
158 }
159
160 static struct ast_switch loopback_switch =
161 {
162         name:                   "Loopback",
163         description:            "Loopback Dialplan Switch",
164         exists:                 loopback_exists,
165         canmatch:               loopback_canmatch,
166         exec:                   loopback_exec,
167         matchmore:              loopback_matchmore,
168 };
169
170 static int unload_module(void)
171 {
172         ast_unregister_switch(&loopback_switch);
173         return 0;
174 }
175
176 static int load_module(void)
177 {
178         if (ast_register_switch(&loopback_switch))
179                 return AST_MODULE_LOAD_FAILURE;
180         return AST_MODULE_LOAD_SUCCESS;
181 }
182
183 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Loopback Switch");