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  * Raymond Chandler <intralanman@freeswitch.org>
28  *
29  * mod_translate.c -- TRANSLATE
30  *
31  */
32 
33 #include <switch.h>
34 
35 SWITCH_MODULE_LOAD_FUNCTION(mod_translate_load);
36 SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_translate_shutdown);
37 SWITCH_MODULE_DEFINITION(mod_translate, mod_translate_load, mod_translate_shutdown, NULL);
38 
39 static switch_mutex_t *MUTEX = NULL;
40 
41 static struct {
42 	switch_memory_pool_t *pool;
43 	switch_hash_t *translate_profiles;
44 	switch_thread_rwlock_t *profile_hash_rwlock;
45 } globals;
46 
47 struct rule {
48 	char *regex;
49 	char *replace;
50 	struct rule *next;
51 };
52 typedef struct rule translate_rule_t;
53 
54 static switch_event_node_t *NODE = NULL;
55 
56 
load_config(void)57 static switch_status_t load_config(void)
58 {
59 	char *cf = "translate.conf";
60 	switch_xml_t cfg, xml, rule, profile, profiles;
61 	switch_status_t status = SWITCH_STATUS_SUCCESS;
62 
63 	if (!(xml = switch_xml_open_cfg(cf, &cfg, NULL))) {
64 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Open of %s failed\n", cf);
65 		status = SWITCH_STATUS_FALSE;
66 		goto done;
67 	}
68 
69 	if ((profiles = switch_xml_child(cfg, "profiles"))) {
70 		for (profile = switch_xml_child(profiles, "profile"); profile; profile = profile->next) {
71 			translate_rule_t *rules_list = NULL;
72 			char *name = (char *) switch_xml_attr_soft(profile, "name");
73 
74 			if (!name) {
75 				continue;
76 			}
77 
78 			for (rule = switch_xml_child(profile, "rule"); rule; rule = rule->next) {
79 				char *regex = (char *) switch_xml_attr_soft(rule, "regex");
80 				char *replace = (char *) switch_xml_attr_soft(rule, "replace");
81 
82 				if (regex && replace) {
83 					translate_rule_t *this_rule = NULL, *rl = NULL;
84 
85 					this_rule = switch_core_alloc(globals.pool, sizeof(translate_rule_t));
86 					this_rule->regex = switch_core_strdup(globals.pool, regex);
87 					this_rule->replace = switch_core_strdup(globals.pool, replace);
88 
89 					switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Replace number matching [%s] with [%s]\n", regex, replace);
90 					if (rules_list == NULL) {
91 						switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "starting with an empty list\n");
92 						rules_list = this_rule;
93 					} else {
94 						for (rl = rules_list; rl && rl->next; rl = rl->next);
95 						rl->next = this_rule;
96 					}
97 				} else {
98 					switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid Translation!\n");
99 				}
100 			}
101 			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Adding rules to profile [%s]\n", name);
102 
103 			switch_core_hash_insert_wrlock(globals.translate_profiles, name, rules_list, globals.profile_hash_rwlock);
104 		}
105 	}
106 
107   done:
108 	if (xml) {
109 		switch_xml_free(xml);
110 	}
111 
112 	return status;
113 }
114 
translate_number(char * number,char * profile,char ** translated,switch_core_session_t * session,switch_event_t * event,switch_memory_pool_t * pool)115 static void translate_number(char *number, char *profile, char **translated, switch_core_session_t *session, switch_event_t *event, switch_memory_pool_t *pool)
116 {
117 	translate_rule_t *hi = NULL;
118 	translate_rule_t *rule = NULL;
119 	switch_regex_t *re = NULL;
120 	int proceed = 0, ovector[30];
121 	char *substituted = NULL, *subbed = NULL;
122 	uint32_t len = 0;
123 
124 	if (!profile) {
125 		profile = "US";
126 	}
127 
128 	switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "translating [%s] against [%s] profile\n", number, profile);
129 
130 	hi = switch_core_hash_find_rdlock(globals.translate_profiles, (const char *)profile, globals.profile_hash_rwlock);
131 	if (!hi) {
132 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "can't find key for profile matching [%s]\n", profile);
133 		return;
134 	}
135 
136 	for (rule = hi; rule; rule = rule->next) {
137 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s =~ /%s/\n", number, rule->regex);
138 		if ((proceed = switch_regex_perform(number, rule->regex, &re, ovector, sizeof(ovector) / sizeof(ovector[0])))) {
139 			len = (uint32_t) (strlen(number) + strlen(rule->replace) + 10) * proceed;
140 			if (!(substituted = malloc(len))) {
141 				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Memory Error!\n");
142 				switch_regex_safe_free(re);
143 				goto end;
144 			}
145 			memset(substituted, 0, len);
146 
147 			switch_perform_substitution(re, proceed, rule->replace, number, substituted, len, ovector);
148 
149 			if ((switch_string_var_check_const(substituted) || switch_string_has_escaped_data(substituted))) {
150 				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "perform variable expansion\n");
151 				if (session) {
152 					subbed = switch_channel_expand_variables(switch_core_session_get_channel(session), substituted);
153 				} else if (event) {
154 					subbed = switch_event_expand_headers(event, substituted);
155 				}
156 				if (session) {
157 					substituted = switch_core_session_strdup(session, subbed);
158 				} else {
159 					substituted = switch_core_strdup(pool, subbed);
160 				}
161 				if (subbed != substituted) {
162 					switch_safe_free(subbed);
163 				}
164 			}
165 
166 			break;
167 		}
168 	}
169 
170  end:
171 	*translated = substituted ? substituted : NULL;
172 }
173 
174 
do_unload(void)175 static void do_unload(void) {
176 	switch_hash_index_t *hi = NULL;
177 
178 	switch_mutex_lock(MUTEX);
179 
180 	while ((hi = switch_core_hash_first_iter( globals.translate_profiles, hi))) {
181 		void *val = NULL;
182 		const void *key;
183 		switch_ssize_t keylen;
184 		translate_rule_t *rl = NULL, *nrl;
185 
186 		switch_core_hash_this(hi, &key, &keylen, &val);
187 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "deleting translate profile [%s]\n", (char *) key);
188 
189 		for (nrl = val; rl;) {
190 			rl = nrl;
191 			nrl = nrl->next;
192 			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "deleting rule for [%s]\n", rl->regex);
193 			switch_safe_free(rl->regex);
194 			switch_safe_free(rl->replace);
195 			switch_safe_free(rl);
196 		}
197 
198 		switch_core_hash_delete_wrlock(globals.translate_profiles, key, globals.profile_hash_rwlock);
199 	}
200 
201 	switch_thread_rwlock_destroy(globals.profile_hash_rwlock);
202 	switch_core_hash_destroy(&globals.translate_profiles);
203 
204 	switch_mutex_unlock(MUTEX);
205 }
206 
do_load(void)207 static void do_load(void)
208 {
209 	switch_mutex_lock(MUTEX);
210 
211 	switch_core_hash_init(&globals.translate_profiles);
212 	switch_thread_rwlock_create(&globals.profile_hash_rwlock, globals.pool);
213 	load_config();
214 
215 	switch_mutex_unlock(MUTEX);
216 }
217 
event_handler(switch_event_t * event)218 static void event_handler(switch_event_t *event)
219 {
220 	switch_mutex_lock(MUTEX);
221 	do_unload();
222 	do_load();
223 	switch_mutex_unlock(MUTEX);
224 	switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Number Translations Reloaded\n");
225 }
226 
SWITCH_STANDARD_APP(translate_app_function)227 SWITCH_STANDARD_APP(translate_app_function)
228 {
229 	int argc = 0;
230 	char *argv[32] = { 0 };
231 	char *mydata = NULL;
232 	char *translated = NULL;
233 	switch_channel_t *channel  = switch_core_session_get_channel(session);
234 
235 	if (zstr(data)) {
236 		return;
237 	}
238 
239 	mydata = switch_core_session_strdup(session, data);
240 
241 	argc = switch_separate_string(mydata, ' ', argv, (sizeof(argv) / sizeof(argv[0])));
242 
243 	if (!argc) {
244 		return;
245 	}
246 
247 	translate_number(argv[0], argv[1], &translated, session, NULL, NULL);
248 
249 	switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Translated: %s\n", translated);
250 
251 	switch_channel_set_variable_var_check(channel, "translated", translated, SWITCH_FALSE);
252 
253 	return;
254 }
255 
SWITCH_STANDARD_DIALPLAN(translate_dialplan_hunt)256 SWITCH_STANDARD_DIALPLAN(translate_dialplan_hunt)
257 {
258 	switch_channel_t *channel = switch_core_session_get_channel(session);
259 	char *translated_dest = NULL;
260 	char *translated_cid_num = NULL;
261 	char *translated_ani = NULL;
262 	char *translate_profile = NULL;
263 	char *areacode = NULL;
264 
265 
266 	if (!caller_profile) {
267 		if (!(caller_profile = switch_channel_get_caller_profile(channel))) {
268 			switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Error Obtaining Profile!\n");
269 			goto done;
270 		}
271 	}
272 
273 	switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Processing %s <%s>->%s in translate\n",
274 					  caller_profile->caller_id_name, caller_profile->caller_id_number, caller_profile->destination_number);
275 
276 
277 
278 	if ((translate_profile = (char *) arg)) {
279 		switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG,
280 						  "using translate:<profile> [%s] for translate profile\n",translate_profile);
281 	} else 	if ((translate_profile = (char *) switch_channel_get_variable(channel, "translate_profile"))) {
282 		switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG,
283 						  "using translate_profile variable [%s] for translate profile\n", translate_profile);
284 	} else 	if ((translate_profile = (char *) switch_channel_get_variable(channel, "country"))) {
285 		switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG,
286 						  "using country variable [%s] for translate profile\n", translate_profile);
287 	} else if ((translate_profile = (char *) switch_channel_get_variable(channel, "default_country"))) {
288 		switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG,
289 						  "using default_country variable [%s] for translate profile\n", translate_profile);
290 	} else {
291 		translate_profile = "US";
292 	}
293 
294 	areacode = (char *) switch_channel_get_variable(channel, "areacode");
295 
296 	if (zstr(areacode)) {
297 		areacode = (char *) switch_channel_get_variable(channel, "default_areacode");
298 		if (!zstr(areacode)) {
299 			switch_channel_set_variable_safe(channel, "areacode", areacode);
300 		}
301 	}
302 
303 	translate_number((char *) caller_profile->destination_number, translate_profile, &translated_dest, session, NULL, NULL);
304 	translate_number((char *) caller_profile->caller_id_number, translate_profile, &translated_cid_num, session, NULL, NULL);
305 	translate_number((char *) caller_profile->ani, translate_profile, &translated_ani, session, NULL, NULL);
306 
307 	/* maybe we should translate aniii here too? */
308 	switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO,
309 					  "Profile: [%s], Translated Destination: [%s], Translated CID: [%s], Translated ANI: [%s]\n", translate_profile, translated_dest, translated_cid_num, translated_ani);
310 
311 	if (!zstr(translated_cid_num)) {
312 		caller_profile->caller_id_number = translated_cid_num;
313 	}
314 
315 	if (!zstr(translated_ani)) {
316 		caller_profile->ani = translated_ani;
317 	}
318 
319 
320 	if (!zstr(translated_dest)) {
321 		caller_profile->destination_number = translated_dest;
322 	}
323 
324  done:
325 	return NULL;
326 }
327 
328 #define TRANSLATE_SYNTAX "translate <number> [<profile>]"
SWITCH_STANDARD_API(translate_function)329 SWITCH_STANDARD_API(translate_function)
330 {
331 	char *mydata = NULL;
332 	switch_memory_pool_t *pool = NULL;
333 	char *translated = NULL;
334 	switch_event_t *event = NULL;
335 	char *argv[32] = { 0 };
336 	int argc = 0;
337 
338 	if (zstr(cmd)) {
339 		stream->write_function(stream, "USAGE: %s\n", TRANSLATE_SYNTAX);
340 		return SWITCH_STATUS_SUCCESS;
341 	}
342 
343 	switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "%s\n", cmd);
344 
345 	mydata = strdup(cmd);
346 
347 	if ((argc = switch_separate_string(mydata, ' ', argv, (sizeof(argv) / sizeof(argv[0]))))) {
348 		if (!session) {
349 			char *areacode = switch_core_get_variable("default_areacode");
350 			switch_core_new_memory_pool(&pool);
351 			switch_event_create(&event, SWITCH_EVENT_REQUEST_PARAMS);
352 
353 			if (zstr(areacode)) {
354 				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "no default_areacode set, using default of 777\n");
355 				switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "areacode", "777");
356 			} else {
357 				switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "areacode", areacode);
358 			}
359 		}
360 		translate_number(argv[0], argv[1], &translated, session, event, pool);
361 
362 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Translated: %s\n", translated);
363 
364 		stream->write_function(stream, "%s", translated);
365 	}
366 
367 	free(mydata);
368 
369 	if (pool) {
370 		switch_core_destroy_memory_pool(&pool);
371 	}
372 
373 	return SWITCH_STATUS_SUCCESS;
374 }
375 
SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_translate_shutdown)376 SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_translate_shutdown)
377 {
378 	switch_event_unbind(&NODE);
379 
380 	do_unload();
381 
382 	return SWITCH_STATUS_UNLOAD;
383 }
384 
SWITCH_MODULE_LOAD_FUNCTION(mod_translate_load)385 SWITCH_MODULE_LOAD_FUNCTION(mod_translate_load)
386 {
387 	switch_api_interface_t *api_interface;
388 	switch_application_interface_t *app_interface;
389 	switch_dialplan_interface_t *dp_interface;
390 
391 	memset(&globals, 0, sizeof(globals));
392 	globals.pool = pool;
393 
394 
395 	switch_mutex_init(&MUTEX, SWITCH_MUTEX_NESTED, pool);
396 
397 	if ((switch_event_bind_removable(modname, SWITCH_EVENT_RELOADXML, NULL, event_handler, NULL, &NODE) != SWITCH_STATUS_SUCCESS)) {
398 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't bind!\n");
399 		return SWITCH_STATUS_TERM;
400 	}
401 
402 	do_load();
403 
404 	*module_interface = switch_loadable_module_create_module_interface(pool, modname);
405 	SWITCH_ADD_API(api_interface, "translate", "TRANSLATE", translate_function, "");
406 	SWITCH_ADD_APP(app_interface, "translate", "Perform an TRANSLATE lookup", "Translate a number based on predefined rules", translate_app_function, "<number> <profile>]",
407 				   SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC);
408 	SWITCH_ADD_DIALPLAN(dp_interface, "translate", translate_dialplan_hunt);
409 
410 	return SWITCH_STATUS_SUCCESS;
411 }
412 
413 
414 
415 /* For Emacs:
416  * Local Variables:
417  * mode:c
418  * indent-tabs-mode:t
419  * tab-width:4
420  * c-basic-offset:4
421  * End:
422  * For VIM:
423  * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet:
424  */
425