since the module API is changing, it's a good time to const-ify the description(...
[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 <stdlib.h>
26 #include <stdio.h>
27 #include <unistd.h>
28 #include <string.h>
29 #include <errno.h>
30
31 #include "asterisk.h"
32
33 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
34
35 #include "asterisk/file.h"
36 #include "asterisk/logger.h"
37 #include "asterisk/channel.h"
38 #include "asterisk/config.h"
39 #include "asterisk/options.h"
40 #include "asterisk/pbx.h"
41 #include "asterisk/module.h"
42 #include "asterisk/frame.h"
43 #include "asterisk/file.h"
44 #include "asterisk/cli.h"
45 #include "asterisk/lock.h"
46 #include "asterisk/md5.h"
47 #include "asterisk/linkedlists.h"
48 #include "asterisk/chanvars.h"
49 #include "asterisk/sched.h"
50 #include "asterisk/io.h"
51 #include "asterisk/utils.h"
52 #include "asterisk/crypto.h"
53 #include "asterisk/astdb.h"
54
55 static char *tdesc = "Loopback Switch";
56
57 /* Loopback switch substitutes ${EXTEN}, ${CONTEXT}, and ${PRIORITY} into
58    the data passed to it to try to get a string of the form:
59
60         [exten]@context[:priority][/extramatch]
61    
62    Where exten, context, and priority are another extension, context, and priority
63    to lookup and "extramatch" is an extra match restriction the *original* number 
64    must fit if  specified.  The "extramatch" begins with _ like an exten pattern
65    if it is specified.  Note that the search context MUST be a different context
66    from the current context or the search will not succeed in an effort to reduce
67    the 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_helper(buf, sizeof(buf), exten, context, priority, data); \
79         loopback_subst(&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
84 static char *loopback_helper(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         memset(buf, 0, buflen);
92         AST_LIST_HEAD_INIT_NOLOCK(&headp);
93         AST_LIST_INSERT_HEAD(&headp, ast_var_assign("EXTEN", exten), entries);
94         AST_LIST_INSERT_HEAD(&headp, ast_var_assign("CONTEXT", context), entries);
95         AST_LIST_INSERT_HEAD(&headp, ast_var_assign("PRIORITY", tmp), entries);
96         /* Substitute variables */
97         pbx_substitute_variables_varshead(&headp, data, buf, buflen);
98         /* free the list */
99         while ((newvariable = AST_LIST_REMOVE_HEAD(&headp, entries)))
100                 ast_var_delete(newvariable);
101         return buf;
102 }
103
104 static void loopback_subst(char **newexten, char **newcontext, int *priority, char **newpattern, char *buf)
105 {
106         char *con;
107         char *pri;
108         *newpattern = strchr(buf, '/');
109         if (*newpattern)
110                 *(*newpattern)++ = '\0';
111         con = strchr(buf, '@');
112         if (con) {
113                 *con++ = '\0';
114                 pri = strchr(con, ':');
115         } else
116                 pri = strchr(buf, ':');
117         if (!ast_strlen_zero(buf))
118                 *newexten = buf;
119         if (!ast_strlen_zero(con))
120                 *newcontext = con;
121         if (!ast_strlen_zero(pri))
122                 sscanf(pri, "%d", priority);
123 }
124
125 static int loopback_exists(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
126 {
127         LOOPBACK_COMMON;
128         res = ast_exists_extension(chan, newcontext, newexten, newpriority, callerid);
129         if (newpattern && !ast_extension_match(newpattern, exten))
130                 res = 0;
131         return res;
132 }
133
134 static int loopback_canmatch(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
135 {
136         LOOPBACK_COMMON;
137         res = ast_canmatch_extension(chan, newcontext, newexten, newpriority, callerid);
138         if (newpattern && !ast_extension_match(newpattern, exten))
139                 res = 0;
140         return res;
141 }
142
143 static int loopback_exec(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
144 {
145         LOOPBACK_COMMON;
146         res = ast_spawn_extension(chan, newcontext, newexten, newpriority, callerid);
147         /* XXX hmmm... res is overridden ? */
148         if (newpattern && !ast_extension_match(newpattern, exten))
149                 res = -1;
150         return res;
151 }
152
153 static int loopback_matchmore(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
154 {
155         LOOPBACK_COMMON;
156         res = ast_matchmore_extension(chan, newcontext, newexten, newpriority, callerid);
157         if (newpattern && !ast_extension_match(newpattern, exten))
158                 res = 0;
159         return res;
160 }
161
162 static struct ast_switch loopback_switch =
163 {
164         name:                   "Loopback",
165         description:                    "Loopback Dialplan Switch",
166         exists:                 loopback_exists,
167         canmatch:               loopback_canmatch,
168         exec:                   loopback_exec,
169         matchmore:              loopback_matchmore,
170 };
171
172 const char *description(void)
173 {
174         return tdesc;
175 }
176
177 int usecount(void)
178 {
179         return 1;
180 }
181
182 const char *key()
183 {
184         return ASTERISK_GPL_KEY;
185 }
186
187 int unload_module(void)
188 {
189         ast_unregister_switch(&loopback_switch);
190         return 0;
191 }
192
193 int load_module(void)
194 {
195         ast_register_switch(&loopback_switch);
196         return 0;
197 }
198