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