blob: 000002a3d9f4c4287b2539e980cd36b7083d7e07 [file] [log] [blame]
Harald Welted43c22e2022-05-14 15:35:49 +02001/* Simple HLR/VLR database backend using sqlite3 */
Jan Luebbefaaa49c2008-12-27 01:07:07 +00002/* (C) 2008 by Jan Luebbe <jluebbe@debian.org>
Holger Freyther12aa50d2009-01-01 18:02:05 +00003 * (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
Harald Welted43c22e2022-05-14 15:35:49 +02004 * (C) 2009,2022 by Harald Welte <laforge@gnumonks.org>
Jan Luebbefaaa49c2008-12-27 01:07:07 +00005 * All Rights Reserved
6 *
7 * This program is free software; you can redistribute it and/or modify
Harald Welte9af6ddf2011-01-01 15:25:50 +01008 * it under the terms of the GNU Affero General Public License as published by
9 * the Free Software Foundation; either version 3 of the License, or
Jan Luebbefaaa49c2008-12-27 01:07:07 +000010 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
Harald Welte9af6ddf2011-01-01 15:25:50 +010015 * GNU Affero General Public License for more details.
Jan Luebbefaaa49c2008-12-27 01:07:07 +000016 *
Harald Welte9af6ddf2011-01-01 15:25:50 +010017 * You should have received a copy of the GNU Affero General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
Jan Luebbefaaa49c2008-12-27 01:07:07 +000019 *
20 */
21
Harald Weltef2b4cd72010-05-13 11:45:07 +020022#include <stdint.h>
23#include <inttypes.h>
Holger Freytherbde36102008-12-28 22:51:39 +000024#include <libgen.h>
Jan Luebbe7398eb92008-12-27 00:45:41 +000025#include <stdio.h>
Maxe6052c42016-06-30 10:25:49 +020026#include <stdbool.h>
Jan Luebbe5c15c852008-12-27 15:59:25 +000027#include <stdlib.h>
28#include <string.h>
Harald Welte7e310b12009-03-30 20:56:32 +000029#include <errno.h>
Stefan Sperlingd4941622018-01-18 17:36:28 +010030#include <time.h>
Harald Welte467fc572022-05-13 19:48:08 +020031#include <sqlite3.h>
Jan Luebbe7398eb92008-12-27 00:45:41 +000032
Neels Hofmeyr90843962017-09-04 15:04:35 +020033#include <osmocom/msc/gsm_data.h>
34#include <osmocom/msc/gsm_subscriber.h>
35#include <osmocom/msc/gsm_04_11.h>
36#include <osmocom/msc/db.h>
37#include <osmocom/msc/debug.h>
38#include <osmocom/msc/vlr.h>
Holger Hans Peter Freytherc5faf662010-12-22 18:16:01 +010039
Harald Welted3fa84d2016-04-20 17:50:17 +020040#include <osmocom/gsm/protocol/gsm_23_003.h>
Pablo Neira Ayuso136f4532011-03-22 16:47:59 +010041#include <osmocom/core/talloc.h>
42#include <osmocom/core/statistics.h>
43#include <osmocom/core/rate_ctr.h>
Neels Hofmeyr93bafb62017-01-13 03:12:08 +010044#include <osmocom/core/utils.h>
Harald Weltef2b4cd72010-05-13 11:45:07 +020045
Harald Welted43c22e2022-05-14 15:35:49 +020046enum stmt_idx {
47 DB_STMT_SMS_STORE,
48 DB_STMT_SMS_GET,
49 DB_STMT_SMS_GET_NEXT_UNSENT,
50 DB_STMT_SMS_GET_UNSENT_FOR_SUBSCR,
51 DB_STMT_SMS_GET_NEXT_UNSENT_RR_MSISDN,
52 DB_STMT_SMS_MARK_DELIVERED,
53 DB_STMT_SMS_INC_DELIVER_ATTEMPTS,
54 DB_STMT_SMS_DEL_BY_MSISDN,
55 DB_STMT_SMS_DEL_BY_ID,
56 DB_STMT_SMS_DEL_EXPIRED,
57 DB_STMT_SMS_GET_VALID_UNTIL_BY_ID,
58 DB_STMT_SMS_GET_OLDEST_EXPIRED,
59 _NUM_DB_STMT
60};
61
62struct db_context {
63 char *fname;
64 sqlite3 *db;
65 sqlite3_stmt *stmt[_NUM_DB_STMT];
66};
67
68static struct db_context *g_dbc;
69
70
71/***********************************************************************
72 * DATABASE SCHEMA AND MIGRATION
73 ***********************************************************************/
Jan Luebbe7398eb92008-12-27 00:45:41 +000074
Pablo Neira Ayuso9891dae2017-08-07 14:01:33 +010075#define SCHEMA_REVISION "5"
Jan Luebbebfbdeec2012-12-27 00:27:16 +010076
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +010077enum {
78 SCHEMA_META,
79 INSERT_META,
80 SCHEMA_SUBSCRIBER,
81 SCHEMA_AUTH,
82 SCHEMA_EQUIPMENT,
83 SCHEMA_EQUIPMENT_WATCH,
84 SCHEMA_SMS,
85 SCHEMA_VLR,
86 SCHEMA_APDU,
87 SCHEMA_COUNTERS,
88 SCHEMA_RATE,
89 SCHEMA_AUTHKEY,
90 SCHEMA_AUTHLAST,
91};
92
93static const char *create_stmts[] = {
94 [SCHEMA_META] = "CREATE TABLE IF NOT EXISTS Meta ("
Harald Welte7e310b12009-03-30 20:56:32 +000095 "id INTEGER PRIMARY KEY AUTOINCREMENT, "
96 "key TEXT UNIQUE NOT NULL, "
97 "value TEXT NOT NULL"
98 ")",
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +010099 [INSERT_META] = "INSERT OR IGNORE INTO Meta "
Harald Welte7e310b12009-03-30 20:56:32 +0000100 "(key, value) "
101 "VALUES "
Jan Luebbebfbdeec2012-12-27 00:27:16 +0100102 "('revision', " SCHEMA_REVISION ")",
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100103 [SCHEMA_SUBSCRIBER] = "CREATE TABLE IF NOT EXISTS Subscriber ("
Harald Welte7e310b12009-03-30 20:56:32 +0000104 "id INTEGER PRIMARY KEY AUTOINCREMENT, "
105 "created TIMESTAMP NOT NULL, "
106 "updated TIMESTAMP NOT NULL, "
107 "imsi NUMERIC UNIQUE NOT NULL, "
108 "name TEXT, "
109 "extension TEXT UNIQUE, "
110 "authorized INTEGER NOT NULL DEFAULT 0, "
111 "tmsi TEXT UNIQUE, "
Jan Luebbebfbdeec2012-12-27 00:27:16 +0100112 "lac INTEGER NOT NULL DEFAULT 0, "
113 "expire_lu TIMESTAMP DEFAULT NULL"
Harald Welte7e310b12009-03-30 20:56:32 +0000114 ")",
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100115 [SCHEMA_AUTH] = "CREATE TABLE IF NOT EXISTS AuthToken ("
Jan Luebbe31bef492009-08-12 14:31:14 +0200116 "id INTEGER PRIMARY KEY AUTOINCREMENT, "
117 "subscriber_id INTEGER UNIQUE NOT NULL, "
118 "created TIMESTAMP NOT NULL, "
119 "token TEXT UNIQUE NOT NULL"
120 ")",
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100121 [SCHEMA_EQUIPMENT] = "CREATE TABLE IF NOT EXISTS Equipment ("
Harald Welte7e310b12009-03-30 20:56:32 +0000122 "id INTEGER PRIMARY KEY AUTOINCREMENT, "
123 "created TIMESTAMP NOT NULL, "
124 "updated TIMESTAMP NOT NULL, "
125 "name TEXT, "
Harald Weltec2e302d2009-07-05 14:08:13 +0200126 "classmark1 NUMERIC, "
127 "classmark2 BLOB, "
128 "classmark3 BLOB, "
Harald Welte7e310b12009-03-30 20:56:32 +0000129 "imei NUMERIC UNIQUE NOT NULL"
130 ")",
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100131 [SCHEMA_EQUIPMENT_WATCH] = "CREATE TABLE IF NOT EXISTS EquipmentWatch ("
Harald Welte7e310b12009-03-30 20:56:32 +0000132 "id INTEGER PRIMARY KEY AUTOINCREMENT, "
133 "created TIMESTAMP NOT NULL, "
134 "updated TIMESTAMP NOT NULL, "
135 "subscriber_id NUMERIC NOT NULL, "
136 "equipment_id NUMERIC NOT NULL, "
137 "UNIQUE (subscriber_id, equipment_id) "
138 ")",
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100139 [SCHEMA_SMS] = "CREATE TABLE IF NOT EXISTS SMS ("
Harald Welte76042182009-08-08 16:03:15 +0200140 /* metadata, not part of sms */
Harald Welte7e310b12009-03-30 20:56:32 +0000141 "id INTEGER PRIMARY KEY AUTOINCREMENT, "
142 "created TIMESTAMP NOT NULL, "
143 "sent TIMESTAMP, "
Harald Welte (local)db552c52009-08-15 20:15:14 +0200144 "deliver_attempts INTEGER NOT NULL DEFAULT 0, "
Harald Welte76042182009-08-08 16:03:15 +0200145 /* data directly copied/derived from SMS */
Harald Weltef3efc592009-07-27 20:11:35 +0200146 "valid_until TIMESTAMP, "
Harald Welte76042182009-08-08 16:03:15 +0200147 "reply_path_req INTEGER NOT NULL, "
148 "status_rep_req INTEGER NOT NULL, "
Pablo Neira Ayuso9891dae2017-08-07 14:01:33 +0100149 "is_report INTEGER NOT NULL, "
150 "msg_ref INTEGER NOT NULL, "
Harald Welte76042182009-08-08 16:03:15 +0200151 "protocol_id INTEGER NOT NULL, "
152 "data_coding_scheme INTEGER NOT NULL, "
Harald Welted0b7b772009-08-09 19:03:42 +0200153 "ud_hdr_ind INTEGER NOT NULL, "
Holger Hans Peter Freytherca3c2562013-10-08 03:17:30 +0200154 "src_addr TEXT NOT NULL, "
155 "src_ton INTEGER NOT NULL, "
156 "src_npi INTEGER NOT NULL, "
157 "dest_addr TEXT NOT NULL, "
158 "dest_ton INTEGER NOT NULL, "
159 "dest_npi INTEGER NOT NULL, "
Harald Welte76042182009-08-08 16:03:15 +0200160 "user_data BLOB, " /* TP-UD */
161 /* additional data, interpreted from SMS */
162 "header BLOB, " /* UD Header */
163 "text TEXT " /* decoded UD after UDH */
Harald Welte7e310b12009-03-30 20:56:32 +0000164 ")",
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100165 [SCHEMA_VLR] = "CREATE TABLE IF NOT EXISTS VLR ("
Holger Freytherc2995ea2009-04-19 06:35:23 +0000166 "id INTEGER PRIMARY KEY AUTOINCREMENT, "
167 "created TIMESTAMP NOT NULL, "
168 "updated TIMESTAMP NOT NULL, "
169 "subscriber_id NUMERIC UNIQUE NOT NULL, "
170 "last_bts NUMERIC NOT NULL "
171 ")",
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100172 [SCHEMA_APDU] = "CREATE TABLE IF NOT EXISTS ApduBlobs ("
Harald Welte (local)026531e2009-08-16 10:40:10 +0200173 "id INTEGER PRIMARY KEY AUTOINCREMENT, "
174 "created TIMESTAMP NOT NULL, "
175 "apdu_id_flags INTEGER NOT NULL, "
176 "subscriber_id INTEGER NOT NULL, "
177 "apdu BLOB "
178 ")",
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100179 [SCHEMA_COUNTERS] = "CREATE TABLE IF NOT EXISTS Counters ("
Harald Welteffa55a42009-12-22 19:07:32 +0100180 "id INTEGER PRIMARY KEY AUTOINCREMENT, "
181 "timestamp TIMESTAMP NOT NULL, "
Harald Weltef9a43c42009-12-22 21:40:42 +0100182 "value INTEGER NOT NULL, "
183 "name TEXT NOT NULL "
Harald Welte09f7ad02009-12-24 09:42:07 +0100184 ")",
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100185 [SCHEMA_RATE] = "CREATE TABLE IF NOT EXISTS RateCounters ("
Harald Weltec1919862010-05-13 12:55:20 +0200186 "id INTEGER PRIMARY KEY AUTOINCREMENT, "
187 "timestamp TIMESTAMP NOT NULL, "
188 "value INTEGER NOT NULL, "
189 "name TEXT NOT NULL, "
Harald Welted94d6a02010-05-14 17:38:47 +0200190 "idx INTEGER NOT NULL "
Harald Weltec1919862010-05-13 12:55:20 +0200191 ")",
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100192 [SCHEMA_AUTHKEY] = "CREATE TABLE IF NOT EXISTS AuthKeys ("
Sylvain Munaut10bf8122010-06-09 11:31:32 +0200193 "subscriber_id INTEGER PRIMARY KEY, "
Sylvain Munaut77d334a2009-12-27 19:26:12 +0100194 "algorithm_id INTEGER NOT NULL, "
Harald Welte3606cc52009-12-05 15:13:22 +0530195 "a3a8_ki BLOB "
196 ")",
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100197 [SCHEMA_AUTHLAST] = "CREATE TABLE IF NOT EXISTS AuthLastTuples ("
Sylvain Munaut10bf8122010-06-09 11:31:32 +0200198 "subscriber_id INTEGER PRIMARY KEY, "
Sylvain Munaut70881b72009-12-27 15:41:59 +0100199 "issued TIMESTAMP NOT NULL, "
200 "use_count INTEGER NOT NULL DEFAULT 0, "
201 "key_seq INTEGER NOT NULL, "
202 "rand BLOB NOT NULL, "
203 "sres BLOB NOT NULL, "
204 "kc BLOB NOT NULL "
Harald Welteffa55a42009-12-22 19:07:32 +0100205 ")",
Harald Welte7e310b12009-03-30 20:56:32 +0000206};
207
Harald Welted43c22e2022-05-14 15:35:49 +0200208/***********************************************************************
209 * PREPARED STATEMENTS
210 ***********************************************************************/
Neels Hofmeyrdb827292017-12-10 14:45:15 +0100211
Harald Welted43c22e2022-05-14 15:35:49 +0200212/* don't change this order as the code assumes this ordering when dereferencing
213 * database query results! */
214#define SEL_COLUMNS \
215 "id," \
216 "strftime('%s',created)," \
217 "sent," \
218 "deliver_attempts," \
219 "strftime('%s', valid_until)," \
220 "reply_path_req," \
221 "status_rep_req," \
222 "is_report," \
223 "msg_ref," \
224 "protocol_id," \
225 "data_coding_scheme," \
226 "ud_hdr_ind," \
227 "src_addr," \
228 "src_ton," \
229 "src_npi," \
230 "dest_addr," \
231 "dest_ton," \
232 "dest_npi," \
233 "user_data," \
234 "header," \
235 "text"
236
237enum db_sms_column_idx {
238 COL_ID,
239 COL_CREATED,
240 COL_SENT,
241 COL_DELIVER_ATTEMPTS,
242 COL_VALID_UNTIL,
243 COL_REPLY_PATH_REQ,
244 COL_STATUS_REP_REQ,
245 COL_IS_REPORT,
246 COL_MSG_REF,
247 COL_PROTOCOL_ID,
248 COL_DATA_CODING_SCHEME,
249 COL_UD_HDR_IND,
250 COL_SRC_ADDR,
251 COL_SRC_TON,
252 COL_SRC_NPI,
253 COL_DEST_ADDR,
254 COL_DEST_TON,
255 COL_DEST_NPI,
256 COL_USER_DATA,
257 COL_HEADER,
258 COL_TEXT,
259};
260
261static const char *stmt_sql[] = {
262 [DB_STMT_SMS_STORE] =
263 "INSERT INTO SMS "
264 "(created, valid_until, reply_path_req, status_rep_req, is_report, "
265 " msg_ref, protocol_id, data_coding_scheme, ud_hdr_ind, user_data, text, "
266 " dest_addr, dest_ton, dest_npi, src_addr, src_ton, src_npi) "
267 "VALUES "
268 "(datetime($created, 'unixepoch'), datetime($valid_until, 'unixepoch'), "
269 "$reply_path_req, $status_rep_req, $is_report, "
270 "$msg_ref, $protocol_id, $data_coding_scheme, $ud_hdr_ind, $user_data, $text, "
271 "$dest_addr, $dest_ton, $dest_npi, $src_addr, $src_ton, $src_npi)",
272 [DB_STMT_SMS_GET] = "SELECT " SEL_COLUMNS " FROM SMS WHERE SMS.id = $id",
273 [DB_STMT_SMS_GET_NEXT_UNSENT] =
274 "SELECT " SEL_COLUMNS " FROM SMS"
275 " WHERE sent IS NULL"
276 " AND id >= $id"
277 " AND deliver_attempts <= $attempts"
278 " ORDER BY id LIMIT 1",
279 [DB_STMT_SMS_GET_UNSENT_FOR_SUBSCR] =
280 "SELECT " SEL_COLUMNS " FROM SMS"
281 " WHERE sent IS NULL"
282 " AND dest_addr = $dest_addr"
283 " AND deliver_attempts <= $attempts"
284 " ORDER BY id LIMIT 1",
285 [DB_STMT_SMS_GET_NEXT_UNSENT_RR_MSISDN] =
286 "SELECT " SEL_COLUMNS " FROM SMS"
287 " WHERE sent IS NULL"
288 " AND dest_addr > $dest_addr"
289 " AND deliver_attempts <= $attempts"
290 " ORDER BY dest_addr, id LIMIT 1",
291 [DB_STMT_SMS_MARK_DELIVERED] =
292 "UPDATE SMS "
293 " SET sent = datetime('now') "
294 " WHERE id = $id",
295 [DB_STMT_SMS_INC_DELIVER_ATTEMPTS] =
296 "UPDATE SMS "
297 " SET deliver_attempts = deliver_attempts + 1 "
298 " WHERE id = $id",
299 [DB_STMT_SMS_DEL_BY_MSISDN] =
300 "DELETE FROM SMS WHERE src_addr=$src_addr OR dest_addr=$dest_addr",
301 [DB_STMT_SMS_DEL_BY_ID] =
302 "DELETE FROM SMS WHERE id = $id AND sent is NOT NULL",
303 [DB_STMT_SMS_DEL_EXPIRED] =
304 "DELETE FROM SMS WHERE id = $id",
305 [DB_STMT_SMS_GET_VALID_UNTIL_BY_ID] =
306 "SELECT strftime('%s', valid_until) FROM SMS WHERE id = $id",
307 [DB_STMT_SMS_GET_OLDEST_EXPIRED] =
308 "SELECT id, strftime('%s', valid_until) FROM SMS ORDER BY valid_until LIMIT 1",
309};
310
311/***********************************************************************
312 * libsqlite3 helpers
313 ***********************************************************************/
314
315/* libsqlite3 call-back for error logging */
316static void sql3_error_log_cb(void *arg, int err_code, const char *msg)
Harald Welte0b906d02009-12-24 11:21:42 +0100317{
Harald Welted43c22e2022-05-14 15:35:49 +0200318 LOGP(DDB, LOGL_ERROR, "SQLITE3: (%d) %s\n", err_code, msg);
Harald Weltec7548a12014-07-10 20:18:15 +0200319 osmo_log_backtrace(DDB, LOGL_ERROR);
Jan Luebbe7398eb92008-12-27 00:45:41 +0000320}
321
Harald Welted43c22e2022-05-14 15:35:49 +0200322/* libsqlite3 call-back for normal logging */
323static void sql3_sql_log_cb(void *arg, sqlite3 *s3, const char *stmt, int type)
Jan Luebbebfbdeec2012-12-27 00:27:16 +0100324{
Harald Welted43c22e2022-05-14 15:35:49 +0200325 switch (type) {
326 case 0:
327 LOGP(DDB, LOGL_DEBUG, "Opened database\n");
328 break;
329 case 1:
330 LOGP(DDB, LOGL_DEBUG, "%s\n", stmt);
331 break;
332 case 2:
333 LOGP(DDB, LOGL_DEBUG, "Closed database\n");
334 break;
335 default:
336 LOGP(DDB, LOGL_DEBUG, "Unknown %d\n", type);
337 break;
338 }
339}
Jan Luebbebfbdeec2012-12-27 00:27:16 +0100340
Harald Welted43c22e2022-05-14 15:35:49 +0200341/* remove statement bindings and reset statement to be re-executed */
342static void db_remove_reset(sqlite3_stmt *stmt)
343{
344 sqlite3_clear_bindings(stmt);
345 /* sqlite3_reset() just repeats an error code already evaluated during sqlite3_step(). */
346 /* coverity[CHECKED_RETURN] */
347 sqlite3_reset(stmt);
348}
349
350/** bind blob arg and do proper cleanup in case of failure. If param_name is
351 * NULL, bind to the first parameter (useful for SQL statements that have only
352 * one parameter). */
353static bool db_bind_blob(sqlite3_stmt *stmt, const char *param_name,
354 const uint8_t *blob, size_t blob_len)
355{
356 int rc;
357 int idx = param_name ? sqlite3_bind_parameter_index(stmt, param_name) : 1;
358 if (idx < 1) {
359 LOGP(DDB, LOGL_ERROR, "Error composing SQL, cannot bind parameter '%s'\n",
360 param_name);
361 return false;
362 }
363 rc = sqlite3_bind_blob(stmt, idx, blob, blob_len, SQLITE_STATIC);
364 if (rc != SQLITE_OK) {
365 LOGP(DDB, LOGL_ERROR, "Error binding blob to SQL parameter %s: %d\n",
366 param_name ? param_name : "#1", rc);
367 db_remove_reset(stmt);
368 return false;
369 }
370 return true;
371}
372
373/** bind text arg and do proper cleanup in case of failure. If param_name is
374 * NULL, bind to the first parameter (useful for SQL statements that have only
375 * one parameter). */
376static bool db_bind_text(sqlite3_stmt *stmt, const char *param_name, const char *text)
377{
378 int rc;
379 int idx = param_name ? sqlite3_bind_parameter_index(stmt, param_name) : 1;
380 if (idx < 1) {
381 LOGP(DDB, LOGL_ERROR, "Error composing SQL, cannot bind parameter '%s'\n",
382 param_name);
383 return false;
384 }
385 rc = sqlite3_bind_text(stmt, idx, text, -1, SQLITE_STATIC);
386 if (rc != SQLITE_OK) {
387 LOGP(DDB, LOGL_ERROR, "Error binding text to SQL parameter %s: %d\n",
388 param_name ? param_name : "#1", rc);
389 db_remove_reset(stmt);
390 return false;
391 }
392 return true;
393}
394
395/** bind int arg and do proper cleanup in case of failure. If param_name is
396 * NULL, bind to the first parameter (useful for SQL statements that have only
397 * one parameter). */
398static bool db_bind_int(sqlite3_stmt *stmt, const char *param_name, int nr)
399{
400 int rc;
401 int idx = param_name ? sqlite3_bind_parameter_index(stmt, param_name) : 1;
402 if (idx < 1) {
403 LOGP(DDB, LOGL_ERROR, "Error composing SQL, cannot bind parameter '%s'\n",
404 param_name);
405 return false;
406 }
407 rc = sqlite3_bind_int(stmt, idx, nr);
408 if (rc != SQLITE_OK) {
409 LOGP(DDB, LOGL_ERROR, "Error binding int64 to SQL parameter %s: %d\n",
410 param_name ? param_name : "#1", rc);
411 db_remove_reset(stmt);
412 return false;
413 }
414 return true;
415}
416
417/** bind int64 arg and do proper cleanup in case of failure. If param_name is
418 * NULL, bind to the first parameter (useful for SQL statements that have only
419 * one parameter). */
420static bool db_bind_int64(sqlite3_stmt *stmt, const char *param_name, int64_t nr)
421{
422 int rc;
423 int idx = param_name ? sqlite3_bind_parameter_index(stmt, param_name) : 1;
424 if (idx < 1) {
425 LOGP(DDB, LOGL_ERROR, "Error composing SQL, cannot bind parameter '%s'\n",
426 param_name);
427 return false;
428 }
429 rc = sqlite3_bind_int64(stmt, idx, nr);
430 if (rc != SQLITE_OK) {
431 LOGP(DDB, LOGL_ERROR, "Error binding int64 to SQL parameter %s: %d\n",
432 param_name ? param_name : "#1", rc);
433 db_remove_reset(stmt);
434 return false;
435 }
436 return true;
437}
438
439/* callback for sqlite3_exec() below */
440static int db_rev_exec_cb(void *priv, int num_cols, char **vals, char **names)
441{
442 char **rev_s = priv;
443 OSMO_ASSERT(!strcmp(names[0], "value"));
444 *rev_s = talloc_strdup(NULL, vals[0]);
445 return 0;
446}
447
448static int check_db_revision(struct db_context *dbc)
449{
450 char *errstr = NULL;
451 char *rev_s;
452 int db_rev = 0;
453 int rc;
454
455 /* Make a query */
456 rc = sqlite3_exec(dbc->db, "SELECT value FROM Meta WHERE key = 'revision'",
457 db_rev_exec_cb, &rev_s, &errstr);
458 if (rc != SQLITE_OK) {
459 LOGP(DDB, LOGL_ERROR, "Cannot execute SELECT value from META: %s\n", errstr);
460 sqlite3_free(errstr);
Jan Luebbebfbdeec2012-12-27 00:27:16 +0100461 return -EINVAL;
462 }
Jan Luebbebfbdeec2012-12-27 00:27:16 +0100463
Harald Welted43c22e2022-05-14 15:35:49 +0200464 if (!strcmp(rev_s, SCHEMA_REVISION)) {
465 /* Everything is fine */
466 talloc_free(rev_s);
467 return 0;
468 }
469
470 LOGP(DDB, LOGL_NOTICE, "Detected DB Revision %s, expected %s\n", rev_s, SCHEMA_REVISION);
471
472 db_rev = atoi(rev_s);
473 talloc_free(rev_s);
474
475 /* Incremental migration waterfall */
476 switch (db_rev) {
477 case 2:
478 case 3:
479 case 4:
480 LOGP(DDB, LOGL_FATAL, "You must use osmo-msc 1.1.0 to 1.8.0 to upgrade database "
481 "schema from '%u' to '5', sorry\n", db_rev);
482 break;
483#if 0
484 case 5:
485 if (update_db_revision_5())
486 goto error;
487
488 /* The end of waterfall */
489 break;
490#endif
491 default:
492 LOGP(DDB, LOGL_FATAL, "Invalid database schema revision '%d'.\n", db_rev);
Jan Luebbebfbdeec2012-12-27 00:27:16 +0100493 return -EINVAL;
494 }
Harald Welted43c22e2022-05-14 15:35:49 +0200495
496 return 0;
497
498//error:
499 LOGP(DDB, LOGL_FATAL, "Failed to update database from schema revision '%d'.\n", db_rev);
500 talloc_free(rev_s);
501
502 return -EINVAL;
503}
504
505/***********************************************************************
506 * USER API
507 ***********************************************************************/
508
509int db_init(void *ctx, const char *fname, bool enable_sqlite_logging)
510{
511 unsigned int i;
512 int rc;
513 bool has_sqlite_config_sqllog = false;
514
515 g_dbc = talloc_zero(ctx, struct db_context);
516 OSMO_ASSERT(g_dbc);
517
518 /* we are a single-threaded program; we want to avoid all the mutex/etc. overhead */
519 sqlite3_config(SQLITE_CONFIG_SINGLETHREAD);
520
521 LOGP(DDB, LOGL_NOTICE, "Init database connection to '%s' using SQLite3 lib version %s\n",
522 fname, sqlite3_libversion());
523
524 g_dbc->fname = talloc_strdup(g_dbc, fname);
525
526 for (i = 0; i < 0xfffff; i++) {
527 const char *o = sqlite3_compileoption_get(i);
528 if (!o)
529 break;
530 LOGP(DDB, LOGL_DEBUG, "SQLite3 compiled with '%s'\n", o);
531 if (!strcmp(o, "ENABLE_SQLLOG"))
532 has_sqlite_config_sqllog = true;
533 }
534
535 if (enable_sqlite_logging) {
536 rc = sqlite3_config(SQLITE_CONFIG_LOG, sql3_error_log_cb, NULL);
537 if (rc != SQLITE_OK)
538 LOGP(DDB, LOGL_NOTICE, "Unable to set SQLite3 error log callback\n");
539 }
540
541 if (has_sqlite_config_sqllog) {
542 rc = sqlite3_config(SQLITE_CONFIG_SQLLOG, sql3_sql_log_cb, NULL);
543 if (rc != SQLITE_OK)
544 LOGP(DDB, LOGL_NOTICE, "Unable to set SQLite3 SQL log callback\n");
545 } else {
546 LOGP(DDB, LOGL_DEBUG, "Not setting SQL log callback:"
547 " SQLite3 compiled without support for it\n");
548 }
549
550 rc = sqlite3_open(g_dbc->fname, &g_dbc->db);
551 if (rc != SQLITE_OK) {
552 LOGP(DDB, LOGL_ERROR, "Unable to open DB; rc =%d\n", rc);
553 talloc_free(g_dbc);
554 return -1;
555 }
556
557 /* enable extended result codes */
558 rc = sqlite3_extended_result_codes(g_dbc->db, 1);
559 if (rc != SQLITE_OK) {
560 LOGP(DDB, LOGL_ERROR, "Unable to enable SQLite3 extended result codes\n");
561 /* non-fatal */
562 }
563
564 char *err_msg;
565 rc = sqlite3_exec(g_dbc->db, "PRAGMA journal_mode=WAL; PRAGMA synchonous = NORMAL;", 0, 0, &err_msg);
566 if (rc != SQLITE_OK) {
567 LOGP(DDB, LOGL_ERROR, "Unable to set Write-Ahead Logging: %s\n", err_msg);
568 sqlite3_free(err_msg);
569 /* non-fatal */
570 }
Jan Luebbebfbdeec2012-12-27 00:27:16 +0100571
572 return 0;
573}
574
Harald Welted43c22e2022-05-14 15:35:49 +0200575int db_fini(void)
576{
577 unsigned int i;
578 int rc;
579
580 if (!g_dbc)
581 return 0;
582
583 for (i = 0; i < ARRAY_SIZE(g_dbc->stmt); i++) {
584 /* it is ok to call finalize on NULL */
585 sqlite3_finalize(g_dbc->stmt[i]);
586 }
587
588 /* Ask sqlite3 to close DB */
589 rc = sqlite3_close(g_dbc->db);
590 if (rc != SQLITE_OK) { /* Make sure it's actually closed! */
591 LOGP(DDB, LOGL_ERROR, "Couldn't close database: (rc=%d) %s\n",
592 rc, sqlite3_errmsg(g_dbc->db));
593 }
594
595 talloc_free(g_dbc);
596 g_dbc = NULL;
597
598 return 0;
599}
600
601/* run (execute) a series of SQL statements */
602static int db_run_statements(struct db_context *dbc, const char **statements, size_t statements_count)
603{
604 int i;
605 for (i = 0; i < statements_count; i++) {
606 const char *stmt_str = statements[i];
607 char *errmsg = NULL;
608 int rc;
609
610 rc = sqlite3_exec(dbc->db, stmt_str, NULL, NULL, &errmsg);
611 if (rc != SQLITE_OK) {
612 LOGP(DDB, LOGL_ERROR, "SQL error during SQL statement '%s': %s\n", stmt_str, errmsg);
613 sqlite3_free(errmsg);
614 return -1;
615 }
616 }
617 return 0;
618}
619
620static int db_configure(struct db_context *dbc)
621{
622 const char *sync_stmts[] = { "PRAGMA synchronous = FULL" };
623
624 return db_run_statements(dbc, sync_stmts, ARRAY_SIZE(sync_stmts));
625}
626
627int db_prepare(void)
628{
629 unsigned int i;
630 int rc;
631
632 OSMO_ASSERT(g_dbc);
633 rc = db_run_statements(g_dbc, create_stmts, ARRAY_SIZE(create_stmts));
634 if (rc < 0) {
635 LOGP(DDB, LOGL_ERROR, "Failed to create some table.\n");
636 return 1;
637 }
638
639 if (check_db_revision(g_dbc) < 0) {
640 LOGP(DDB, LOGL_FATAL, "Database schema revision invalid, "
641 "please update your database schema\n");
642 return -1;
643 }
644
645 db_configure(g_dbc);
646
647 /* prepare all SQL statements */
648 for (i = 0; i < ARRAY_SIZE(g_dbc->stmt); i++) {
649 rc = sqlite3_prepare_v2(g_dbc->db, stmt_sql[i], -1,
650 &g_dbc->stmt[i], NULL);
651 if (rc != SQLITE_OK) {
652 LOGP(DDB, LOGL_ERROR, "Unable to prepare SQL statement '%s'\n", stmt_sql[i]);
653 return -1;
654 }
655 }
656
657 return 0;
658}
659
660/* store an [unsent] SMS to the database */
661int db_sms_store(struct gsm_sms *sms)
662{
663 OSMO_ASSERT(g_dbc);
664 sqlite3_stmt *stmt = g_dbc->stmt[DB_STMT_SMS_STORE];
665 time_t now, validity_timestamp;
666 int rc;
667
668 now = time(NULL);
669 validity_timestamp = now + sms->validity_minutes * 60;
670
671 db_bind_int64(stmt, "$created", (int64_t) now);
672 db_bind_int64(stmt, "$valid_until", (int64_t) validity_timestamp);
673 db_bind_int(stmt, "$reply_path_req", sms->reply_path_req);
674 db_bind_int(stmt, "$status_rep_req", sms->status_rep_req);
675 db_bind_int(stmt, "$is_report", sms->is_report);
676 db_bind_int(stmt, "$msg_ref", sms->msg_ref);
677 db_bind_int(stmt, "$protocol_id", sms->protocol_id);
678 db_bind_int(stmt, "$data_coding_scheme", sms->data_coding_scheme);
679 db_bind_int(stmt, "$ud_hdr_ind", sms->ud_hdr_ind);
680 /* FIXME: do we need to use legacy DBI compatible quoting of sms->user_data? */
681 db_bind_blob(stmt, "$user_data", sms->user_data, sms->user_data_len);
682 db_bind_text(stmt, "$text", (char *)sms->text);
683 db_bind_text(stmt, "$dest_addr", (char *)sms->dst.addr);
684 db_bind_int(stmt, "$dest_ton", sms->dst.ton);
685 db_bind_int(stmt, "$dest_npi", sms->dst.npi);
686 db_bind_text(stmt, "$src_addr", (char *)sms->src.addr);
687 db_bind_int(stmt, "$src_ton", sms->src.ton);
688 db_bind_int(stmt, "$src_npi", sms->src.npi);
689
690 /* execute statement */
691 rc = sqlite3_step(stmt);
692 db_remove_reset(stmt);
693 if (rc != SQLITE_DONE) {
694 LOGP(DDB, LOGL_ERROR, "Cannot create SMS: SQL error: (%d) %s\n", rc, sqlite3_errmsg(g_dbc->db));
695 return -EIO;
696 }
697
698 sms->id = sqlite3_last_insert_rowid(g_dbc->db);
699
700 LOGP(DLSMS, LOGL_INFO, "Stored SMS id=%llu in DB\n", sms->id);
701
702 return 0;
703}
704
705static void parse_tp_ud_from_result(struct gsm_sms *sms, sqlite3_stmt *stmt)
Vadim Yanitskiyea24bb52019-04-02 17:52:08 +0700706{
707 const unsigned char *user_data;
708 unsigned int user_data_len;
Vadim Yanitskiy0d13e832019-04-02 18:25:08 +0700709 unsigned int text_len;
Vadim Yanitskiyea24bb52019-04-02 17:52:08 +0700710 const char *text;
711
712 /* Retrieve TP-UDL (User-Data-Length) in octets (regardless of DCS) */
Harald Welted43c22e2022-05-14 15:35:49 +0200713 user_data_len = sqlite3_column_bytes(stmt, COL_USER_DATA);
Vadim Yanitskiyea24bb52019-04-02 17:52:08 +0700714 if (user_data_len > sizeof(sms->user_data)) {
715 LOGP(DDB, LOGL_ERROR,
716 "SMS TP-UD length %u is too big, truncating to %zu\n",
717 user_data_len, sizeof(sms->user_data));
718 user_data_len = (uint8_t) sizeof(sms->user_data);
719 }
720 sms->user_data_len = user_data_len;
721
722 /* Retrieve the TP-UD (User-Data) itself */
723 if (user_data_len > 0) {
Harald Welted43c22e2022-05-14 15:35:49 +0200724 user_data = sqlite3_column_blob(stmt, COL_USER_DATA);
Vadim Yanitskiyea24bb52019-04-02 17:52:08 +0700725 memcpy(sms->user_data, user_data, user_data_len);
726 }
727
Vadim Yanitskiy0d13e832019-04-02 18:25:08 +0700728 /* Retrieve the text length (excluding '\0') */
Harald Welted43c22e2022-05-14 15:35:49 +0200729 text_len = sqlite3_column_bytes(stmt, COL_TEXT);
Vadim Yanitskiy0d13e832019-04-02 18:25:08 +0700730 if (text_len >= sizeof(sms->text)) {
731 LOGP(DDB, LOGL_ERROR,
732 "SMS text length %u is too big, truncating to %zu\n",
733 text_len, sizeof(sms->text) - 1);
734 /* OSMO_STRLCPY_ARRAY() does truncation for us */
735 }
736
Vadim Yanitskiyea24bb52019-04-02 17:52:08 +0700737 /* Retrieve the text parsed from TP-UD (User-Data) */
Harald Welted43c22e2022-05-14 15:35:49 +0200738 text = (const char *)sqlite3_column_text(stmt, COL_TEXT);
Vadim Yanitskiyea24bb52019-04-02 17:52:08 +0700739 if (text)
740 OSMO_STRLCPY_ARRAY(sms->text, text);
741}
742
Harald Welted43c22e2022-05-14 15:35:49 +0200743static struct gsm_sms *sms_from_result(struct gsm_network *net, sqlite3_stmt *stmt)
Harald Welte7e310b12009-03-30 20:56:32 +0000744{
Harald Welte76042182009-08-08 16:03:15 +0200745 struct gsm_sms *sms = sms_alloc();
Vadim Yanitskiyea24bb52019-04-02 17:52:08 +0700746 const char *daddr, *saddr;
Stefan Sperlingd4941622018-01-18 17:36:28 +0100747 time_t validity_timestamp;
Harald Welte7e310b12009-03-30 20:56:32 +0000748
Harald Welte76042182009-08-08 16:03:15 +0200749 if (!sms)
Harald Welte7e310b12009-03-30 20:56:32 +0000750 return NULL;
Harald Welte7e310b12009-03-30 20:56:32 +0000751
Harald Welted43c22e2022-05-14 15:35:49 +0200752 sms->id = sqlite3_column_int64(stmt, COL_ID);
Harald Welte7e310b12009-03-30 20:56:32 +0000753
Harald Welted43c22e2022-05-14 15:35:49 +0200754 sms->created = sqlite3_column_int64(stmt, COL_CREATED);
755 validity_timestamp = sqlite3_column_int64(stmt, COL_VALID_UNTIL);
756
Stefan Sperlingd4941622018-01-18 17:36:28 +0100757 sms->validity_minutes = (validity_timestamp - sms->created) / 60;
Harald Welted43c22e2022-05-14 15:35:49 +0200758 sms->reply_path_req = sqlite3_column_int(stmt, COL_REPLY_PATH_REQ);
759 sms->status_rep_req = sqlite3_column_int(stmt, COL_STATUS_REP_REQ);
760 sms->is_report = sqlite3_column_int(stmt, COL_IS_REPORT);
761 sms->msg_ref = sqlite3_column_int(stmt, COL_MSG_REF);
762 sms->ud_hdr_ind = sqlite3_column_int(stmt, COL_UD_HDR_IND);
763 sms->protocol_id = sqlite3_column_int(stmt, COL_PROTOCOL_ID);
764 sms->data_coding_scheme = sqlite3_column_int(stmt, COL_DATA_CODING_SCHEME);
Harald Weltef3efc592009-07-27 20:11:35 +0200765
Harald Welted43c22e2022-05-14 15:35:49 +0200766 sms->dst.npi = sqlite3_column_int(stmt, COL_DEST_NPI);
767 sms->dst.ton = sqlite3_column_int(stmt, COL_DEST_TON);
768 daddr = (const char *)sqlite3_column_text(stmt, COL_DEST_ADDR);
Neels Hofmeyr93bafb62017-01-13 03:12:08 +0100769 if (daddr)
Max98f74672018-02-05 12:57:06 +0100770 OSMO_STRLCPY_ARRAY(sms->dst.addr, daddr);
Vadim Yanitskiye1e72472019-04-09 16:55:44 +0700771
772 if (net != NULL) /* db_sms_test passes NULL, so we need to be tolerant */
773 sms->receiver = vlr_subscr_find_by_msisdn(net->vlr, sms->dst.addr,
774 VSUB_USE_SMS_RECEIVER);
Harald Welte76042182009-08-08 16:03:15 +0200775
Harald Welted43c22e2022-05-14 15:35:49 +0200776 sms->src.npi = sqlite3_column_int(stmt, COL_SRC_NPI);
777 sms->src.ton = sqlite3_column_int(stmt, COL_SRC_TON);
778 saddr = (const char *)sqlite3_column_text(stmt, COL_SRC_ADDR);
Neels Hofmeyr93bafb62017-01-13 03:12:08 +0100779 if (saddr)
Max98f74672018-02-05 12:57:06 +0100780 OSMO_STRLCPY_ARRAY(sms->src.addr, saddr);
Holger Hans Peter Freytherca3c2562013-10-08 03:17:30 +0200781
Vadim Yanitskiyea24bb52019-04-02 17:52:08 +0700782 /* Parse TP-UD, TP-UDL and decoded text */
Harald Welted43c22e2022-05-14 15:35:49 +0200783 parse_tp_ud_from_result(sms, stmt);
Harald Weltebe3e3782009-07-05 14:06:41 +0200784
Harald Welte2ebabca2009-08-09 19:05:21 +0200785 return sms;
786}
787
Holger Hans Peter Freyther812dad02010-12-24 23:18:31 +0100788struct gsm_sms *db_sms_get(struct gsm_network *net, unsigned long long id)
789{
Harald Welted43c22e2022-05-14 15:35:49 +0200790 OSMO_ASSERT(g_dbc);
791 sqlite3_stmt *stmt = g_dbc->stmt[DB_STMT_SMS_GET];
Holger Hans Peter Freyther812dad02010-12-24 23:18:31 +0100792 struct gsm_sms *sms;
Harald Welted43c22e2022-05-14 15:35:49 +0200793 int rc;
Holger Hans Peter Freyther812dad02010-12-24 23:18:31 +0100794
Harald Welted43c22e2022-05-14 15:35:49 +0200795 db_bind_int64(stmt, "$id", id);
Holger Hans Peter Freyther812dad02010-12-24 23:18:31 +0100796
Harald Welted43c22e2022-05-14 15:35:49 +0200797 rc = sqlite3_step(stmt);
798 if (rc != SQLITE_ROW) {
799 db_remove_reset(stmt);
Holger Hans Peter Freyther812dad02010-12-24 23:18:31 +0100800 return NULL;
801 }
802
Harald Welted43c22e2022-05-14 15:35:49 +0200803 sms = sms_from_result(net, stmt);
Holger Hans Peter Freyther812dad02010-12-24 23:18:31 +0100804
Harald Welted43c22e2022-05-14 15:35:49 +0200805 db_remove_reset(stmt);
Holger Hans Peter Freyther812dad02010-12-24 23:18:31 +0100806 return sms;
807}
808
Harald Welte2483f1b2016-06-19 18:06:02 +0200809struct gsm_sms *db_sms_get_next_unsent(struct gsm_network *net,
810 unsigned long long min_sms_id,
Harald Welted43c22e2022-05-14 15:35:49 +0200811 int max_failed)
Harald Welte2ebabca2009-08-09 19:05:21 +0200812{
Harald Welted43c22e2022-05-14 15:35:49 +0200813 OSMO_ASSERT(g_dbc);
814 sqlite3_stmt *stmt = g_dbc->stmt[DB_STMT_SMS_GET_NEXT_UNSENT];
Harald Welte2ebabca2009-08-09 19:05:21 +0200815 struct gsm_sms *sms;
Harald Welted43c22e2022-05-14 15:35:49 +0200816 int rc;
Harald Welte2ebabca2009-08-09 19:05:21 +0200817
Harald Welted43c22e2022-05-14 15:35:49 +0200818 db_bind_int64(stmt, "$id", min_sms_id);
819 db_bind_int(stmt, "$attempts", max_failed);
Harald Welte2ebabca2009-08-09 19:05:21 +0200820
Harald Welted43c22e2022-05-14 15:35:49 +0200821 rc = sqlite3_step(stmt);
822 if (rc != SQLITE_ROW) {
823 db_remove_reset(stmt);
Sylvain Munautff1f19e2009-12-22 13:22:29 +0100824 return NULL;
825 }
826
Harald Welted43c22e2022-05-14 15:35:49 +0200827 sms = sms_from_result(net, stmt);
Sylvain Munautff1f19e2009-12-22 13:22:29 +0100828
Harald Welted43c22e2022-05-14 15:35:49 +0200829 db_remove_reset(stmt);
Sylvain Munautff1f19e2009-12-22 13:22:29 +0100830 return sms;
831}
832
Sylvain Munautd5778fc2009-12-21 01:09:57 +0100833/* retrieve the next unsent SMS for a given subscriber */
Harald Welte2483f1b2016-06-19 18:06:02 +0200834struct gsm_sms *db_sms_get_unsent_for_subscr(struct vlr_subscr *vsub,
Harald Welted43c22e2022-05-14 15:35:49 +0200835 int max_failed)
Harald Welte2ebabca2009-08-09 19:05:21 +0200836{
Harald Welted43c22e2022-05-14 15:35:49 +0200837 OSMO_ASSERT(g_dbc);
838 sqlite3_stmt *stmt = g_dbc->stmt[DB_STMT_SMS_GET_UNSENT_FOR_SUBSCR];
Harald Welte2483f1b2016-06-19 18:06:02 +0200839 struct gsm_network *net = vsub->vlr->user_ctx;
Harald Welte2ebabca2009-08-09 19:05:21 +0200840 struct gsm_sms *sms;
Harald Welted43c22e2022-05-14 15:35:49 +0200841 int rc;
Harald Welte2ebabca2009-08-09 19:05:21 +0200842
Harald Welte2483f1b2016-06-19 18:06:02 +0200843 if (!vsub->lu_complete)
844 return NULL;
845
Neels Hofmeyrf6704f12017-12-05 12:34:44 +0100846 /* A subscriber having no phone number cannot possibly receive SMS. */
847 if (*vsub->msisdn == '\0')
848 return NULL;
849
Harald Welted43c22e2022-05-14 15:35:49 +0200850 db_bind_text(stmt, "$dest_addr", vsub->msisdn);
851 db_bind_int(stmt, "$attempts", max_failed);
Neels Hofmeyr50d09002017-12-05 12:39:00 +0100852
Harald Welted43c22e2022-05-14 15:35:49 +0200853 rc = sqlite3_step(stmt);
854 if (rc != SQLITE_ROW) {
855 db_remove_reset(stmt);
Harald Welte2ebabca2009-08-09 19:05:21 +0200856 return NULL;
857 }
858
Harald Welted43c22e2022-05-14 15:35:49 +0200859 sms = sms_from_result(net, stmt);
Harald Welte2483f1b2016-06-19 18:06:02 +0200860
Harald Welted43c22e2022-05-14 15:35:49 +0200861 db_remove_reset(stmt);
Harald Welte2483f1b2016-06-19 18:06:02 +0200862 return sms;
863}
864
865struct gsm_sms *db_sms_get_next_unsent_rr_msisdn(struct gsm_network *net,
866 const char *last_msisdn,
Harald Welted43c22e2022-05-14 15:35:49 +0200867 int max_failed)
Harald Welte2483f1b2016-06-19 18:06:02 +0200868{
Harald Welted43c22e2022-05-14 15:35:49 +0200869 OSMO_ASSERT(g_dbc);
870 sqlite3_stmt *stmt = g_dbc->stmt[DB_STMT_SMS_GET_NEXT_UNSENT_RR_MSISDN];
Harald Welte2483f1b2016-06-19 18:06:02 +0200871 struct gsm_sms *sms;
Harald Welted43c22e2022-05-14 15:35:49 +0200872 int rc;
Harald Welte2483f1b2016-06-19 18:06:02 +0200873
Harald Welted43c22e2022-05-14 15:35:49 +0200874 db_bind_text(stmt, "$dest_addr", last_msisdn);
875 db_bind_int(stmt, "$attempts", max_failed);
Neels Hofmeyr50d09002017-12-05 12:39:00 +0100876
Harald Welted43c22e2022-05-14 15:35:49 +0200877 rc = sqlite3_step(stmt);
878 if (rc != SQLITE_ROW) {
879 db_remove_reset(stmt);
Harald Welte2483f1b2016-06-19 18:06:02 +0200880 return NULL;
881 }
882
Harald Welted43c22e2022-05-14 15:35:49 +0200883 sms = sms_from_result(net, stmt);
Harald Welte2ebabca2009-08-09 19:05:21 +0200884
Harald Welted43c22e2022-05-14 15:35:49 +0200885 db_remove_reset(stmt);
Harald Welte2ebabca2009-08-09 19:05:21 +0200886
Harald Welte7e310b12009-03-30 20:56:32 +0000887 return sms;
888}
889
Alexander Chemeris1e77e3d2014-03-08 21:27:37 +0100890/* mark a given SMS as delivered */
891int db_sms_mark_delivered(struct gsm_sms *sms)
Harald Welte7e310b12009-03-30 20:56:32 +0000892{
Harald Welted43c22e2022-05-14 15:35:49 +0200893 sqlite3_stmt *stmt;
894 int rc;
Harald Welte7e310b12009-03-30 20:56:32 +0000895
Harald Welted43c22e2022-05-14 15:35:49 +0200896 /* this only happens in unit tests that don't db_init() */
897 if (!g_dbc)
898 return 0;
899
900 stmt = g_dbc->stmt[DB_STMT_SMS_MARK_DELIVERED];
901 db_bind_int64(stmt, "$id", sms->id);
902
903 rc = sqlite3_step(stmt);
904 if (rc != SQLITE_DONE) {
905 db_remove_reset(stmt);
Harald Welteae1f1592009-12-24 11:39:14 +0100906 LOGP(DDB, LOGL_ERROR, "Failed to mark SMS %llu as sent.\n", sms->id);
Harald Welte7e310b12009-03-30 20:56:32 +0000907 return 1;
908 }
909
Harald Welted43c22e2022-05-14 15:35:49 +0200910 db_remove_reset(stmt);
Harald Welte7e310b12009-03-30 20:56:32 +0000911 return 0;
912}
Harald Welte (local)db552c52009-08-15 20:15:14 +0200913
914/* increase the number of attempted deliveries */
915int db_sms_inc_deliver_attempts(struct gsm_sms *sms)
916{
Harald Welted43c22e2022-05-14 15:35:49 +0200917 sqlite3_stmt *stmt;
918 int rc;
Harald Welte (local)db552c52009-08-15 20:15:14 +0200919
Harald Welted43c22e2022-05-14 15:35:49 +0200920 /* this only happens in unit tests that don't db_init() */
921 if (!g_dbc)
922 return 0;
923
924 stmt = g_dbc->stmt[DB_STMT_SMS_INC_DELIVER_ATTEMPTS];
925 db_bind_int64(stmt, "$id", sms->id);
926
927 rc = sqlite3_step(stmt);
928 if (rc != SQLITE_DONE) {
929 db_remove_reset(stmt);
930 LOGP(DDB, LOGL_ERROR, "Failed to inc deliver attempts for SMS %llu.\n", sms->id);
Harald Welte (local)db552c52009-08-15 20:15:14 +0200931 return 1;
932 }
933
Harald Welted43c22e2022-05-14 15:35:49 +0200934 db_remove_reset(stmt);
Harald Welte (local)db552c52009-08-15 20:15:14 +0200935 return 0;
936}
Harald Welte (local)026531e2009-08-16 10:40:10 +0200937
Harald Welte2483f1b2016-06-19 18:06:02 +0200938/* Drop all pending SMS to or from the given extension */
939int db_sms_delete_by_msisdn(const char *msisdn)
Harald Welte (local)026531e2009-08-16 10:40:10 +0200940{
Harald Welted43c22e2022-05-14 15:35:49 +0200941 OSMO_ASSERT(g_dbc);
942 sqlite3_stmt *stmt = g_dbc->stmt[DB_STMT_SMS_DEL_BY_MSISDN];
943 int rc;
944
Harald Welte2483f1b2016-06-19 18:06:02 +0200945 if (!msisdn || !*msisdn)
946 return 0;
Neels Hofmeyr50d09002017-12-05 12:39:00 +0100947
Harald Welted43c22e2022-05-14 15:35:49 +0200948 db_bind_text(stmt, "$src_addr", msisdn);
949 db_bind_text(stmt, "$dest_addr", msisdn);
Neels Hofmeyr50d09002017-12-05 12:39:00 +0100950
Harald Welted43c22e2022-05-14 15:35:49 +0200951 rc = sqlite3_step(stmt);
952 if (rc != SQLITE_DONE) {
953 db_remove_reset(stmt);
954 LOGP(DDB, LOGL_ERROR, "Failed to delete SMS for %s\n", msisdn);
Harald Welte2483f1b2016-06-19 18:06:02 +0200955 return -1;
956 }
Harald Welted43c22e2022-05-14 15:35:49 +0200957
958 db_remove_reset(stmt);
Harald Welte (local)026531e2009-08-16 10:40:10 +0200959 return 0;
960}
Harald Welteffa55a42009-12-22 19:07:32 +0100961
Stefan Sperling6ba2d5a2018-01-18 18:55:26 +0100962int db_sms_delete_sent_message_by_id(unsigned long long sms_id)
963{
Harald Welted43c22e2022-05-14 15:35:49 +0200964 OSMO_ASSERT(g_dbc);
965 sqlite3_stmt *stmt = g_dbc->stmt[DB_STMT_SMS_DEL_BY_ID];
966 int rc;
Stefan Sperling6ba2d5a2018-01-18 18:55:26 +0100967
Harald Welted43c22e2022-05-14 15:35:49 +0200968 db_bind_int64(stmt, "$id", sms_id);
969
970 rc = sqlite3_step(stmt);
971 if (rc != SQLITE_DONE) {
972 db_remove_reset(stmt);
Stefan Sperling6ba2d5a2018-01-18 18:55:26 +0100973 LOGP(DDB, LOGL_ERROR, "Failed to delete SMS %llu.\n", sms_id);
974 return 1;
975 }
976
Harald Welted43c22e2022-05-14 15:35:49 +0200977 db_remove_reset(stmt);
Stefan Sperling6ba2d5a2018-01-18 18:55:26 +0100978 return 0;
979}
980
Vadim Yanitskiy570b4c82019-04-14 16:39:12 +0700981static int delete_expired_sms(unsigned long long sms_id, time_t validity_timestamp)
Stefan Sperling87cba1f2018-01-22 17:05:37 +0100982{
Harald Welted43c22e2022-05-14 15:35:49 +0200983 OSMO_ASSERT(g_dbc);
984 sqlite3_stmt *stmt = g_dbc->stmt[DB_STMT_SMS_DEL_EXPIRED];
Vadim Yanitskiy570b4c82019-04-14 16:39:12 +0700985 time_t now;
Harald Welted43c22e2022-05-14 15:35:49 +0200986 int rc;
Stefan Sperling87cba1f2018-01-22 17:05:37 +0100987
988 now = time(NULL);
Stefan Sperling87cba1f2018-01-22 17:05:37 +0100989
Vadim Yanitskiy570b4c82019-04-14 16:39:12 +0700990 /* Net yet expired */
991 if (validity_timestamp > now)
Stefan Sperling87cba1f2018-01-22 17:05:37 +0100992 return -1;
993
Harald Welted43c22e2022-05-14 15:35:49 +0200994 db_bind_int64(stmt, "$id", sms_id);
995
996 rc = sqlite3_step(stmt);
997 if (rc != SQLITE_DONE) {
998 db_remove_reset(stmt);
Stefan Sperling87cba1f2018-01-22 17:05:37 +0100999 LOGP(DDB, LOGL_ERROR, "Failed to delete SMS %llu.\n", sms_id);
1000 return -1;
1001 }
Harald Welted43c22e2022-05-14 15:35:49 +02001002
1003 db_remove_reset(stmt);
Stefan Sperling87cba1f2018-01-22 17:05:37 +01001004 return 0;
1005}
1006
Stefan Sperling14e05172018-01-22 17:31:20 +01001007int db_sms_delete_expired_message_by_id(unsigned long long sms_id)
1008{
Harald Welted43c22e2022-05-14 15:35:49 +02001009 OSMO_ASSERT(g_dbc);
1010 sqlite3_stmt *stmt = g_dbc->stmt[DB_STMT_SMS_GET_VALID_UNTIL_BY_ID];
Vadim Yanitskiy570b4c82019-04-14 16:39:12 +07001011 time_t validity_timestamp;
Harald Welted43c22e2022-05-14 15:35:49 +02001012 int rc;
Stefan Sperling14e05172018-01-22 17:31:20 +01001013
Harald Welted43c22e2022-05-14 15:35:49 +02001014 db_bind_int64(stmt, "$id", sms_id);
1015
1016 rc = sqlite3_step(stmt);
1017 if (rc != SQLITE_ROW) {
1018 db_remove_reset(stmt);
Stefan Sperling14e05172018-01-22 17:31:20 +01001019 return -1;
1020 }
1021
Harald Welted43c22e2022-05-14 15:35:49 +02001022 validity_timestamp = sqlite3_column_int64(stmt, 0);
Stefan Sperling14e05172018-01-22 17:31:20 +01001023
Harald Welted43c22e2022-05-14 15:35:49 +02001024 db_remove_reset(stmt);
Vadim Yanitskiy570b4c82019-04-14 16:39:12 +07001025 return delete_expired_sms(sms_id, validity_timestamp);
Stefan Sperling14e05172018-01-22 17:31:20 +01001026}
1027
1028void db_sms_delete_oldest_expired_message(void)
1029{
Harald Welted43c22e2022-05-14 15:35:49 +02001030 OSMO_ASSERT(g_dbc);
1031 sqlite3_stmt *stmt = g_dbc->stmt[DB_STMT_SMS_GET_OLDEST_EXPIRED];
1032 int rc;
Stefan Sperling14e05172018-01-22 17:31:20 +01001033
Harald Welted43c22e2022-05-14 15:35:49 +02001034 rc = sqlite3_step(stmt);
1035 if (rc == SQLITE_ROW) {
Stefan Sperling14e05172018-01-22 17:31:20 +01001036 unsigned long long sms_id;
Vadim Yanitskiy570b4c82019-04-14 16:39:12 +07001037 time_t validity_timestamp;
Stefan Sperling14e05172018-01-22 17:31:20 +01001038
Harald Welted43c22e2022-05-14 15:35:49 +02001039 sms_id = sqlite3_column_int64(stmt, 0);
1040 validity_timestamp = sqlite3_column_int64(stmt, 1);
Vadim Yanitskiy570b4c82019-04-14 16:39:12 +07001041 delete_expired_sms(sms_id, validity_timestamp);
Stefan Sperling14e05172018-01-22 17:31:20 +01001042 }
1043
Harald Welted43c22e2022-05-14 15:35:49 +02001044 db_remove_reset(stmt);
Stefan Sperling14e05172018-01-22 17:31:20 +01001045}