Merged revisions 120226 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/crypto.h"
45 #include "asterisk/astdb.h"
46
47
48 /* Loopback switch creates a 'tunnel' to another context.  When extension
49    lookups pass through the 'tunnel', Asterisk expressions can be used
50    to modify the target extension, context, and priority in any way desired.
51    If there is a match at the far end, execution jumps through the 'tunnel'
52    to the matched context, extension, and priority.
53  
54    Global variables as well as ${CONTEXT}, ${EXTEN}, and ${PRIORITY} are 
55    available for substitution.  After substitution Loopback expects to get
56    a string of the form:
57
58         [exten]@context[:priority][/extramatch]
59    
60    Where exten, context, and priority are another extension, context, and priority
61    to lookup and "extramatch" is a dialplan extension pattern which the *original*
62    number must match.  If exten or priority are empty, the original values are 
63    used.
64
65    Note that the search context MUST be a different context from the current
66    context or the search will not succeed.  This is intended to reduce the
67    likelihood of loops (they're still possible if you try hard, so be careful!)
68
69 */
70
71
72 #define LOOPBACK_COMMON \
73         char buf[1024]; \
74         int res; \
75         char *newexten=(char *)exten, *newcontext=(char *)context; \
76         int newpriority=priority; \
77         char *newpattern=NULL; \
78         loopback_subst(buf, sizeof(buf), exten, context, priority, data); \
79         loopback_parse(&newexten, &newcontext, &newpriority, &newpattern, buf); \
80         ast_log(LOG_DEBUG, "Parsed into %s @ %s priority %d\n", newexten, newcontext, newpriority); \
81         if (!strcasecmp(newcontext, context)) return -1
82
83 static char *loopback_subst(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         AST_LIST_HEAD_INIT_NOLOCK(&headp);
91         newvariable = ast_var_assign("EXTEN", exten);
92         AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
93         newvariable = ast_var_assign("CONTEXT", context);
94         AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
95         newvariable = ast_var_assign("PRIORITY", tmp);
96         AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
97         /* Substitute variables */
98         pbx_substitute_variables_varshead(&headp, data, buf, buflen);
99         /* free the list */
100         while ((newvariable = AST_LIST_REMOVE_HEAD(&headp, entries)))
101                 ast_var_delete(newvariable);
102         return buf;
103 }
104
105 static void loopback_parse(char **newexten, char **newcontext, int *priority, char **newpattern, char *buf)
106 {
107         char *con;
108         char *pri;
109         *newpattern = strchr(buf, '/');
110         if (*newpattern)
111                 *(*newpattern)++ = '\0';
112         con = strchr(buf, '@');
113         if (con) {
114                 *con++ = '\0';
115                 pri = strchr(con, ':');
116         } else
117                 pri = strchr(buf, ':');
118         if (!ast_strlen_zero(buf))
119                 *newexten = buf;
120         if (!ast_strlen_zero(con))
121                 *newcontext = con;
122         if (!ast_strlen_zero(pri))
123                 sscanf(pri, "%d", priority);
124 }
125
126 static int loopback_exists(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
127 {
128         LOOPBACK_COMMON;
129         res = ast_exists_extension(chan, newcontext, newexten, newpriority, callerid);
130         if (newpattern && !ast_extension_match(newpattern, exten))
131                 res = 0;
132         return res;
133 }
134
135 static int loopback_canmatch(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
136 {
137         LOOPBACK_COMMON;
138         res = ast_canmatch_extension(chan, newcontext, newexten, newpriority, callerid);
139         if (newpattern && !ast_extension_match(newpattern, exten))
140                 res = 0;
141         return res;
142 }
143
144 static int loopback_exec(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
145 {
146         int found;
147         LOOPBACK_COMMON;
148         res = ast_spawn_extension(chan, newcontext, newexten, newpriority, callerid, &found, 0);
149         return res;
150 }
151
152 static int loopback_matchmore(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
153 {
154         LOOPBACK_COMMON;
155         res = ast_matchmore_extension(chan, newcontext, newexten, newpriority, callerid);
156         if (newpattern && !ast_extension_match(newpattern, exten))
157                 res = 0;
158         return res;
159 }
160
161 static struct ast_switch loopback_switch =
162 {
163         name:                   "Loopback",
164         description:            "Loopback Dialplan Switch",
165         exists:                 loopback_exists,
166         canmatch:               loopback_canmatch,
167         exec:                   loopback_exec,
168         matchmore:              loopback_matchmore,
169 };
170
171 static int unload_module(void)
172 {
173         ast_unregister_switch(&loopback_switch);
174         return 0;
175 }
176
177 static int load_module(void)
178 {
179         if (ast_register_switch(&loopback_switch))
180                 return AST_MODULE_LOAD_FAILURE;
181         return AST_MODULE_LOAD_SUCCESS;
182 }
183
184 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Loopback Switch");