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