[PATCH] msg storage abstraction to instances
Stipe Tolj <[email protected]>
| Newsgroups | gmane.comp.mobile.kannel.devel |
|---|---|
| Organization | tolj.org system architecture |
| Message-ID | <[email protected]> |
Hi list,
I have attached a patch against CVS HEAD, that abstracts our current code in
gw/bb_store.[ch]
gw/bb_store_[file|spool].c
to allow INSTANTIATION of a message storage by a context handler. I moved the
code to
gwlib/msg_store[_p].[ch]
gwlib/msg_store_[file|spool].c
and changed the store_foobar(...) references to msg_store_foobar(msg_store,...)
in the bearerbox specific files
gw/bb_[boxc|http|smsconn].c
gw/bearerbox.[ch]
to comply with the existing behavior. Hence the patch is a NLC (no logic
change), but allows using several message storage instances/contexts in any daemon.
Example: We could implement a SQL msg storage implementation
(gwlib/msg_store_mysql.c) or on the "other" side, implement a DLR storage type
of a msg store. (gw/dlr_msg_store.c).
Kindly have a look and vote for commitment. If there are no objections to have
the message storage more abstracted this way, I will commit the code and remove
the "old" static module approach.
Stipe
--
-------------------------------------------------------------------
Kölner Landstrasse 419
40589 DÃŒsseldorf, NRW, Germany
tolj.org system architecture Kannel Software Foundation (KSF)
http://www.tolj.org/ http://www.kannel.org/
mailto:st_{at}_tolj.org mailto:stolj_{at}_kannel.org
-------------------------------------------------------------------
msg_store.diff
(text/plain, 63.6 KB)
### Eclipse Workspace Patch 1.0
#P gateway-cvs-head
Index: gw/bearerbox.h
===================================================================
RCS file: /home/cvs/gateway/gw/bearerbox.h,v
retrieving revision 1.37
diff -u -r1.37 bearerbox.h
--- gw/bearerbox.h 31 Aug 2009 10:54:07 -0000 1.37
+++ gw/bearerbox.h 1 Mar 2010 11:29:07 -0000
@@ -64,6 +64,7 @@
#include "msg.h"
#include "smscconn.h"
#include "bb_store.h"
+#include "gwlib/msg_store.h"
/* Default outgoing queue length */
#define DEFAULT_OUTGOING_SMS_QLENGTH 1000000
Index: gw/bb_http.c
===================================================================
RCS file: /home/cvs/gateway/gw/bb_http.c,v
retrieving revision 1.54
diff -u -r1.54 bb_http.c
--- gw/bb_http.c 8 Jul 2009 14:29:39 -0000 1.54
+++ gw/bb_http.c 1 Mar 2010 11:29:07 -0000
@@ -73,6 +73,7 @@
/* passed from bearerbox core */
+extern MsgStore *msg_store;
extern volatile sig_atomic_t bb_status;
/* our own thingies */
@@ -148,7 +149,7 @@
{
Octstr *reply;
if ((reply = httpd_check_authorization(cgivars, 1))!= NULL) return reply;
- return store_status(status_type);
+ return msg_store_status(msg_store, status_type);
}
static Octstr *httpd_loglevel(List *cgivars, int status_type)
Index: gw/bb_boxc.c
===================================================================
RCS file: /home/cvs/gateway/gw/bb_boxc.c,v
retrieving revision 1.95
diff -u -r1.95 bb_boxc.c
--- gw/bb_boxc.c 21 Jan 2010 16:53:02 -0000 1.95
+++ gw/bb_boxc.c 1 Mar 2010 11:29:07 -0000
@@ -97,6 +97,7 @@
/* incoming/outgoing sms queue control */
extern long max_incoming_sms_qlength;
+extern MsgStore *msg_store;
/* our own thingies */
@@ -223,7 +224,7 @@
uuid_copy(mack->ack.id, msg->sms.id);
mack->ack.time = msg->sms.time;
- store_save(msg);
+ msg_store_save(msg_store, msg);
rc = smsc2_rout(msg, 0);
switch (rc) {
@@ -240,7 +241,7 @@
warning(0, "Message rejected by bearerbox, no router!");
/*
- * we don't store_save_ack() here, since the call to
+ * we don't msg_store_save_ack() here, since the call to
* bb_smscconn_send_failed() within smsc2_route() did
* it already.
*/
@@ -258,7 +259,7 @@
* message from store-file.
*/
mack->ack.nack = ack_failed_tmp;
- store_save_ack(msg, ack_failed_tmp);
+ msg_store_save_ack(msg_store, msg, ack_failed_tmp);
/* destroy original message */
msg_destroy(msg);
@@ -347,7 +348,7 @@
gwlist_append(conn->retry, orig);
} else {
boxc_sent_pop(conn, msg, NULL);
- store_save(msg);
+ msg_store_save(msg_store, msg);
}
debug("bb.boxc", 0, "boxc_receiver: got ack");
}
Index: gw/bb_smscconn.c
===================================================================
RCS file: /home/cvs/gateway/gw/bb_smscconn.c,v
retrieving revision 1.108
diff -u -r1.108 bb_smscconn.c
--- gw/bb_smscconn.c 15 Nov 2009 14:12:28 -0000 1.108
+++ gw/bb_smscconn.c 1 Mar 2010 11:29:07 -0000
@@ -122,6 +122,8 @@
/* configuration filename */
extern Octstr *cfg_filename;
+extern MsgStore *msg_store;
+
/* our own thingies */
static volatile sig_atomic_t smsc_running;
@@ -267,7 +269,7 @@
}
/* write ACK to store file */
- store_save_ack(sms, ack_success);
+ msg_store_save_ack(msg_store, sms, ack_success);
if (sms->sms.sms_type != report_mt) {
bb_alog_sms(conn, sms, "Sent SMS");
@@ -339,7 +341,7 @@
default:
/* write NACK to store file */
- store_save_ack(sms, ack_failed);
+ msg_store_save_ack(msg_store, sms, ack_failed);
if (conn) counter_increase(conn->failed);
if (reason == SMSCCONN_FAILED_DISCARDED)
@@ -436,7 +438,7 @@
sms->sms.sms_type = mo;
/* write to store (if enabled) */
- if (store_save(sms) == -1) {
+ if (msg_store_save(msg_store, sms) == -1) {
msg_destroy(sms);
return SMSCCONN_FAILED_TEMPORARILY;
}
@@ -497,7 +499,7 @@
bb_alog_sms(conn, sms, "DROPPED Received SMS");
/* put nack into store-file */
- store_save_ack(sms, ack_failed);
+ msg_store_save_ack(msg_store, sms, ack_failed);
msg_destroy(copy);
msg_destroy(sms);
@@ -1335,18 +1337,18 @@
*/
if (conn->reroute) {
/* change message direction */
- store_save_ack(msg, ack_success);
+ msg_store_save_ack(msg_store, msg, ack_success);
msg->sms.sms_type = mt_push;
- store_save(msg);
+ msg_store_save(msg_store, msg);
/* drop into outbound queue again for routing */
return smsc2_rout(msg, 0);
}
if (conn->reroute_to_smsc) {
/* change message direction */
- store_save_ack(msg, ack_success);
+ msg_store_save_ack(msg_store, msg, ack_success);
msg->sms.sms_type = mt_push;
- store_save(msg);
+ msg_store_save(msg_store, msg);
/* apply directly to the given smsc-id for MT traffic */
octstr_destroy(msg->sms.smsc_id);
msg->sms.smsc_id = octstr_duplicate(conn->reroute_to_smsc);
@@ -1356,9 +1358,9 @@
if (conn->reroute_by_receiver && msg->sms.receiver &&
(smsc = dict_get(conn->reroute_by_receiver, msg->sms.receiver))) {
/* change message direction */
- store_save_ack(msg, ack_success);
+ msg_store_save_ack(msg_store, msg, ack_success);
msg->sms.sms_type = mt_push;
- store_save(msg);
+ msg_store_save(msg_store, msg);
/* route by receiver number */
/* XXX implement wildcard matching too! */
octstr_destroy(msg->sms.smsc_id);
@@ -1397,7 +1399,7 @@
gw_assert(msg);
for (i = 0; i < msg->total_parts; i++) {
if (msg->parts[i]) {
- store_save_ack(msg->parts[i], msg->ack);
+ msg_store_save_ack(msg_store, msg->parts[i], msg->ack);
msg_destroy(msg->parts[i]);
}
}
@@ -1462,7 +1464,7 @@
if (x->parts[i] == NULL)
continue;
msg = msg_duplicate(x->parts[i]);
- store_save_ack(x->parts[i], ack_success);
+ msg_store_save_ack(msg_store, x->parts[i], ack_success);
switch(bb_smscconn_receive(NULL, msg)) {
case SMSCCONN_FAILED_REJECTED:
case SMSCCONN_SUCCESS:
@@ -1474,7 +1476,7 @@
case SMSCCONN_FAILED_QFULL:
default:
/* oops put it back into dict and retry on next run */
- store_save(x->parts[i]);
+ msg_store_save(msg_store, x->parts[i]);
destroy = 0;
break;
}
@@ -1517,7 +1519,7 @@
/* Checks if message is concatenated. Returns:
* - returns concat_complete if no concat parts, or message complete
* - returns concat_pending (and sets *pmsg to NULL) if parts pending
- * - returns concat_error if store_save fails
+ * - returns concat_error if msg_store_save() fails
*/
static int check_concatenation(Msg **pmsg, Octstr *smscid)
{
@@ -1588,7 +1590,7 @@
if (cmsg->parts[part - 1] != NULL) {
warning(0, "Duplicate message part %d, ref %d, from %s, to %s. Discarded!",
part, refnum, octstr_get_cstr(msg->sms.sender), octstr_get_cstr(msg->sms.receiver));
- store_save_ack(msg, ack_success);
+ msg_store_save_ack(msg_store, msg, ack_success);
msg_destroy(msg);
*pmsg = msg = NULL;
} else {
@@ -1615,7 +1617,7 @@
octstr_append(msg->sms.msgdata, cmsg->parts[i]->sms.msgdata);
/* Attempt to save the new one, if that fails, then reply with fail. */
- if (store_save(msg) == -1) {
+ if (msg_store_save(msg_store, msg) == -1) {
mutex_unlock(concat_lock);
msg_destroy(msg);
*pmsg = msg = NULL;
Index: gw/bearerbox.c
===================================================================
RCS file: /home/cvs/gateway/gw/bearerbox.c,v
retrieving revision 1.175
diff -u -r1.175 bearerbox.c
--- gw/bearerbox.c 28 Feb 2010 22:43:32 -0000 1.175
+++ gw/bearerbox.c 1 Mar 2010 11:29:07 -0000
@@ -129,6 +129,9 @@
/* configuration filename */
Octstr *cfg_filename;
+/* Main message store context. */
+MsgStore *msg_store;
+
volatile sig_atomic_t bb_status;
/*
@@ -428,8 +431,19 @@
log = cfg_get(grp, octstr_imm("store-location"));
val = cfg_get(grp, octstr_imm("store-type"));
}
- if (store_init(val, log, store_dump_freq, msg_pack, msg_unpack_wrapper) == -1)
- panic(0, "Could not start with store init failed.");
+ if (val && octstr_compare(val, octstr_imm("file")) == 0) {
+ msg_store = msg_store_init(STORE_FILE, msg_pack, msg_unpack_wrapper,
+ log, (long) store_dump_freq);
+ if (msg_store == NULL)
+ panic(0, "Could not start with store init failed.");
+ } else if (val && octstr_compare(val, octstr_imm("spool")) == 0) {
+ msg_store = msg_store_init(STORE_SPOOL, msg_pack, msg_unpack_wrapper, log);
+ if (msg_store == NULL)
+ panic(0, "Could not start with store init failed.");
+ } else if (val) {
+ panic(0, "Directive 'store-type = %s' not supported!",
+ octstr_get_cstr(val));
+ }
octstr_destroy(val);
octstr_destroy(log);
@@ -666,7 +680,7 @@
gwthread_sleep(5.0); /* give time to threads to register themselves */
- if (store_load(dispatch_into_queue) == -1)
+ if (msg_store_load(msg_store, dispatch_into_queue) == -1)
panic(0, "Cannot start with store-file failing");
info(0, "MAIN: Start-up done, entering mainloop");
@@ -728,7 +742,7 @@
boxc_cleanup();
smsc2_cleanup();
- store_shutdown();
+ msg_store_shutdown(msg_store);
empty_msg_lists();
gwlist_destroy(flow_threads, NULL);
gwlist_destroy(suspended, NULL);
@@ -972,7 +986,7 @@
counter_value(outgoing_wdp_counter), gwlist_len(outgoing_wdp) + udp_outgoing_queue(),
counter_value(incoming_sms_counter), gwlist_len(incoming_sms),
counter_value(outgoing_sms_counter), gwlist_len(outgoing_sms),
- store_messages(),
+ msg_store_messages(msg_store),
load_get(incoming_sms_load,0), load_get(incoming_sms_load,1), load_get(incoming_sms_load,2),
load_get(outgoing_sms_load,0), load_get(outgoing_sms_load,1), load_get(outgoing_sms_load,2),
counter_value(incoming_dlr_counter), counter_value(outgoing_dlr_counter),
Index: gwlib/msg_store_file.c
===================================================================
RCS file: gwlib/msg_store_file.c
diff -N gwlib/msg_store_file.c
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ gwlib/msg_store_file.c 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,649 @@
+/* ====================================================================
+ * The Kannel Software License, Version 1.0
+ *
+ * Copyright (c) 2001-2009 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.
+ */
+
+/*
+ * msg_store_file.c - MsgStore type with a single sotrage file
+ *
+ * Based on the gw/bb_store.c from Kalle Marjola in 2001.
+ */
+
+#ifdef DO_MSG_STORE
+
+#include "sms.h"
+
+
+struct msg_store_file {
+ FILE *file;
+ Octstr *filename;
+ Octstr *newfile;
+ Octstr *bakfile;
+ Mutex *file_mutex;
+ long cleanup_thread;
+ long dump_frequency;
+
+ Dict *sms_dict;
+
+ int active;
+ time_t last_dict_mod;
+ List *loaded;
+
+ /* pointer back to the MsgStore */
+ MsgStore *func;
+};
+
+typedef struct msg_store_file msg_store_file;
+
+static int store_file_dump(void *arg);
+
+
+static void write_msg(msg_store_file *context, Msg *msg)
+{
+ Octstr *pack;
+ unsigned char buf[4];
+
+ pack = context->func->store_msg_pack(msg);
+ encode_network_long(buf, octstr_len(pack));
+ octstr_insert_data(pack, 0, (char*)buf, 4);
+
+ octstr_print(context->file, pack);
+ fflush(context->file);
+
+ octstr_destroy(pack);
+}
+
+
+static int read_msg(msg_store_file *context, Msg **msg, Octstr *os, long *off)
+{
+ unsigned char buf[4];
+ long i;
+ Octstr *pack;
+
+ gw_assert(*off >= 0);
+ if (*off + 4 > octstr_len(os)) {
+ error(0, "Packet too short while unpacking Msg.");
+ return -1;
+ }
+
+ octstr_get_many_chars((char*)buf, os, *off, 4);
+ i = decode_network_long(buf);
+ *off += 4;
+
+ pack = octstr_copy(os, *off, i);
+ *off += octstr_len(pack);
+ *msg = context->func->store_msg_unpack(pack);
+ octstr_destroy(pack);
+
+ if (!*msg)
+ return -1;
+
+ return 0;
+}
+
+
+static int open_file(msg_store_file *context)
+{
+ context->file = fopen(octstr_get_cstr(context->newfile), "w");
+ if (context->file == NULL) {
+ error(errno, "Failed to open '%s' for writing, cannot create store-file",
+ octstr_get_cstr(context->newfile));
+ return -1;
+ }
+ return 0;
+}
+
+
+static int rename_store(msg_store_file *context)
+{
+ if (rename(octstr_get_cstr(context->filename), octstr_get_cstr(context->bakfile)) == -1) {
+ if (errno != ENOENT) {
+ error(errno, "Failed to rename old store '%s' as '%s'",
+ octstr_get_cstr(context->filename), octstr_get_cstr(context->bakfile));
+ return -1;
+ }
+ }
+ if (rename(octstr_get_cstr(context->newfile), octstr_get_cstr(context->filename)) == -1) {
+ error(errno, "Failed to rename new store '%s' as '%s'",
+ octstr_get_cstr(context->newfile), octstr_get_cstr(context->filename));
+ return -1;
+ }
+ return 0;
+}
+
+
+static int do_dump(msg_store_file *context)
+{
+ Octstr *key;
+ Msg *msg;
+ List *sms_list;
+ long l;
+
+ if (context->filename == NULL)
+ return 0;
+
+ /*
+ * create a new store-file and save all non-acknowledged
+ * messages into it
+ */
+ if (open_file(context) == -1)
+ return -1;
+
+ sms_list = dict_keys(context->sms_dict);
+ for (l = 0; l < gwlist_len(sms_list); l++) {
+ key = gwlist_get(sms_list, l);
+ msg = dict_get(context->sms_dict, key);
+ if (msg != NULL)
+ write_msg(context, msg);
+ }
+ fflush(context->file);
+ gwlist_destroy(sms_list, octstr_destroy_item);
+
+ /* rename old storefile as .bak, and then new as regular file
+ * without .new ending */
+
+ return rename_store(context);
+}
+
+
+/*
+ * Thread to write current store to file now and then, to prevent
+ * it from becoming far too big (slows startup)
+ */
+static void store_dumper(void *arg)
+{
+ msg_store_file *context = arg;
+
+ time_t now;
+ int busy = 0;
+
+ while (context->active) {
+ now = time(NULL);
+ /*
+ * write store to file up to each N. second, providing
+ * that something happened or if we are constantly busy.
+ */
+ if (now - context->last_dict_mod > context->dump_frequency || busy) {
+ store_file_dump(context);
+ /*
+ * make sure that no new dump is done for a while unless
+ * something happens. This moves the trigger in the future
+ * and allows the if statement to pass if nothing happened
+ * in the mean time while sleeping. The busy flag is needed
+ * to garantee we do dump in case we are constantly busy
+ * and hence the difference between now and last dict
+ * operation is less then dump frequency, otherwise we
+ * would never dump. This is for constant high load.
+ */
+ context->last_dict_mod = time(NULL) + 3600 * 24;
+ busy = 0;
+ } else {
+ busy = (now - context->last_dict_mod) > 0;
+ }
+ gwthread_sleep(context->dump_frequency);
+ }
+ store_file_dump(context);
+ if (context->file != NULL)
+ fclose(context->file);
+ octstr_destroy(context->filename);
+ octstr_destroy(context->newfile);
+ octstr_destroy(context->bakfile);
+ mutex_destroy(context->file_mutex);
+
+ dict_destroy(context->sms_dict);
+ /* set all vars to NULL */
+ context->filename = context->newfile = context->bakfile = NULL;
+ context->file_mutex = NULL;
+ context->sms_dict = NULL;
+}
+
+
+/*------------------------------------------------------*/
+
+static Octstr *store_file_status(void *arg, int status_type)
+{
+ msg_store_file *context = arg;
+ char *frmt;
+ Octstr *ret, *key;
+ unsigned long l;
+ struct tm tm;
+ Msg *msg;
+ List *keys;
+ char id[UUID_STR_LEN + 1];
+
+ ret = octstr_create("");
+
+ /* set the type based header */
+ if (status_type == MSG_STORE_STATUS_HTML) {
+ octstr_append_cstr(ret, "<table border=1>\n"
+ "<tr><td>SMS ID</td><td>Type</td><td>Time</td><td>Sender</td><td>Receiver</td>"
+ "<td>SMSC ID</td><td>BOX ID</td><td>UDH</td><td>Message</td>"
+ "</tr>\n");
+ } else if (status_type == MSG_STORE_STATUS_TEXT) {
+ octstr_append_cstr(ret, "[SMS ID] [Type] [Time] [Sender] [Receiver] [SMSC ID] [BOX ID] [UDH] [Message]\n");
+ }
+
+ /* if there is no store-file, then don't loop in sms_store */
+ if (context->filename == NULL)
+ goto finish;
+
+ keys = dict_keys(context->sms_dict);
+
+ for (l = 0; l < gwlist_len(keys); l++) {
+ key = gwlist_get(keys, l);
+ msg = dict_get(context->sms_dict, key);
+ if (msg == NULL)
+ continue;
+
+ if (msg_type(msg) == sms) {
+
+ if (status_type == MSG_STORE_STATUS_HTML) {
+ frmt = "<tr><td>%s</td><td>%s</td>"
+ "<td>%04d-%02d-%02d %02d:%02d:%02d</td>"
+ "<td>%s</td><td>%s</td><td>%s</td>"
+ "<td>%s</td><td>%s</td><td>%s</td></tr>\n";
+ } else if (status_type == MSG_STORE_STATUS_XML) {
+ frmt = "<message>\n\t<id>%s</id>\n\t<type>%s</type>\n\t"
+ "<time>%04d-%02d-%02d %02d:%02d:%02d</time>\n\t"
+ "<sender>%s</sender>\n\t"
+ "<receiver>%s</receiver>\n\t<smsc-id>%s</smsc-id>\n\t"
+ "<box-id>%s</box-id>\n\t"
+ "<udh-data>%s</udh-data>\n\t<msg-data>%s</msg-data>\n\t"
+ "</message>\n";
+ } else {
+ frmt = "[%s] [%s] [%04d-%02d-%02d %02d:%02d:%02d] [%s] [%s] [%s] [%s] [%s] [%s]\n";
+ }
+
+ /* transform the time value */
+#if LOG_TIMESTAMP_LOCALTIME
+ tm = gw_localtime(msg->sms.time);
+#else
+ tm = gw_gmtime(msg->sms.time);
+#endif
+ if (msg->sms.udhdata)
+ octstr_binary_to_hex(msg->sms.udhdata, 1);
+ if (msg->sms.msgdata &&
+ (msg->sms.coding == DC_8BIT || msg->sms.coding == DC_UCS2 ||
+ (msg->sms.coding == DC_UNDEF && msg->sms.udhdata)))
+ octstr_binary_to_hex(msg->sms.msgdata, 1);
+
+ uuid_unparse(msg->sms.id, id);
+
+ octstr_format_append(ret, frmt, id,
+ (msg->sms.sms_type == mo ? "MO" :
+ msg->sms.sms_type == mt_push ? "MT-PUSH" :
+ msg->sms.sms_type == mt_reply ? "MT-REPLY" :
+ msg->sms.sms_type == report_mo ? "DLR-MO" :
+ msg->sms.sms_type == report_mt ? "DLR-MT" : ""),
+ tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
+ tm.tm_hour, tm.tm_min, tm.tm_sec,
+ (msg->sms.sender ? octstr_get_cstr(msg->sms.sender) : ""),
+ (msg->sms.receiver ? octstr_get_cstr(msg->sms.receiver) : ""),
+ (msg->sms.smsc_id ? octstr_get_cstr(msg->sms.smsc_id) : ""),
+ (msg->sms.boxc_id ? octstr_get_cstr(msg->sms.boxc_id) : ""),
+ (msg->sms.udhdata ? octstr_get_cstr(msg->sms.udhdata) : ""),
+ (msg->sms.msgdata ? octstr_get_cstr(msg->sms.msgdata) : ""));
+
+ if (msg->sms.udhdata)
+ octstr_hex_to_binary(msg->sms.udhdata);
+ if (msg->sms.msgdata &&
+ (msg->sms.coding == DC_8BIT || msg->sms.coding == DC_UCS2 ||
+ (msg->sms.coding == DC_UNDEF && msg->sms.udhdata)))
+ octstr_hex_to_binary(msg->sms.msgdata);
+ }
+ }
+ gwlist_destroy(keys, octstr_destroy_item);
+
+finish:
+ /* set the type based footer */
+ if (status_type == MSG_STORE_STATUS_HTML) {
+ octstr_append_cstr(ret,"</table>");
+ }
+
+ return ret;
+}
+
+
+static long store_file_messages(void *arg)
+{
+ msg_store_file *context = arg;
+
+ return (context->sms_dict ? dict_key_count(context->sms_dict) : -1);
+}
+
+
+static int store_to_dict(msg_store_file *context, Msg *msg)
+{
+ Msg *copy;
+ Octstr *uuid_os;
+ char id[UUID_STR_LEN + 1];
+
+ /* always set msg id and timestamp */
+ if (msg_type(msg) == sms && uuid_is_null(msg->sms.id))
+ uuid_generate(msg->sms.id);
+
+ if (msg_type(msg) == sms && msg->sms.time == MSG_PARAM_UNDEFINED)
+ time(&msg->sms.time);
+
+ if (msg_type(msg) == sms) {
+ copy = msg_duplicate(msg);
+
+ uuid_unparse(copy->sms.id, id);
+ uuid_os = octstr_create(id);
+
+ dict_put(context->sms_dict, uuid_os, copy);
+ octstr_destroy(uuid_os);
+ context->last_dict_mod = time(NULL);
+ } else if (msg_type(msg) == ack) {
+ uuid_unparse(msg->ack.id, id);
+ uuid_os = octstr_create(id);
+ copy = dict_remove(context->sms_dict, uuid_os);
+ octstr_destroy(uuid_os);
+ if (copy == NULL) {
+ warning(0, "bb_store: get ACK of message not found "
+ "from store, strange?");
+ } else {
+ msg_destroy(copy);
+ context->last_dict_mod = time(NULL);
+ }
+ } else
+ return -1;
+ return 0;
+}
+
+static int store_file_save(void *arg, Msg *msg)
+{
+ msg_store_file *context = arg;
+
+ if (context->filename == NULL)
+ return 0;
+
+ /* block here until store not loaded */
+ gwlist_consume(context->loaded);
+
+ /* lock file_mutex in order to have dict and file in sync */
+ mutex_lock(context->file_mutex);
+ if (store_to_dict(context, msg) == -1) {
+ mutex_unlock(context->file_mutex);
+ return -1;
+ }
+
+ /* write to file, too */
+ write_msg(context, msg);
+ fflush(context->file);
+ mutex_unlock(context->file_mutex);
+
+ return 0;
+}
+
+
+static int store_file_save_ack(void *arg, Msg *msg, ack_status_t status)
+{
+ msg_store_file *context = arg;
+ Msg *mack;
+ int ret;
+
+ /* only sms are handled */
+ if (!msg || msg_type(msg) != sms)
+ return -1;
+
+ if (context->filename == NULL)
+ return 0;
+
+ mack = msg_create(ack);
+ if (!mack)
+ return -1;
+
+ mack->ack.time = msg->sms.time;
+ uuid_copy(mack->ack.id, msg->sms.id);
+ mack->ack.nack = status;
+
+ ret = store_file_save(context, mack);
+ msg_destroy(mack);
+
+ return ret;
+}
+
+
+static int store_file_load(void *arg, void (*receive_msg)(Msg*))
+{
+ msg_store_file *context = arg;
+ List *keys;
+ Octstr *store_file, *key;
+ Msg *msg;
+ int retval, msgs;
+ long end, pos;
+
+ gw_assert(context != NULL);
+
+ if (context->filename == NULL)
+ return 0;
+
+ mutex_lock(context->file_mutex);
+ if (context->file != NULL) {
+ fclose(context->file);
+ context->file = NULL;
+ }
+
+ store_file = octstr_read_file(octstr_get_cstr(context->filename));
+ if (store_file != NULL)
+ info(0, "Loading store file `%s'", octstr_get_cstr(context->filename));
+ else {
+ store_file = octstr_read_file(octstr_get_cstr(context->newfile));
+ if (store_file != NULL)
+ info(0, "Loading store file `%s'", octstr_get_cstr(context->newfile));
+ else {
+ store_file = octstr_read_file(octstr_get_cstr(context->bakfile));
+ if (store_file != NULL)
+ info(0, "Loading store file `%s'", octstr_get_cstr(context->bakfile));
+ else {
+ info(0, "Cannot open any store file, starting a new one");
+ retval = open_file(context);
+ goto end;
+ }
+ }
+ }
+
+ info(0, "Store-file size %ld, starting to unpack%s", octstr_len(store_file),
+ octstr_len(store_file) > 10000 ? " (may take awhile)" : "");
+
+ pos = 0;
+ msgs = 0;
+ end = octstr_len(store_file);
+
+ while (pos < end) {
+ if (read_msg(context, &msg, store_file, &pos) == -1) {
+ error(0, "Garbage at store-file, skipped.");
+ continue;
+ }
+ if (msg_type(msg) == sms) {
+ store_to_dict(context, msg);
+ msgs++;
+ } else if (msg_type(msg) == ack) {
+ store_to_dict(context, msg);
+ } else {
+ warning(0, "Strange message in store-file, discarded, "
+ "dump follows:");
+ msg_dump(msg, 0);
+ }
+ msg_destroy(msg);
+ }
+ octstr_destroy(store_file);
+
+ info(0, "Retrieved %d messages, non-acknowledged messages: %ld",
+ msgs, dict_key_count(context->sms_dict));
+
+ /* now create a new sms_store out of messages left */
+
+ keys = dict_keys(context->sms_dict);
+ while ((key = gwlist_extract_first(keys)) != NULL) {
+ msg = dict_remove(context->sms_dict, key);
+ if (store_to_dict(context, msg) != -1) {
+ receive_msg(msg);
+ } else {
+ error(0, "Found unknown message type in store file.");
+ msg_dump(msg, 0);
+ msg_destroy(msg);
+ }
+ octstr_destroy(key);
+ }
+ gwlist_destroy(keys, octstr_destroy_item);
+
+ /* Finally, generate new store file out of left messages */
+ retval = do_dump(context);
+
+end:
+ mutex_unlock(context->file_mutex);
+
+ /* allow using of store */
+ gwlist_remove_producer(context->loaded);
+
+ /* start dumper thread */
+ if ((context->cleanup_thread = gwthread_create(store_dumper, context)) == -1)
+ panic(0, "Failed to create a cleanup thread!");
+
+ return retval;
+}
+
+
+static int store_file_dump(void *arg)
+{
+ msg_store_file *context = arg;
+ int retval;
+
+ debug("msg.store", 0, "Dumping %ld messages to store",
+ dict_key_count(context->sms_dict));
+ mutex_lock(context->file_mutex);
+ if (context->file != NULL) {
+ fclose(context->file);
+ context->file = NULL;
+ }
+ retval = do_dump(context);
+ mutex_unlock(context->file_mutex);
+
+ return retval;
+}
+
+
+static void store_file_shutdown(void *arg)
+{
+ msg_store_file *context = arg;
+
+ if (context->filename == NULL)
+ return;
+
+ context->active = 0;
+ gwthread_wakeup(context->cleanup_thread);
+ /* wait for cleanup thread */
+ if (context->cleanup_thread != -1)
+ gwthread_join(context->cleanup_thread);
+
+ gwlist_destroy(context->loaded, NULL);
+
+ gw_free(context);
+}
+
+
+static int store_file_init(MsgStore *ms, va_list ap)
+{
+ msg_store_file *msf;
+ Octstr *fname = NULL;
+ long dump_freq = -1;
+
+ /* Get our required arguments. */
+ fname = va_arg(ap, Octstr*);
+ dump_freq = va_arg(ap, long);
+ va_end(ap);
+
+ /* Constraint checks first. */
+ if (fname == NULL)
+ panic(0, "Message store for store-file needs a file name.");
+
+ if (octstr_len(fname) > (FILENAME_MAX-5))
+ panic(0, "Message store filename too long: `%s', failed to init.",
+ octstr_get_cstr(fname));
+
+ /* Initialize the type specific data. */
+ msf = gw_malloc(sizeof(*msf));
+ msf->file = NULL;
+ msf->filename = octstr_duplicate(fname);
+ msf->newfile = octstr_format("%s.new", octstr_get_cstr(fname));
+ msf->bakfile = octstr_format("%s.bak", octstr_get_cstr(fname));
+ msf->sms_dict = dict_create(1024, msg_destroy_item);
+ msf->cleanup_thread = -1;
+ msf->dump_frequency = (dump_freq > 0 ? dump_freq : MSG_STORE_DEFAULT_DUMP_FREQ);
+ msf->file_mutex = mutex_create();
+ msf->active = 1;
+ msf->loaded = gwlist_create();
+ gwlist_add_producer(msf->loaded);
+
+ msf->func = ms;
+ ms->context = msf;
+
+ return 1;
+}
+
+
+static struct store_ops file_ops = {
+ .store_init = store_file_init,
+ .store_messages = store_file_messages,
+ .store_save = store_file_save,
+ .store_save_ack = store_file_save_ack,
+ .store_load = store_file_load,
+ .store_dump = store_file_dump,
+ .store_shutdown = store_file_shutdown,
+ .store_status = store_file_status
+};
+
+#endif /* DO_MSG_STORE */
Index: gwlib/msg_store_p.h
===================================================================
RCS file: gwlib/msg_store_p.h
diff -N gwlib/msg_store_p.h
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ gwlib/msg_store_p.h 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,107 @@
+/* ====================================================================
+ * The Kannel Software License, Version 1.0
+ *
+ * Copyright (c) 2001-2009 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.
+ */
+
+/*
+ * msg_store_p.h - abstracted Msg storage, based on the gw/bb_store.c code.
+ *
+ * Alexander Malysh <amalysh at kannel dot org>, 2006
+ * Stipe Tolj <stolj at kannel dot org>, 2010
+ */
+
+#ifndef MSG_STORE_P_H
+#define MSG_STORE_P_H
+
+#define MSG_STORE_DEFAULT_DUMP_FREQ 10
+
+/* type of output given by various status functions */
+enum {
+ MSG_STORE_STATUS_HTML = 0,
+ MSG_STORE_STATUS_TEXT = 1,
+ MSG_STORE_STATUS_WML = 2,
+ MSG_STORE_STATUS_XML = 3
+};
+
+
+struct store_ops {
+ int (*store_init)(MsgStore *ms, va_list ap);
+ long (*store_messages)(void *arg);
+ int (*store_save)(void *arg, Msg *msg);
+ int (*store_save_ack)(void *arg, Msg *msg, ack_status_t status);
+ int (*store_load)(void *arg, void (*receive_msg)(Msg*));
+ int (*store_dump)(void *arg);
+ void (*store_shutdown)(void *arg);
+ Octstr* (*store_status)(void *arg, int status_type);
+};
+
+
+struct MsgStore {
+
+ /* Type of storage. */
+ enum store_type store_type;
+
+ /* Type specific data structure. */
+ void *context;
+
+ /* Type specific operations. */
+ struct store_ops *store_ops;
+
+ /* Function pointers. */
+ Octstr* (*store_msg_pack)(Msg *msg);
+ Msg* (*store_msg_unpack)(Octstr *os);
+};
+
+
+#endif /* MSG_STORE_P_H */
Index: gwlib/msg_store.h
===================================================================
RCS file: gwlib/msg_store.h
diff -N gwlib/msg_store.h
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ gwlib/msg_store.h 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,93 @@
+/* ====================================================================
+ * The Kannel Software License, Version 1.0
+ *
+ * Copyright (c) 2001-2009 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.
+ */
+
+/*
+ * msg_store.h - abstracted Msg storage, based on the gw/bb_store.c code.
+ *
+ * Alexander Malysh <amalysh at kannel dot org>, 2006
+ * Stipe Tolj <stolj at kannel dot org>, 2010
+ */
+
+#ifndef MSG_STORE_H
+#define MSG_STORE_H
+
+
+/* Supported storage types. */
+enum store_type {
+ STORE_FILE, STORE_SPOOL
+};
+
+typedef struct MsgStore MsgStore;
+
+
+/*
+ * Initialize the message storage of the specified type.
+ * Supported type values: 'spool', 'file'.
+ * Returns the pointer to the message storage instance, or NULL if
+ * any error occured.
+ */
+MsgStore *msg_store_init(enum store_type type, void *pack_func, void *unpack_func, ...);
+
+void msg_store_shutdown(MsgStore *ms);
+long msg_store_messages(MsgStore *ms);
+int msg_store_save(MsgStore *ms, Msg *msg);
+int msg_store_save_ack(MsgStore *ms, Msg *msg, ack_status_t status);
+int msg_store_load(MsgStore *ms, void (*receive_msg)(Msg*));
+int msg_store_dump(MsgStore *ms);
+Octstr *msg_store_status(MsgStore *ms, int status_type);
+
+
+#endif /* MSG_STORE_H */
Index: gwlib/msg_store.c
===================================================================
RCS file: gwlib/msg_store.c
diff -N gwlib/msg_store.c
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ gwlib/msg_store.c 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,150 @@
+/* ====================================================================
+ * The Kannel Software License, Version 1.0
+ *
+ * Copyright (c) 2001-2009 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.
+ */
+
+/*
+ * msg_store.c - abstracted Msg storage, based on the gw/bb_store.c code.
+ *
+ * Alexander Malysh <amalysh at kannel dot org>, 2006
+ * Stipe Tolj <stolj at kannel dot org>, 2010
+ */
+
+#include <errno.h>
+
+#include "gw-config.h"
+
+#include "gwlib/gwlib.h"
+#include "msg.h"
+#include "msg_store.h"
+#include "msg_store_p.h"
+
+#define DO_MSG_STORE 1
+
+#include "msg_store_file.c"
+#include "msg_store_spool.c"
+
+
+MsgStore *msg_store_init(enum store_type type, void *pack_func, void *unpack_func, ...)
+{
+ MsgStore *ms;
+ va_list ap;
+ va_list aq;
+
+ ms = gw_malloc(sizeof(*ms));
+ ms->store_msg_pack = pack_func;
+ ms->store_msg_unpack = unpack_func;
+
+ switch (type) {
+ case STORE_FILE:
+ ms->store_ops = &file_ops;
+ break;
+ case STORE_SPOOL:
+ ms->store_ops = &spool_ops;
+ break;
+ default:
+ panic(0, "Unknown 'store-type' defined.");
+ break;
+ }
+
+ va_start(ap, unpack_func);
+ va_copy(aq, ap);
+ ms->store_ops->store_init(ms, aq);
+ va_end(aq);
+ va_end(ap);
+
+ return ms;
+}
+
+
+inline void msg_store_shutdown(MsgStore *ms)
+{
+ gw_assert(ms != NULL);
+
+ /* Destroy the context. */
+ ms->store_ops->store_shutdown(ms->context);
+
+ /* Destroy our own structure. */
+ gw_free(ms);
+}
+
+inline long msg_store_messages(MsgStore *ms)
+{
+ return ms->store_ops->store_messages(ms->context);
+}
+
+inline int msg_store_save(MsgStore *ms, Msg *msg)
+{
+ return ms->store_ops->store_save(ms->context, msg);
+}
+
+inline int msg_store_save_ack(MsgStore *ms, Msg *msg, ack_status_t status)
+{
+ return ms->store_ops->store_save_ack(ms->context, msg, status);
+}
+
+inline int msg_store_load(MsgStore *ms, void (*receive_msg)(Msg*))
+{
+ return ms->store_ops->store_load(ms->context, receive_msg);
+}
+
+inline int msg_store_dump(MsgStore *ms)
+{
+ return ms->store_ops->store_dump(ms->context);
+}
+
+inline Octstr *msg_store_status(MsgStore *ms, int status_type)
+{
+ return ms->store_ops->store_status(ms->context, status_type);
+}
Index: gwlib/msg_store_spool.c
===================================================================
RCS file: gwlib/msg_store_spool.c
diff -N gwlib/msg_store_spool.c
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ gwlib/msg_store_spool.c 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,458 @@
+/* ====================================================================
+ * The Kannel Software License, Version 1.0
+ *
+ * Copyright (c) 2001-2009 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.
+ */
+
+/**
+ * msg_store_spool.c - MsgStore type storage/retrieval module using spool directory
+ *
+ * Author: Alexander Malysh, 2006
+ * Modified to MsgStore abstraction: Stipe Tolj, 2010
+ */
+
+#ifdef DO_MSG_STORE
+
+#include <unistd.h>
+#include <dirent.h>
+#include <fcntl.h>
+
+
+/* how much subdirs allowed ? */
+#define MAX_DIRS 100
+
+struct msg_store_spool {
+ Octstr *spool;
+ Counter *counter;
+ List *loaded;
+
+ /* pointer back to the MsgStore */
+ MsgStore *func;
+};
+
+typedef struct msg_store_spool msg_store_spool;
+
+
+static int store_spool_dump(void *arg)
+{
+ /* This type doesn't dump at all */
+ return 0;
+}
+
+
+static long store_spool_messages(void *arg)
+{
+ msg_store_spool *context = arg;
+
+ return context->counter ? counter_value(context->counter) : -1;
+}
+
+
+static int for_each_file(msg_store_spool *context, const Octstr *dir_s, int ignore_err,
+ void (*cb)(msg_store_spool*, const Octstr*, void*), void *data)
+{
+ DIR *dir;
+ struct dirent *ent;
+ struct stat stat;
+ int ret = 0;
+
+ if ((dir = opendir(octstr_get_cstr(dir_s))) == NULL) {
+ error(errno, "Could not open directory `%s'", octstr_get_cstr(dir_s));
+ return -1;
+ }
+ while((ent = readdir(dir)) != NULL) {
+ Octstr *filename;
+ if (*(ent->d_name) == '.') /* skip hidden files */
+ continue;
+ filename = octstr_format("%S/%s", dir_s, ent->d_name);
+ if (lstat(octstr_get_cstr(filename), &stat) == -1) {
+ if (!ignore_err)
+ error(errno, "Could not get stat for `%s'", octstr_get_cstr(filename));
+ ret = -1;
+ } else if (S_ISDIR(stat.st_mode) &&
+ for_each_file(context, filename, ignore_err, cb, data) == -1) {
+ ret = -1;
+ } else if (S_ISREG(stat.st_mode) && cb != NULL)
+ cb(context, filename, data);
+ octstr_destroy(filename);
+ if (ret == -1 && ignore_err)
+ ret = 0;
+ else if (ret == -1)
+ break;
+ }
+ closedir(dir);
+
+ return ret;
+}
+
+
+struct status {
+ const char *format;
+ Octstr *status;
+};
+
+
+static void status_cb(msg_store_spool *context, const Octstr *filename, void *d)
+{
+ struct status *data = d;
+ struct tm tm;
+ char id[UUID_STR_LEN + 1];
+ Octstr *msg_s;
+ Msg *msg;
+
+ msg_s = octstr_read_file(octstr_get_cstr(filename));
+ msg = context->func->store_msg_unpack(msg_s);
+ octstr_destroy(msg_s);
+ if (msg == NULL)
+ return;
+
+ /* transform the time value */
+#if LOG_TIMESTAMP_LOCALTIME
+ tm = gw_localtime(msg->sms.time);
+#else
+ tm = gw_gmtime(msg->sms.time);
+#endif
+ if (msg->sms.udhdata)
+ octstr_binary_to_hex(msg->sms.udhdata, 1);
+ if (msg->sms.msgdata &&
+ (msg->sms.coding == DC_8BIT || msg->sms.coding == DC_UCS2 ||
+ (msg->sms.coding == DC_UNDEF && msg->sms.udhdata)))
+ octstr_binary_to_hex(msg->sms.msgdata, 1);
+
+ uuid_unparse(msg->sms.id, id);
+
+ octstr_format_append(data->status, data->format,
+ id,
+ (msg->sms.sms_type == mo ? "MO" :
+ msg->sms.sms_type == mt_push ? "MT-PUSH" :
+ msg->sms.sms_type == mt_reply ? "MT-REPLY" :
+ msg->sms.sms_type == report_mo ? "DLR-MO" :
+ msg->sms.sms_type == report_mt ? "DLR-MT" : ""),
+ tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
+ tm.tm_hour, tm.tm_min, tm.tm_sec,
+ (msg->sms.sender ? octstr_get_cstr(msg->sms.sender) : ""),
+ (msg->sms.receiver ? octstr_get_cstr(msg->sms.receiver) : ""),
+ (msg->sms.smsc_id ? octstr_get_cstr(msg->sms.smsc_id) : ""),
+ (msg->sms.boxc_id ? octstr_get_cstr(msg->sms.boxc_id) : ""),
+ (msg->sms.udhdata ? octstr_get_cstr(msg->sms.udhdata) : ""),
+ (msg->sms.msgdata ? octstr_get_cstr(msg->sms.msgdata) : ""));
+
+ msg_destroy(msg);
+}
+
+
+static Octstr *store_spool_status(void *arg, int status_type)
+{
+ msg_store_spool *context = arg;
+ Octstr *ret = octstr_create("");
+ const char *format;
+ struct status data;
+
+ /* check if we are active */
+ if (context->spool == NULL)
+ return ret;
+
+ /* set the type based header */
+ if (status_type == MSG_STORE_STATUS_HTML) {
+ octstr_append_cstr(ret, "<table border=1>\n"
+ "<tr><td>SMS ID</td><td>Type</td><td>Time</td><td>Sender</td><td>Receiver</td>"
+ "<td>SMSC ID</td><td>BOX ID</td><td>UDH</td><td>Message</td>"
+ "</tr>\n");
+
+ format = "<tr><td>%s</td><td>%s</td>"
+ "<td>%04d-%02d-%02d %02d:%02d:%02d</td>"
+ "<td>%s</td><td>%s</td><td>%s</td>"
+ "<td>%s</td><td>%s</td><td>%s</td></tr>\n";
+ } else if (status_type == MSG_STORE_STATUS_XML) {
+ format = "<message>\n\t<id>%s</id>\n\t<type>%s</type>\n\t"
+ "<time>%04d-%02d-%02d %02d:%02d:%02d</time>\n\t"
+ "<sender>%s</sender>\n\t"
+ "<receiver>%s</receiver>\n\t<smsc-id>%s</smsc-id>\n\t"
+ "<box-id>%s</box-id>\n\t"
+ "<udh-data>%s</udh-data>\n\t<msg-data>%s</msg-data>\n\t"
+ "</message>\n";
+ } else {
+ octstr_append_cstr(ret, "[SMS ID] [Type] [Time] [Sender] [Receiver] [SMSC ID] [BOX ID] [UDH] [Message]\n");
+ format = "[%s] [%s] [%04d-%02d-%02d %02d:%02d:%02d] [%s] [%s] [%s] [%s] [%s] [%s]\n";
+ }
+
+ data.format = format;
+ data.status = ret;
+ /* ignore error because files may disappear */
+ for_each_file(context, context->spool, 1, status_cb, &data);
+
+ /* set the type based footer */
+ if (status_type == MSG_STORE_STATUS_HTML) {
+ octstr_append_cstr(ret,"</table>");
+ }
+
+ return ret;
+}
+
+
+static void dispatch(msg_store_spool *context, const Octstr *filename, void *data)
+{
+ Octstr *msg_s;
+ Msg *msg;
+ void (*receive_msg)(Msg*) = data;
+
+ /* debug("", 0, "dispatch(%s,...) called", octstr_get_cstr(filename)); */
+
+ msg_s = octstr_read_file(octstr_get_cstr(filename));
+ if (msg_s == NULL)
+ return;
+ msg = context->func->store_msg_unpack(msg_s);
+ octstr_destroy(msg_s);
+ if (msg != NULL) {
+ receive_msg(msg);
+ counter_increase(context->counter);
+ } else {
+ error(0, "Could not unpack message `%s'", octstr_get_cstr(filename));
+ }
+}
+
+
+static int store_spool_load(void *arg, void (*receive_msg)(Msg*))
+{
+ msg_store_spool *context = arg;
+ int rc;
+
+ /* check if we are active */
+ if (context->spool == NULL)
+ return 0;
+
+ /* sanity check */
+ if (receive_msg == NULL)
+ return -1;
+
+ rc = for_each_file(context, context->spool, 0, dispatch, receive_msg);
+
+ info(0, "Loaded %ld messages from store.", counter_value(context->counter));
+
+ /* allow using of storage */
+ gwlist_remove_producer(context->loaded);
+
+ return rc;
+}
+
+
+static int store_spool_save(void *arg, Msg *msg)
+{
+ msg_store_spool *context = arg;
+ char id[UUID_STR_LEN + 1];
+ Octstr *id_s;
+
+ /* always set msg id and timestamp */
+ if (msg_type(msg) == sms && uuid_is_null(msg->sms.id))
+ uuid_generate(msg->sms.id);
+
+ if (msg_type(msg) == sms && msg->sms.time == MSG_PARAM_UNDEFINED)
+ time(&msg->sms.time);
+
+ if (context->spool == NULL)
+ return 0;
+
+ /* Block here if the store isn't loaded still */
+ gwlist_consume(context->loaded);
+
+ switch (msg_type(msg)) {
+ case sms:
+ {
+ Octstr *os = context->func->store_msg_pack(msg);
+ Octstr *filename, *dir;
+ int fd;
+ size_t wrc;
+
+ if (os == NULL) {
+ error(0, "Could not pack message.");
+ return -1;
+ }
+ uuid_unparse(msg->sms.id, id);
+ id_s = octstr_create(id);
+ dir = octstr_format("%S/%ld", context->spool, octstr_hash_key(id_s) % MAX_DIRS);
+ octstr_destroy(id_s);
+ if (mkdir(octstr_get_cstr(dir), S_IRUSR|S_IWUSR|S_IXUSR) == -1 && errno != EEXIST) {
+ error(errno, "Could not create directory `%s'.", octstr_get_cstr(dir));
+ octstr_destroy(dir);
+ octstr_destroy(os);
+ return -1;
+ }
+ filename = octstr_format("%S/%s", dir, id);
+ octstr_destroy(dir);
+ if ((fd = open(octstr_get_cstr(filename), O_CREAT|O_EXCL|O_WRONLY, S_IRUSR|S_IWUSR)) == -1) {
+ error(errno, "Could not open file `%s'.", octstr_get_cstr(filename));
+ octstr_destroy(filename);
+ octstr_destroy(os);
+ return -1;
+ }
+ for (wrc = 0; wrc < octstr_len(os); ) {
+ size_t rc = write(fd, octstr_get_cstr(os) + wrc, octstr_len(os) - wrc);
+ if (rc == -1) {
+ /* remove file */
+ error(errno, "Could not write message to `%s'.", octstr_get_cstr(filename));
+ close(fd);
+ if (unlink(octstr_get_cstr(filename)) == -1)
+ error(errno, "Oops, Could not remove failed file `%s'.", octstr_get_cstr(filename));
+ octstr_destroy(os);
+ octstr_destroy(filename);
+ return -1;
+ }
+ wrc += rc;
+ }
+ close(fd);
+ counter_increase(context->counter);
+ octstr_destroy(filename);
+ octstr_destroy(os);
+ break;
+ }
+ case ack:
+ {
+ Octstr *filename;
+ uuid_unparse(msg->ack.id, id);
+ id_s = octstr_create(id);
+ filename = octstr_format("%S/%ld/%s", context->spool, octstr_hash_key(id_s) % MAX_DIRS, id);
+ octstr_destroy(id_s);
+ if (unlink(octstr_get_cstr(filename)) == -1) {
+ error(errno, "Could not unlink file `%s'.", octstr_get_cstr(filename));
+ octstr_destroy(filename);
+ return -1;
+ }
+ counter_decrease(context->counter);
+ octstr_destroy(filename);
+ break;
+ }
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int store_spool_save_ack(void *arg, Msg *msg, ack_status_t status)
+{
+ msg_store_spool *context = arg;
+ int ret;
+ Msg *nack = msg_create(ack);
+
+ nack->ack.nack = status;
+ uuid_copy(nack->ack.id, msg->sms.id);
+ nack->ack.time = msg->sms.time;
+ ret = store_spool_save(context, nack);
+ msg_destroy(nack);
+
+ return ret;
+}
+
+
+static void store_spool_shutdown(void *arg)
+{
+ msg_store_spool *context = arg;
+
+ if (context->spool == NULL)
+ return;
+
+ counter_destroy(context->counter);
+ octstr_destroy(context->spool);
+ gwlist_destroy(context->loaded, NULL);
+
+ gw_free(context);
+}
+
+
+static int store_spool_init(MsgStore *ms, va_list ap)
+{
+ msg_store_spool *mss;
+ Octstr *store_dir;
+ DIR *dir;
+
+ /* Get our required arguments. */
+ store_dir = va_arg(ap, Octstr*);
+ va_end(ap);
+
+ /* Constraint checks first. */
+ if (store_dir == NULL)
+ panic(0, "Message store for spool-dir needs a directory name.");
+
+ /* Check if we can open directory */
+ if ((dir = opendir(octstr_get_cstr(store_dir))) == NULL) {
+ panic(errno, "Could not open directory `%s'", octstr_get_cstr(store_dir));
+ }
+ closedir(dir);
+
+ /* Initialize the type specific data. */
+ mss = gw_malloc(sizeof(*mss));
+ mss->loaded = gwlist_create();
+ gwlist_add_producer(mss->loaded);
+ mss->spool = octstr_duplicate(store_dir);
+ mss->counter = counter_create();
+
+ mss->func = ms;
+ ms->context = mss;
+
+ return 1;
+}
+
+
+static struct store_ops spool_ops = {
+ .store_init = store_spool_init,
+ .store_messages = store_spool_messages,
+ .store_save = store_spool_save,
+ .store_save_ack = store_spool_save_ack,
+ .store_load = store_spool_load,
+ .store_dump = store_spool_dump,
+ .store_shutdown = store_spool_shutdown,
+ .store_status = store_spool_status
+};
+
+#endif /* DO_MSG_STORE */
Index: test/test_msg_store_dump.c
===================================================================
RCS file: test/test_msg_store_dump.c
diff -N test/test_msg_store_dump.c
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ test/test_msg_store_dump.c 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,115 @@
+/* ====================================================================
+ * The Kannel Software License, Version 1.0
+ *
+ * Copyright (c) 2001-2009 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.
+ */
+
+/*
+ * Simple tool that prints out content of a kannel storefile.
+ *
+ * Tobias Weber <[email protected]>
+ * Stipe Tolj <[email protected]>
+ */
+
+#include "gwlib/gwlib.h"
+#include "shared.h"
+#include "bearerbox.h"
+
+#include "gwlib/msg_store.h"
+
+static int counter = 0;
+
+static void print_msg(Msg *msg)
+{
+ counter++;
+ msg_dump(msg, 0);
+}
+
+/* void function to make gwlib happy */
+static int check_args(int i, int argc, char **argv) {
+ return -1;
+}
+
+int main(int argc, char **argv)
+{
+ int cf_index;
+ Octstr *type, *filename;
+ MsgStore *ms;
+
+ gwlib_init();
+
+ cf_index = get_and_set_debugs(argc, argv, check_args);
+
+ if (argv[cf_index] == NULL)
+ panic(0, "Usage: %s <store-file>", argv[0]);
+
+ type = octstr_create("file");
+ filename = octstr_create(argv[cf_index]);
+
+ /* init store subsystem */
+ ms = msg_store_init(STORE_FILE, msg_pack, msg_unpack_wrapper, filename, (long) -1);
+
+ /* pass every entry in the store to callback print_msg() */
+ msg_store_load(ms, print_msg);
+
+ info(0, "Store file contains %d msg entries", counter);
+ info(0, "Store file funciton returns %ld", msg_store_messages(ms));
+ info(0, "Shutting down.");
+
+ msg_store_shutdown(ms);
+
+ gwlib_shutdown();
+
+ return 1;
+}
+