diff --git a/build/modules.conf.in b/build/modules.conf.in
index b15ae9dcd6..b95f20029e 100644
--- a/build/modules.conf.in
+++ b/build/modules.conf.in
@@ -106,6 +106,7 @@ event_handlers/mod_event_socket
#event_handlers/mod_radius_cdr
#event_handlers/mod_odbc_cdr
#event_handlers/mod_rayo
+#event_handlers/mod_smpp
#event_handlers/mod_snmp
#event_handlers/mod_event_zmq
#formats/mod_imagick
diff --git a/conf/vanilla/autoload_configs/smpp.conf.xml b/conf/vanilla/autoload_configs/smpp.conf.xml
new file mode 100644
index 0000000000..d5b7c0d6fc
--- /dev/null
+++ b/conf/vanilla/autoload_configs/smpp.conf.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/configure.ac b/configure.ac
index 3952bff909..3b58e14685 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1383,6 +1383,10 @@ PKG_CHECK_MODULES([AMQP], [librabbitmq >= 0.5.2],[
AM_CONDITIONAL([HAVE_AMQP],[true])],[
AC_MSG_RESULT([no]); AM_CONDITIONAL([HAVE_AMQP],[false])])
+PKG_CHECK_MODULES([SMPP34], [libsmpp34 >= 1.10],[
+ AM_CONDITIONAL([HAVE_SMPP34],[true])],[
+ AC_MSG_RESULT([no]); AM_CONDITIONAL([HAVE_SMPP34],[false])])
+
AC_ARG_ENABLE(core-libedit-support,
[AS_HELP_STRING([--disable-core-libedit-support], [Compile without libedit Support])])
@@ -1771,6 +1775,7 @@ AC_CONFIG_FILES([Makefile
src/mod/event_handlers/mod_radius_cdr/Makefile
src/mod/event_handlers/mod_odbc_cdr/Makefile
src/mod/event_handlers/mod_rayo/Makefile
+ src/mod/event_handlers/mod_smpp/Makefile
src/mod/event_handlers/mod_snmp/Makefile
src/mod/event_handlers/mod_event_zmq/Makefile
src/mod/formats/mod_imagick/Makefile
diff --git a/src/mod/event_handlers/mod_smpp/Makefile.am b/src/mod/event_handlers/mod_smpp/Makefile.am
new file mode 100644
index 0000000000..c67643787c
--- /dev/null
+++ b/src/mod/event_handlers/mod_smpp/Makefile.am
@@ -0,0 +1,17 @@
+include $(top_srcdir)/build/modmake.rulesam
+MODNAME=mod_smpp
+
+if HAVE_SMPP34
+
+mod_LTLIBRARIES = mod_smpp.la
+mod_smpp_la_SOURCES = mod_smpp_utils.c mod_smpp_gateway.c mod_smpp_message.c mod_smpp.c
+mod_smpp_la_CFLAGS = $(AM_CFLAGS) $(SMPP34_CFLAGS)
+mod_smpp_la_LIBADD = $(switch_builddir)/libfreeswitch.la
+mod_smpp_la_LDFLAGS = -avoid-version -module -no-undefined -shared $(SMPP34_LIBS) $(SWITCH_AM_LDFLAGS)
+
+else
+install: error
+all: error
+error:
+ $(error You must install libsmpp34-dev to build this module)
+endif
diff --git a/src/mod/event_handlers/mod_smpp/TODO b/src/mod/event_handlers/mod_smpp/TODO
new file mode 100644
index 0000000000..da902b9654
--- /dev/null
+++ b/src/mod/event_handlers/mod_smpp/TODO
@@ -0,0 +1,9 @@
+TODO:
+1. add more encoding options
+2. Improve partial read and write support
+3. Add storage options, and registration delivery checks.
+4. SSL connection support
+5. SMPP 5.0 support
+6. async message support
+7. Add support for configuring chatplan profile for inbound messages from a gateway
+8. ... ?
diff --git a/src/mod/event_handlers/mod_smpp/chatplan.conf.xml b/src/mod/event_handlers/mod_smpp/chatplan.conf.xml
new file mode 100644
index 0000000000..5768702d0b
--- /dev/null
+++ b/src/mod/event_handlers/mod_smpp/chatplan.conf.xml
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/mod/event_handlers/mod_smpp/mod_smpp.c b/src/mod/event_handlers/mod_smpp/mod_smpp.c
new file mode 100644
index 0000000000..d6b76ca74f
--- /dev/null
+++ b/src/mod/event_handlers/mod_smpp/mod_smpp.c
@@ -0,0 +1,273 @@
+/*
+* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+* Copyright (C) 2005-2015, Anthony Minessale II
+*
+* 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
+* Portions created by the Initial Developer are Copyright (C)
+* the Initial Developer. All Rights Reserved.
+*
+* Contributor(s):
+*
+* William King
+*
+* mod_smpp.c -- smpp client and server implementation using libsmpp
+*
+* using libsmpp from: http://cgit.osmocom.org/libsmpp/
+*
+*/
+
+#include
+
+SWITCH_MODULE_LOAD_FUNCTION(mod_smpp_load);
+SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_smpp_shutdown);
+SWITCH_MODULE_DEFINITION(mod_smpp, mod_smpp_load, mod_smpp_shutdown, NULL);
+
+mod_smpp_globals_t mod_smpp_globals;
+
+switch_status_t mod_smpp_interface_chat_send(switch_event_t *event)
+{
+ mod_smpp_gateway_t *gateway = NULL;
+ char *gw_name = switch_event_get_header(event, "smpp_gateway");
+
+ if (zstr(gw_name)) {
+ gw_name = "default";
+ }
+
+ gateway = switch_core_hash_find(mod_smpp_globals.gateways, gw_name);
+
+ if (!gateway) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "NO SUCH SMPP GATEWAY[%s].", gw_name);
+ return SWITCH_STATUS_GENERR;
+ }
+
+ mod_smpp_gateway_send_message(gateway, event);
+
+ return SWITCH_STATUS_SUCCESS;
+}
+
+/* static switch_status_t name (switch_event_t *message, const char *data) */
+SWITCH_STANDARD_CHAT_APP(mod_smpp_chat_send_function)
+{
+ mod_smpp_gateway_t *gateway = NULL;
+
+ gateway = switch_core_hash_find(mod_smpp_globals.gateways, data);
+
+ if ( !gateway ) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "NO SUCH SMPP GATEWAY[%s].", data);
+ return SWITCH_STATUS_GENERR;
+ }
+
+ mod_smpp_gateway_send_message(gateway, message);
+ return SWITCH_STATUS_SUCCESS;
+}
+
+/* static void name (switch_core_session_t *session, const char *data) */
+SWITCH_STANDARD_APP(mod_smpp_app_send_function)
+{
+ switch_event_header_t *chan_var = NULL;
+ switch_channel_t *channel = switch_core_session_get_channel(session);
+ switch_event_t *message = NULL;
+
+ if (switch_event_create(&message, SWITCH_EVENT_MESSAGE) != SWITCH_STATUS_SUCCESS) {
+ return;
+ }
+
+ /* Copy over recognized channel vars. Then call the chat send function */
+ /* Cycle through all of the channel headers, and ones with 'smpp_' prefix copy over without the prefix */
+ for ( chan_var = switch_channel_variable_first(channel); chan_var; chan_var = chan_var->next) {
+ if ( !strncmp(chan_var->name, "smpp_", 5) ) {
+ switch_event_add_header_string(message, SWITCH_STACK_BOTTOM, chan_var->name + 5, chan_var->value);
+ } else {
+ switch_event_add_header_string(message, SWITCH_STACK_BOTTOM, chan_var->name, chan_var->value);
+ }
+ }
+
+ /* Unlock the channel variables */
+ switch_channel_variable_last(channel);
+ mod_smpp_chat_send_function(message, data);
+ return;
+}
+
+/* static switch_status_t name (_In_opt_z_ const char *cmd, _In_opt_ switch_core_session_t *session, _In_ switch_stream_handle_t *stream) */
+SWITCH_STANDARD_API(mod_smpp_debug_api)
+{
+ mod_smpp_globals.debug = switch_true(cmd);
+
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "debug is %s\n", (mod_smpp_globals.debug ? "on" : "off") );
+ return SWITCH_STATUS_SUCCESS;
+}
+
+/* static switch_status_t name (_In_opt_z_ const char *cmd, _In_opt_ switch_core_session_t *session, _In_ switch_stream_handle_t *stream) */
+SWITCH_STANDARD_API(mod_smpp_send_api)
+{
+ mod_smpp_gateway_t *gateway = NULL;
+ switch_status_t status = SWITCH_STATUS_SUCCESS;
+ switch_event_t *message = NULL;
+ char *argv[1024] = { 0 };
+ int argc = 0;
+ char *cmd_dup = strdup(cmd);
+
+ if (!(argc = switch_separate_string(cmd_dup, '|', argv, (sizeof(argv) / sizeof(argv[0]))))) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "Invalid format. Must be | separated like: gateway|destination|source|message\n");
+ switch_goto_status(SWITCH_STATUS_GENERR, done);
+ }
+
+ gateway = switch_core_hash_find(mod_smpp_globals.gateways, argv[0]);
+
+ if ( !gateway ) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "NO SUCH SMPP GATEWAY[%s].", argv[0]);
+ switch_goto_status(SWITCH_STATUS_GENERR, done);
+ }
+
+ if (switch_event_create(&message, SWITCH_EVENT_MESSAGE) != SWITCH_STATUS_SUCCESS) {
+ switch_goto_status(SWITCH_STATUS_GENERR, done);
+ }
+
+ switch_event_add_header_string(message, SWITCH_STACK_BOTTOM, "destination_addr", argv[1]);
+ switch_event_add_header_string(message, SWITCH_STACK_BOTTOM, "source_addr", argv[2]);
+ switch_event_set_body(message, argv[3]);
+
+ if (mod_smpp_gateway_send_message(gateway, message) != SWITCH_STATUS_SUCCESS) {
+ switch_goto_status(SWITCH_STATUS_GENERR, done);
+ }
+
+ done:
+ switch_safe_free(cmd_dup);
+ return status;
+
+}
+
+switch_status_t mod_smpp_do_config()
+{
+ char *conf = "smpp.conf";
+ switch_xml_t xml, cfg, gateways, gateway, params, param;
+
+ if (!(xml = switch_xml_open_cfg(conf, &cfg, NULL))) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "open of %s failed\n", conf);
+ goto err;
+ }
+
+ if ( (gateways = switch_xml_child(cfg, "gateways")) != NULL) {
+ for (gateway = switch_xml_child(gateways, "gateway"); gateway; gateway = gateway->next) {
+ mod_smpp_gateway_t *new_gateway = NULL;
+ char *host = NULL, *system_id = NULL, *password = NULL, *profile = NULL, *system_type = NULL;
+ int port = 0, debug = 0;
+
+ char *name = (char *)switch_xml_attr_soft(gateway, "name");
+
+ // Load params
+ if ( (params = switch_xml_child(gateway, "params")) != NULL) {
+ for (param = switch_xml_child(params, "param"); param; param = param->next) {
+ char *var = (char *) switch_xml_attr_soft(param, "name");
+ if ( ! strncmp(var, "host", 4) ) {
+ host = (char *) switch_xml_attr_soft(param, "value");
+ } else if ( ! strncmp(var, "port", 4) ) {
+ port = atoi(switch_xml_attr_soft(param, "value"));
+ } else if ( ! strncmp(var, "debug", 5) ) {
+ debug = atoi(switch_xml_attr_soft(param, "value"));
+ } else if ( ! strncmp(var, "system_id", 9) ) {
+ system_id = (char *) switch_xml_attr_soft(param, "value");
+ } else if ( ! strncmp(var, "password", 8) ) {
+ password = (char *) switch_xml_attr_soft(param, "value");
+ } else if ( ! strncmp(var, "profile", 7) ) {
+ profile = (char *) switch_xml_attr_soft(param, "value");
+ } else if ( ! strncmp(var, "system_type", 11) ) {
+ system_type = (char *) switch_xml_attr_soft(param, "value");
+ }
+ }
+ }
+
+ if ( mod_smpp_gateway_create(&new_gateway, name, host, port, debug, system_id, password, system_type, profile) == SWITCH_STATUS_SUCCESS) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Created gateway[%s]\n", name);
+ } else {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to create gateway[%s]\n", name);
+ }
+ }
+ } else {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Gateways config is missing\n");
+ goto err;
+ }
+
+ return SWITCH_STATUS_SUCCESS;
+
+ err:
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Configuration failed\n");
+ return SWITCH_STATUS_GENERR;
+}
+
+/* switch_status_t name (switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool) */
+SWITCH_MODULE_LOAD_FUNCTION(mod_smpp_load)
+{
+ switch_api_interface_t *mod_smpp_api_interface;
+ switch_chat_interface_t *mod_smpp_chat_interface;
+ switch_chat_application_interface_t *mod_smpp_chat_app_interface;
+ switch_application_interface_t *mod_smpp_app_interface;
+
+ /* connect my internal structure to the blank pointer passed to me */
+ *module_interface = switch_loadable_module_create_module_interface(pool, modname);
+
+ memset(&mod_smpp_globals, 0, sizeof(mod_smpp_globals_t));
+ mod_smpp_globals.pool = pool;
+ mod_smpp_globals.debug = 0;
+ switch_core_hash_init(&(mod_smpp_globals.gateways));
+
+ if ( mod_smpp_do_config() != SWITCH_STATUS_SUCCESS ) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to load due to bad configs\n");
+ return SWITCH_STATUS_TERM;
+ }
+
+ SWITCH_ADD_CHAT(mod_smpp_chat_interface, "smpp", mod_smpp_interface_chat_send);
+ SWITCH_ADD_API(mod_smpp_api_interface, "smpp_debug", "mod_smpp toggle debug", mod_smpp_debug_api, NULL);
+ SWITCH_ADD_API(mod_smpp_api_interface, "smpp_send", "mod_smpp send", mod_smpp_send_api, NULL);
+ SWITCH_ADD_CHAT_APP(mod_smpp_chat_app_interface, "smpp_send", "send message to gateway", "send message to gateway",
+ mod_smpp_chat_send_function, "", SCAF_NONE);
+ SWITCH_ADD_APP(mod_smpp_app_interface, "smpp_send", NULL, NULL, mod_smpp_app_send_function,
+ "smpp_send", SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC);
+
+ /* indicate that the module should continue to be loaded */
+ return SWITCH_STATUS_SUCCESS;
+}
+
+SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_smpp_shutdown)
+{
+ switch_hash_index_t *hi;
+ mod_smpp_gateway_t *gateway = NULL;
+ /* loop through gateways, and destroy them */
+ /* destroy gateways hash */
+
+ while ((hi = switch_core_hash_first(mod_smpp_globals.gateways))) {
+ switch_core_hash_this(hi, NULL, NULL, (void **)&gateway);
+ mod_smpp_gateway_destroy(&gateway);
+ switch_safe_free(hi);
+ }
+
+ switch_core_hash_destroy(&(mod_smpp_globals.gateways));
+
+ 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 noet:
+ */
diff --git a/src/mod/event_handlers/mod_smpp/mod_smpp.h b/src/mod/event_handlers/mod_smpp/mod_smpp.h
new file mode 100644
index 0000000000..318ceb765d
--- /dev/null
+++ b/src/mod/event_handlers/mod_smpp/mod_smpp.h
@@ -0,0 +1,109 @@
+/*
+* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+* Copyright (C) 2005-2015, Anthony Minessale II
+*
+* 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
+* Portions created by the Initial Developer are Copyright (C)
+* the Initial Developer. All Rights Reserved.
+*
+* Contributor(s):
+*
+* William King
+*
+* mod_smpp.c -- smpp client and server implementation using libsmpp
+*
+* using libsmpp from: http://cgit.osmocom.org/libsmpp/
+*
+*/
+
+#ifndef MOD_SMPP_H
+#define MOD_SMPP_H
+
+#include
+#include
+#include
+#include
+
+typedef struct mod_smpp_globals_s {
+ switch_memory_pool_t *pool;
+ switch_hash_t *gateways;
+ uint8_t debug;
+} mod_smpp_globals_t;
+
+extern mod_smpp_globals_t mod_smpp_globals;
+
+typedef struct mod_smpp_gateway_s {
+ char *name;
+ char *host;
+ char *system_id;
+ char *password;
+ char *profile;
+ char *system_type;
+ char address_range[64];
+ uint32_t port;
+ uint32_t sequence;
+ uint32_t running;
+ uint32_t debug;
+ switch_socket_t *socket;
+ switch_sockaddr_t *socketaddr;
+ switch_thread_t *thread;
+ switch_threadattr_t *thd_attr;
+ switch_mutex_t *conn_mutex;
+ switch_memory_pool_t *pool;
+} mod_smpp_gateway_t;
+
+typedef struct mod_smpp_message_s {
+ submit_sm_t req;
+ submit_sm_resp_t res;
+} mod_smpp_message_t;
+
+#define MOD_SMPP_TRANS_RESP "mod_smpp::bind_transceiver_resp"
+
+/* mod_smpp_gateway.c */
+switch_status_t mod_smpp_gateway_authenticate(mod_smpp_gateway_t *gateway);
+switch_status_t mod_smpp_gateway_connect(mod_smpp_gateway_t *gateway);
+switch_status_t mod_smpp_gateway_create(mod_smpp_gateway_t **gw, char *name, char*host, int port, int debug, char *system_id,
+ char *password, char *system_type, char *profile);
+switch_status_t mod_smpp_gateway_destroy(mod_smpp_gateway_t **gateway);
+switch_status_t mod_smpp_gateway_send_message(mod_smpp_gateway_t *gateway, switch_event_t *message);
+switch_status_t mod_smpp_gateway_get_next_sequence(mod_smpp_gateway_t *gateway, uint32_t *seq);
+switch_status_t mod_smpp_gateway_connection_read(mod_smpp_gateway_t *gateway, switch_event_t **event, unsigned int *command_id);
+switch_status_t mod_smpp_gateway_send_enquire_link_response(mod_smpp_gateway_t *gateway);
+switch_status_t mod_smpp_gateway_send_deliver_sm_response(mod_smpp_gateway_t *gateway, switch_event_t *event);
+
+/* mod_smpp_message.c */
+switch_status_t mod_smpp_message_encode_body(char *body, int length, unsigned char *bin, uint8_t *enc_length);
+switch_status_t mod_smpp_message_create(mod_smpp_gateway_t *gateway, switch_event_t *event, mod_smpp_message_t **message);
+switch_status_t mod_smpp_message_decode(mod_smpp_gateway_t *gateway, deliver_sm_t *res, switch_event_t **event);
+switch_status_t mod_smpp_message_destroy(mod_smpp_message_t **msg);
+
+/* mod_smpp_utils.c */
+void mod_smpp_dump_pdu(void *pdu);
+
+#endif /* MOD_SMPP_H */
+
+/* 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 noet:
+ */
diff --git a/src/mod/event_handlers/mod_smpp/mod_smpp_gateway.c b/src/mod/event_handlers/mod_smpp/mod_smpp_gateway.c
new file mode 100644
index 0000000000..1bf3771912
--- /dev/null
+++ b/src/mod/event_handlers/mod_smpp/mod_smpp_gateway.c
@@ -0,0 +1,549 @@
+/*
+* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+* Copyright (C) 2005-2015, Anthony Minessale II
+*
+* 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
+* Portions created by the Initial Developer are Copyright (C)
+* the Initial Developer. All Rights Reserved.
+*
+* Contributor(s):
+*
+* William King
+*
+* mod_smpp.c -- smpp client and server implementation using libsmpp
+*
+* using libsmpp from: http://cgit.osmocom.org/libsmpp/
+*
+*/
+
+
+#include
+
+static void *SWITCH_THREAD_FUNC mod_smpp_gateway_read_thread(switch_thread_t *thread, void *obj);
+
+switch_status_t mod_smpp_gateway_create(mod_smpp_gateway_t **gw, char *name, char *host, int port, int debug, char *system_id,
+ char *password, char *system_type, char *profile) {
+ mod_smpp_gateway_t *gateway = NULL;
+ switch_memory_pool_t *pool = NULL;
+
+ switch_core_new_memory_pool(&pool);
+
+ gateway = switch_core_alloc(pool, sizeof(mod_smpp_gateway_t));
+
+ gateway->pool = pool;
+ gateway->running = 1;
+ gateway->sequence = 1;
+ gateway->name = name ? switch_core_strdup(gateway->pool, name) : "default";
+ gateway->host = host ? switch_core_strdup(gateway->pool, host) : "localhost";
+ gateway->port = port ? port : 8000;
+ gateway->debug = debug;
+ gateway->system_id = system_id ? switch_core_strdup(gateway->pool, system_id) : "username";
+ gateway->password = password ? switch_core_strdup(gateway->pool, password) : "password";
+ gateway->system_type = system_type ? switch_core_strdup(gateway->pool, system_type) : "freeswitch_smpp";
+ gateway->profile = profile ? switch_core_strdup(gateway->pool, profile) : "default";
+
+ if ( switch_sockaddr_info_get(&(gateway->socketaddr), gateway->host, SWITCH_INET,
+ gateway->port, 0, mod_smpp_globals.pool) != SWITCH_STATUS_SUCCESS ) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to get socketaddr info\n");
+ goto err;
+ }
+
+ if ( switch_socket_create(&(gateway->socket), switch_sockaddr_get_family(gateway->socketaddr),
+ SOCK_STREAM, SWITCH_PROTO_TCP, mod_smpp_globals.pool) != SWITCH_STATUS_SUCCESS ) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to create the socket\n");
+ goto err;
+ }
+
+ switch_mutex_init(&(gateway->conn_mutex), SWITCH_MUTEX_UNNESTED, mod_smpp_globals.pool);
+ if (mod_smpp_gateway_connect(gateway) != SWITCH_STATUS_SUCCESS) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to connect to gateway[%s]\n", gateway->name);
+ goto err;
+ }
+
+ /* Start gateway read thread */
+ switch_threadattr_create(&(gateway->thd_attr), mod_smpp_globals.pool);
+ switch_threadattr_detach_set(gateway->thd_attr, 1);
+ switch_threadattr_stacksize_set(gateway->thd_attr, SWITCH_THREAD_STACKSIZE);
+ switch_thread_create(&(gateway->thread), gateway->thd_attr, mod_smpp_gateway_read_thread, (void *) gateway, mod_smpp_globals.pool);
+
+ switch_core_hash_insert(mod_smpp_globals.gateways, name, (void *) gateway);
+
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Gateway %s created\n", gateway->host);
+
+ *gw = gateway;
+ return SWITCH_STATUS_SUCCESS;
+
+ err:
+ mod_smpp_gateway_destroy(&gateway);
+ return SWITCH_STATUS_GENERR;
+
+}
+
+switch_status_t mod_smpp_gateway_authenticate(mod_smpp_gateway_t *gateway) {
+ bind_transceiver_t *req_b = calloc(sizeof(bind_transceiver_t), 1);
+ switch_status_t status = SWITCH_STATUS_SUCCESS;
+ switch_event_t *event = NULL;
+ switch_size_t write_len = 0;
+ unsigned int command_id = 0;
+ uint8_t local_buffer[1024] = {0};
+ int local_buffer_len = 1024;
+
+ req_b->command_length = 0;
+ req_b->command_id = BIND_TRANSCEIVER;
+ req_b->command_status = ESME_ROK;
+ req_b->addr_npi = 1;
+ req_b->addr_ton = 1;
+
+ strncpy( (char *)req_b->address_range, gateway->host, sizeof(req_b->address_range));
+
+ if ( gateway->system_id ) {
+ strncpy((char *)req_b->system_id, gateway->system_id, sizeof(req_b->system_id));
+ }
+
+ if ( gateway->password ) {
+ strncpy((char *)req_b->password, gateway->password, sizeof(req_b->password));
+ }
+
+ if ( gateway->system_type ) {
+ strncpy((char *)req_b->system_type, gateway->system_type, sizeof(req_b->system_type));
+ }
+
+ req_b->interface_version = SMPP_VERSION;
+
+ mod_smpp_gateway_get_next_sequence(gateway, &(req_b->sequence_number));
+
+ // Not thread safe due to smpp34_errno and smpp34_strerror since they are global to running process.
+ // The risk here is that the errno and strerror variables will be corrupted due to race conditions if there are errors
+ if ( smpp34_pack2( local_buffer, sizeof(local_buffer), &local_buffer_len, (void*)req_b) != 0 ) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error in smpp_pack():%d:\n%s\n", smpp34_errno, smpp34_strerror);
+ status = SWITCH_STATUS_GENERR;
+ goto done;
+ }
+
+ write_len = local_buffer_len;
+
+ if ( mod_smpp_globals.debug || gateway->debug ) {
+ mod_smpp_dump_pdu((void *) req_b);
+ }
+
+ if ( switch_socket_send(gateway->socket, (char *) local_buffer, &write_len) != SWITCH_STATUS_SUCCESS ) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to send on the socket\n");
+ switch_goto_status(SWITCH_STATUS_GENERR, done);
+ }
+
+ if( write_len != local_buffer_len ){
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "smpp: Was not able to send entire buffer\n");
+ switch_goto_status(SWITCH_STATUS_GENERR, done);
+ }
+
+ /* Receive the response */
+ if ( mod_smpp_gateway_connection_read(gateway, &event, &command_id) != SWITCH_STATUS_SUCCESS){
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Authentication failed for gateawy[%s]\n", gateway->name);
+ switch_goto_status(SWITCH_STATUS_GENERR, done);
+ } else {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Authentication successful for gateway[%s]\n", gateway->name);
+ }
+
+ done:
+ if ( (mod_smpp_globals.debug || gateway->debug ) && event ) {
+ char *str = NULL;
+ switch_event_serialize(event, &str, SWITCH_TRUE);
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Auth response: %s\n", str);
+ switch_safe_free(str);
+ }
+
+ if ( req_b ) {
+ switch_safe_free(req_b);
+ }
+
+ return status;
+}
+
+/* When connecting to a gateway, the first message must be an authentication attempt */
+switch_status_t mod_smpp_gateway_connect(mod_smpp_gateway_t *gateway) {
+ switch_status_t status;
+
+ if ( (status = switch_socket_connect(gateway->socket, gateway->socketaddr)) != SWITCH_STATUS_SUCCESS ) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to connect the socket %d\n", status);
+ return SWITCH_STATUS_GENERR;
+ }
+
+ if ( mod_smpp_gateway_authenticate(gateway) != SWITCH_STATUS_SUCCESS ) {
+ return SWITCH_STATUS_GENERR;
+ }
+
+ return SWITCH_STATUS_SUCCESS;
+}
+
+
+/* Expects the gateway to be locked already */
+switch_status_t mod_smpp_gateway_connection_read(mod_smpp_gateway_t *gateway, switch_event_t **event, unsigned int *command_id)
+{
+ switch_size_t read_len = 4; /* Default to reading only the first 4 bytes to read how large the PDU is */
+ switch_status_t status = SWITCH_STATUS_SUCCESS;
+ uint8_t *local_buffer = calloc(2048, 1);
+ uint32_t *local_buffer32 = (uint32_t *) local_buffer; /* TODO: Convert this into a union */
+ switch_event_t *evt = NULL;
+ generic_nack_t *gennack = NULL;
+ char data[2048] = {0}; /* local buffer for unpacked PDU */
+
+ /* Read from socket */
+ /* TODO: Add/Expand support for partial reads */
+ if ( switch_socket_recv(gateway->socket, (char *) local_buffer, &read_len) != SWITCH_STATUS_SUCCESS ) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to recv on the socket\n");
+ switch_goto_status(SWITCH_STATUS_GENERR, done);
+ }
+
+ if ( read_len != 4 ){
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error in recv(PEEK) %d\n", (unsigned int )read_len);
+ switch_goto_status(SWITCH_STATUS_GENERR, done);
+ }
+
+ read_len = ntohl(*local_buffer32 );
+
+ if ( read_len > 1500 ) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Corrupted PDU size from gateway [%s]\n", gateway->name);
+ switch_goto_status(SWITCH_STATUS_GENERR, done);
+ }
+
+ if ( ( status = switch_socket_recv(gateway->socket, (char *) local_buffer + 4, &read_len)) != SWITCH_STATUS_SUCCESS ) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to recv on the socket %d\n", status);
+ switch_goto_status(SWITCH_STATUS_GENERR, done);
+ }
+
+ if ( smpp34_unpack2((void *)data, local_buffer, read_len + 4) ) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "smpp: error decoding the receive buffer:%d:%s\n", smpp34_errno, smpp34_strerror);
+ switch_goto_status(SWITCH_STATUS_GENERR, done);
+ }
+
+ if ( mod_smpp_globals.debug || gateway->debug ) {
+ mod_smpp_dump_pdu((void *) data);
+ }
+
+ gennack = (generic_nack_t *) data;
+ *command_id = gennack->command_id;
+ switch(*command_id) {
+ case BIND_TRANSCEIVER_RESP:
+ if ( gennack->command_status != ESME_ROK ) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Authentication Failure %d\n", gennack->command_status);
+ }
+ break;
+ case DELIVER_SM:
+ if ( gennack->command_status == ESME_ROK ) {
+ deliver_sm_t *res = (deliver_sm_t *) data;
+
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "New SMS received from[%s] to[%s] message[%s]\n",
+ res->source_addr, res->destination_addr, res->short_message);
+
+ mod_smpp_message_decode(gateway, res, &evt);
+ }
+ break;
+ case ENQUIRE_LINK:
+ case ENQUIRE_LINK_RESP:
+ case SUBMIT_SM_RESP:
+ break;
+ default:
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unrecognized Command ID: %u\n", *command_id);
+ }
+
+ /* Only need to create/expand the event if it is one of the PDU's that we care about */
+ if (evt) {
+ *event = evt;
+ }
+
+ done:
+ switch_safe_free(local_buffer);
+ return status;
+}
+
+static void *SWITCH_THREAD_FUNC mod_smpp_gateway_read_thread(switch_thread_t *thread, void *obj)
+{
+ mod_smpp_gateway_t *gateway = (mod_smpp_gateway_t *) obj;
+
+ while ( gateway->running ) {
+ switch_event_t *event = NULL;
+ unsigned int command_id = 0;
+
+ if ( mod_smpp_gateway_connection_read(gateway, &event, &command_id) != SWITCH_STATUS_SUCCESS) {
+ if ( gateway->running ) {
+ if ( mod_smpp_gateway_connect(gateway) != SWITCH_STATUS_SUCCESS) {
+ switch_sleep(1000 * 1000);
+ }
+ }
+ continue;
+ }
+
+ if ( (mod_smpp_globals.debug || gateway->debug) && event) {
+ char *str = NULL;
+ switch_event_serialize(event, &str, 0);
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Received message[%d]:\n%s\n\n", command_id, str);
+ switch_safe_free(str);
+ }
+
+ switch(command_id) {
+ case BIND_TRANSCEIVER_RESP:
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Why did we get an unexpected authentication response?\n");
+ break;
+ case ENQUIRE_LINK:
+ mod_smpp_gateway_send_enquire_link_response(gateway);
+ break;
+ case DELIVER_SM:
+ if ( event ) {
+ char *str = NULL;
+ switch_event_serialize(event, &str, SWITCH_TRUE);
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Inbound SMS packet event: \n%s\n\n", str);
+ switch_safe_free(str);
+ }
+
+ mod_smpp_gateway_send_deliver_sm_response(gateway, event);
+
+ switch_core_chat_send("smpp", event);
+ switch_event_destroy(&event);
+ /* Fire message to the chat plan, then respond */
+ break;
+ case SUBMIT_SM_RESP:
+ case ENQUIRE_LINK_RESP:
+ break;
+ default:
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unknown command_id[%d]\n", command_id);
+ if ( event ) {
+ char *str = NULL;
+ switch_event_serialize(event, &str, SWITCH_FALSE);
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unknown packet event: %s\n", str);
+ switch_safe_free(str);
+ }
+ }
+ }
+ return 0;
+}
+
+switch_status_t mod_smpp_gateway_send_enquire_link_response(mod_smpp_gateway_t *gateway)
+{
+ enquire_link_resp_t *enquire_resp = calloc(sizeof(enquire_link_resp_t), 1);
+ switch_status_t status = SWITCH_STATUS_SUCCESS;
+ switch_size_t write_len = 0;
+ uint8_t local_buffer[128] = {0};
+ int local_buffer_len = sizeof(local_buffer);
+
+ enquire_resp->command_length = 0;
+ enquire_resp->command_id = ENQUIRE_LINK_RESP;
+ enquire_resp->command_status = ESME_ROK;
+
+ mod_smpp_gateway_get_next_sequence(gateway, &(enquire_resp->sequence_number));
+
+ if ( smpp34_pack2( local_buffer, sizeof(local_buffer), &local_buffer_len, (void*)enquire_resp) != 0 ) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error in smpp_pack():%d:\n%s\n", smpp34_errno, smpp34_strerror);
+ switch_goto_status(SWITCH_STATUS_GENERR, done);
+ }
+
+ write_len = local_buffer_len;
+
+ if ( mod_smpp_globals.debug || gateway->debug ) {
+ mod_smpp_dump_pdu((void *) enquire_resp);
+ }
+
+ if ( switch_socket_send(gateway->socket, (char *) local_buffer, &write_len) != SWITCH_STATUS_SUCCESS ) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to send on the socket\n");
+ switch_goto_status(SWITCH_STATUS_GENERR, done);
+ }
+
+ if( write_len != local_buffer_len ){
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "smpp: Was not able to send entire buffer\n");
+ switch_goto_status(SWITCH_STATUS_GENERR, done);
+ }
+
+ done:
+ switch_safe_free(enquire_resp);
+
+ return status;
+}
+
+switch_status_t mod_smpp_gateway_send_deliver_sm_response(mod_smpp_gateway_t *gateway, switch_event_t *event)
+{
+ deliver_sm_resp_t *deliver_sm_resp = calloc(sizeof(deliver_sm_resp_t), 1);
+ switch_status_t status = SWITCH_STATUS_SUCCESS;
+ switch_size_t write_len = 0;
+ uint8_t local_buffer[128] = {0};
+ int local_buffer_len = sizeof(local_buffer);
+
+ deliver_sm_resp->command_length = 0;
+ deliver_sm_resp->command_id = DELIVER_SM_RESP;
+ deliver_sm_resp->command_status = ESME_ROK;
+/* deliver_sm_resp->message_id = 0; Not used. calloc defaults to 0 */
+ deliver_sm_resp->sequence_number = atoi(switch_event_get_header(event, "sequence_number"));
+
+ if ( smpp34_pack2( local_buffer, sizeof(local_buffer), &local_buffer_len, (void*)deliver_sm_resp) != 0 ) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error in smpp_pack():%d:\n%s\n", smpp34_errno, smpp34_strerror);
+ switch_goto_status(SWITCH_STATUS_GENERR, done);
+ }
+
+ write_len = local_buffer_len;
+
+ if ( mod_smpp_globals.debug || gateway->debug ) {
+ mod_smpp_dump_pdu((void *) deliver_sm_resp);
+ }
+
+ if ( switch_socket_send(gateway->socket, (char *) local_buffer, &write_len) != SWITCH_STATUS_SUCCESS ) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to send on the socket\n");
+ switch_goto_status(SWITCH_STATUS_GENERR, done);
+ }
+
+ if( write_len != local_buffer_len ){
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "smpp: Was not able to send entire buffer\n");
+ switch_goto_status(SWITCH_STATUS_GENERR, done);
+ }
+
+ done:
+ switch_safe_free(deliver_sm_resp);
+ return status;
+}
+
+switch_status_t mod_smpp_gateway_send_unbind(mod_smpp_gateway_t *gateway)
+{
+ unbind_t *unbind_req = calloc(sizeof(unbind_t), 1);
+ switch_status_t status = SWITCH_STATUS_SUCCESS;
+ switch_size_t write_len = 0;
+ uint8_t local_buffer[128] = {0};
+ int local_buffer_len = sizeof(local_buffer);
+
+ unbind_req->command_length = 0;
+ unbind_req->command_id = UNBIND;
+ unbind_req->command_status = ESME_ROK;
+
+ mod_smpp_gateway_get_next_sequence(gateway, &(unbind_req->sequence_number));
+
+ if ( smpp34_pack2( local_buffer, sizeof(local_buffer), &local_buffer_len, (void*)unbind_req) != 0 ) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error in smpp_pack():%d:\n%s\n", smpp34_errno, smpp34_strerror);
+ switch_goto_status(SWITCH_STATUS_GENERR, done);
+ }
+
+ write_len = local_buffer_len;
+
+ if ( mod_smpp_globals.debug || gateway->debug ) {
+ mod_smpp_dump_pdu((void *) unbind_req);
+ }
+
+ if ( switch_socket_send(gateway->socket, (char *) local_buffer, &write_len) != SWITCH_STATUS_SUCCESS ) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to send on the socket\n");
+ switch_goto_status(SWITCH_STATUS_GENERR, done);
+ }
+
+ if( write_len != local_buffer_len ){
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "smpp: Was not able to send entire buffer\n");
+ switch_goto_status(SWITCH_STATUS_GENERR, done);
+ }
+
+ done:
+ switch_safe_free(unbind_req);
+
+ return status;
+}
+
+switch_status_t mod_smpp_gateway_destroy(mod_smpp_gateway_t **gw)
+{
+ mod_smpp_gateway_t *gateway = NULL;
+
+ if ( !gw || !*gw ) {
+ return SWITCH_STATUS_SUCCESS;
+ }
+
+ gateway = *gw;
+
+ switch_core_hash_delete(mod_smpp_globals.gateways, gateway->name);
+
+ gateway->running = 0;
+
+ mod_smpp_gateway_send_unbind(gateway);
+
+ switch_socket_shutdown(gateway->socket, SWITCH_SHUTDOWN_READWRITE);
+ switch_socket_close(gateway->socket);
+
+ switch_core_destroy_memory_pool(&(gateway->pool));
+ switch_mutex_destroy(gateway->conn_mutex);
+
+ *gw = NULL;
+ return SWITCH_STATUS_SUCCESS;
+}
+
+switch_status_t mod_smpp_gateway_get_next_sequence(mod_smpp_gateway_t *gateway, uint32_t *seq)
+{
+
+ switch_mutex_lock(gateway->conn_mutex);
+ gateway->sequence++;
+ *seq = gateway->sequence;
+ switch_mutex_unlock(gateway->conn_mutex);
+
+ return SWITCH_STATUS_SUCCESS;
+}
+
+switch_status_t mod_smpp_gateway_send_message(mod_smpp_gateway_t *gateway, switch_event_t *message) {
+ mod_smpp_message_t *msg = NULL;
+ uint8_t local_buffer[1024];
+ int local_buffer_len = sizeof(local_buffer);
+ switch_status_t status = SWITCH_STATUS_SUCCESS;
+ switch_size_t write_len = 0;
+
+ if ( mod_smpp_message_create(gateway, message, &msg) != SWITCH_STATUS_SUCCESS) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unable to send message due to message_create failure\n");
+ switch_goto_status(SWITCH_STATUS_GENERR, done);
+ }
+
+ memset(local_buffer, 0, sizeof(local_buffer));
+
+ if( smpp34_pack2( local_buffer, sizeof(local_buffer), &local_buffer_len, (void*)&(msg->req)) != 0 ){
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "smpp: Unable to encode message:%d:\n%s\n", smpp34_errno, smpp34_strerror);
+ switch_goto_status(SWITCH_STATUS_GENERR, done);
+ }
+
+ write_len = local_buffer_len;
+
+ if ( mod_smpp_gateway_get_next_sequence(gateway, &(msg->req.sequence_number)) ) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to fetch next gateway sequence number\n");
+ switch_goto_status(SWITCH_STATUS_GENERR, done);
+ }
+
+ if ( mod_smpp_globals.debug || gateway->debug ) {
+ mod_smpp_dump_pdu((void *) &(msg->req));
+ }
+
+ if ( switch_socket_send(gateway->socket, (char *) local_buffer, &write_len) != SWITCH_STATUS_SUCCESS ) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to send on the socket\n");
+ switch_goto_status(SWITCH_STATUS_GENERR, done);
+ }
+
+ if ( write_len != local_buffer_len ){
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "smpp: Did not send all of message to gateway");
+ switch_goto_status(SWITCH_STATUS_GENERR, done);
+ }
+
+ done:
+ mod_smpp_message_destroy(&msg);
+ return status;
+}
+
+
+/* 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 noet:
+ */
diff --git a/src/mod/event_handlers/mod_smpp/mod_smpp_message.c b/src/mod/event_handlers/mod_smpp/mod_smpp_message.c
new file mode 100644
index 0000000000..6c168b5b83
--- /dev/null
+++ b/src/mod/event_handlers/mod_smpp/mod_smpp_message.c
@@ -0,0 +1,192 @@
+/*
+* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+* Copyright (C) 2005-2015, Anthony Minessale II
+*
+* 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
+* Portions created by the Initial Developer are Copyright (C)
+* the Initial Developer. All Rights Reserved.
+*
+* Contributor(s):
+*
+* William King
+*
+* mod_smpp.c -- smpp client and server implementation using libsmpp
+*
+* using libsmpp from: http://cgit.osmocom.org/libsmpp/
+*
+*/
+
+#include
+
+switch_status_t mod_smpp_message_encode_body(char *body, int length, unsigned char *bin, uint8_t *enc_length)
+{
+ int i = 0;
+
+ for ( i = 0; i < length; i++ ) {
+ bin[i*2] = body[i] / 16;
+ bin[i*2 + 1] = body[i] % 16;
+ }
+
+ *enc_length = (i * 2) + 1;
+ return SWITCH_STATUS_SUCCESS;
+}
+
+/*
+ Scratch notes taken during development/interop:
+
+ char *message = "5361792048656c6c6f20746f204d79204c6974746c6520467269656e64";
+ ''.join('%02x' % ord(c) for c in u'Скажите привет моему маленькому другу'.encode('utf16'))
+
+ Variable length UTF-16 russian text:
+
+ char *message = "fffe21043a043004360438044204350420003f044004380432043504420420003c043e0435043c04430420003c0430043b0435043d044c043a043e043c044304200034044004430433044304";
+ char *mesg_txt = "This is a test SMS message from FreeSWITCH over SMPP";
+ */
+
+switch_status_t mod_smpp_message_create(mod_smpp_gateway_t *gateway, switch_event_t *event, mod_smpp_message_t **message)
+{
+ mod_smpp_message_t *msg = calloc(1, sizeof(mod_smpp_message_t));
+ char *body = switch_event_get_body(event);
+
+ assert(*message == NULL);
+
+ if ( !body ) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unable to encode message missing body\n");
+ goto err;
+ }
+
+ if ( mod_smpp_globals.debug || gateway->debug ) {
+ char *str = NULL;
+ switch_event_serialize(event, &str, 0);
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Creating message from event:\n%s\n", str);
+ switch_safe_free(str);
+ }
+
+ msg->req.command_id = SUBMIT_SM;
+ msg->req.command_status = ESME_ROK;
+ msg->req.command_length = 0;
+ msg->req.protocol_id = 0;
+ msg->req.priority_flag = 0;
+ msg->req.registered_delivery = 0;
+ msg->req.replace_if_present_flag = 0;
+ msg->req.sm_default_msg_id = 0;
+ msg->req.data_coding = 0;
+ msg->req.source_addr_ton = 1;
+ msg->req.source_addr_npi = 1;
+ msg->req.dest_addr_ton = 1;
+ msg->req.dest_addr_npi = 1;
+ msg->req.esm_class = 1; /* 0 => default, 1 => datagram, 2 => forward(transaction), 3 => store and forward
+ 2 is endpoint delivery, all others are into a DB first.
+ */
+
+ mod_smpp_gateway_get_next_sequence(gateway, &(msg->req.sequence_number));
+
+ snprintf((char *)msg->req.service_type, sizeof(msg->req.service_type), "%s", "SMS");
+ snprintf((char *)msg->req.source_addr, sizeof(msg->req.source_addr), "%s", switch_event_get_header(event, "from_user"));
+ snprintf((char *)msg->req.destination_addr, sizeof(msg->req.destination_addr), "%s", switch_event_get_header(event, "to_user"));
+ snprintf((char *)msg->req.schedule_delivery_time, sizeof(msg->req.schedule_delivery_time), "%s", "");
+ snprintf((char *)msg->req.validity_period, sizeof(msg->req.validity_period), "%s", "");
+ snprintf((char *)msg->req.short_message, sizeof(msg->req.short_message), "%s", body);
+ msg->req.sm_length = strlen(body);
+
+ if ( 0 && mod_smpp_message_encode_body(body, strlen(body),
+ (unsigned char *) &(msg->req.short_message), &(msg->req.sm_length)) != SWITCH_STATUS_SUCCESS ) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to encode message body\n");
+ goto err;
+ }
+
+ *message = msg;
+ return SWITCH_STATUS_SUCCESS;
+
+ err:
+ switch_safe_free(msg);
+ return SWITCH_STATUS_GENERR;
+}
+
+switch_status_t mod_smpp_message_decode(mod_smpp_gateway_t *gateway, deliver_sm_t *res, switch_event_t **event)
+{
+ switch_event_t *evt = NULL;
+ char *str = NULL;
+
+ if (switch_event_create(&evt, SWITCH_EVENT_MESSAGE) != SWITCH_STATUS_SUCCESS) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to create new event\n");
+ }
+
+ switch_event_add_header_string(evt, SWITCH_STACK_BOTTOM, "endpoint", "mod_smpp");
+
+ str = switch_mprintf("%d", res->sequence_number);
+ switch_event_add_header_string(evt, SWITCH_STACK_BOTTOM | SWITCH_STACK_NODUP, "sequence_number", str);
+
+ str = switch_mprintf("%d", res->command_status);
+ switch_event_add_header_string(evt, SWITCH_STACK_BOTTOM | SWITCH_STACK_NODUP, "command_status", str);
+
+ str = switch_mprintf("%d", res->command_id);
+ switch_event_add_header_string(evt, SWITCH_STACK_BOTTOM | SWITCH_STACK_NODUP, "command_id", str);
+
+ switch_event_add_header_string(evt, SWITCH_STACK_BOTTOM, "smpp_gateway", gateway->name);
+ switch_event_add_header_string(evt, SWITCH_STACK_BOTTOM, "proto", "smpp");
+
+ str = switch_mprintf("%d", res->source_addr_ton);
+ switch_event_add_header_string(evt, SWITCH_STACK_BOTTOM | SWITCH_STACK_NODUP, "source_addr_ton", str);
+
+ str = switch_mprintf("%d", res->source_addr_npi);
+ switch_event_add_header_string(evt, SWITCH_STACK_BOTTOM | SWITCH_STACK_NODUP, "source_addr_npi", str);
+
+ switch_event_add_header_string(evt, SWITCH_STACK_BOTTOM, "from_user", (const char *) res->source_addr);
+
+ str = switch_mprintf("%d", res->dest_addr_ton);
+ switch_event_add_header_string(evt, SWITCH_STACK_BOTTOM | SWITCH_STACK_NODUP, "dest_addr_ton", str);
+
+ str = switch_mprintf("%d", res->dest_addr_npi);
+ switch_event_add_header_string(evt, SWITCH_STACK_BOTTOM | SWITCH_STACK_NODUP, "dest_addr_npi", str);
+
+ switch_event_add_header_string(evt, SWITCH_STACK_BOTTOM, "to_user", (const char *) res->destination_addr);
+
+ str = switch_mprintf("%d", res->data_coding);
+ switch_event_add_header_string(evt, SWITCH_STACK_BOTTOM | SWITCH_STACK_NODUP, "data_coding", str);
+ str = NULL;
+
+ switch_event_add_header_string(evt, SWITCH_STACK_BOTTOM, "profile", gateway->profile);
+
+ switch_event_add_body(evt, "%s", (const char *) res->short_message);
+
+ *event = evt;
+
+ return SWITCH_STATUS_SUCCESS;
+}
+
+switch_status_t mod_smpp_message_destroy(mod_smpp_message_t **msg)
+{
+ if ( msg ) {
+ switch_safe_free(*msg);
+ }
+
+ 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 noet:
+ */
diff --git a/src/mod/event_handlers/mod_smpp/mod_smpp_utils.c b/src/mod/event_handlers/mod_smpp/mod_smpp_utils.c
new file mode 100644
index 0000000000..d948372d2b
--- /dev/null
+++ b/src/mod/event_handlers/mod_smpp/mod_smpp_utils.c
@@ -0,0 +1,58 @@
+/*
+* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+* Copyright (C) 2005-2015, Anthony Minessale II
+*
+* 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
+* Portions created by the Initial Developer are Copyright (C)
+* the Initial Developer. All Rights Reserved.
+*
+* Contributor(s):
+*
+* William King
+*
+* mod_smpp.c -- smpp client and server implementation using libsmpp
+*
+* using libsmpp from: http://cgit.osmocom.org/libsmpp/
+*
+*/
+
+#include
+
+void mod_smpp_dump_pdu(void *pdu)
+{
+ uint8_t *print_buffer = calloc(4096, 1);
+
+ if ( !smpp34_dumpPdu2(print_buffer, 4096, (void*) pdu) ) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "PDU \n%s\n", print_buffer);
+ } else {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error in smpp_dumpPdu():%d:\n%s\n", smpp34_errno, smpp34_strerror);
+ }
+
+ free(print_buffer);
+}
+
+/* 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 noet:
+ */