pbx/pbx_loopback: Speed up switches by avoiding unneeded lookups
[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 pattern %s\n", newexten, newcontext, newpriority, newpattern); \
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         if (newpattern && !ast_extension_match(newpattern, exten))
136                 res = 0;
137         else
138                 res = ast_exists_extension(chan, newcontext, newexten, newpriority, callerid);
139         return res;
140 }
141
142 static int loopback_canmatch(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
143 {
144         LOOPBACK_COMMON;
145         if (newpattern && !ast_extension_match(newpattern, exten))
146                 res = 0;
147         else
148                 res = ast_canmatch_extension(chan, newcontext, newexten, newpriority, callerid);
149         return res;
150 }
151
152 static int loopback_exec(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
153 {
154         int found;
155         LOOPBACK_COMMON;
156         res = ast_spawn_extension(chan, newcontext, newexten, newpriority, callerid, &found, 0);
157         return res;
158 }
159
160 static int loopback_matchmore(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
161 {
162         LOOPBACK_COMMON;
163         if (newpattern && !ast_extension_match(newpattern, exten))
164                 res = 0;
165         else
166                 res = ast_matchmore_extension(chan, newcontext, newexten, newpriority, callerid);
167         return res;
168 }
169
170 static struct ast_switch loopback_switch =
171 {
172         .name                   = "Loopback",
173         .description            = "Loopback Dialplan Switch",
174         .exists                 = loopback_exists,
175         .canmatch               = loopback_canmatch,
176         .exec                   = loopback_exec,
177         .matchmore              = loopback_matchmore,
178 };
179
180 static int unload_module(void)
181 {
182         ast_unregister_switch(&loopback_switch);
183         return 0;
184 }
185
186 static int load_module(void)
187 {
188         if (ast_register_switch(&loopback_switch))
189                 return AST_MODULE_LOAD_FAILURE;
190         return AST_MODULE_LOAD_SUCCESS;
191 }
192
193 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Loopback Switch");