more file version tags
[asterisk/asterisk.git] / pbx / pbx_loopback.c
1 /*
2  * Loopback PBX Module
3  *
4  * Copyright (C) 2004 - 2005, Digium Inc.
5  *
6  * Written by Mark Spencer <markster@digium.com>
7  *
8  * This program is Free Software distributed under the terms of
9  * of the GNU General Public License.
10  */
11
12 #include <stdlib.h>
13 #include <unistd.h>
14 #include <string.h>
15 #include <errno.h>
16
17 #include "asterisk.h"
18
19 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
20
21 #include "asterisk/file.h"
22 #include "asterisk/logger.h"
23 #include "asterisk/channel.h"
24 #include "asterisk/config.h"
25 #include "asterisk/options.h"
26 #include "asterisk/pbx.h"
27 #include "asterisk/module.h"
28 #include "asterisk/frame.h"
29 #include "asterisk/file.h"
30 #include "asterisk/cli.h"
31 #include "asterisk/lock.h"
32 #include "asterisk/md5.h"
33 #include "asterisk/linkedlists.h"
34 #include "asterisk/chanvars.h"
35 #include "asterisk/sched.h"
36 #include "asterisk/io.h"
37 #include "asterisk/utils.h"
38 #include "asterisk/crypto.h"
39 #include "asterisk/astdb.h"
40
41 static char *tdesc = "Loopback Switch";
42
43 /* Loopback switch substitutes ${EXTEN}, ${CONTEXT}, and ${PRIORITY} into
44    the data passed to it to try to get a string of the form:
45
46         [exten]@context[:priority][/extramatch]
47    
48    Where exten, context, and priority are another extension, context, and priority
49    to lookup and "extramatch" is an extra match restriction the *original* number 
50    must fit if  specified.  The "extramatch" begins with _ like an exten pattern
51    if it is specified.  Note that the search context MUST be a different context
52    from the current context or the search will not succeed in an effort to reduce
53    the likelihood of loops (they're still possible if you try hard, so be careful!)
54
55 */
56
57
58 #define LOOPBACK_COMMON \
59         char buf[1024]; \
60         int res; \
61         char *newexten=(char *)exten, *newcontext=(char *)context; \
62         int newpriority=priority; \
63         char *newpattern=NULL; \
64         loopback_helper(buf, sizeof(buf), exten, context, priority, data); \
65         loopback_subst(&newexten, &newcontext, &newpriority, &newpattern, buf); \
66         ast_log(LOG_DEBUG, "Parsed into %s @ %s priority %d\n", newexten, newcontext, newpriority); \
67         if (!strcasecmp(newcontext, context)) return -1
68
69
70 static char *loopback_helper(char *buf, int buflen, const char *exten, const char *context, int priority, const char *data)
71 {
72         struct ast_var_t *newvariable;
73         struct varshead headp;
74         char tmp[80];
75
76         snprintf(tmp, sizeof(tmp), "%d", priority);
77         memset(buf, 0, buflen);
78         AST_LIST_HEAD_INIT(&headp);
79         newvariable = ast_var_assign("EXTEN", exten);
80         AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
81         newvariable = ast_var_assign("CONTEXT", context);
82         AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
83         newvariable = ast_var_assign("PRIORITY", tmp);
84         AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
85         pbx_substitute_variables_varshead(&headp, data, buf, buflen);
86         /* Substitute variables */
87         while (!AST_LIST_EMPTY(&headp)) {           /* List Deletion. */
88                 newvariable = AST_LIST_REMOVE_HEAD(&headp, entries);
89                 ast_var_delete(newvariable);
90         }
91         return buf;
92 }
93
94 static void loopback_subst(char **newexten, char **newcontext, int *priority, char **newpattern, char *buf)
95 {
96         char *con;
97         char *pri;
98         *newpattern = strchr(buf, '/');
99         if (*newpattern) {
100                 *(*newpattern) = '\0';
101                 (*newpattern)++;
102         }
103         con = strchr(buf, '@');
104         if (con) {
105                 *con = '\0';
106                 con++;
107                 pri = strchr(con, ':');
108         } else
109                 pri = strchr(buf, ':');
110         if (!ast_strlen_zero(buf))
111                 *newexten = buf;
112         if (con && !ast_strlen_zero(con))
113                 *newcontext = con;
114         if (pri && !ast_strlen_zero(pri))
115                 sscanf(pri, "%d", priority);
116 }
117
118 static int loopback_exists(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
119 {
120         LOOPBACK_COMMON;
121         res = ast_exists_extension(chan, newcontext, newexten, newpriority, callerid);
122         if (newpattern && !ast_extension_match(newpattern, exten))
123                 res = 0;
124         return res;
125 }
126
127 static int loopback_canmatch(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
128 {
129         LOOPBACK_COMMON;
130         res = ast_canmatch_extension(chan, newcontext, newexten, newpriority, callerid);
131         if (newpattern && !ast_extension_match(newpattern, exten))
132                 res = 0;
133         return res;
134 }
135
136 static int loopback_exec(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, int newstack, const char *data)
137 {
138         LOOPBACK_COMMON;
139         if (newstack)
140                 res = ast_spawn_extension(chan, newcontext, newexten, newpriority, callerid);
141         else
142                 res = ast_exec_extension(chan, newcontext, newexten, newpriority, callerid);
143         if (newpattern && !ast_extension_match(newpattern, exten))
144                 res = -1;
145         return res;
146 }
147
148 static int loopback_matchmore(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
149 {
150         LOOPBACK_COMMON;
151         res = ast_matchmore_extension(chan, newcontext, newexten, newpriority, callerid);
152         if (newpattern && !ast_extension_match(newpattern, exten))
153                 res = 0;
154         return res;
155 }
156
157 static struct ast_switch loopback_switch =
158 {
159         name:                   "Loopback",
160         description:                    "Loopback Dialplan Switch",
161         exists:                 loopback_exists,
162         canmatch:               loopback_canmatch,
163         exec:                   loopback_exec,
164         matchmore:              loopback_matchmore,
165 };
166
167 char *description(void)
168 {
169         return tdesc;
170 }
171
172 int usecount(void)
173 {
174         return 1;
175 }
176
177 char *key()
178 {
179         return ASTERISK_GPL_KEY;
180 }
181
182 int unload_module(void)
183 {
184         ast_unregister_switch(&loopback_switch);
185         return 0;
186 }
187
188 int load_module(void)
189 {
190         ast_register_switch(&loopback_switch);
191         return 0;
192 }
193