]> git.ipfire.org Git - thirdparty/freeswitch.git/commitdiff
add number translation module
authorRaymond Chandler <intralanman@gmail.com>
Tue, 2 Apr 2013 01:00:26 +0000 (21:00 -0400)
committerTravis Cross <tc@traviscross.com>
Thu, 6 Jun 2013 19:15:17 +0000 (19:15 +0000)
build/modules.conf.in
conf/insideout/autoload_configs/modules.conf.xml
conf/insideout/autoload_configs/translate.conf.xml [new file with mode: 0644]
conf/sbc/autoload_configs/modules.conf.xml
conf/sbc/autoload_configs/translate.conf.xml [new file with mode: 0644]
conf/vanilla/autoload_configs/modules.conf.xml
conf/vanilla/autoload_configs/translate.conf.xml [new file with mode: 0644]
src/mod/applications/mod_translate/conf/autoload_configs/translate.conf.xml [new file with mode: 0644]
src/mod/applications/mod_translate/conf/dialplan/default/translate.xml [new file with mode: 0644]
src/mod/applications/mod_translate/mod_translate.c [new file with mode: 0644]

index 9f82b93518b309307feb9eb932c3337cbe3f6ea5..5bdae18a95308aff32d0f484e56ed2d686898eb2 100644 (file)
@@ -38,6 +38,7 @@ applications/mod_sms
 applications/mod_spandsp
 #applications/mod_spy
 #applications/mod_stress
+#applications/mod_translate
 applications/mod_valet_parking
 #applications/mod_vmd
 applications/mod_voicemail
index 7f17362509a9ae81618165128e176021d7ee37b2..692a3629772c2c7aad9bab927d4a573cde17c925 100644 (file)
@@ -46,6 +46,7 @@
     <load module="mod_limit"/>
     <load module="mod_esf"/>
     <load module="mod_fsv"/>
+    <!--<load module="mod_translate"/>-->
 
     <!-- SNOM Module -->
     <!--<load module="mod_snom"/>-->
diff --git a/conf/insideout/autoload_configs/translate.conf.xml b/conf/insideout/autoload_configs/translate.conf.xml
new file mode 100644 (file)
index 0000000..453ef3a
--- /dev/null
@@ -0,0 +1,28 @@
+<include>
+  <configuration name="translate.conf" description="Number Translation Rules">
+    <profiles>
+      <profile name="US">
+       <rule regex="^\+(\d+)$" replace="$1"/>
+       <rule regex="^(1[2-9]\d{2}[2-9]\d{6})$" replace="$1"/>
+       <rule regex="^([2-9]\d{2}[2-9]\d{6})$" replace="1$1"/>
+       <rule regex="^([2-9]\d{6})$" replace="1${areacode}$1"/>
+       <rule regex="^011(\d+)$" replace="$1"/>
+      </profile>
+      <profile name="GB">
+       <rule regex="^\+(\d+)$" replace="$1"/>
+       <rule regex="^$" replace="$1"/>
+      </profile>
+      <profile name="HK">
+       <rule regex="\+(\d+)$" replace="$1"/>
+       <rule regex="^(852\d{8})$" replace="$1"/>
+       <rule regex="^(\d{8})$" replace="852$1"/>
+      </profile>
+    </profiles>
+    <!--
+      <profile name="">
+       <rule regex="^\+(\d+)$" replace="$1"/>
+       <rule regex="^$" replace="$1"/>
+      </profile>
+    -->
+  </configuration>
+</include>
index 71d7a110f6b014b264ef7c0437cb3700e72109ba..c6c0273a939f850776882f5faf1c7729d45ed962 100644 (file)
@@ -30,6 +30,7 @@
     <load module="mod_dptools"/>
     <load module="mod_expr"/>
     <load module="mod_limit"/>
+    <!--<load module="mod_translate"/>-->
 
     <!-- Dialplan Interfaces -->
     <load module="mod_dialplan_xml"/>
