[PATCH] Database SMSC driver
Donald Jackson <[email protected]>
| Newsgroups | gmane.comp.mobile.kannel.devel |
|---|---|
| Message-ID | <[email protected]> |
Hi everyone, Here is a new SMSC driver which takes messages and inserts them into a database table for processing. It also polls (configurable) database tables for MO's and DLR's if necessary. This is useful if you have messages incoming over SMPP (or other scenarios I haven't thought of :D) and want them in an easy to use format for bulk processing. I intentionally didn't use the mysql-connection group because it would require too many changes in layers above as the full Cfg* context isn't provided to the driver. It currently only supports MySQL but could support others in future. Enjoy! Thanks, Donald Jackson www.panaceamobile.com
smsc_db.patch
(application/octet-stream, 33.9 KB)
Index: gwlib/cfg.def
===================================================================
--- gwlib/cfg.def (revision 4883)
+++ gwlib/cfg.def (working copy)
@@ -418,6 +418,14 @@
OCTSTR(generic-status-sent)
OCTSTR(generic-status-error)
OCTSTR(generic-foreign-id-regex)
+ OCTSTR(db-type)
+ OCTSTR(db-name)
+ OCTSTR(db-max-connections)
+ OCTSTR(db-msg-table)
+ OCTSTR(db-dlr-table)
+ OCTSTR(db-mo-table)
+ OCTSTR(db-dlr-interval)
+ OCTSTR(db-mo-interval)
)
Index: doc/userguide/userguide.xml
===================================================================
--- doc/userguide/userguide.xml (revision 4883)
+++ doc/userguide/userguide.xml (working copy)
@@ -4493,7 +4493,112 @@
</tbody></tgroup></informaltable>
</sect2>
+<sect2>
+<title>Database SMSC</title>
+ <para>This SMSC takes messages that are routed to it and adds them to a database table to be processed by another application. It also polls tables for incoming DLR's as well as MO messages. As it receives these messages it injects them back into the bearerbox as with a traditional SMSC.
+ </para>
+
+<programlisting>
+group = smsc
+smsc = db
+port = 3306
+db-type = mysql
+db-name = smsc_db_name
+host = dbhost
+smsc-username = db_username
+smsc-password = db_password
+db-msg-table = mt_messages
+db-dlr-table = dlr_messages
+db-mo-table = mo_messages
+</programlisting>
+
+ <informaltable frame="none">
+ <tgroup cols="3"><thead><row>
+ <entry>Variable</entry>
+ <entry>Value</entry>
+ <entry>Description</entry>
+ </row></thead><tbody>
+
+ <row><entry><literal>host (m)</literal></entry>
+ <entry><literal>hostname</literal></entry>
+ <entry valign="bottom">
+ Hostname / IP address of the database server
+ </entry></row>
+
+ <row><entry><literal>port (m)</literal></entry>
+ <entry><literal>port-number</literal></entry>
+ <entry valign="bottom">
+ Port number of the database server
+ </entry></row>
+
+ <row><entry><literal>db-type (m)</literal></entry>
+ <entry><literal>string</literal></entry>
+ <entry valign="bottom">
+ The database type of the connection (currently only MySQL supported)
+ </entry></row>
+
+ <row><entry><literal>db-name (m)</literal></entry>
+ <entry><literal>string</literal></entry>
+ <entry valign="bottom">
+ The database name
+ </entry></row>
+
+ <row><entry><literal>smsc-username (m)</literal></entry>
+ <entry><literal>string</literal></entry>
+ <entry valign="bottom">
+ Username to use to connect to the database
+ </entry></row>
+
+ <row><entry><literal>smsc-password (o)</literal></entry>
+ <entry><literal>string</literal></entry>
+ <entry valign="bottom">
+ Password to use to connect to the database
+ </entry></row>
+
+ <row><entry><literal>db-msg-table (o)</literal></entry>
+ <entry><literal>string</literal></entry>
+ <entry valign="bottom">
+ Table to store MT messages in
+ </entry></row>
+
+ <row><entry><literal>db-mo-table (o)</literal></entry>
+ <entry><literal>string</literal></entry>
+ <entry valign="bottom">
+ Table to to read MO messages from
+ </entry></row>
+
+ <row><entry><literal>db-dlr-table (o)</literal></entry>
+ <entry><literal>string</literal></entry>
+ <entry valign="bottom">
+ Table to to read DLR messages from
+ </entry></row>
+
+ <row><entry><literal>db-max-connections (o)</literal></entry>
+ <entry><literal>number</literal></entry>
+ <entry valign="bottom">
+ Maximum amount of connections to the database
+ </entry></row>
+
+ <row><entry><literal>db-dlr-interval (o)</literal></entry>
+ <entry><literal>number</literal></entry>
+ <entry valign="bottom">
+ How often (in seconds) to poll the DLR table for new messages
+ </entry></row>
+
+ <row><entry><literal>db-mo-interval (o)</literal></entry>
+ <entry><literal>number</literal></entry>
+ <entry valign="bottom">
+ How often (in seconds) to poll the MO table for new messages
+ </entry></row>
+
+
+
+ </tbody></tgroup></informaltable>
+
+</sect2>
+
+
<sect2>
<title>Using multiple SMS centers</title>
Index: gw/smscconn_p.h
===================================================================
--- gw/smscconn_p.h (revision 4883)
+++ gw/smscconn_p.h (working copy)
@@ -289,6 +289,9 @@
/* Responsible file: smsc/smsc_loopback.c */
int smsc_loopback_create(SMSCConn *conn, CfgGroup *cfg);
+/* Responsible file: smsc/smsc_db.c */
+int smsc_db_create(SMSCConn *conn, CfgGroup *cfg);
+
/* ADD NEW CREATE FUNCTIONS HERE
*
* int smsc_xxx_create(SMSCConn *conn, CfgGroup *cfg);
Index: gw/smscconn.c
===================================================================
--- gw/smscconn.c (revision 4883)
+++ gw/smscconn.c (working copy)
@@ -284,7 +284,9 @@
ret = smsc_oisd_create(conn, grp);
else if (octstr_compare(smsc_type, octstr_imm("loopback")) == 0)
ret = smsc_loopback_create(conn, grp);
- else
+ else if(octstr_compare(smsc_type, octstr_imm("db")) == 0) {
+ ret = smsc_db_create(conn, grp);
+ } else
ret = smsc_wrapper_create(conn, grp);
octstr_destroy(smsc_type);
Index: gw/smsc/smsc_db_mysql.c
===================================================================
--- gw/smsc/smsc_db_mysql.c (revision 0)
+++ gw/smsc/smsc_db_mysql.c (revision 0)
@@ -0,0 +1,443 @@
+/* ====================================================================
+ * The Kannel Software License, Version 1.0
+ *
+ * Copyright (c) 2001-2010 Kannel Group
+ * Copyright (c) 1998-2001 WapIT Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Kannel Group (http://www.kannel.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Kannel" and "Kannel Group" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission. For written permission, please
+ * contact [email protected].
+ *
+ * 5. Products derived from this software may not be called "Kannel",
+ * nor may "Kannel" appear in their name, without prior written
+ * permission of the Kannel Group.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE KANNEL GROUP OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Kannel Group. For more information on
+ * the Kannel Group, please see <http://www.kannel.org/>.
+ *
+ * Portions of this software are based upon software originally written at
+ * WapIT Ltd., Helsinki, Finland for the Kannel project.
+ */
+
+/*
+ * smsc_db_mysql.c - MySQL connection for smsc_db.c
+ *
+ * Donald Jackson (www.panaceamobile.com), 2011
+ *
+ * MT and MO messages proposed structure (just change table name)
+ *
+ * CREATE TABLE `messages` (
+ `sender` VARCHAR( 32 ) NULL ,
+ `receiver` VARCHAR( 32 ) NULL ,
+ `udhdata` VARCHAR( 64 ) NULL ,
+ `msgdata` VARCHAR( 255 ) NULL ,
+ `time` INT UNSIGNED NOT NULL ,
+ `smsc_id` VARCHAR( 64 ) NULL ,
+ `smsc_number` VARCHAR( 32 ) NULL ,
+ `foreign_id` VARCHAR( 255 ) NULL ,
+ `service` VARCHAR( 64 ) NULL ,
+ `account` VARCHAR( 64 ) NULL ,
+ `id` VARCHAR( 36 ) NULL ,
+ `sms_type` TINYINT NOT NULL ,
+ `mclass` TINYINT NOT NULL ,
+ `mwi` TINYINT NOT NULL ,
+ `coding` TINYINT NOT NULL ,
+ `compress` TINYINT NOT NULL ,
+ `validity` INT NOT NULL ,
+ `deferred` INT NOT NULL ,
+ `dlr_mask` TINYINT NOT NULL ,
+ `dlr_url` VARCHAR( 255 ) NULL ,
+ `pid` INT NOT NULL ,
+ `alt_dcs` TINYINT NOT NULL ,
+ `rpi` INT NOT NULL ,
+ `charset` VARCHAR( 64 ) NULL ,
+ `boxc_id` VARCHAR( 64 ) NULL ,
+ `binfo` VARCHAR( 255 ) NULL ,
+ `msg_left` TINYINT NOT NULL ,
+ `split_parts` VARCHAR( 255 ) NULL ,
+ `priority` TINYINT NOT NULL ,
+ `resend_try` TINYINT NOT NULL ,
+ `resend_time` INT NOT NULL ,
+ `meta_data` TEXT NULL
+ ) ENGINE = INNODB;
+ *
+ * Proposed DLR table structure
+ *
+ * CREATE TABLE IF NOT EXISTS `dlrs` (
+ `msg_id` varchar(36) NOT NULL,
+ `status` tinyint(4) NOT NULL,
+ UNIQUE KEY `msg_id` (`msg_id`,`status`)
+ ) ENGINE=MyISAM;
+ *
+ */
+
+#include "gwlib/gwlib.h"
+#include "gwlib/dbpool.h"
+#include "msg.h"
+#include "smsc_db.h"
+
+#ifdef HAVE_MYSQL
+
+void smsc_db_mysql_mo_remove(smsc_db_conn *dbconn, Msg *msg) {
+ char id[UUID_STR_LEN + 1];
+
+ List *binds = gwlist_create();
+
+ uuid_unparse(msg->sms.id, id);
+
+ debug("smsc.db.mysql.mo.remove", 0, "Removing %s from MO table", id);
+
+ gwlist_produce(binds, octstr_create(id));
+
+ Octstr *sql = octstr_format("DELETE FROM %S WHERE id = ? ", dbconn->mo_table);
+
+ DBPoolConn *conn = dbpool_conn_consume(dbconn->pool);
+
+ if((dbpool_conn_update(conn, sql, binds)) == -1) {
+ error(0, "Error performing query '%s'", octstr_get_cstr(sql));
+ }
+
+ dbpool_conn_produce(conn);
+
+ gwlist_destroy(binds, octstr_destroy_item);
+ octstr_destroy(sql);
+}
+
+void smsc_db_mysql_dlr_remove(smsc_db_conn *dbconn, smsc_db_dlr *dlr) {
+
+ List *binds = gwlist_create();
+ gwlist_produce(binds, octstr_duplicate(dlr->msg_id));
+ gwlist_produce(binds, octstr_format("%d", dlr->status));
+
+ Octstr *sql = octstr_format("DELETE FROM %S WHERE msg_id = ? AND status = ?", dbconn->dlr_table);
+
+ DBPoolConn *conn = dbpool_conn_consume(dbconn->pool);
+
+ if((dbpool_conn_update(conn, sql, binds)) == -1) {
+ error(0, "Error performing query '%s'", octstr_get_cstr(sql));
+ }
+
+ dbpool_conn_produce(conn);
+
+ gwlist_destroy(binds, octstr_destroy_item);
+ octstr_destroy(sql);
+}
+
+void smsc_db_mysql_shutdown(smsc_db_conn *dbconn) {
+ debug("smsc.db.mysql.shutdown", 0,"SMSC DB MySQL shutdown called");
+ dbpool_destroy(dbconn->pool);
+ octstr_destroy(dbconn->mo_table);
+ octstr_destroy(dbconn->dlr_table);
+ octstr_destroy(dbconn->message_table);
+
+ gw_free(dbconn);
+}
+
+List *smsc_db_mysql_dlr_get(smsc_db_conn *dbconn) {
+ int state;
+ Octstr *sql = octstr_format("SELECT msg_id, status FROM %S", dbconn->dlr_table);
+
+ DBPoolConn *conn = dbpool_conn_consume(dbconn->pool);
+
+ List *result, *row;
+
+ List *dlrs = gwlist_create();
+
+ smsc_db_dlr *dlr;
+
+ if(dbpool_conn_select(conn, sql, NULL, &result) != 0) {
+ error(0, "Error with query '%s'", octstr_get_cstr(sql));
+ } else {
+ while((row = gwlist_extract_first(result)) != NULL) {
+ dlr = smsc_db_dlr_create();
+ dlr->msg_id = octstr_duplicate(gwlist_get(row, 0));
+ dlr->status = atoi(octstr_get_cstr(gwlist_get(row, 1)));
+ gwlist_produce(dlrs, dlr);
+ gwlist_destroy(row, octstr_destroy_item);
+ }
+
+ gwlist_destroy(result, NULL);
+ }
+
+ octstr_destroy(sql);
+ dbpool_conn_produce(conn);
+
+ return dlrs;
+
+}
+
+List *smsc_db_mysql_messages_mo_get(smsc_db_conn *dbconn) {
+ Octstr *sql;
+ Octstr *tmp = NULL;
+ List *messages = gwlist_create();
+ List *results = NULL;
+ List *row;
+
+ Msg *msg = msg_create(sms);
+
+ int res = 1;
+
+ long position = 0;
+
+ DBPoolConn *conn;
+
+ char id[UUID_STR_LEN + 1];
+
+ sql = octstr_format("SELECT ");
+
+#define INTEGER(name) octstr_append_cstr(sql, "`" #name "`,");
+#define OCTSTR(name) octstr_append_cstr(sql, "`" #name "`,");
+#define UUID(name) uuid_unparse(p->name, id); \
+ octstr_append_cstr(sql, "`" #name "`,");
+#define VOID(name) ;
+#define MSG(type, stmt) \
+ case type: {struct type *p = &msg->type; stmt} break;
+ switch (msg->type) {
+#include "msg-decl.h"
+ default:
+ error(0, "DB: Internal error: unknown message type %d", msg->type);
+ msg_destroy(msg);
+ return messages;
+ }
+
+ msg_destroy(msg);
+
+ octstr_delete(sql, (octstr_len(sql)-1), 1);
+ octstr_format_append(sql, " FROM %S ", dbconn->mo_table);
+
+ conn = dbpool_conn_consume(dbconn->pool);
+
+ dbpool_conn_select(conn, sql, NULL, &results);
+
+ octstr_destroy(sql);
+
+ dbpool_conn_produce(conn);
+
+ if(gwlist_len(results) > 0) {
+ while((row = gwlist_extract_first(results)) != NULL) {
+ /* Let's reconstruct a message packet from this data */
+ debug("smsc.db.mysql", 0, "Got MO message to process");
+ msg = msg_create(sms);
+#define INTEGER(name) p->name = atoi(octstr_get_cstr(gwlist_get(row,position))); ++position;
+#define OCTSTR(name) p->name = octstr_duplicate(gwlist_get(row,position)); ++position;
+#define UUID(name) uuid_parse(gwlist_get(row, position), p->name); ++position;
+#define VOID(name) ;
+#define MSG(type, stmt) \
+ case type: { struct type *p = &msg->type; position = 0; stmt; } break;
+ switch (msg->type) {
+#include "msg-decl.h"
+ default:
+ error(0, "DB: Internal error: unknown message type %d", msg->type);
+ return NULL;
+ }
+
+ gwlist_produce(messages, msg);
+
+ gwlist_destroy(row, octstr_destroy_item);
+ }
+ }
+
+ gwlist_destroy(results, NULL);
+
+ return messages;
+}
+
+int smsc_db_mysql_message_add(smsc_db_conn *dbconn, Msg *msg) {
+ Octstr *sql;
+ Octstr *sql_values;
+ Octstr *tmp;
+ List *binds = gwlist_create();
+
+ int res = 1;
+
+ DBPoolConn *conn;
+
+ char id[UUID_STR_LEN + 1];
+
+ sql = octstr_format("INSERT INTO %S ( ", dbconn->message_table);
+
+ sql_values = octstr_create(" ) VALUES ( ");
+
+#define INTEGER(name) octstr_append_cstr(sql, #name ","); gwlist_produce(binds, octstr_format("%d", p->name)); octstr_append_cstr(sql_values, "?,");
+#define OCTSTR(name) octstr_append_cstr(sql, #name ","); gwlist_produce(binds, octstr_duplicate(p->name)); octstr_append_cstr(sql_values, "?,");
+#define UUID(name) uuid_unparse(p->name, id); \
+ octstr_append_cstr(sql, #name ","); gwlist_produce(binds, octstr_format("%s", id)); octstr_append_cstr(sql_values, "?,");
+#define VOID(name) ;
+#define MSG(type, stmt) \
+ case type: {struct type *p = &msg->type; stmt} break;
+ switch (msg->type) {
+#include "msg-decl.h"
+ default:
+ error(0, "DB: Internal error: unknown message type %d", msg->type);
+ return -1;
+ }
+
+ if(msg_type(msg) == sms) {
+ /* Don't care about non-sms */
+ octstr_delete(sql, (octstr_len(sql)-1), 1);
+ octstr_delete(sql_values, (octstr_len(sql_values)-1), 1);
+ octstr_append(sql, sql_values);
+ octstr_append_cstr(sql, ");");
+
+ conn = dbpool_conn_consume(dbconn->pool);
+
+ if ((res = dbpool_conn_update(conn, sql, binds)) == -1) {
+ error(0, "Error with insert query '%s'", octstr_get_cstr(sql));
+ } else {
+ debug("smsc.db.mysql.message.add", 0, "Message inserted into database");
+ }
+
+ dbpool_conn_produce(conn);
+ } else {
+ warning(0, "Got non-sms message for processing, ignored");
+ res = -1;
+ }
+
+ gwlist_destroy(binds, octstr_destroy_item);
+ octstr_destroy(sql);
+ octstr_destroy(sql_values);
+
+ return res;
+}
+
+smsc_db_conn *smsc_db_init_mysql(CfgGroup *grp) {
+ /* Unfortunately because the entire Cfg structure is not passed from higher levels, we must use config information from the SMSC CfgGroup */
+
+ smsc_db_conn *dbconn;
+
+ long pool_size;
+ long mysql_port = 0;
+
+ DBConf *db_conf;
+ DBPool *pool;
+
+ Octstr *mysql_user;
+ Octstr *mysql_pass;
+ Octstr *mysql_host;
+ Octstr *mysql_db;
+
+ if (cfg_get_integer(&pool_size, grp, octstr_imm("db-max-connections")) == -1 || pool_size == 0)
+ {
+ pool_size = 1;
+ }
+
+ if (!(mysql_host = cfg_get(grp, octstr_imm("host"))))
+ {
+ warning(0, "SMSC: MySQL: directive 'host' is not specified!");
+ }
+ if (!(mysql_user = cfg_get(grp, octstr_imm("smsc-username"))))
+ {
+ warning(0, "SMSC: MySQL: directive 'smsc-username' is not specified!");
+ }
+ if (!(mysql_pass = cfg_get(grp, octstr_imm("smsc-password"))))
+ {
+ warning(0, "SMSC: MySQL: directive 'password' is not specified!");
+ }
+ if (!(mysql_db = cfg_get(grp, octstr_imm("db-name"))))
+ {
+ warning(0, "SMSC: MySQL: directive 'db-name' is not specified!");
+ }
+
+ cfg_get_integer(&mysql_port, grp, octstr_imm("port"));
+
+ /*
+ * ok, ready to connect to MySQL
+ */
+ db_conf = gw_malloc(sizeof(DBConf));
+ gw_assert(db_conf != NULL);
+
+ db_conf->mysql = gw_malloc(sizeof(MySQLConf));
+ gw_assert(db_conf->mysql != NULL);
+
+ db_conf->mysql->host = mysql_host;
+ db_conf->mysql->port = mysql_port;
+ db_conf->mysql->username = mysql_user;
+ db_conf->mysql->password = mysql_pass;
+ db_conf->mysql->database = mysql_db;
+
+ pool = dbpool_create(DBPOOL_MYSQL, db_conf, pool_size);
+
+ if(pool != NULL) {
+ dbconn = gw_malloc(sizeof(smsc_db_conn));
+ dbconn->type = "mysql";
+ dbconn->pool = pool;
+ dbconn->message_add = smsc_db_mysql_message_add;
+ dbconn->shutdown = smsc_db_mysql_shutdown;
+ dbconn->messages_dlr_get = smsc_db_mysql_dlr_get;
+ dbconn->message_dlr_remove = smsc_db_mysql_dlr_remove;
+ dbconn->messages_mo_get = smsc_db_mysql_messages_mo_get;
+ dbconn->message_mo_remove = smsc_db_mysql_mo_remove;
+ dbconn->mo_table = cfg_get(grp, octstr_imm("db-mo-table"));
+ dbconn->dlr_table = cfg_get(grp, octstr_imm("db-dlr-table"));
+ dbconn->message_table = cfg_get(grp, octstr_imm("db-msg-table"));
+
+ dbconn->supports_mo = 0;
+ dbconn->supports_dlr = 0;
+ dbconn->supports_messages = 0;
+
+ if(dbconn->mo_table != NULL) {
+ dbconn->supports_mo = 1;
+ }
+
+ if(dbconn->dlr_table != NULL) {
+ dbconn->supports_dlr = 1;
+ }
+
+ if(dbconn->message_table != NULL) {
+ dbconn->supports_messages = 1;
+ }
+
+ if((!dbconn->supports_mo) && (!dbconn->supports_dlr) && (!dbconn->supports_messages)) {
+ /* What is the point ? */
+ error(0, "No messaging, DLR or MO support, no point starting");
+ smsc_db_mysql_shutdown(dbconn);
+ return NULL;
+ }
+
+ return dbconn;
+ }
+
+ return NULL;
+}
+#else
+smsc_db_conn *smsc_db_init_mysql(CfgGroup *grp) {
+ error(0, "MySQL not supported in for your current build");
+ return NULL;
+}
+#endif
+
Index: gw/smsc/smsc_db.c
===================================================================
--- gw/smsc/smsc_db.c (revision 0)
+++ gw/smsc/smsc_db.c (revision 0)
@@ -0,0 +1,378 @@
+/* ====================================================================
+ * The Kannel Software License, Version 1.0
+ *
+ * Copyright (c) 2001-2010 Kannel Group
+ * Copyright (c) 1998-2001 WapIT Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Kannel Group (http://www.kannel.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Kannel" and "Kannel Group" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission. For written permission, please
+ * contact [email protected].
+ *
+ * 5. Products derived from this software may not be called "Kannel",
+ * nor may "Kannel" appear in their name, without prior written
+ * permission of the Kannel Group.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE KANNEL GROUP OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Kannel Group. For more information on
+ * the Kannel Group, please see <http://www.kannel.org/>.
+ *
+ * Portions of this software are based upon software originally written at
+ * WapIT Ltd., Helsinki, Finland for the Kannel project.
+ */
+
+/*
+ * smsc_db.c - interface to use a database as an SMSC
+ *
+ * Donald Jackson (www.panaceamobile.com), 2011
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include <errno.h>
+#include <time.h>
+#include <limits.h>
+
+#include "gwlib/gwlib.h"
+#include "smscconn.h"
+#include "smscconn_p.h"
+#include "bb_smscconn_cb.h"
+#include "msg.h"
+#include "sms.h"
+#include "dlr.h"
+#include "gwlib/dbpool.h"
+#include "smsc_db.h"
+
+#define DB_RUNNING 0
+#define DB_SHUTDOWN 1
+#define DB_DEAD 2
+
+#define DB_DEFAULT_INTERVAL 5
+
+typedef struct privdata {
+ Octstr *connection_type;
+ List *running_threads;
+ List *outgoing_queue;
+ long insert_thread;
+ int status; /* So we know what's going on */
+ smsc_db_conn *handles;
+
+ long mo_interval;
+ long dlr_interval;
+ SMSCConn *conn;
+} PrivData;
+
+void smsc_db_insert_thread(void *privdata) {
+ PrivData *priv = privdata;
+ gwlist_add_producer(priv->running_threads);
+
+ debug("smsc.db.insert.thread", 0,
+ "SMSC[%s] starting database insert thread",
+ octstr_get_cstr(priv->conn->id));
+ Msg *msg;
+
+ Octstr *mid;
+
+ char id[UUID_STR_LEN + 1];
+
+ int res;
+
+ while ((msg = gwlist_consume(priv->outgoing_queue)) != NULL) {
+ /* Bearerbox sent us a message */
+ res = priv->handles->message_add(priv->handles, msg);
+ if (res == -1) {
+ error(0, "SMSC[%s] Failed to process message",
+ octstr_get_cstr(priv->conn->id));
+ bb_smscconn_send_failed(priv->conn, msg,
+ SMSCCONN_FAILED_TEMPORARILY, NULL); /* Not sure what appropriate error code should be, database could come back */
+ } else {
+ if (DLR_IS_ENABLED_DEVICE(msg->sms.dlr_mask)) {
+
+ uuid_unparse(msg->sms.id, id);
+ mid = octstr_create(id);
+ dlr_add(priv->conn->id, mid, msg);
+ octstr_destroy(mid);
+ }
+
+ bb_smscconn_sent(priv->conn, msg, NULL);
+ }
+ }
+
+ debug("smsc.db.insert.thread", 0,
+ "SMSC[%s] exiting database insert thread",
+ octstr_get_cstr(priv->conn->id));
+
+ gwlist_remove_producer(priv->running_threads);
+}
+
+void smsc_db_mo_thread(void *privdata) {
+ PrivData *priv = privdata;
+ gwlist_add_producer(priv->running_threads);
+ List *messages;
+
+ Msg *msg, *copy;
+
+ while (priv->status == DB_RUNNING) {
+ messages = priv->handles->messages_mo_get(priv->handles);
+
+ while ((msg = gwlist_extract_first(messages)) != NULL) {
+ copy = msg_duplicate(msg);
+ octstr_destroy(msg->sms.smsc_id);
+ msg->sms.smsc_id = octstr_duplicate(priv->conn->id);
+ if (bb_smscconn_receive(priv->conn, msg) != -1) {
+ debug("smsc.db.mo.thread", 0, "SMSC[%s] MO sent to bearerbox",
+ octstr_get_cstr(priv->conn->id));
+ }
+ priv->handles->message_mo_remove(priv->handles, copy);
+ msg_destroy(copy);
+ }
+
+ gwlist_destroy(messages, NULL);
+
+ gwthread_sleep(priv->mo_interval);
+ }
+
+ gwlist_remove_producer(priv->running_threads);
+}
+
+void smsc_db_dlr_thread(void *privdata) {
+ PrivData *priv = privdata;
+ gwlist_add_producer(priv->running_threads);
+
+ List *dlrs;
+ smsc_db_dlr *dlr;
+
+ Msg *dlrmsg;
+
+ int res;
+
+ while (priv->status == DB_RUNNING) {
+ dlrs = priv->handles->messages_dlr_get(priv->handles);
+ while ((dlr = gwlist_extract_first(dlrs)) != NULL) {
+ /* Handle dlr */
+ smsc_db_dlr_dump(dlr);
+ dlrmsg
+ = dlr_find(priv->conn->id, dlr->msg_id, NULL, dlr->status,
+ 0);
+
+ if (dlrmsg != NULL) {
+ dlrmsg->sms.msgdata = octstr_duplicate(dlr->msg_id);
+ dlrmsg->sms.sms_type = report_mo;
+
+ res = bb_smscconn_receive(priv->conn, dlrmsg);
+ if (res == 0) {
+ /* We must remove the DLR from the database table */
+ priv->handles->message_dlr_remove(priv->handles, dlr);
+ }
+ } else {
+ error(0,
+ "SMSC[%s]: got DLR but could not find message or was not interested "
+ "in it id<%s> dst<%s>, type<%d>",
+ octstr_get_cstr(priv->conn->id),
+ octstr_get_cstr(dlr->msg_id), "(null)", dlr->status);
+
+ /* Delete from database anyway, this will never change */
+ priv->handles->message_dlr_remove(priv->handles, dlr);
+ }
+
+ smsc_db_dlr_destroy(dlr);
+ }
+ gwlist_destroy(dlrs, NULL);
+ gwthread_sleep(priv->dlr_interval);
+ }
+
+ gwlist_remove_producer(priv->running_threads);
+}
+
+void smsc_db_start(SMSCConn *conn) {
+ PrivData *privdata = conn->data;
+
+ privdata->status = DB_RUNNING;
+
+ int status = SMSCCONN_ACTIVE_RECV;
+
+ /* Let's start our threads */
+ if (privdata->handles->supports_messages) {
+ gwlist_add_producer(privdata->outgoing_queue);
+ privdata->insert_thread
+ = gwthread_create(smsc_db_insert_thread, privdata);
+ status = SMSCCONN_ACTIVE;
+ }
+
+ if (privdata->handles->supports_mo) {
+ gwthread_create(smsc_db_mo_thread, privdata);
+ }
+
+ if (privdata->handles->supports_dlr) {
+ gwthread_create(smsc_db_dlr_thread, privdata);
+ }
+
+ conn->status = status;
+
+ conn->connect_time = time(NULL);
+
+}
+
+void smsc_db_stop(SMSCConn *conn) {
+ PrivData *privdata = conn->data;
+ privdata->status = DB_SHUTDOWN;
+
+ if (privdata->handles->supports_messages) {
+ gwlist_remove_producer(privdata->outgoing_queue);
+ }
+
+ /* Wait for the threads to exit */
+ while (gwlist_consume(privdata->running_threads) != NULL)
+ ;
+
+ conn->status = SMSCCONN_DISCONNECTED;
+ conn->why_killed = SMSCCONN_KILLED_SHUTDOWN;
+
+ privdata->status = DB_DEAD;
+}
+
+long smsc_db_queued(SMSCConn *conn) {
+ PrivData *priv = conn->data;
+ return gwlist_len(priv->outgoing_queue);
+}
+
+int smsc_db_send_msg(SMSCConn *conn, Msg *msg) {
+ PrivData *priv = conn->data;
+ if (priv->handles->supports_messages) {
+ gwlist_produce(priv->outgoing_queue, msg_duplicate(msg));
+ debug("smsc.db.send.msg", 0, "SMSC[%s] queued message for processing",
+ octstr_get_cstr(conn->id));
+ return 1;
+ }
+ return -1;
+}
+
+int smsc_db_shutdown(SMSCConn *conn, int finish_sending) {
+ smsc_db_stop(conn);
+ PrivData *privdata = conn->data;
+
+ debug("smsc.db.shutdown", 0, "Shutting down database handles");
+ privdata->handles->shutdown(privdata->handles);
+
+ debug("smsc.db.shutdown", 0, "Destroying connection data");
+ octstr_destroy(privdata->connection_type);
+ gwlist_destroy(privdata->outgoing_queue, NULL); /* We assume the threads did their job */
+ gwlist_destroy(privdata->running_threads, NULL);
+
+ conn->status = SMSCCONN_DEAD;
+
+ gw_free(privdata);
+
+ bb_smscconn_killed();
+
+ return 0;
+}
+
+int smsc_db_create(SMSCConn *conn, CfgGroup *cfg) {
+ debug("smsc.db.create", 0, "Creating new DB connection for '%s'",
+ octstr_get_cstr(conn->id));
+ long tmp;
+ PrivData *privdata = NULL;
+ privdata = gw_malloc(sizeof(PrivData));
+ gw_assert(privdata != NULL);
+ privdata->handles = NULL;
+ privdata->status = DB_DEAD;
+ privdata->conn = conn;
+ privdata->running_threads = gwlist_create();
+ privdata->outgoing_queue = gwlist_create();
+
+ privdata->connection_type = cfg_get(cfg, octstr_imm("db-type"));
+
+ if (privdata->connection_type == NULL) {
+ warning(
+ 0,
+ "You must specify the db-type field in your smsc configuration in order to use the database smsc");
+ return -1;
+ }
+
+ if (octstr_case_compare(privdata->connection_type, octstr_imm("mysql")) == 0) {
+ privdata->handles = smsc_db_init_mysql(cfg);
+ } else {
+ warning(0, "Unsupported database type specified '%s'",
+ octstr_get_cstr(privdata->connection_type));
+ }
+
+ if (cfg_get_integer(&privdata->mo_interval, cfg, octstr_imm(
+ "db-mo-interval")) == -1) {
+ privdata->mo_interval = DB_DEFAULT_INTERVAL;
+ }
+
+ if (cfg_get_integer(&privdata->mo_interval, cfg, octstr_imm(
+ "db-dlr-interval")) == -1) {
+ privdata->dlr_interval = DB_DEFAULT_INTERVAL;
+ }
+
+ if (privdata->handles != NULL) {
+ conn->shutdown = smsc_db_shutdown;
+ conn->queued = smsc_db_queued;
+ conn->send_msg = smsc_db_send_msg;
+ conn->data = privdata;
+
+ smsc_db_start(conn);
+
+ return 1;
+ }
+
+ error(0, "Could not initialized DB");
+
+ return -1;
+
+}
+
+smsc_db_dlr *smsc_db_dlr_create() {
+ smsc_db_dlr *dlr = gw_malloc(sizeof(smsc_db_dlr));
+ dlr->msg_id = NULL;
+ dlr->status = 0;
+
+ return dlr;
+}
+
+void smsc_db_dlr_destroy(smsc_db_dlr *dlr) {
+ octstr_destroy(dlr->msg_id);
+ gw_free(dlr);
+}
+
+void smsc_db_dlr_dump(smsc_db_dlr *dlr) {
+ debug("smsc.db.dlr.dump", 0, "dlr msg_id = %s, status = %d",
+ octstr_get_cstr(dlr->msg_id), dlr->status);
+}
+
Index: gw/smsc/smsc_db.h
===================================================================
--- gw/smsc/smsc_db.h (revision 0)
+++ gw/smsc/smsc_db.h (revision 0)
@@ -0,0 +1,140 @@
+/* ====================================================================
+ * The Kannel Software License, Version 1.0
+ *
+ * Copyright (c) 2001-2010 Kannel Group
+ * Copyright (c) 1998-2001 WapIT Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Kannel Group (http://www.kannel.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Kannel" and "Kannel Group" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission. For written permission, please
+ * contact [email protected].
+ *
+ * 5. Products derived from this software may not be called "Kannel",
+ * nor may "Kannel" appear in their name, without prior written
+ * permission of the Kannel Group.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE KANNEL GROUP OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Kannel Group. For more information on
+ * the Kannel Group, please see <http://www.kannel.org/>.
+ *
+ * Portions of this software are based upon software originally written at
+ * WapIT Ltd., Helsinki, Finland for the Kannel project.
+ */
+
+/**
+ * Donald Jackson (www.panaceamobile.com), 2011
+ */
+
+#ifndef SMSC_DB_H_
+#define SMSC_DB_H_
+
+typedef struct smsc_db_conn smsc_db_conn;
+
+typedef struct {
+ Octstr *msg_id;
+ int status;
+} smsc_db_dlr;
+
+struct smsc_db_conn {
+ /*
+ * Type of connection.
+ */
+ const char* type;
+ /*
+ * Add a message to the database, returns 1 if successful
+ */
+ int (*message_add)(smsc_db_conn *dbconn, Msg *msg);
+
+ /*
+ * Returns all pending MO messages
+ */
+ List *(*messages_mo_get)(smsc_db_conn *dbconn);
+
+ /*
+ * Returns all pending DLR messages
+ */
+ List * (*messages_dlr_get)(smsc_db_conn *dbconn);
+
+ /*
+ * Removes a DLR message with a matching message UUID
+ */
+
+ void (*message_dlr_remove)(smsc_db_conn *dbconn, smsc_db_dlr *dlr);
+
+ /*
+ * Removes an MO message with a matching message UUID
+ */
+
+ void (*message_mo_remove)(smsc_db_conn *dbconn, Msg *);
+
+ /*
+ * Shut down the database connections
+ */
+ void (*shutdown)(smsc_db_conn *dbconn);
+
+ /*
+ * Table name to use for MO
+ */
+ Octstr *mo_table;
+
+ /*
+ * Table name to use for DLR
+ */
+ Octstr *dlr_table;
+
+ /*
+ * Table name to use for sent messages
+ */
+ Octstr *message_table;
+
+ /*
+ * Database pool
+ */
+ DBPool *pool;
+
+ int supports_mo;
+ int supports_dlr;
+ int supports_messages;
+
+};
+
+void smsc_db_dlr_destroy(smsc_db_dlr *dlr);
+smsc_db_dlr *smsc_db_dlr_create();
+void smsc_db_dlr_dump(smsc_db_dlr *dlr);
+
+smsc_db_conn *smsc_db_init_mysql(CfgGroup *grp);
+
+#endif /* SMSC_DB_H_ */