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