diff --git a/conf/sbc/autoload_configs/translate.conf.xml b/conf/sbc/autoload_configs/translate.conf.xml
new file mode 100644 (file)
index 0000000..453ef3a
--- /dev/null
@@ -0,0 +1,28 @@
+<include>
+  <configuration name="translate.conf" description="Number Translation Rules">
+    <profiles>
+      <profile name="US">
+       <rule regex="^\+(\d+)$" replace="$1"/>
+       <rule regex="^(1[2-9]\d{2}[2-9]\d{6})$" replace="$1"/>
+       <rule regex="^([2-9]\d{2}[2-9]\d{6})$" replace="1$1"/>
+       <rule regex="^([2-9]\d{6})$" replace="1${areacode}$1"/>
+       <rule regex="^011(\d+)$" replace="$1"/>
+      </profile>
+      <profile name="GB">
+       <rule regex="^\+(\d+)$" replace="$1"/>
+       <rule regex="^$" replace="$1"/>
+      </profile>
+      <profile name="HK">
+       <rule regex="\+(\d+)$" replace="$1"/>
+       <rule regex="^(852\d{8})$" replace="$1"/>
+       <rule regex="^(\d{8})$" replace="852$1"/>
+      </profile>
+    </profiles>
+    <!--
+      <profile name="">
+       <rule regex="^\+(\d+)$" replace="$1"/>
+       <rule regex="^$" replace="$1"/>
+      </profile>
+    -->
+  </configuration>
+</include>
index d271b2ea96e475388bbe8614d892002046dca9e3..b07c59a6ed21dbca58a783cbaba1e5b75d7862ce 100644 (file)
@@ -66,6 +66,7 @@
     <!--<load module="mod_spy"/>-->
     <!--<load module="mod_random"/>-->
     <load module="mod_httapi"/>
+    <!--<load module="mod_translate"/>-->
 
     <!-- SNOM Module -->
     <!--<load module="mod_snom"/>-->
