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