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