/* ====================================================================
* The Kannel Software License, Version 1.0
*
* Copyright (c) 2001-2005 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 org@kannel.org.
*
* 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 .
*
* Portions of this software are based upon software originally written at
* WapIT Ltd., Helsinki, Finland for the Kannel project.
*/
/*
* bb_store.c : bearerbox box SMS storage/retrieval module
*
* Kalle Marjola 2001 for project Kannel
*
* Updated Oct 2004
*
* New features:
* - uses dict to save messages, for faster retrieval
* - acks are no longer saved (to memory), they simply delete
* messages from dict
* - better choice when dump done; configurable frequency
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "gwlib/gwlib.h"
#include "msg.h"
#include "bearerbox.h"
#include "sms.h"
static FILE *file = NULL;
static Octstr *filename = NULL;
static Octstr *newfile = NULL;
static Octstr *bakfile = NULL;
static Mutex *file_mutex = NULL;
static long cleanup_thread = -1;
static long dump_frequency = 0;
static Dict *sms_dict = NULL;
static int active = 1;
static time_t last_dict_mod = 0;
static List *loaded;
static void write_msg(Msg *msg)
{
Octstr *pack;
unsigned char buf[4];
pack = msg_pack(msg);
encode_network_long(buf, octstr_len(pack));
octstr_insert_data(pack, 0, (char*)buf, 4);
octstr_print(file, pack);
fflush(file);
octstr_destroy(pack);
}
static int read_msg(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 = msg_unpack(pack);
octstr_destroy(pack);
if (!*msg)
return -1;
return 0;
}
static int open_file(Octstr *name)
{
file = fopen(octstr_get_cstr(name), "w");
if (file == NULL) {
error(errno, "Failed to open '%s' for writing, cannot create store-file",
octstr_get_cstr(name));
return -1;
}
return 0;
}
static int rename_store(void)
{
if (rename(octstr_get_cstr(filename), octstr_get_cstr(bakfile)) == -1) {
if (errno != ENOENT) {
error(errno, "Failed to rename old store '%s' as '%s'",
octstr_get_cstr(filename), octstr_get_cstr(bakfile));
return -1;
}
}
if (rename(octstr_get_cstr(newfile), octstr_get_cstr(filename)) == -1) {
error(errno, "Failed to rename new store '%s' as '%s'",
octstr_get_cstr(newfile), octstr_get_cstr(filename));
return -1;
}
return 0;
}
static int do_dump(void)
{
Octstr *key;
Msg *msg;
List *sms_list;
long l;
if (filename == NULL)
return 0;
/* create a new store-file and save all non-acknowledged
* messages into it
*/
if (open_file(newfile)==-1)
return -1;
sms_list = dict_keys(sms_dict);
for (l=0; l < gwlist_len(sms_list); l++) {
key = gwlist_get(sms_list, l);
msg = dict_get(sms_dict, key);
if (msg != NULL)
write_msg(msg);
}
fflush(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();
}
/*
* 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)
{
time_t now;
int busy = 0;
while (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 - last_dict_mod > dump_frequency || busy) {
store_dump();
/*
* 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.
*/
last_dict_mod = time(NULL) + 3600*24;
busy = 0;
} else {
busy = (now - last_dict_mod) > 0;
}
gwthread_sleep(dump_frequency);
}
store_dump();
if (file != NULL)
fclose(file);
octstr_destroy(filename);
octstr_destroy(newfile);
octstr_destroy(bakfile);
mutex_destroy(file_mutex);
dict_destroy(sms_dict);
/* set all vars to NULL */
filename = newfile = bakfile = NULL;
file_mutex = NULL;
sms_dict = NULL;
}
/*------------------------------------------------------*/
Octstr *store_status(int status_type)
{
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 == BBSTATUS_HTML) {
octstr_append_cstr(ret, "
\n"
"
SMS ID
Type
Time
Sender
Receiver
"
"
SMSC ID
BOX ID
UDH
Message
"
"
\n");
} else if (status_type == BBSTATUS_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 (filename == NULL)
goto finish;
keys = dict_keys(sms_dict);
for (l = 0; l < gwlist_len(keys); l++) {
key = gwlist_get(keys, l);
msg = dict_get(sms_dict, key);
if (msg == NULL)
continue;
if (msg_type(msg) == sms) {
if (status_type == BBSTATUS_HTML) {
frmt = "