1 /*
2 * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
3 * Copyright (C) 2005-2014, Anthony Minessale II <anthm@freeswitch.org>
4 *
5 * Version: MPL 1.1
6 *
7 * The contents of this file are subject to the Mozilla Public License Version
8 * 1.1 (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
10 * http://www.mozilla.org/MPL/
11 *
12 * Software distributed under the License is distributed on an "AS IS" basis,
13 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 * for the specific language governing rights and limitations under the
15 * License.
16 *
17 * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
18 *
19 * The Initial Developer of the Original Code is
20 * Anthony Minessale II <anthm@freeswitch.org>
21 * Portions created by the Initial Developer are Copyright (C)
22 * the Initial Developer. All Rights Reserved.
23 *
24 * Contributor(s):
25 *
26 * Anthony Minessale II <anthm@freeswitch.org>
27 *
28 * mod_dialplan_asterisk.c -- Asterisk extensions.conf style dialplan parser.
29 *
30 */
31 #include <switch.h>
32 #include <sys/types.h>
33 #include <sys/stat.h>
34 #include <fcntl.h>
35
36 SWITCH_MODULE_LOAD_FUNCTION(mod_dialplan_asterisk_load);
37 SWITCH_MODULE_DEFINITION(mod_dialplan_asterisk, mod_dialplan_asterisk_load, NULL, NULL);
38
exec_app(switch_core_session_t * session,char * app,char * arg)39 static switch_status_t exec_app(switch_core_session_t *session, char *app, char *arg)
40 {
41 switch_application_interface_t *application_interface;
42 switch_status_t status = SWITCH_STATUS_FALSE;
43
44 if ((application_interface = switch_loadable_module_get_application_interface(app))) {
45 status = switch_core_session_exec(session, application_interface, arg);
46 UNPROTECT_INTERFACE(application_interface);
47 }
48
49 return status;
50 }
51
SWITCH_STANDARD_APP(dial_function)52 SWITCH_STANDARD_APP(dial_function)
53 {
54 int argc;
55 char *argv[4] = { 0 };
56 char *mydata;
57 switch_channel_t *channel = switch_core_session_get_channel(session);
58
59 if (data && (mydata = switch_core_session_strdup(session, data))) {
60 if ((argc = switch_separate_string(mydata, '|', argv, (sizeof(argv) / sizeof(argv[0])))) < 2) {
61 goto error;
62 }
63
64 if (argc > 1) {
65 switch_channel_set_variable(channel, "call_timeout", argv[1]);
66 }
67
68 switch_replace_char(argv[0], '&', ',', SWITCH_FALSE);
69
70 if (exec_app(session, "bridge", argv[0]) != SWITCH_STATUS_SUCCESS) {
71 goto error;
72 }
73
74 goto ok;
75 }
76
77 error:
78 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error!\n");
79
80 ok:
81
82 return;
83 }
84
SWITCH_STANDARD_APP(avoid_function)85 SWITCH_STANDARD_APP(avoid_function)
86 {
87 #if 0
88 void *y = NULL;
89 #endif
90 int x = 0;
91 switch_channel_t *channel = switch_core_session_get_channel(session);
92
93 for (x = 0; x < 5; x++) {
94 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Avoiding initial deadlock on channel %s.\n", switch_channel_get_name(channel));
95 switch_yield(100000);
96 }
97
98 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "I should never be called!.\n");
99 #if 0
100 memset((void *) y, 0, 1000);
101 #endif
102 }
103
SWITCH_STANDARD_APP(goto_function)104 SWITCH_STANDARD_APP(goto_function)
105 {
106 int argc;
107 char *argv[3] = { 0 };
108 char *mydata;
109
110 if (data && (mydata = switch_core_session_strdup(session, data))) {
111 if ((argc = switch_separate_string(mydata, '|', argv, (sizeof(argv) / sizeof(argv[0])))) < 1) {
112 goto error;
113 }
114
115 switch_ivr_session_transfer(session, argv[1], "asterisk", argv[0]);
116 goto ok;
117 }
118
119 error:
120 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error!\n");
121
122 ok:
123
124 return;
125 }
126
SWITCH_STANDARD_DIALPLAN(asterisk_dialplan_hunt)127 SWITCH_STANDARD_DIALPLAN(asterisk_dialplan_hunt)
128 {
129 switch_caller_extension_t *extension = NULL;
130 switch_channel_t *channel = switch_core_session_get_channel(session);
131 char *cf = "extensions.conf";
132 switch_config_t cfg;
133 char *var, *val;
134 const char *context = NULL;
135
136 if (arg) {
137 cf = arg;
138 }
139
140 if (!caller_profile) {
141 caller_profile = switch_channel_get_caller_profile(channel);
142 }
143
144 if (!caller_profile || zstr(caller_profile->destination_number)) {
145 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error Obtaining Profile!\n");
146 return NULL;
147 }
148
149 context = caller_profile->context ? caller_profile->context : "default";
150
151 if (!switch_config_open_file(&cfg, cf)) {
152 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Open of %s failed\n", cf);
153 switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
154 return NULL;
155 }
156
157 while (switch_config_next_pair(&cfg, &var, &val)) {
158 if (!strcasecmp(cfg.category, context)) {
159 char *field_expanded = NULL;
160
161 if (!strcasecmp(var, "include")) {
162 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "param '%s' not implemented at line %d!\n", var, cfg.lineno);
163 } else {
164 int argc;
165 char *argv[3] = { 0 };
166 char *pattern = NULL;
167 char *app = NULL;
168 char *argument = NULL;
169 char *expression = NULL, expression_buf[1024] = { 0 };
170 char substituted[2048] = "";
171 const char *field_data = caller_profile->destination_number;
172 int proceed = 0;
173 switch_regex_t *re = NULL;
174 int ovector[30] = { 0 };
175 char *cid = NULL;
176
177 expression = expression_buf;
178
179 argc = switch_separate_string(val, ',', argv, (sizeof(argv) / sizeof(argv[0])));
180 if (argc < 3) {
181 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Parse error line %d!\n", cfg.lineno);
182 continue;
183 }
184
185 pattern = argv[0];
186
187 if (!strcasecmp(var, "exten")) {
188 char *p;
189 if (pattern && (p = strchr(pattern, '/'))) {
190 *p++ = '\0';
191 cid = pattern;
192 pattern = p;
193 }
194 } else {
195 if (strchr(var, '$')) {
196 if ((field_expanded = switch_channel_expand_variables(channel, var)) == var) {
197 field_expanded = NULL;
198 field_data = var;
199 } else {
200 field_data = field_expanded;
201 }
202 } else {
203 field_data = switch_caller_get_field_by_name(caller_profile, var);
204 }
205 }
206
207 if (pattern && (*pattern == '_' || *pattern == '~')) {
208 if (*pattern == '_') {
209 pattern++;
210 if (switch_ast2regex(pattern, expression_buf, sizeof(expression_buf))) {
211 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Converting [%s] to real regex [%s] you should try them!\n",
212 pattern, expression_buf);
213 }
214 } else {
215 pattern++;
216 expression = pattern;
217 }
218
219 if (!field_data) {
220 field_data = "";
221 }
222
223 if (!(proceed = switch_regex_perform(field_data, expression, &re, ovector, sizeof(ovector) / sizeof(ovector[0])))) {
224 switch_regex_safe_free(re);
225 switch_safe_free(field_expanded);
226 continue;
227 }
228 } else {
229 if (pattern && strcasecmp(pattern, field_data)) {
230 continue;
231 }
232 }
233
234 if (cid) {
235 if (strcasecmp(cid, caller_profile->caller_id_number)) {
236 continue;
237 }
238 }
239
240 switch_channel_set_variable(channel, "EXTEN", caller_profile->destination_number);
241 switch_channel_set_variable(channel, "CHANNEL", switch_channel_get_name(channel));
242 switch_channel_set_variable(channel, "UNIQUEID", switch_core_session_get_uuid(session));
243
244 //pri = argv[1];
245 app = argv[2];
246
247 if ((argument = strchr(app, '('))) {
248 char *p;
249 *argument++ = '\0';
250 p = strrchr(argument, ')');
251 if (p) {
252 *p = '\0';
253 }
254 } else if ((argument = strchr(app, ','))) {
255 *argument++ = '\0';
256 }
257
258 if (!argument) {
259 argument = "";
260 }
261
262 if (!field_data) {
263 field_data = "";
264 }
265
266 if (strchr(expression, '(')) {
267 switch_perform_substitution(re, proceed, argument, field_data, substituted, sizeof(substituted), ovector);
268 argument = substituted;
269 }
270 switch_regex_safe_free(re);
271
272 if (!extension) {
273 if (zstr(field_data)) {
274 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "No extension!\n");
275 break;
276 }
277 if ((extension = switch_caller_extension_new(session, field_data, field_data)) == 0) {
278 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Memory Error!\n");
279 break;
280 }
281 }
282
283 switch_caller_extension_add_application(session, extension, app, argument);
284 }
285
286 switch_safe_free(field_expanded);
287 }
288 }
289
290 switch_config_close_file(&cfg);
291
292 return extension;
293 }
294
295 /* fake chan_sip */
296 switch_endpoint_interface_t *sip_endpoint_interface;
297 static switch_call_cause_t sip_outgoing_channel(switch_core_session_t *session, switch_event_t *var_event,
298 switch_caller_profile_t *outbound_profile,
299 switch_core_session_t **new_session, switch_memory_pool_t **pool, switch_originate_flag_t flags,
300 switch_call_cause_t *cancel_cause);
301 switch_io_routines_t sip_io_routines = {
302 /*.outgoing_channel */ sip_outgoing_channel
303 };
304
sip_outgoing_channel(switch_core_session_t * session,switch_event_t * var_event,switch_caller_profile_t * outbound_profile,switch_core_session_t ** new_session,switch_memory_pool_t ** pool,switch_originate_flag_t flags,switch_call_cause_t * cancel_cause)305 static switch_call_cause_t sip_outgoing_channel(switch_core_session_t *session, switch_event_t *var_event,
306 switch_caller_profile_t *outbound_profile,
307 switch_core_session_t **new_session, switch_memory_pool_t **pool, switch_originate_flag_t flags,
308 switch_call_cause_t *cancel_cause)
309 {
310 const char *profile;
311 char *dup_profile = NULL;
312
313 if (session) {
314 profile = switch_channel_get_variable(switch_core_session_get_channel(session), "sip_profile");
315 } else {
316 dup_profile = switch_core_get_variable_dup("sip_profile");
317 profile = dup_profile;
318 }
319 if (zstr(profile)) {
320 profile = "default";
321 }
322
323 outbound_profile->destination_number = switch_core_sprintf(outbound_profile->pool, "%s/%s", profile, outbound_profile->destination_number);
324
325 UNPROTECT_INTERFACE(sip_endpoint_interface);
326
327 switch_safe_free(dup_profile);
328
329 return switch_core_session_outgoing_channel(session, var_event, "sofia", outbound_profile, new_session, pool, SOF_NONE, cancel_cause);
330 }
331
332
333
334
335 /* fake chan_iax2 */
336 switch_endpoint_interface_t *iax2_endpoint_interface;
337 static switch_call_cause_t iax2_outgoing_channel(switch_core_session_t *session, switch_event_t *var_event,
338 switch_caller_profile_t *outbound_profile,
339 switch_core_session_t **new_session, switch_memory_pool_t **pool, switch_originate_flag_t flags,
340 switch_call_cause_t *cancel_cause);
341 switch_io_routines_t iax2_io_routines = {
342 /*.outgoing_channel */ iax2_outgoing_channel
343 };
344
iax2_outgoing_channel(switch_core_session_t * session,switch_event_t * var_event,switch_caller_profile_t * outbound_profile,switch_core_session_t ** new_session,switch_memory_pool_t ** pool,switch_originate_flag_t flags,switch_call_cause_t * cancel_cause)345 static switch_call_cause_t iax2_outgoing_channel(switch_core_session_t *session, switch_event_t *var_event,
346 switch_caller_profile_t *outbound_profile,
347 switch_core_session_t **new_session, switch_memory_pool_t **pool, switch_originate_flag_t flags,
348 switch_call_cause_t *cancel_cause)
349 {
350 UNPROTECT_INTERFACE(iax2_endpoint_interface);
351
352 return switch_core_session_outgoing_channel(session, var_event, "iax", outbound_profile, new_session, pool, SOF_NONE, cancel_cause);
353 }
354
355
356 #define WE_DONT_NEED_NO_STINKIN_KEY "true"
key()357 static char *key()
358 {
359 return WE_DONT_NEED_NO_STINKIN_KEY;
360 }
361
SWITCH_MODULE_LOAD_FUNCTION(mod_dialplan_asterisk_load)362 SWITCH_MODULE_LOAD_FUNCTION(mod_dialplan_asterisk_load)
363 {
364 switch_dialplan_interface_t *dp_interface;
365 switch_application_interface_t *app_interface;
366 char *mykey = NULL;
367 int x = 0;
368
369 if ((mykey = key())) {
370 mykey = NULL;
371 }
372
373 /* connect my internal structure to the blank pointer passed to me */
374 *module_interface = switch_loadable_module_create_module_interface(pool, modname);
375 /* add a dialplan interface */
376 SWITCH_ADD_DIALPLAN(dp_interface, "asterisk", asterisk_dialplan_hunt);
377
378 /* a few fake apps for the sake of emulation */
379 SWITCH_ADD_APP(app_interface, "Dial", "Dial", "Dial", dial_function, "Dial", SAF_SUPPORT_NOMEDIA);
380 SWITCH_ADD_APP(app_interface, "Goto", "Goto", "Goto", goto_function, "Goto", SAF_SUPPORT_NOMEDIA);
381 SWITCH_ADD_APP(app_interface, "AvoidingDeadlock", "Avoid", "Avoid", avoid_function, "Avoid", SAF_SUPPORT_NOMEDIA);
382
383 /* fake chan_sip facade */
384 sip_endpoint_interface = switch_loadable_module_create_interface(*module_interface, SWITCH_ENDPOINT_INTERFACE);
385 sip_endpoint_interface->interface_name = "SIP";
386 sip_endpoint_interface->io_routines = &sip_io_routines;
387
388 /* fake chan_iax2 facade */
389 iax2_endpoint_interface = switch_loadable_module_create_interface(*module_interface, SWITCH_ENDPOINT_INTERFACE);
390 iax2_endpoint_interface->interface_name = "IAX2";
391 iax2_endpoint_interface->io_routines = &iax2_io_routines;
392
393 if (getenv("FAITHFUL_EMULATION")) {
394 for (x = 0; x < 10; x++) {
395 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Avoiding Deadlock.\n");
396 switch_yield(100000);
397 }
398 }
399
400 /* indicate that the module should continue to be loaded */
401 return SWITCH_STATUS_SUCCESS;
402 }
403
404 /* For Emacs:
405 * Local Variables:
406 * mode:c
407 * indent-tabs-mode:t
408 * tab-width:4
409 * c-basic-offset:4
410 * End:
411 * For VIM:
412 * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet:
413 */
414