/* * * 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 * \author Mark Spencer * * The original code is from the astdb prat of the Asterisk project. */ #include "asterisk.h" #include #include #include #include #include #include #include #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; }