Fix UPGRADE.txt files for Asterisk 10.
[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         newvariable = ast_var_assign("EXTEN", exten);
95         AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
96         newvariable = ast_var_assign("CONTEXT", context);
97         AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
98         newvariable = ast_var_assign("PRIORITY", tmp);
99         AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
100         /* Substitute variables */
101         pbx_substitute_variables_varshead(&headp, data, buf, buflen);
102         /* free the list */
103         while ((newvariable = AST_LIST_REMOVE_HEAD(&headp, entries)))
104                 ast_var_delete(newvariable);
105         return buf;
106 }
107
108 static void loopback_parse(char **newexten, char **newcontext, int *priority, char **newpattern, char *buf)
109 {
110         char *con;
111         char *pri;
112         *newpattern = strchr(buf, '/');
113         if (*newpattern)
114                 *(*newpattern)++ = '\0';
115         con = strchr(buf, '@');
116         if (con) {
117                 *con++ = '\0';
118                 pri = strchr(con, ':');
119         } else
120                 pri = strchr(buf, ':');
121         if (!ast_strlen_zero(buf))
122                 *newexten = buf;
123         if (!ast_strlen_zero(con))
124                 *newcontext = con;
125         if (!ast_strlen_zero(pri))
126                 sscanf(pri, "%30d", priority);
127 }
128
129 static int loopback_exists(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
130 {
131         LOOPBACK_COMMON;
132         res = ast_exists_extension(chan, newcontext, newexten, newpriority, callerid);
133         if (newpattern && !ast_extension_match(newpattern, exten))
134                 res = 0;
135         return res;
136 }
137
138 static int loopback_canmatch(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
139 {
140         LOOPBACK_COMMON;
141         res = ast_canmatch_extension(chan, newcontext, newexten, newpriority, callerid);
142         if (newpattern && !ast_extension_match(newpattern, exten))
143                 res = 0;
144         return res;
145 }
146
147 static int loopback_exec(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
148 {
149         int found;
150         LOOPBACK_COMMON;
151         res = ast_spawn_extension(chan, newcontext, newexten, newpriority, callerid, &found, 0);
152         return res;
153 }
154
155 static int loopback_matchmore(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
156 {
157         LOOPBACK_COMMON;
158         res = ast_matchmore_extension(chan, newcontext, newexten, newpriority, callerid);
159         if (newpattern && !ast_extension_match(newpattern, exten))
160                 res = 0;
161         return res;
162 }
163
164 static struct ast_switch loopback_switch =
165 {
166         name:                   "Loopback",
167         description:            "Loopback Dialplan Switch",
168         exists:                 loopback_exists,
169         canmatch:               loopback_canmatch,
170         exec:                   loopback_exec,
171         matchmore:              loopback_matchmore,
172 };
173
174 static int unload_module(void)
175 {
176         ast_unregister_switch(&loopback_switch);
177         return 0;
178 }
179
180 static int load_module(void)
181 {
182         if (ast_register_switch(&loopback_switch))
183                 return AST_MODULE_LOAD_FAILURE;
184         return AST_MODULE_LOAD_SUCCESS;
185 }
186
187 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Loopback Switch");