diff --git a/conf/vanilla/autoload_configs/translate.conf.xml b/conf/vanilla/autoload_configs/translate.conf.xml
new file mode 100644 (file)
index 0000000..453ef3a
--- /dev/null
@@ -0,0 +1,28 @@
+<include>
+  <configuration name="translate.conf" description="Number Translation Rules">
+    <profiles>
+      <profile name="US">
+       <rule regex="^\+(\d+)$" replace="$1"/>
+       <rule regex="^(1[2-9]\d{2}[2-9]\d{6})$" replace="$1"/>
+       <rule regex="^([2-9]\d{2}[2-9]\d{6})$" replace="1$1"/>
+       <rule regex="^([2-9]\d{6})$" replace="1${areacode}$1"/>
+       <rule regex="^011(\d+)$" replace="$1"/>
+      </profile>
+      <profile name="GB">
+       <rule regex="^\+(\d+)$" replace="$1"/>
+       <rule regex="^$" replace="$1"/>
+      </profile>
+      <profile name="HK">
+       <rule regex="\+(\d+)$" replace="$1"/>
+       <rule regex="^(852\d{8})$" replace="$1"/>
+       <rule regex="^(\d{8})$" replace="852$1"/>
+      </profile>
+    </profiles>
+    <!--
+      <profile name="">
+       <rule regex="^\+(\d+)$" replace="$1"/>
+       <rule regex="^$" replace="$1"/>
+      </profile>
+    -->
+  </configuration>
+</include>
diff --git a/src/mod/applications/mod_translate/conf/autoload_configs/translate.conf.xml b/src/mod/applications/mod_translate/conf/autoload_configs/translate.conf.xml
new file mode 100644 (file)
index 0000000..453ef3a
--- /dev/null
@@ -0,0 +1,28 @@
+<include>
+  <configuration name="translate.conf" description="Number Translation Rules">
+    <profiles>
+      <profile name="US">
+       <rule regex="^\+(\d+)$" replace="$1"/>
+       <rule regex="^(1[2-9]\d{2}[2-9]\d{6})$" replace="$1"/>
+       <rule regex="^([2-9]\d{2}[2-9]\d{6})$" replace="1$1"/>
+       <rule regex="^([2-9]\d{6})$" replace="1${areacode}$1"/>
+       <rule regex="^011(\d+)$" replace="$1"/>
+      </profile>
+      <profile name="GB">
+       <rule regex="^\+(\d+)$" replace="$1"/>
+       <rule regex="^$" replace="$1"/>
+      </profile>
+      <profile name="HK">
+       <rule regex="\+(\d+)$" replace="$1"/>
+       <rule regex="^(852\d{8})$" replace="$1"/>
+       <rule regex="^(\d{8})$" replace="852$1"/>
+      </profile>
+    </profiles>
+    <!--
+      <profile name="">
+       <rule regex="^\+(\d+)$" replace="$1"/>
+       <rule regex="^$" replace="$1"/>
+      </profile>
+    -->
+  </configuration>
+</include>
diff --git a/src/mod/applications/mod_translate/conf/dialplan/default/translate.xml b/src/mod/applications/mod_translate/conf/dialplan/default/translate.xml
new file mode 100644 (file)
index 0000000..0d19d04
--- /dev/null
@@ -0,0 +1,11 @@
+<include>
+  <extension>
+    <condition>
+      <!-- translate the desintation_number and caller_id_number
+          according to the default numbering plan (as seen in brian.xml) -->
+      <action application="translate" data="${destination_number} ${numbering_plan}"/>
+      <action application="log" data="info Before: ${destination_number}"/>
+      <action application="log" data="info After:  ${translated}"/>
+    </condition>
+  </extension>
+</include>
\ No newline at end of file
diff --git a/src/mod/applications/mod_translate/mod_translate.c b/src/mod/applications/mod_translate/mod_translate.c
new file mode 100644 (file)
index 0000000..219da47
--- /dev/null
@@ -0,0 +1,420 @@
+/*
+ * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ * Copyright (C) 2005-2012, Anthony Minessale II <anthm@freeswitch.org>
+ *
+ * Version: MPL 1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ *
+ * The Initial Developer of the Original Code is
+ * Anthony Minessale II <anthm@freeswitch.org>
+ * Portions created by the Initial Developer are Copyright (C)
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Anthony Minessale II <anthm@freeswitch.org>
+ * Raymond Chandler <intralanman@freeswitch.org>
+ *
+ * mod_translate.c -- TRANSLATE
+ *
+ */
+
+#include <switch.h>
+
+SWITCH_MODULE_LOAD_FUNCTION(mod_translate_load);
+SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_translate_shutdown);
+SWITCH_MODULE_DEFINITION(mod_translate, mod_translate_load, mod_translate_shutdown, NULL);
+
+static switch_mutex_t *MUTEX = NULL;
+
+static struct {
+       switch_memory_pool_t *pool;
+       switch_hash_t *translate_profiles;
+       switch_thread_rwlock_t *profile_hash_rwlock;
+} globals;
+
+struct rule {
+       char *regex;
+       char *replace;
+       struct rule *next;
+};
+typedef struct rule translate_rule_t;
+
+static switch_event_node_t *NODE = NULL;
+
+
+static switch_status_t load_config(void)
+{
+       char *cf = "translate.conf";
+       switch_xml_t cfg, xml, rule, profile, profiles;
+       switch_status_t status = SWITCH_STATUS_SUCCESS;
+
+       if (!(xml = switch_xml_open_cfg(cf, &cfg, NULL))) {
+               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Open of %s failed\n", cf);
+               status = SWITCH_STATUS_FALSE;
+               goto done;
+       }
+
+       if ((profiles = switch_xml_child(cfg, "profiles"))) {
+               for (profile = switch_xml_child(profiles, "profile"); profile; profile = profile->next) {
+                       translate_rule_t *rules_list = NULL;
+                       char *name = (char *) switch_xml_attr_soft(profile, "name");
+
+                       if (!name) {
+                               continue;
+                       }
+
+                       for (rule = switch_xml_child(profile, "rule"); rule; rule = rule->next) {
+                               char *regex = (char *) switch_xml_attr_soft(rule, "regex");
+                               char *replace = (char *) switch_xml_attr_soft(rule, "replace");
+
+                               if (regex && replace) {
+                                       translate_rule_t *this_rule = NULL, *rl = NULL;
+
+                                       this_rule = switch_core_alloc(globals.pool, sizeof(translate_rule_t));
+                                       this_rule->regex = switch_core_strdup(globals.pool, regex);
+                                       this_rule->replace = switch_core_strdup(globals.pool, replace);
+
+                                       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Replace number matching [%s] with [%s]\n", regex, replace);
+                                       if (rules_list == NULL) {
+                                               rules_list = switch_core_alloc(globals.pool, sizeof(translate_rule_t));
+                                               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "starting with an empty list\n");
+                                               rules_list = this_rule;
+                                       } else {
+                                               for (rl = rules_list; rl && rl->next; rl = rl->next);
+                                               rl->next = this_rule;
+                                       }
+                               } else {
+                                       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid Translation!\n");
+                               }
+                       }
+                       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Adding rules to profile [%s]\n", name);
+
+                       switch_core_hash_insert_wrlock(globals.translate_profiles, name, rules_list, globals.profile_hash_rwlock);
+               }
+       }
+
+  done:
+       if (xml) {
+               switch_xml_free(xml);
+       }
+
+       return status;
+}
+
+static void translate_number(char *number, char *profile, char **translated, switch_core_session_t *session, switch_event_t *event)
+{
+       translate_rule_t *hi = NULL;
+       translate_rule_t *rule = NULL;
+       switch_regex_t *re = NULL;
+       int proceed = 0, ovector[30];
+       char *substituted = NULL;
+       uint32_t len = 0;
+
+       if (!profile) {
+               profile = "US";
+       }
+
+       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "translating [%s] against [%s] profile\n", number, profile);
+
+       hi = switch_core_hash_find_rdlock(globals.translate_profiles, (const char *)profile, globals.profile_hash_rwlock);
+       if (!hi) {
+               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "can't find key for profile matching [%s]\n", profile);
+               return;
+       }
+
+       for (rule = hi; rule; rule = rule->next) {
+               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s =~ /%s/\n", number, rule->regex);
+               if ((proceed = switch_regex_perform(number, rule->regex, &re, ovector, sizeof(ovector) / sizeof(ovector[0])))) {
+                       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s matched %s, replacing with %s\n", number, rule->regex, rule->replace);
+                       if (!(substituted = malloc(len))) {
+                               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Memory Error!\n");
+                               switch_regex_safe_free(re);
+                               goto end;
+                       }
+                       memset(substituted, 0, len);
+
+                       switch_perform_substitution(re, proceed, rule->replace, number, substituted, len, ovector);
+
+                       if ((switch_string_var_check_const(substituted) || switch_string_has_escaped_data(substituted))) {
+                               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "perform variable expansion\n");
+                               if (session) {
+                                       substituted = switch_channel_expand_variables(switch_core_session_get_channel(session), substituted);
+                               } else if (event) {
+                                       substituted = switch_event_expand_headers(event, substituted);
+                               }
+                       }
+
+                       break;
+               }
+       }
+
+ end:
+       *translated = substituted ? substituted : NULL;
+}
+
+
+static void do_unload(void) {
+       switch_hash_index_t *hi = NULL;
+
+       switch_mutex_lock(MUTEX);
+
+       while ((hi = switch_hash_first(NULL, globals.translate_profiles))) {
+               void *val = NULL;
+               const void *key;
+               switch_ssize_t keylen;
+               translate_rule_t *rl, *nrl;
+
+               switch_hash_this(hi, &key, &keylen, &val);
+               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "deleting translate profile [%s]\n", (char *) key);
+
+               for (nrl = val; rl;) {
+                       rl = nrl;
+                       nrl = nrl->next;
+                       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "deleting rule for [%s]\n", rl->regex);
+                       switch_safe_free(rl->regex);
+                       switch_safe_free(rl->replace);
+                       switch_safe_free(rl);
+               }
+
+               switch_core_hash_delete_wrlock(globals.translate_profiles, key, globals.profile_hash_rwlock);
+       }
+
+       switch_thread_rwlock_destroy(globals.profile_hash_rwlock);
+       switch_core_hash_destroy(&globals.translate_profiles);
+
+       switch_mutex_unlock(MUTEX);
+}
+
+static void do_load(void)
+{
+       switch_mutex_lock(MUTEX);
+
+       switch_core_hash_init(&globals.translate_profiles, globals.pool);
+       switch_thread_rwlock_create(&globals.profile_hash_rwlock, globals.pool);
+       load_config();
+
+       switch_mutex_unlock(MUTEX);
+}
+
+static void event_handler(switch_event_t *event)
+{
+       switch_mutex_lock(MUTEX);
+       do_unload();
+       do_load();
+       switch_mutex_unlock(MUTEX);
+       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Number Translations Reloaded\n");
+}
+
+SWITCH_STANDARD_APP(translate_app_function)
+{
+       int argc = 0;
+       char *argv[32] = { 0 };
+       char *mydata = NULL;
+       char *translated = NULL;
+       switch_channel_t *channel = switch_core_session_get_channel(session);
+       switch_memory_pool_t *pool;
+       switch_event_t *event;
+
+       if (!(mydata = switch_core_session_strdup(session, data))) {
+               goto end;
+       }
+
+       if ((argc = switch_separate_string(mydata, ' ', argv, (sizeof(argv) / sizeof(argv[0]))))) {
+               char *areacode = switch_core_get_variable("default_areacode");
+
+               if (session) {
+                       pool = switch_core_session_get_pool(session);
+               } else {
+                       switch_core_new_memory_pool(&pool);
+                       switch_event_create(&event, SWITCH_EVENT_MESSAGE);
+
+                       if (zstr(areacode)) {
+                               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "no default_areacode set, using default of 777\n");
+                               switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "areacode", "777");
+                       } else {
+                               switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "areacode", areacode);
+                       }
+               }
+
+               translate_number(argv[0], argv[1], &translated, session, event);
+
+               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Translated: %s\n", translated);
+
+               switch_channel_set_variable_var_check(channel, "translated", translated, SWITCH_FALSE);
+       }
+
+end:
+       if (!session) {
+               if (pool) {
+                       switch_core_destroy_memory_pool(&pool);
+               }
+       }
+       return;
+}
+
+SWITCH_STANDARD_DIALPLAN(translate_dialplan_hunt)
+{
+       switch_channel_t *channel = switch_core_session_get_channel(session);
+       char *translated_dest = NULL;
+       char *translated_cid_num = NULL;
+       char *translate_profile = NULL;
+       char *areacode = NULL;
+       switch_event_t *event = NULL;
+
+       if (!caller_profile) {
+               if (!(caller_profile = switch_channel_get_caller_profile(channel))) {
+                       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Error Obtaining Profile!\n");
+                       goto done;
+               }
+       }
+
+       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Processing %s <%s>->%s in translate\n",
+                                         caller_profile->caller_id_name, caller_profile->caller_id_number, caller_profile->destination_number);
+
+       if ((translate_profile = (char *) switch_channel_get_variable(channel, "translate_profile"))) {
+               switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "using translate_profile variable [%s] for translate profile\n", translate_profile);
+       } else  if ((translate_profile = (char *) switch_channel_get_variable(channel, "country"))) {
+               switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "using country variable [%s] for translate profile\n", translate_profile);
+       } else if ((translate_profile = (char *) switch_channel_get_variable(channel, "default_country"))) {
+               switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "using default_country variable [%s] for translate profile\n", translate_profile);
+       } else {
+               translate_profile = "US";
+       }
+
+       areacode = (char *) switch_channel_get_variable(channel, "areacode");
+       if (zstr(areacode)) {
+               areacode = (char *) switch_channel_get_variable(channel, "default_areacode");
+               if (!zstr(areacode)) {
+                       switch_channel_set_variable_safe(channel, "areacode", areacode);
+               }
+       }
+
+       translate_number((char *) caller_profile->destination_number, translate_profile, &translated_dest, session, event);
+       translate_number((char *) caller_profile->caller_id_number, translate_profile, &translated_cid_num, session, event);
+       /* maybe we should translate ani/aniii here too? */
+       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO,
+                                         "Profile: [%s] Translated Destination: [%s] Translated CID: [%s]\n", translate_profile, translated_dest, translated_cid_num);
+
+       if (!zstr(translated_cid_num)) {
+               caller_profile->caller_id_number = translated_cid_num;
+       }
+
+       if (!zstr(translated_dest)) {
+               caller_profile->destination_number = translated_dest;
+       }
+
+ done:
+       return NULL;
+}
+
+#define TRANSLATE_SYNTAX "translate <number> [<profile>]"
+SWITCH_STANDARD_API(translate_function)
+{
+       char *mydata = NULL;
+       switch_memory_pool_t *pool = NULL;
+       char *translated = NULL;
+       switch_event_t *event = NULL;
+       char *argv[32] = { 0 };
+       int argc = 0;
+
+       if (zstr(cmd)) {
+               goto usage;
+       }
+       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "%s\n", cmd);
+
+       mydata = switch_core_strdup(globals.pool, cmd);
+
+       if ((argc = switch_separate_string(mydata, ' ', argv, (sizeof(argv) / sizeof(argv[0]))))) {
+               if (!session) {
+                       char *areacode = switch_core_get_variable("default_areacode");
+
+                       switch_event_create(&event, SWITCH_EVENT_REQUEST_PARAMS);
+
+                       if (zstr(areacode)) {
+                               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "no default_areacode set, using default of 777\n");
+                               switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "areacode", "777");
+                       } else {
+                               switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "areacode", areacode);
+                       }
+               }
+               translate_number(argv[0], argv[1], &translated, session, event);
+
+               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Translated: %s\n", translated);
+
+               stream->write_function(stream, "%s", translated);
+       }
+
+end:
+       if (!session) {
+               if (pool) {
+                       switch_core_destroy_memory_pool(&pool);
+               }
+       }
+       return SWITCH_STATUS_SUCCESS;
+
+usage:
+       stream->write_function(stream, "USAGE: %s\n", TRANSLATE_SYNTAX);
+       goto end;
+}
+
+SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_translate_shutdown)
+{
+       switch_event_unbind(&NODE);
+
+       do_unload();
+
+       return SWITCH_STATUS_UNLOAD;
+}
+
+SWITCH_MODULE_LOAD_FUNCTION(mod_translate_load)
+{
+       switch_api_interface_t *api_interface;
+       switch_application_interface_t *app_interface;
+       switch_dialplan_interface_t *dp_interface;
+
+       memset(&globals, 0, sizeof(globals));
+       globals.pool = pool;
+
+
+       switch_mutex_init(&MUTEX, SWITCH_MUTEX_NESTED, pool);
+
+       if ((switch_event_bind_removable(modname, SWITCH_EVENT_RELOADXML, NULL, event_handler, NULL, &NODE) != SWITCH_STATUS_SUCCESS)) {
+               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't bind!\n");
+               return SWITCH_STATUS_TERM;
+       }
+
+       do_load();
+
+       *module_interface = switch_loadable_module_create_module_interface(pool, modname);
+       SWITCH_ADD_API(api_interface, "translate", "TRANSLATE", translate_function, "");
+       SWITCH_ADD_APP(app_interface, "translate", "Perform an TRANSLATE lookup", "Translate a number based on predefined rules", translate_app_function, "<number> <profile>]",
+                                  SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC);
+       SWITCH_ADD_DIALPLAN(dp_interface, "translate", translate_dialplan_hunt);
+
+       return SWITCH_STATUS_SUCCESS;
+}
+
+
+
+/* For Emacs:
+ * Local Variables:
+ * mode:c
+ * indent-tabs-mode:t
+ * tab-width:4
+ * c-basic-offset:4
+ * End:
+ * For VIM:
+ * vim:set softtabstop=4 shiftwidth=4 tabstop=4:
+ */