asterisk-chan-dongle/smsdb.c

775 lines
27 KiB
C

/*
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
/*! \file
*
* \brief SMSdb
*
* \author Max von Buelow <max@m9x.de>
* \author Mark Spencer <markster@digium.com>
*
* The original code is from the astdb prat of the Asterisk project.
*/
#include "asterisk.h"
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <signal.h>
#include <dirent.h>
#include <sqlite3.h>
#include "asterisk/app.h"
#include "asterisk/utils.h"
#include "smsdb.h"
#include "chan_dongle.h"
#define MAX_DB_FIELD 256
AST_MUTEX_DEFINE_STATIC(dblock);
static sqlite3 *smsdb;
#define DEFINE_SQL_STATEMENT(stmt,sql) static sqlite3_stmt *stmt; \
const char stmt##_sql[] = sql;
DEFINE_SQL_STATEMENT(get_full_message_stmt, "SELECT message FROM incoming WHERE key = ? ORDER BY seqorder")
DEFINE_SQL_STATEMENT(put_message_stmt, "INSERT OR REPLACE INTO incoming (key, seqorder, expiration, message) VALUES (?, ?, datetime(julianday(CURRENT_TIMESTAMP) + ? / 86400.0), ?)")
DEFINE_SQL_STATEMENT(clear_messages_stmt, "DELETE FROM incoming WHERE key = ?")
DEFINE_SQL_STATEMENT(purge_messages_stmt, "DELETE FROM incoming WHERE expiration < CURRENT_TIMESTAMP")
DEFINE_SQL_STATEMENT(get_cnt_stmt, "SELECT COUNT(seqorder) FROM incoming WHERE key = ?")
DEFINE_SQL_STATEMENT(create_incoming_stmt, "CREATE TABLE IF NOT EXISTS incoming (key VARCHAR(256), seqorder INTEGER, expiration TIMESTAMP DEFAULT CURRENT_TIMESTAMP, message VARCHAR(256), PRIMARY KEY(key, seqorder))")
DEFINE_SQL_STATEMENT(create_index_stmt, "CREATE INDEX IF NOT EXISTS incoming_key ON incoming(key)")
DEFINE_SQL_STATEMENT(create_outgoingref_stmt, "CREATE TABLE IF NOT EXISTS outgoing_ref (key VARCHAR(256), refid INTEGER, PRIMARY KEY(key))") // key: IMSI/DEST_ADDR
DEFINE_SQL_STATEMENT(create_outgoingmsg_stmt, "CREATE TABLE IF NOT EXISTS outgoing_msg (dev VARCHAR(256), dst VARCHAR(255), cnt INTEGER, expiration TIMESTAMP, srr BOOLEAN, payload BLOB)")
DEFINE_SQL_STATEMENT(create_outgoingpart_stmt, "CREATE TABLE IF NOT EXISTS outgoing_part (key VARCHAR(256), msg INTEGER, status INTEGER, PRIMARY KEY(key))") // key: IMSI/DEST_ADDR/MR
DEFINE_SQL_STATEMENT(create_outgoingmsg_index_stmt, "CREATE INDEX IF NOT EXISTS outgoing_part_msg ON outgoing_part(msg)")
DEFINE_SQL_STATEMENT(ins_outgoingref_stmt, "INSERT INTO outgoing_ref (refid, key) VALUES (?, ?)") // This have to be the same order as set_outgoingref_stmt
DEFINE_SQL_STATEMENT(set_outgoingref_stmt, "UPDATE outgoing_ref SET refid = ? WHERE key = ?")
DEFINE_SQL_STATEMENT(get_outgoingref_stmt, "SELECT refid FROM outgoing_ref WHERE key = ?")
DEFINE_SQL_STATEMENT(put_outgoingmsg_stmt, "INSERT INTO outgoing_msg (dev, dst, cnt, expiration, srr, payload) VALUES (?, ?, ?, datetime(julianday(CURRENT_TIMESTAMP) + ? / 86400.0), ?, ?)")
DEFINE_SQL_STATEMENT(put_outgoingpart_stmt, "INSERT INTO outgoing_part (key, msg, status) VALUES (?, ?, NULL)")
DEFINE_SQL_STATEMENT(del_outgoingmsg_stmt, "DELETE FROM outgoing_msg WHERE rowid = ?")
DEFINE_SQL_STATEMENT(del_outgoingpart_stmt, "DELETE FROM outgoing_part WHERE msg = ?")
DEFINE_SQL_STATEMENT(get_outgoingmsg_stmt, "SELECT dev, dst, srr FROM outgoing_msg WHERE rowid = ?")
DEFINE_SQL_STATEMENT(set_outgoingpart_stmt, "UPDATE outgoing_part SET status = ? WHERE rowid = ?")
DEFINE_SQL_STATEMENT(get_outgoingpart_stmt, "SELECT rowid, msg FROM outgoing_part WHERE key = ?")
DEFINE_SQL_STATEMENT(cnt_outgoingpart_stmt, "SELECT m.cnt, (SELECT COUNT(p.rowid) FROM outgoing_part p WHERE p.msg = m.rowid AND (p.status & 64 != 0 OR p.status & 32 = 0)) FROM outgoing_msg m WHERE m.rowid = ?") // count all failed and completed messages; don't count messages without delivery notification and temporary failed ones
DEFINE_SQL_STATEMENT(cnt_all_outgoingpart_stmt, "SELECT m.cnt, (SELECT COUNT(p.rowid) FROM outgoing_part p WHERE p.msg = m.rowid) FROM outgoing_msg m WHERE m.rowid = ?")
DEFINE_SQL_STATEMENT(get_payload_stmt, "SELECT payload, dst FROM outgoing_msg WHERE rowid = ?")
DEFINE_SQL_STATEMENT(get_all_status_stmt, "SELECT status FROM outgoing_part WHERE msg = ? ORDER BY rowid")
DEFINE_SQL_STATEMENT(get_expired_stmt, "SELECT rowid, payload, dst FROM outgoing_msg WHERE expiration < CURRENT_TIMESTAMP LIMIT 1") // only fetch one expired row to balance the load of each transaction
static int init_stmt(sqlite3_stmt **stmt, const char *sql, size_t len)
{
ast_mutex_lock(&dblock);
if (sqlite3_prepare(smsdb, sql, len, stmt, NULL) != SQLITE_OK) {
ast_log(LOG_WARNING, "Couldn't prepare statement '%s': %s\n", sql, sqlite3_errmsg(smsdb));
ast_mutex_unlock(&dblock);
return -1;
}
ast_mutex_unlock(&dblock);
return 0;
}
/*! \internal
* \brief Clean up the prepared SQLite3 statement
* \note dblock should already be locked prior to calling this method
*/
static int clean_stmt(sqlite3_stmt **stmt, const char *sql)
{
if (sqlite3_finalize(*stmt) != SQLITE_OK) {
ast_log(LOG_WARNING, "Couldn't finalize statement '%s': %s\n", sql, sqlite3_errmsg(smsdb));
*stmt = NULL;
return -1;
}
*stmt = NULL;
return 0;
}
/*! \internal
* \brief Clean up all prepared SQLite3 statements
* \note dblock should already be locked prior to calling this method
*/
static void clean_statements(void)
{
clean_stmt(&get_full_message_stmt, get_full_message_stmt_sql);
clean_stmt(&put_message_stmt, put_message_stmt_sql);
clean_stmt(&clear_messages_stmt, clear_messages_stmt_sql);
clean_stmt(&purge_messages_stmt, purge_messages_stmt_sql);
clean_stmt(&get_cnt_stmt, get_cnt_stmt_sql);
clean_stmt(&create_incoming_stmt, create_incoming_stmt_sql);
clean_stmt(&create_index_stmt, create_index_stmt_sql);
clean_stmt(&create_outgoingref_stmt, create_outgoingref_stmt_sql);
clean_stmt(&create_outgoingmsg_stmt, create_outgoingmsg_stmt_sql);
clean_stmt(&create_outgoingpart_stmt, create_outgoingpart_stmt_sql);
clean_stmt(&create_outgoingmsg_index_stmt, create_outgoingmsg_index_stmt_sql);
clean_stmt(&ins_outgoingref_stmt, ins_outgoingref_stmt_sql);
clean_stmt(&set_outgoingref_stmt, set_outgoingref_stmt_sql);
clean_stmt(&get_outgoingref_stmt, get_outgoingref_stmt_sql);
clean_stmt(&put_outgoingmsg_stmt, put_outgoingmsg_stmt_sql);
clean_stmt(&put_outgoingpart_stmt, put_outgoingpart_stmt_sql);
clean_stmt(&del_outgoingmsg_stmt, del_outgoingmsg_stmt_sql);
clean_stmt(&del_outgoingpart_stmt, del_outgoingpart_stmt_sql);
clean_stmt(&get_outgoingmsg_stmt, get_outgoingmsg_stmt_sql);
clean_stmt(&set_outgoingpart_stmt, set_outgoingpart_stmt_sql);
clean_stmt(&get_outgoingpart_stmt, get_outgoingpart_stmt_sql);
clean_stmt(&cnt_outgoingpart_stmt, cnt_outgoingpart_stmt_sql);
clean_stmt(&cnt_all_outgoingpart_stmt, cnt_all_outgoingpart_stmt_sql);
clean_stmt(&get_payload_stmt, get_payload_stmt_sql);
clean_stmt(&get_all_status_stmt, get_all_status_stmt_sql);
clean_stmt(&get_expired_stmt, get_expired_stmt_sql);
}
static int init_statements(void)
{
/* Don't initialize create_smsdb_statement here as the smsdb table needs to exist
* brefore these statements can be initialized */
return init_stmt(&get_full_message_stmt, get_full_message_stmt_sql, sizeof(get_full_message_stmt_sql))
|| init_stmt(&put_message_stmt, put_message_stmt_sql, sizeof(put_message_stmt_sql))
|| init_stmt(&clear_messages_stmt, clear_messages_stmt_sql, sizeof(clear_messages_stmt_sql))
|| init_stmt(&purge_messages_stmt, purge_messages_stmt_sql, sizeof(purge_messages_stmt_sql))
|| init_stmt(&get_cnt_stmt, get_cnt_stmt_sql, sizeof(get_cnt_stmt_sql))
|| init_stmt(&ins_outgoingref_stmt, ins_outgoingref_stmt_sql, sizeof(ins_outgoingref_stmt_sql))
|| init_stmt(&set_outgoingref_stmt, set_outgoingref_stmt_sql, sizeof(set_outgoingref_stmt_sql))
|| init_stmt(&get_outgoingref_stmt, get_outgoingref_stmt_sql, sizeof(get_outgoingref_stmt_sql))
|| init_stmt(&put_outgoingmsg_stmt, put_outgoingmsg_stmt_sql, sizeof(put_outgoingmsg_stmt_sql))
|| init_stmt(&put_outgoingpart_stmt, put_outgoingpart_stmt_sql, sizeof(put_outgoingpart_stmt_sql))
|| init_stmt(&del_outgoingmsg_stmt, del_outgoingmsg_stmt_sql, sizeof(del_outgoingmsg_stmt_sql))
|| init_stmt(&del_outgoingpart_stmt, del_outgoingpart_stmt_sql, sizeof(del_outgoingpart_stmt_sql))
|| init_stmt(&get_outgoingmsg_stmt, get_outgoingmsg_stmt_sql, sizeof(get_outgoingmsg_stmt_sql))
|| init_stmt(&get_outgoingpart_stmt, get_outgoingpart_stmt_sql, sizeof(get_outgoingpart_stmt_sql))
|| init_stmt(&set_outgoingpart_stmt, set_outgoingpart_stmt_sql, sizeof(set_outgoingpart_stmt_sql))
|| init_stmt(&cnt_outgoingpart_stmt, cnt_outgoingpart_stmt_sql, sizeof(cnt_outgoingpart_stmt_sql))
|| init_stmt(&cnt_all_outgoingpart_stmt, cnt_all_outgoingpart_stmt_sql, sizeof(cnt_all_outgoingpart_stmt_sql))
|| init_stmt(&get_payload_stmt, get_payload_stmt_sql, sizeof(get_payload_stmt_sql))
|| init_stmt(&get_all_status_stmt, get_all_status_stmt_sql, sizeof(get_all_status_stmt_sql))
|| init_stmt(&get_expired_stmt, get_expired_stmt_sql, sizeof(get_expired_stmt_sql));
}
static int db_create_smsdb(void)
{
int res = 0;
if (!create_incoming_stmt) {
init_stmt(&create_incoming_stmt, create_incoming_stmt_sql, sizeof(create_incoming_stmt_sql));
}
ast_mutex_lock(&dblock);
if (sqlite3_step(create_incoming_stmt) != SQLITE_DONE) {
ast_log(LOG_WARNING, "Couldn't create smsdb table: %s\n", sqlite3_errmsg(smsdb));
res = -1;
}
sqlite3_reset(create_incoming_stmt);
ast_mutex_unlock(&dblock);
if (!create_index_stmt) {
init_stmt(&create_index_stmt, create_index_stmt_sql, sizeof(create_index_stmt_sql));
}
ast_mutex_lock(&dblock);
if (sqlite3_step(create_index_stmt) != SQLITE_DONE) {
ast_log(LOG_WARNING, "Couldn't create smsdb index: %s\n", sqlite3_errmsg(smsdb));
res = -1;
}
sqlite3_reset(create_index_stmt);
ast_mutex_unlock(&dblock);
if (!create_outgoingref_stmt) {
init_stmt(&create_outgoingref_stmt, create_outgoingref_stmt_sql, sizeof(create_outgoingref_stmt_sql));
}
ast_mutex_lock(&dblock);
if (sqlite3_step(create_outgoingref_stmt) != SQLITE_DONE) {
ast_log(LOG_WARNING, "Couldn't create smsdb outgoing table: %s\n", sqlite3_errmsg(smsdb));
res = -1;
}
sqlite3_reset(create_outgoingref_stmt);
ast_mutex_unlock(&dblock);
if (!create_outgoingmsg_stmt) {
init_stmt(&create_outgoingmsg_stmt, create_outgoingmsg_stmt_sql, sizeof(create_outgoingmsg_stmt_sql));
}
ast_mutex_lock(&dblock);
if (sqlite3_step(create_outgoingmsg_stmt) != SQLITE_DONE) {
ast_log(LOG_WARNING, "Couldn't create smsdb outgoing table: %s\n", sqlite3_errmsg(smsdb));
res = -1;
}
sqlite3_reset(create_outgoingmsg_stmt);
ast_mutex_unlock(&dblock);
if (!create_outgoingpart_stmt) {
init_stmt(&create_outgoingpart_stmt, create_outgoingpart_stmt_sql, sizeof(create_outgoingpart_stmt_sql));
}
ast_mutex_lock(&dblock);
if (sqlite3_step(create_outgoingpart_stmt) != SQLITE_DONE) {
ast_log(LOG_WARNING, "Couldn't create smsdb outgoing table: %s\n", sqlite3_errmsg(smsdb));
res = -1;
}
sqlite3_reset(create_outgoingpart_stmt);
ast_mutex_unlock(&dblock);
if (!create_outgoingmsg_index_stmt) {
init_stmt(&create_outgoingmsg_index_stmt, create_outgoingmsg_index_stmt_sql, sizeof(create_outgoingmsg_index_stmt_sql));
}
ast_mutex_lock(&dblock);
if (sqlite3_step(create_outgoingmsg_index_stmt) != SQLITE_DONE) {
ast_log(LOG_WARNING, "Couldn't create smsdb outgoing index: %s\n", sqlite3_errmsg(smsdb));
res = -1;
}
sqlite3_reset(create_outgoingmsg_index_stmt);
ast_mutex_unlock(&dblock);
return res;
}
static int db_open(void)
{
char *dbname;
if (!(dbname = ast_alloca(strlen(CONF_GLOBAL(sms_db)) + sizeof(".sqlite3")))) {
return -1;
}
strcpy(dbname, CONF_GLOBAL(sms_db));
strcat(dbname, ".sqlite3");
ast_mutex_lock(&dblock);
if (sqlite3_open(dbname, &smsdb) != SQLITE_OK) {
ast_log(LOG_WARNING, "Unable to open Asterisk database '%s': %s\n", dbname, sqlite3_errmsg(smsdb));
sqlite3_close(smsdb);
ast_mutex_unlock(&dblock);
return -1;
}
ast_mutex_unlock(&dblock);
return 0;
}
static int db_init()
{
if (smsdb) {
return 0;
}
if (db_open() || db_create_smsdb() || init_statements()) {
return -1;
}
return 0;
}
/* We purposely don't lock around the sqlite3 call because the transaction
* calls will be called with the database lock held. For any other use, make
* sure to take the dblock yourself. */
static int db_execute_sql(const char *sql, int (*callback)(void *, int, char **, char **), void *arg)
{
char *errmsg = NULL;
int res =0;
if (sqlite3_exec(smsdb, sql, callback, arg, &errmsg) != SQLITE_OK) {
ast_log(LOG_WARNING, "Error executing SQL (%s): %s\n", sql, errmsg);
sqlite3_free(errmsg);
res = -1;
}
return res;
}
static int smsdb_begin_transaction(void)
{
ast_mutex_lock(&dblock);
int res = db_execute_sql("BEGIN TRANSACTION", NULL, NULL);
return res;
}
static int smsdb_commit_transaction(void)
{
int res = db_execute_sql("COMMIT", NULL, NULL);
ast_mutex_unlock(&dblock);
return res;
}
#if 0
static int smsdb_rollback_transaction(void)
{
int res = db_execute_sql("ROLLBACK", NULL, NULL);
ast_mutex_unlock(&dblock);
return res;
}
#endif
/*!
* \brief Adds a message part into the DB and returns the whole message into 'out' when the message is complete.
* \param id -- Some ID for the device or so, e.g. the IMSI
* \param addr -- The sender address
* \param ref -- The reference ID
* \param parts -- The total number of messages
* \param order -- The current message number
* \param msg -- The current message part
* \param out -- Output: Only written if parts == cnt
* \retval <=0 Error
* \retval >0 Current number of messages in the DB
*/
EXPORT_DEF int smsdb_put(const char *id, const char *addr, int ref, int parts, int order, const char *msg, char *out)
{
const char *part;
char fullkey[MAX_DB_FIELD + 1];
int fullkey_len;
int res = 0;
int ttl = CONF_GLOBAL(csms_ttl);
fullkey_len = snprintf(fullkey, sizeof(fullkey), "%s/%s/%d/%d", id, addr, ref, parts);
if (fullkey_len < 0) {
ast_log(LOG_ERROR, "Key length must be less than %zu bytes\n", sizeof(fullkey));
return -1;
}
smsdb_begin_transaction();
if (sqlite3_bind_text(put_message_stmt, 1, fullkey, fullkey_len, SQLITE_STATIC) != SQLITE_OK) {
ast_log(LOG_WARNING, "Couldn't bind key to stmt: %s\n", sqlite3_errmsg(smsdb));
res = -1;
} else if (sqlite3_bind_int(put_message_stmt, 2, order) != SQLITE_OK) {
ast_log(LOG_WARNING, "Couldn't bind order to stmt: %s\n", sqlite3_errmsg(smsdb));
res = -1;
} else if (sqlite3_bind_int(put_message_stmt, 3, ttl) != SQLITE_OK) {
ast_log(LOG_WARNING, "Couldn't bind TTL to stmt: %s\n", sqlite3_errmsg(smsdb));
res = -1;
} else if (sqlite3_bind_text(put_message_stmt, 4, msg, -1, SQLITE_STATIC) != SQLITE_OK) {
ast_log(LOG_WARNING, "Couldn't bind msg to stmt: %s\n", sqlite3_errmsg(smsdb));
res = -1;
} else if (sqlite3_step(put_message_stmt) != SQLITE_DONE) {
ast_log(LOG_WARNING, "Couldn't execute statement: %s\n", sqlite3_errmsg(smsdb));
res = -1;
}
sqlite3_reset(put_message_stmt);
if (sqlite3_bind_text(get_cnt_stmt, 1, fullkey, fullkey_len, SQLITE_STATIC) != SQLITE_OK) {
ast_log(LOG_WARNING, "Couldn't bind key to stmt: %s\n", sqlite3_errmsg(smsdb));
res = -1;
} else if (sqlite3_step(get_cnt_stmt) != SQLITE_ROW) {
ast_debug(1, "Unable to find key '%s'\n", fullkey);
res = -1;
}
res = sqlite3_column_int(get_cnt_stmt, 0);
sqlite3_reset(get_cnt_stmt);
if (res != -1 && res == parts) {
if (sqlite3_bind_text(get_full_message_stmt, 1, fullkey, fullkey_len, SQLITE_STATIC) != SQLITE_OK) {
ast_log(LOG_WARNING, "Couldn't bind key to stmt: %s\n", sqlite3_errmsg(smsdb));
res = -1;
} else while (sqlite3_step(get_full_message_stmt) == SQLITE_ROW) {
part = (const char*)sqlite3_column_text(get_full_message_stmt, 0);
int partlen = sqlite3_column_bytes(get_full_message_stmt, 0);
if (!part) {
ast_log(LOG_WARNING, "Couldn't get value\n");
res = -1;
break;
}
out = stpncpy(out, part, partlen);
}
out[0] = '\0';
sqlite3_reset(get_full_message_stmt);
if (res >= 0) {
if (sqlite3_bind_text(clear_messages_stmt, 1, fullkey, fullkey_len, SQLITE_STATIC) != SQLITE_OK) {
ast_log(LOG_WARNING, "Couldn't bind key to stmt: %s\n", sqlite3_errmsg(smsdb));
res = -1;
} else if (sqlite3_step(clear_messages_stmt) != SQLITE_DONE) {
ast_debug(1, "Unable to find key '%s'; Ignoring\n", fullkey);
}
sqlite3_reset(clear_messages_stmt);
}
}
smsdb_commit_transaction();
return res;
}
#if 0
static int smsdb_purge()
{
int res = 0;
if (sqlite3_step(purge_messages_stmt) != SQLITE_DONE) {
res = -1;
}
sqlite3_reset(purge_messages_stmt);
return res;
}
#endif
EXPORT_DEF int smsdb_get_refid(const char *id, const char *addr)
{
int res = 0;
smsdb_begin_transaction();
char fullkey[MAX_DB_FIELD + 1];
int fullkey_len;
fullkey_len = snprintf(fullkey, sizeof(fullkey), "%s/%s", id, addr);
if (fullkey_len < 0) {
ast_log(LOG_ERROR, "Key length must be less than %zu bytes\n", sizeof(fullkey));
return -1;
}
int use_insert = 0;
if (sqlite3_bind_text(get_outgoingref_stmt, 1, fullkey, fullkey_len, SQLITE_STATIC) != SQLITE_OK) {
ast_log(LOG_WARNING, "Couldn't bind key to stmt: %s\n", sqlite3_errmsg(smsdb));
res = -1;
} else if (sqlite3_step(get_outgoingref_stmt) != SQLITE_ROW) {
res = 255;
use_insert = 1;
} else {
res = sqlite3_column_int(get_outgoingref_stmt, 0);
}
sqlite3_reset(get_outgoingref_stmt);
if (res >= 0) {
++res;
if (res >= 256) res = 0;
sqlite3_stmt *stmt = use_insert ? ins_outgoingref_stmt : set_outgoingref_stmt;
if (sqlite3_bind_int(stmt, 1, res) != SQLITE_OK) {
ast_log(LOG_WARNING, "Couldn't bind refid to stmt: %s\n", sqlite3_errmsg(smsdb));
res = -1;
} else if (sqlite3_bind_text(stmt, 2, fullkey, fullkey_len, SQLITE_STATIC) != SQLITE_OK) {
ast_log(LOG_WARNING, "Couldn't bind key to stmt: %s\n", sqlite3_errmsg(smsdb));
res = -1;
} else if (sqlite3_step(stmt) != SQLITE_DONE) {
res = -1;
}
sqlite3_reset(stmt);
}
smsdb_commit_transaction();
return res;
}
EXPORT_DEF int smsdb_outgoing_add(const char *id, const char *addr, int cnt, int ttl, int srr, const char *payload, size_t len)
{
int res = 0;
smsdb_begin_transaction();
if (sqlite3_bind_text(put_outgoingmsg_stmt, 1, id, strlen(id), SQLITE_STATIC) != SQLITE_OK) {
ast_log(LOG_WARNING, "Couldn't bind dev to stmt: %s\n", sqlite3_errmsg(smsdb));
res = -1;
} else if (sqlite3_bind_text(put_outgoingmsg_stmt, 2, addr, strlen(addr), SQLITE_STATIC) != SQLITE_OK) {
ast_log(LOG_WARNING, "Couldn't bind destination address to stmt: %s\n", sqlite3_errmsg(smsdb));
res = -1;
} else if (sqlite3_bind_int(put_outgoingmsg_stmt, 3, cnt) != SQLITE_OK) {
ast_log(LOG_WARNING, "Couldn't bind count to stmt: %s\n", sqlite3_errmsg(smsdb));
res = -1;
} else if (sqlite3_bind_int(put_outgoingmsg_stmt, 4, ttl) != SQLITE_OK) {
ast_log(LOG_WARNING, "Couldn't bind TTL to stmt: %s\n", sqlite3_errmsg(smsdb));
res = -1;
} else if (sqlite3_bind_int(put_outgoingmsg_stmt, 5, srr) != SQLITE_OK) {
ast_log(LOG_WARNING, "Couldn't bind SRR to stmt: %s\n", sqlite3_errmsg(smsdb));
res = -1;
} else if (sqlite3_bind_blob(put_outgoingmsg_stmt, 6, payload, len > SMSDB_PAYLOAD_MAX_LEN ? SMSDB_PAYLOAD_MAX_LEN : len, SQLITE_STATIC) != SQLITE_OK) {
ast_log(LOG_WARNING, "Couldn't bind payload to stmt: %s\n", sqlite3_errmsg(smsdb));
res = -1;
} else if (sqlite3_step(put_outgoingmsg_stmt) != SQLITE_DONE) {
res = -1;
} else {
res = sqlite3_last_insert_rowid(smsdb);
}
sqlite3_reset(put_outgoingmsg_stmt);
smsdb_commit_transaction();
return res;
}
static int smsdb_outgoing_clear_nolock(int uid)
{
int res = 0;
if (sqlite3_bind_int(del_outgoingmsg_stmt, 1, uid) != SQLITE_OK) {
ast_log(LOG_WARNING, "Couldn't bind UID to stmt: %s\n", sqlite3_errmsg(smsdb));
res = -1;
} else if (sqlite3_step(del_outgoingmsg_stmt) != SQLITE_DONE) {
res = -1;
}
sqlite3_reset(del_outgoingmsg_stmt);
if (sqlite3_bind_int(del_outgoingpart_stmt, 1, uid) != SQLITE_OK) {
ast_log(LOG_WARNING, "Couldn't bind UID to stmt: %s\n", sqlite3_errmsg(smsdb));
res = -1;
} else if (sqlite3_step(del_outgoingpart_stmt) != SQLITE_DONE) {
res = -1;
}
sqlite3_reset(del_outgoingpart_stmt);
return res;
}
EXPORT_DEF ssize_t smsdb_outgoing_clear(int uid, char *dst, char *payload)
{
int res = 0;
smsdb_begin_transaction();
if (sqlite3_bind_int(get_payload_stmt, 1, uid) != SQLITE_OK) {
ast_log(LOG_WARNING, "Couldn't bind key to stmt: %s\n", sqlite3_errmsg(smsdb));
res = -1;
} else if (sqlite3_step(get_payload_stmt) != SQLITE_ROW) {
res = -1;
} else {
strcpy(dst, (const char*)sqlite3_column_text(get_payload_stmt, 1));
res = sqlite3_column_bytes(get_payload_stmt, 0);
res = res > SMSDB_PAYLOAD_MAX_LEN ? SMSDB_PAYLOAD_MAX_LEN : res;
memcpy(payload, sqlite3_column_blob(get_payload_stmt, 0), res);
}
sqlite3_reset(get_payload_stmt);
if (res != -1 && smsdb_outgoing_clear_nolock(uid) < 0) {
res = -1;
}
smsdb_commit_transaction();
return res;
}
EXPORT_DEF ssize_t smsdb_outgoing_part_put(int uid, int refid, char *dst, char *payload)
{
int res = 0;
char fullkey[MAX_DB_FIELD + 1];
int fullkey_len;
int srr = 0, cnt, cur;
smsdb_begin_transaction();
if (sqlite3_bind_int(get_outgoingmsg_stmt, 1, uid) != SQLITE_OK) {
ast_log(LOG_WARNING, "Couldn't bind UID to stmt: %s\n", sqlite3_errmsg(smsdb));
res = -1;
} else if (sqlite3_step(get_outgoingmsg_stmt) != SQLITE_ROW) {
res = -2;
} else {
const char *dev = (const char*)sqlite3_column_text(get_outgoingmsg_stmt, 0);
const char *dst = (const char*)sqlite3_column_text(get_outgoingmsg_stmt, 1);
srr = sqlite3_column_int(get_outgoingmsg_stmt, 2);
fullkey_len = snprintf(fullkey, sizeof(fullkey), "%s/%s/%d", dev, dst, refid);
if (fullkey_len < 0) {
ast_log(LOG_ERROR, "Key length must be less than %zu bytes\n", sizeof(fullkey));
return -1;
}
}
sqlite3_reset(get_outgoingmsg_stmt);
if (res >= 0) {
if (sqlite3_bind_text(put_outgoingpart_stmt, 1, fullkey, fullkey_len, SQLITE_STATIC) != SQLITE_OK) {
ast_log(LOG_WARNING, "Couldn't bind key to stmt: %s\n", sqlite3_errmsg(smsdb));
res = -1;
} else if (sqlite3_bind_int(put_outgoingpart_stmt, 2, uid) != SQLITE_OK) {
ast_log(LOG_WARNING, "Couldn't bind UID to stmt: %s\n", sqlite3_errmsg(smsdb));
res = -1;
} else if (sqlite3_step(put_outgoingpart_stmt) != SQLITE_DONE) {
res = -1;
}
sqlite3_reset(put_outgoingpart_stmt);
}
if (srr) {
res = -2;
}
// if no status report is requested, just count successfully inserted parts and return payload if the counter reached the number of parts
if (res >= 0) {
if (sqlite3_bind_int(cnt_all_outgoingpart_stmt, 1, uid) != SQLITE_OK) {
ast_log(LOG_WARNING, "Couldn't bind key to stmt: %s\n", sqlite3_errmsg(smsdb));
res = -1;
} else if (sqlite3_step(cnt_all_outgoingpart_stmt) != SQLITE_ROW) {
res = -1;
} else {
cur = sqlite3_column_int(cnt_all_outgoingpart_stmt, 0);
cnt = sqlite3_column_int(cnt_all_outgoingpart_stmt, 1);
}
sqlite3_reset(cnt_all_outgoingpart_stmt);
}
if (res >= 0 && cur != cnt) {
res = -2;
}
// get payload
if (res >= 0) {
if (sqlite3_bind_int(get_payload_stmt, 1, uid) != SQLITE_OK) {
ast_log(LOG_WARNING, "Couldn't bind key to stmt: %s\n", sqlite3_errmsg(smsdb));
res = -1;
} else if (sqlite3_step(get_payload_stmt) != SQLITE_ROW) {
res = -1;
} else {
strcpy(dst, (const char*)sqlite3_column_text(get_payload_stmt, 1));
res = sqlite3_column_bytes(get_payload_stmt, 0);
res = res > SMSDB_PAYLOAD_MAX_LEN ? SMSDB_PAYLOAD_MAX_LEN : res;
memcpy(payload, sqlite3_column_blob(get_payload_stmt, 0), res);
}
sqlite3_reset(get_payload_stmt);
}
// clear if everything is finished
if (res >= 0 && smsdb_outgoing_clear_nolock(uid) < 0) {
res = -1;
}
smsdb_commit_transaction();
return res;
}
EXPORT_DEF ssize_t smsdb_outgoing_part_status(const char *id, const char *addr, int mr, int st, int *status_all, char *payload)
{
char fullkey[MAX_DB_FIELD + 1];
int fullkey_len;
int res = 0;
int partid;
int uid = -1;
int cur;
int cnt;
fullkey_len = snprintf(fullkey, sizeof(fullkey), "%s/%s/%d", id, addr, mr);
if (fullkey_len < 0) {
ast_log(LOG_ERROR, "Key length must be less than %zu bytes\n", sizeof(fullkey));
return -1;
}
smsdb_begin_transaction();
if (sqlite3_bind_text(get_outgoingpart_stmt, 1, fullkey, fullkey_len, SQLITE_STATIC) != SQLITE_OK) {
ast_log(LOG_WARNING, "Couldn't bind key to stmt: %s\n", sqlite3_errmsg(smsdb));
res = -1;
} else if (sqlite3_step(get_outgoingpart_stmt) != SQLITE_ROW) {
res = -1;
} else {
partid = sqlite3_column_int(get_outgoingpart_stmt, 0);
uid = sqlite3_column_int(get_outgoingpart_stmt, 1);
}
sqlite3_reset(get_outgoingpart_stmt);
// set status
if (res >= 0) {
if (sqlite3_bind_int(set_outgoingpart_stmt, 1, st) != SQLITE_OK) {
ast_log(LOG_WARNING, "Couldn't bind status to stmt: %s\n", sqlite3_errmsg(smsdb));
res = -1;
} else if (sqlite3_bind_int(set_outgoingpart_stmt, 2, partid) != SQLITE_OK) {
ast_log(LOG_WARNING, "Couldn't bind ID to stmt: %s\n", sqlite3_errmsg(smsdb));
res = -1;
} else if (sqlite3_step(set_outgoingpart_stmt) != SQLITE_DONE) {
res = -1;
}
sqlite3_reset(set_outgoingpart_stmt);
}
// get count
if (res >= 0) {
if (sqlite3_bind_int(cnt_outgoingpart_stmt, 1, uid) != SQLITE_OK) {
ast_log(LOG_WARNING, "Couldn't bind key to stmt: %s\n", sqlite3_errmsg(smsdb));
res = -1;
} else if (sqlite3_step(cnt_outgoingpart_stmt) != SQLITE_ROW) {
res = -1;
} else {
cur = sqlite3_column_int(cnt_outgoingpart_stmt, 0);
cnt = sqlite3_column_int(cnt_outgoingpart_stmt, 1);
}
sqlite3_reset(cnt_outgoingpart_stmt);
}
if (res != -1 && cur != cnt) {
res = -2;
}
// get status array
if (res >= 0) {
int i = 0;
if (sqlite3_bind_int(get_all_status_stmt, 1, uid) != SQLITE_OK) {
ast_log(LOG_WARNING, "Couldn't bind key to stmt: %s\n", sqlite3_errmsg(smsdb));
res = -1;
} else while (sqlite3_step(get_all_status_stmt) == SQLITE_ROW) {
status_all[i++] = sqlite3_column_int(get_all_status_stmt, 0);
}
status_all[i] = -1;
sqlite3_reset(get_all_status_stmt);
}
// get payload
if (res >= 0) {
if (sqlite3_bind_int(get_payload_stmt, 1, uid) != SQLITE_OK) {
ast_log(LOG_WARNING, "Couldn't bind key to stmt: %s\n", sqlite3_errmsg(smsdb));
res = -1;
} else if (sqlite3_step(get_payload_stmt) != SQLITE_ROW) {
res = -1;
} else {
res = sqlite3_column_bytes(get_payload_stmt, 0);
res = res > SMSDB_PAYLOAD_MAX_LEN ? SMSDB_PAYLOAD_MAX_LEN : res;
memcpy(payload, sqlite3_column_blob(get_payload_stmt, 0), res);
}
sqlite3_reset(get_payload_stmt);
}
// clear if everything is finished
if (res >= 0 && smsdb_outgoing_clear_nolock(uid) < 0) {
res = -1;
}
smsdb_commit_transaction();
return res;
}
EXPORT_DEF ssize_t smsdb_outgoing_purge_one(char *dst, char *payload)
{
int res = -1, uid;
smsdb_begin_transaction();
if (sqlite3_step(get_expired_stmt) != SQLITE_ROW) {
res = -1;
} else {
uid = sqlite3_column_int(get_expired_stmt, 0);
strcpy(dst, (const char*)sqlite3_column_text(get_expired_stmt, 2));
res = sqlite3_column_bytes(get_expired_stmt, 1);
res = res > SMSDB_PAYLOAD_MAX_LEN ? SMSDB_PAYLOAD_MAX_LEN : res;
memcpy(payload, sqlite3_column_blob(get_expired_stmt, 1), res);
}
sqlite3_reset(get_expired_stmt);
if (res != -1 && smsdb_outgoing_clear_nolock(uid) < 0) {
res = -1;
}
smsdb_commit_transaction();
return res;
}
/*!
* \internal
* \brief Clean up resources on Asterisk shutdown
*/
EXPORT_DEF void smsdb_atexit()
{
ast_mutex_lock(&dblock);
clean_statements();
if (sqlite3_close(smsdb) == SQLITE_OK) {
smsdb = NULL;
}
ast_mutex_unlock(&dblock);
}
EXPORT_DEF int smsdb_init()
{
if (db_init()) {
return -1;
}
return 0;
}