blob: d3d37c72d7e3fbcea94786f5e6a6a90e55b4531d [file] [log] [blame]
Holger Freyther12aa50d2009-01-01 18:02:05 +00001/* Simple HLR/VLR database backend using dbi */
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 Weltec2e302d2009-07-05 14:08:13 +02004 * (C) 2009 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#include <dbi/dbi.h>
33
Neels Hofmeyr90843962017-09-04 15:04:35 +020034#include <osmocom/msc/gsm_data.h>
35#include <osmocom/msc/gsm_subscriber.h>
36#include <osmocom/msc/gsm_04_11.h>
37#include <osmocom/msc/db.h>
38#include <osmocom/msc/debug.h>
39#include <osmocom/msc/vlr.h>
Holger Hans Peter Freytherc5faf662010-12-22 18:16:01 +010040
Harald Welted3fa84d2016-04-20 17:50:17 +020041#include <osmocom/gsm/protocol/gsm_23_003.h>
Pablo Neira Ayuso136f4532011-03-22 16:47:59 +010042#include <osmocom/core/talloc.h>
43#include <osmocom/core/statistics.h>
44#include <osmocom/core/rate_ctr.h>
Neels Hofmeyr93bafb62017-01-13 03:12:08 +010045#include <osmocom/core/utils.h>
Harald Weltef2b4cd72010-05-13 11:45:07 +020046
Holger Freytherbde36102008-12-28 22:51:39 +000047static char *db_basename = NULL;
48static char *db_dirname = NULL;
Holger Freyther1d506c82009-04-19 06:35:20 +000049static dbi_conn conn;
Pau Espin Pedrolc22e54d2017-12-15 18:28:43 +010050static dbi_inst inst;
Jan Luebbe7398eb92008-12-27 00:45:41 +000051
Pablo Neira Ayuso9891dae2017-08-07 14:01:33 +010052#define SCHEMA_REVISION "5"
Jan Luebbebfbdeec2012-12-27 00:27:16 +010053
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +010054enum {
55 SCHEMA_META,
56 INSERT_META,
57 SCHEMA_SUBSCRIBER,
58 SCHEMA_AUTH,
59 SCHEMA_EQUIPMENT,
60 SCHEMA_EQUIPMENT_WATCH,
61 SCHEMA_SMS,
62 SCHEMA_VLR,
63 SCHEMA_APDU,
64 SCHEMA_COUNTERS,
65 SCHEMA_RATE,
66 SCHEMA_AUTHKEY,
67 SCHEMA_AUTHLAST,
68};
69
70static const char *create_stmts[] = {
71 [SCHEMA_META] = "CREATE TABLE IF NOT EXISTS Meta ("
Harald Welte7e310b12009-03-30 20:56:32 +000072 "id INTEGER PRIMARY KEY AUTOINCREMENT, "
73 "key TEXT UNIQUE NOT NULL, "
74 "value TEXT NOT NULL"
75 ")",
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +010076 [INSERT_META] = "INSERT OR IGNORE INTO Meta "
Harald Welte7e310b12009-03-30 20:56:32 +000077 "(key, value) "
78 "VALUES "
Jan Luebbebfbdeec2012-12-27 00:27:16 +010079 "('revision', " SCHEMA_REVISION ")",
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +010080 [SCHEMA_SUBSCRIBER] = "CREATE TABLE IF NOT EXISTS Subscriber ("
Harald Welte7e310b12009-03-30 20:56:32 +000081 "id INTEGER PRIMARY KEY AUTOINCREMENT, "
82 "created TIMESTAMP NOT NULL, "
83 "updated TIMESTAMP NOT NULL, "
84 "imsi NUMERIC UNIQUE NOT NULL, "
85 "name TEXT, "
86 "extension TEXT UNIQUE, "
87 "authorized INTEGER NOT NULL DEFAULT 0, "
88 "tmsi TEXT UNIQUE, "
Jan Luebbebfbdeec2012-12-27 00:27:16 +010089 "lac INTEGER NOT NULL DEFAULT 0, "
90 "expire_lu TIMESTAMP DEFAULT NULL"
Harald Welte7e310b12009-03-30 20:56:32 +000091 ")",
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +010092 [SCHEMA_AUTH] = "CREATE TABLE IF NOT EXISTS AuthToken ("
Jan Luebbe31bef492009-08-12 14:31:14 +020093 "id INTEGER PRIMARY KEY AUTOINCREMENT, "
94 "subscriber_id INTEGER UNIQUE NOT NULL, "
95 "created TIMESTAMP NOT NULL, "
96 "token TEXT UNIQUE NOT NULL"
97 ")",
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +010098 [SCHEMA_EQUIPMENT] = "CREATE TABLE IF NOT EXISTS Equipment ("
Harald Welte7e310b12009-03-30 20:56:32 +000099 "id INTEGER PRIMARY KEY AUTOINCREMENT, "
100 "created TIMESTAMP NOT NULL, "
101 "updated TIMESTAMP NOT NULL, "
102 "name TEXT, "
Harald Weltec2e302d2009-07-05 14:08:13 +0200103 "classmark1 NUMERIC, "
104 "classmark2 BLOB, "
105 "classmark3 BLOB, "
Harald Welte7e310b12009-03-30 20:56:32 +0000106 "imei NUMERIC UNIQUE NOT NULL"
107 ")",
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100108 [SCHEMA_EQUIPMENT_WATCH] = "CREATE TABLE IF NOT EXISTS EquipmentWatch ("
Harald Welte7e310b12009-03-30 20:56:32 +0000109 "id INTEGER PRIMARY KEY AUTOINCREMENT, "
110 "created TIMESTAMP NOT NULL, "
111 "updated TIMESTAMP NOT NULL, "
112 "subscriber_id NUMERIC NOT NULL, "
113 "equipment_id NUMERIC NOT NULL, "
114 "UNIQUE (subscriber_id, equipment_id) "
115 ")",
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100116 [SCHEMA_SMS] = "CREATE TABLE IF NOT EXISTS SMS ("
Harald Welte76042182009-08-08 16:03:15 +0200117 /* metadata, not part of sms */
Harald Welte7e310b12009-03-30 20:56:32 +0000118 "id INTEGER PRIMARY KEY AUTOINCREMENT, "
119 "created TIMESTAMP NOT NULL, "
120 "sent TIMESTAMP, "
Harald Welte (local)db552c52009-08-15 20:15:14 +0200121 "deliver_attempts INTEGER NOT NULL DEFAULT 0, "
Harald Welte76042182009-08-08 16:03:15 +0200122 /* data directly copied/derived from SMS */
Harald Weltef3efc592009-07-27 20:11:35 +0200123 "valid_until TIMESTAMP, "
Harald Welte76042182009-08-08 16:03:15 +0200124 "reply_path_req INTEGER NOT NULL, "
125 "status_rep_req INTEGER NOT NULL, "
Pablo Neira Ayuso9891dae2017-08-07 14:01:33 +0100126 "is_report INTEGER NOT NULL, "
127 "msg_ref INTEGER NOT NULL, "
Harald Welte76042182009-08-08 16:03:15 +0200128 "protocol_id INTEGER NOT NULL, "
129 "data_coding_scheme INTEGER NOT NULL, "
Harald Welted0b7b772009-08-09 19:03:42 +0200130 "ud_hdr_ind INTEGER NOT NULL, "
Holger Hans Peter Freytherca3c2562013-10-08 03:17:30 +0200131 "src_addr TEXT NOT NULL, "
132 "src_ton INTEGER NOT NULL, "
133 "src_npi INTEGER NOT NULL, "
134 "dest_addr TEXT NOT NULL, "
135 "dest_ton INTEGER NOT NULL, "
136 "dest_npi INTEGER NOT NULL, "
Harald Welte76042182009-08-08 16:03:15 +0200137 "user_data BLOB, " /* TP-UD */
138 /* additional data, interpreted from SMS */
139 "header BLOB, " /* UD Header */
140 "text TEXT " /* decoded UD after UDH */
Harald Welte7e310b12009-03-30 20:56:32 +0000141 ")",
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100142 [SCHEMA_VLR] = "CREATE TABLE IF NOT EXISTS VLR ("
Holger Freytherc2995ea2009-04-19 06:35:23 +0000143 "id INTEGER PRIMARY KEY AUTOINCREMENT, "
144 "created TIMESTAMP NOT NULL, "
145 "updated TIMESTAMP NOT NULL, "
146 "subscriber_id NUMERIC UNIQUE NOT NULL, "
147 "last_bts NUMERIC NOT NULL "
148 ")",
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100149 [SCHEMA_APDU] = "CREATE TABLE IF NOT EXISTS ApduBlobs ("
Harald Welte (local)026531e2009-08-16 10:40:10 +0200150 "id INTEGER PRIMARY KEY AUTOINCREMENT, "
151 "created TIMESTAMP NOT NULL, "
152 "apdu_id_flags INTEGER NOT NULL, "
153 "subscriber_id INTEGER NOT NULL, "
154 "apdu BLOB "
155 ")",
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100156 [SCHEMA_COUNTERS] = "CREATE TABLE IF NOT EXISTS Counters ("
Harald Welteffa55a42009-12-22 19:07:32 +0100157 "id INTEGER PRIMARY KEY AUTOINCREMENT, "
158 "timestamp TIMESTAMP NOT NULL, "
Harald Weltef9a43c42009-12-22 21:40:42 +0100159 "value INTEGER NOT NULL, "
160 "name TEXT NOT NULL "
Harald Welte09f7ad02009-12-24 09:42:07 +0100161 ")",
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100162 [SCHEMA_RATE] = "CREATE TABLE IF NOT EXISTS RateCounters ("
Harald Weltec1919862010-05-13 12:55:20 +0200163 "id INTEGER PRIMARY KEY AUTOINCREMENT, "
164 "timestamp TIMESTAMP NOT NULL, "
165 "value INTEGER NOT NULL, "
166 "name TEXT NOT NULL, "
Harald Welted94d6a02010-05-14 17:38:47 +0200167 "idx INTEGER NOT NULL "
Harald Weltec1919862010-05-13 12:55:20 +0200168 ")",
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100169 [SCHEMA_AUTHKEY] = "CREATE TABLE IF NOT EXISTS AuthKeys ("
Sylvain Munaut10bf8122010-06-09 11:31:32 +0200170 "subscriber_id INTEGER PRIMARY KEY, "
Sylvain Munaut77d334a2009-12-27 19:26:12 +0100171 "algorithm_id INTEGER NOT NULL, "
Harald Welte3606cc52009-12-05 15:13:22 +0530172 "a3a8_ki BLOB "
173 ")",
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100174 [SCHEMA_AUTHLAST] = "CREATE TABLE IF NOT EXISTS AuthLastTuples ("
Sylvain Munaut10bf8122010-06-09 11:31:32 +0200175 "subscriber_id INTEGER PRIMARY KEY, "
Sylvain Munaut70881b72009-12-27 15:41:59 +0100176 "issued TIMESTAMP NOT NULL, "
177 "use_count INTEGER NOT NULL DEFAULT 0, "
178 "key_seq INTEGER NOT NULL, "
179 "rand BLOB NOT NULL, "
180 "sres BLOB NOT NULL, "
181 "kc BLOB NOT NULL "
Harald Welteffa55a42009-12-22 19:07:32 +0100182 ")",
Harald Welte7e310b12009-03-30 20:56:32 +0000183};
184
Neels Hofmeyrdb827292017-12-10 14:45:15 +0100185static inline int next_row(dbi_result result)
186{
187 if (!dbi_result_has_next_row(result))
188 return 0;
189 return dbi_result_next_row(result);
190}
191
Harald Welte0b906d02009-12-24 11:21:42 +0100192void db_error_func(dbi_conn conn, void *data)
193{
194 const char *msg;
Jan Luebbe5c15c852008-12-27 15:59:25 +0000195 dbi_conn_error(conn, &msg);
Harald Welteae1f1592009-12-24 11:39:14 +0100196 LOGP(DDB, LOGL_ERROR, "DBI: %s\n", msg);
Harald Weltec7548a12014-07-10 20:18:15 +0200197 osmo_log_backtrace(DDB, LOGL_ERROR);
Jan Luebbe7398eb92008-12-27 00:45:41 +0000198}
199
Jan Luebbebfbdeec2012-12-27 00:27:16 +0100200static int update_db_revision_2(void)
201{
202 dbi_result result;
203
204 result = dbi_conn_query(conn,
205 "ALTER TABLE Subscriber "
206 "ADD COLUMN expire_lu "
207 "TIMESTAMP DEFAULT NULL");
208 if (!result) {
209 LOGP(DDB, LOGL_ERROR,
Alexander Chemeris7e20f642014-03-07 16:59:53 +0100210 "Failed to alter table Subscriber (upgrade from rev 2).\n");
Jan Luebbebfbdeec2012-12-27 00:27:16 +0100211 return -EINVAL;
212 }
213 dbi_result_free(result);
214
215 result = dbi_conn_query(conn,
216 "UPDATE Meta "
217 "SET value = '3' "
218 "WHERE key = 'revision'");
219 if (!result) {
220 LOGP(DDB, LOGL_ERROR,
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100221 "Failed to update DB schema revision (upgrade from rev 2).\n");
Jan Luebbebfbdeec2012-12-27 00:27:16 +0100222 return -EINVAL;
223 }
224 dbi_result_free(result);
225
226 return 0;
227}
228
Vadim Yanitskiyea24bb52019-04-02 17:52:08 +0700229static void parse_tp_ud_from_result(struct gsm_sms *sms, dbi_result result)
230{
231 const unsigned char *user_data;
232 unsigned int user_data_len;
Vadim Yanitskiy0d13e832019-04-02 18:25:08 +0700233 unsigned int text_len;
Vadim Yanitskiyea24bb52019-04-02 17:52:08 +0700234 const char *text;
235
236 /* Retrieve TP-UDL (User-Data-Length) in octets (regardless of DCS) */
237 user_data_len = dbi_result_get_field_length(result, "user_data");
238 if (user_data_len > sizeof(sms->user_data)) {
239 LOGP(DDB, LOGL_ERROR,
240 "SMS TP-UD length %u is too big, truncating to %zu\n",
241 user_data_len, sizeof(sms->user_data));
242 user_data_len = (uint8_t) sizeof(sms->user_data);
243 }
244 sms->user_data_len = user_data_len;
245
246 /* Retrieve the TP-UD (User-Data) itself */
247 if (user_data_len > 0) {
248 user_data = dbi_result_get_binary(result, "user_data");
249 memcpy(sms->user_data, user_data, user_data_len);
250 }
251
Vadim Yanitskiy0d13e832019-04-02 18:25:08 +0700252 /* Retrieve the text length (excluding '\0') */
253 text_len = dbi_result_get_field_length(result, "text");
254 if (text_len >= sizeof(sms->text)) {
255 LOGP(DDB, LOGL_ERROR,
256 "SMS text length %u is too big, truncating to %zu\n",
257 text_len, sizeof(sms->text) - 1);
258 /* OSMO_STRLCPY_ARRAY() does truncation for us */
259 }
260
Vadim Yanitskiyea24bb52019-04-02 17:52:08 +0700261 /* Retrieve the text parsed from TP-UD (User-Data) */
262 text = dbi_result_get_string(result, "text");
263 if (text)
264 OSMO_STRLCPY_ARRAY(sms->text, text);
265}
266
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100267/**
268 * Copied from the normal sms_from_result_v3 to avoid having
269 * to make sure that the real routine will remain backward
270 * compatible.
271 */
272static struct gsm_sms *sms_from_result_v3(dbi_result result)
273{
274 struct gsm_sms *sms = sms_alloc();
275 long long unsigned int sender_id;
Vadim Yanitskiyea24bb52019-04-02 17:52:08 +0700276 const char *daddr;
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100277 char buf[32];
Harald Welte2483f1b2016-06-19 18:06:02 +0200278 char *quoted;
279 dbi_result result2;
280 const char *extension;
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100281
282 if (!sms)
283 return NULL;
284
285 sms->id = dbi_result_get_ulonglong(result, "id");
286
Harald Welte2483f1b2016-06-19 18:06:02 +0200287 /* find extension by id, assuming that the subscriber still exists in
288 * the db */
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100289 sender_id = dbi_result_get_ulonglong(result, "sender_id");
290 snprintf(buf, sizeof(buf), "%llu", sender_id);
Harald Welte2483f1b2016-06-19 18:06:02 +0200291
292 dbi_conn_quote_string_copy(conn, buf, &quoted);
293 result2 = dbi_conn_queryf(conn,
294 "SELECT extension FROM Subscriber "
295 "WHERE id = %s ", quoted);
296 free(quoted);
297 extension = dbi_result_get_string(result2, "extension");
298 if (extension)
Max98f74672018-02-05 12:57:06 +0100299 OSMO_STRLCPY_ARRAY(sms->src.addr, extension);
Harald Welte2483f1b2016-06-19 18:06:02 +0200300 dbi_result_free(result2);
301 /* got the extension */
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100302
Holger Hans Peter Freytherb115cb62014-07-03 14:00:30 +0200303 sms->reply_path_req = dbi_result_get_ulonglong(result, "reply_path_req");
304 sms->status_rep_req = dbi_result_get_ulonglong(result, "status_rep_req");
305 sms->ud_hdr_ind = dbi_result_get_ulonglong(result, "ud_hdr_ind");
306 sms->protocol_id = dbi_result_get_ulonglong(result, "protocol_id");
307 sms->data_coding_scheme = dbi_result_get_ulonglong(result,
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100308 "data_coding_scheme");
309
310 daddr = dbi_result_get_string(result, "dest_addr");
Neels Hofmeyr93bafb62017-01-13 03:12:08 +0100311 if (daddr)
Max98f74672018-02-05 12:57:06 +0100312 OSMO_STRLCPY_ARRAY(sms->dst.addr, daddr);
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100313
Vadim Yanitskiyea24bb52019-04-02 17:52:08 +0700314 /* Parse TP-UD, TP-UDL and decoded text */
315 parse_tp_ud_from_result(sms, result);
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100316
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100317 return sms;
318}
319
320static int update_db_revision_3(void)
321{
322 dbi_result result;
323 struct gsm_sms *sms;
324
Holger Hans Peter Freyther61144012014-03-08 16:41:37 +0100325 LOGP(DDB, LOGL_NOTICE, "Going to migrate from revision 3\n");
326
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100327 result = dbi_conn_query(conn, "BEGIN EXCLUSIVE TRANSACTION");
328 if (!result) {
329 LOGP(DDB, LOGL_ERROR,
330 "Failed to begin transaction (upgrade from rev 3)\n");
331 return -EINVAL;
332 }
333 dbi_result_free(result);
334
335 /* Rename old SMS table to be able create a new one */
336 result = dbi_conn_query(conn, "ALTER TABLE SMS RENAME TO SMS_3");
337 if (!result) {
338 LOGP(DDB, LOGL_ERROR,
339 "Failed to rename the old SMS table (upgrade from rev 3).\n");
340 goto rollback;
341 }
342 dbi_result_free(result);
343
344 /* Create new SMS table with all the bells and whistles! */
345 result = dbi_conn_query(conn, create_stmts[SCHEMA_SMS]);
346 if (!result) {
347 LOGP(DDB, LOGL_ERROR,
348 "Failed to create a new SMS table (upgrade from rev 3).\n");
349 goto rollback;
350 }
351 dbi_result_free(result);
352
353 /* Cycle through old messages and convert them to the new format */
Max5c06e402015-07-29 20:20:28 +0200354 result = dbi_conn_query(conn, "SELECT * FROM SMS_3");
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100355 if (!result) {
356 LOGP(DDB, LOGL_ERROR,
357 "Failed fetch messages from the old SMS table (upgrade from rev 3).\n");
358 goto rollback;
359 }
Neels Hofmeyrdb827292017-12-10 14:45:15 +0100360 while (next_row(result)) {
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100361 sms = sms_from_result_v3(result);
362 if (db_sms_store(sms) != 0) {
363 LOGP(DDB, LOGL_ERROR, "Failed to store message to the new SMS table(upgrade from rev 3).\n");
364 sms_free(sms);
365 dbi_result_free(result);
366 goto rollback;
367 }
368 sms_free(sms);
369 }
370 dbi_result_free(result);
371
372 /* Remove the temporary table */
373 result = dbi_conn_query(conn, "DROP TABLE SMS_3");
374 if (!result) {
375 LOGP(DDB, LOGL_ERROR,
376 "Failed to drop the old SMS table (upgrade from rev 3).\n");
377 goto rollback;
378 }
379 dbi_result_free(result);
380
381 /* We're done. Bump DB Meta revision to 4 */
382 result = dbi_conn_query(conn,
383 "UPDATE Meta "
384 "SET value = '4' "
385 "WHERE key = 'revision'");
386 if (!result) {
387 LOGP(DDB, LOGL_ERROR,
388 "Failed to update DB schema revision (upgrade from rev 3).\n");
389 goto rollback;
390 }
391 dbi_result_free(result);
392
393 result = dbi_conn_query(conn, "COMMIT TRANSACTION");
394 if (!result) {
395 LOGP(DDB, LOGL_ERROR,
396 "Failed to commit the transaction (upgrade from rev 3)\n");
397 return -EINVAL;
Alexander Couzensf480b352017-02-04 00:20:17 +0100398 } else {
399 dbi_result_free(result);
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100400 }
401
402 /* Shrink DB file size by actually wiping out SMS_3 table data */
403 result = dbi_conn_query(conn, "VACUUM");
404 if (!result)
405 LOGP(DDB, LOGL_ERROR,
406 "VACUUM failed. Ignoring it (upgrade from rev 3).\n");
407 else
408 dbi_result_free(result);
409
410 return 0;
411
412rollback:
413 result = dbi_conn_query(conn, "ROLLBACK TRANSACTION");
414 if (!result)
415 LOGP(DDB, LOGL_ERROR,
416 "Rollback failed (upgrade from rev 3).\n");
417 else
418 dbi_result_free(result);
419 return -EINVAL;
420}
421
Pablo Neira Ayuso9891dae2017-08-07 14:01:33 +0100422/* Just like v3, but there is a new message reference field for status reports,
423 * that is set to zero for existing entries since there is no way we can infer
424 * this.
425 */
426static struct gsm_sms *sms_from_result_v4(dbi_result result)
427{
428 struct gsm_sms *sms = sms_alloc();
Vadim Yanitskiyea24bb52019-04-02 17:52:08 +0700429 const char *addr;
Pablo Neira Ayuso9891dae2017-08-07 14:01:33 +0100430
431 if (!sms)
432 return NULL;
433
434 sms->id = dbi_result_get_ulonglong(result, "id");
435
436 sms->reply_path_req = dbi_result_get_ulonglong(result, "reply_path_req");
437 sms->status_rep_req = dbi_result_get_ulonglong(result, "status_rep_req");
438 sms->ud_hdr_ind = dbi_result_get_ulonglong(result, "ud_hdr_ind");
439 sms->protocol_id = dbi_result_get_ulonglong(result, "protocol_id");
440 sms->data_coding_scheme = dbi_result_get_ulonglong(result,
441 "data_coding_scheme");
442
443 addr = dbi_result_get_string(result, "src_addr");
Max98f74672018-02-05 12:57:06 +0100444 OSMO_STRLCPY_ARRAY(sms->src.addr, addr);
Pablo Neira Ayuso9891dae2017-08-07 14:01:33 +0100445 sms->src.ton = dbi_result_get_ulonglong(result, "src_ton");
446 sms->src.npi = dbi_result_get_ulonglong(result, "src_npi");
447
448 addr = dbi_result_get_string(result, "dest_addr");
Max98f74672018-02-05 12:57:06 +0100449 OSMO_STRLCPY_ARRAY(sms->dst.addr, addr);
Pablo Neira Ayuso9891dae2017-08-07 14:01:33 +0100450 sms->dst.ton = dbi_result_get_ulonglong(result, "dest_ton");
451 sms->dst.npi = dbi_result_get_ulonglong(result, "dest_npi");
452
Vadim Yanitskiyea24bb52019-04-02 17:52:08 +0700453 /* Parse TP-UD, TP-UDL and decoded text */
454 parse_tp_ud_from_result(sms, result);
Pablo Neira Ayuso9891dae2017-08-07 14:01:33 +0100455
Pablo Neira Ayuso9891dae2017-08-07 14:01:33 +0100456 return sms;
457}
458
459static int update_db_revision_4(void)
460{
461 dbi_result result;
462 struct gsm_sms *sms;
463
464 LOGP(DDB, LOGL_NOTICE, "Going to migrate from revision 4\n");
465
466 result = dbi_conn_query(conn, "BEGIN EXCLUSIVE TRANSACTION");
467 if (!result) {
468 LOGP(DDB, LOGL_ERROR,
469 "Failed to begin transaction (upgrade from rev 4)\n");
470 return -EINVAL;
471 }
472 dbi_result_free(result);
473
474 /* Rename old SMS table to be able create a new one */
475 result = dbi_conn_query(conn, "ALTER TABLE SMS RENAME TO SMS_4");
476 if (!result) {
477 LOGP(DDB, LOGL_ERROR,
478 "Failed to rename the old SMS table (upgrade from rev 4).\n");
479 goto rollback;
480 }
481 dbi_result_free(result);
482
483 /* Create new SMS table with all the bells and whistles! */
484 result = dbi_conn_query(conn, create_stmts[SCHEMA_SMS]);
485 if (!result) {
486 LOGP(DDB, LOGL_ERROR,
487 "Failed to create a new SMS table (upgrade from rev 4).\n");
488 goto rollback;
489 }
490 dbi_result_free(result);
491
492 /* Cycle through old messages and convert them to the new format */
493 result = dbi_conn_query(conn, "SELECT * FROM SMS_4");
494 if (!result) {
495 LOGP(DDB, LOGL_ERROR,
496 "Failed fetch messages from the old SMS table (upgrade from rev 4).\n");
497 goto rollback;
498 }
Neels Hofmeyrdb827292017-12-10 14:45:15 +0100499 while (next_row(result)) {
Pablo Neira Ayuso9891dae2017-08-07 14:01:33 +0100500 sms = sms_from_result_v4(result);
501 if (db_sms_store(sms) != 0) {
502 LOGP(DDB, LOGL_ERROR, "Failed to store message to the new SMS table(upgrade from rev 4).\n");
503 sms_free(sms);
504 dbi_result_free(result);
505 goto rollback;
506 }
507 sms_free(sms);
508 }
509 dbi_result_free(result);
510
511 /* Remove the temporary table */
512 result = dbi_conn_query(conn, "DROP TABLE SMS_4");
513 if (!result) {
514 LOGP(DDB, LOGL_ERROR,
515 "Failed to drop the old SMS table (upgrade from rev 4).\n");
516 goto rollback;
517 }
518 dbi_result_free(result);
519
520 /* We're done. Bump DB Meta revision to 4 */
521 result = dbi_conn_query(conn,
522 "UPDATE Meta "
523 "SET value = '5' "
524 "WHERE key = 'revision'");
525 if (!result) {
526 LOGP(DDB, LOGL_ERROR,
527 "Failed to update DB schema revision (upgrade from rev 4).\n");
528 goto rollback;
529 }
530 dbi_result_free(result);
531
532 result = dbi_conn_query(conn, "COMMIT TRANSACTION");
533 if (!result) {
534 LOGP(DDB, LOGL_ERROR,
535 "Failed to commit the transaction (upgrade from rev 4)\n");
536 return -EINVAL;
537 } else {
538 dbi_result_free(result);
539 }
540
541 /* Shrink DB file size by actually wiping out SMS_4 table data */
542 result = dbi_conn_query(conn, "VACUUM");
543 if (!result)
544 LOGP(DDB, LOGL_ERROR,
545 "VACUUM failed. Ignoring it (upgrade from rev 4).\n");
546 else
547 dbi_result_free(result);
548
549 return 0;
550
551rollback:
552 result = dbi_conn_query(conn, "ROLLBACK TRANSACTION");
553 if (!result)
554 LOGP(DDB, LOGL_ERROR,
555 "Rollback failed (upgrade from rev 4).\n");
556 else
557 dbi_result_free(result);
558 return -EINVAL;
559}
560
Harald Welted0b7b772009-08-09 19:03:42 +0200561static int check_db_revision(void)
562{
563 dbi_result result;
Jan Luebbebfbdeec2012-12-27 00:27:16 +0100564 const char *rev_s;
Vadim Yanitskiya8d8e932016-05-13 15:38:09 +0600565 int db_rev = 0;
Harald Welted0b7b772009-08-09 19:03:42 +0200566
Vadim Yanitskiya8d8e932016-05-13 15:38:09 +0600567 /* Make a query */
Harald Welted0b7b772009-08-09 19:03:42 +0200568 result = dbi_conn_query(conn,
Vadim Yanitskiya8d8e932016-05-13 15:38:09 +0600569 "SELECT value FROM Meta "
570 "WHERE key = 'revision'");
571
Harald Welted0b7b772009-08-09 19:03:42 +0200572 if (!result)
573 return -EINVAL;
574
Neels Hofmeyrdb827292017-12-10 14:45:15 +0100575 if (!next_row(result)) {
Harald Welted0b7b772009-08-09 19:03:42 +0200576 dbi_result_free(result);
577 return -EINVAL;
578 }
Vadim Yanitskiya8d8e932016-05-13 15:38:09 +0600579
580 /* Fetch the DB schema revision */
Jan Luebbebfbdeec2012-12-27 00:27:16 +0100581 rev_s = dbi_result_get_string(result, "value");
582 if (!rev_s) {
Harald Welted0b7b772009-08-09 19:03:42 +0200583 dbi_result_free(result);
584 return -EINVAL;
585 }
Vadim Yanitskiya8d8e932016-05-13 15:38:09 +0600586
587 if (!strcmp(rev_s, SCHEMA_REVISION)) {
588 /* Everything is fine */
Jan Luebbebfbdeec2012-12-27 00:27:16 +0100589 dbi_result_free(result);
Vadim Yanitskiya8d8e932016-05-13 15:38:09 +0600590 return 0;
591 }
592
593 db_rev = atoi(rev_s);
594 dbi_result_free(result);
595
596 /* Incremental migration waterfall */
597 switch (db_rev) {
598 case 2:
599 if (update_db_revision_2())
600 goto error;
Vadim Yanitskiy0cd761c2018-11-21 19:48:09 +0700601 /* fall through */
Vadim Yanitskiya8d8e932016-05-13 15:38:09 +0600602 case 3:
603 if (update_db_revision_3())
604 goto error;
Vadim Yanitskiy0cd761c2018-11-21 19:48:09 +0700605 /* fall through */
Pablo Neira Ayuso9891dae2017-08-07 14:01:33 +0100606 case 4:
607 if (update_db_revision_4())
608 goto error;
Vadim Yanitskiya8d8e932016-05-13 15:38:09 +0600609
610 /* The end of waterfall */
611 break;
612 default:
613 LOGP(DDB, LOGL_FATAL,
614 "Invalid database schema revision '%d'.\n", db_rev);
Jan Luebbebfbdeec2012-12-27 00:27:16 +0100615 return -EINVAL;
616 }
617
Jan Luebbebfbdeec2012-12-27 00:27:16 +0100618 return 0;
Vadim Yanitskiya8d8e932016-05-13 15:38:09 +0600619
620error:
621 LOGP(DDB, LOGL_FATAL, "Failed to update database "
622 "from schema revision '%d'.\n", db_rev);
623 return -EINVAL;
Jan Luebbebfbdeec2012-12-27 00:27:16 +0100624}
625
626static int db_configure(void)
627{
628 dbi_result result;
629
630 result = dbi_conn_query(conn,
631 "PRAGMA synchronous = FULL");
632 if (!result)
633 return -EINVAL;
Harald Welted0b7b772009-08-09 19:03:42 +0200634
635 dbi_result_free(result);
636 return 0;
637}
638
Harald Welte0b906d02009-12-24 11:21:42 +0100639int db_init(const char *name)
640{
Harald Welte467fc572022-05-13 19:48:08 +0200641 sqlite3_config(SQLITE_CONFIG_SINGLETHREAD);
642
Pau Espin Pedrolc22e54d2017-12-15 18:28:43 +0100643 dbi_initialize_r(NULL, &inst);
Harald Welte0b906d02009-12-24 11:21:42 +0100644
Vadim Yanitskiybaccf3a2019-04-15 20:25:57 +0700645 LOGP(DDB, LOGL_NOTICE, "Init database connection to '%s' using %s\n",
646 name, dbi_version());
647
Pau Espin Pedrolc22e54d2017-12-15 18:28:43 +0100648 conn = dbi_conn_new_r("sqlite3", inst);
Harald Welte0b906d02009-12-24 11:21:42 +0100649 if (conn == NULL) {
Stefan Sperling832046d2018-01-15 16:12:37 +0100650 LOGP(DDB, LOGL_FATAL, "Failed to create database connection to sqlite3 db '%s'; "
651 "Is the sqlite3 database driver for libdbi installed on this system?\n", name);
Jan Luebbe5c15c852008-12-27 15:59:25 +0000652 return 1;
653 }
Jan Luebbe7398eb92008-12-27 00:45:41 +0000654
Holger Freyther12aa50d2009-01-01 18:02:05 +0000655 dbi_conn_error_handler( conn, db_error_func, NULL );
Jan Luebbe7398eb92008-12-27 00:45:41 +0000656
Jan Luebbe5c15c852008-12-27 15:59:25 +0000657 /* MySQL
658 dbi_conn_set_option(conn, "host", "localhost");
659 dbi_conn_set_option(conn, "username", "your_name");
660 dbi_conn_set_option(conn, "password", "your_password");
661 dbi_conn_set_option(conn, "dbname", "your_dbname");
662 dbi_conn_set_option(conn, "encoding", "UTF-8");
663 */
Jan Luebbe7398eb92008-12-27 00:45:41 +0000664
Jan Luebbe5c15c852008-12-27 15:59:25 +0000665 /* SqLite 3 */
Holger Freyther12aa50d2009-01-01 18:02:05 +0000666 db_basename = strdup(name);
667 db_dirname = strdup(name);
Holger Freytherbde36102008-12-28 22:51:39 +0000668 dbi_conn_set_option(conn, "sqlite3_dbdir", dirname(db_dirname));
669 dbi_conn_set_option(conn, "dbname", basename(db_basename));
Jan Luebbe7398eb92008-12-27 00:45:41 +0000670
Harald Welted0b7b772009-08-09 19:03:42 +0200671 if (dbi_conn_connect(conn) < 0)
672 goto out_err;
673
Jan Luebbe5c15c852008-12-27 15:59:25 +0000674 return 0;
Harald Welted0b7b772009-08-09 19:03:42 +0200675
676out_err:
677 free(db_dirname);
678 free(db_basename);
679 db_dirname = db_basename = NULL;
680 return -1;
Jan Luebbe7398eb92008-12-27 00:45:41 +0000681}
682
Harald Welted0b7b772009-08-09 19:03:42 +0200683
Harald Welted1476bc2011-07-16 13:24:09 +0200684int db_prepare(void)
Harald Welte0b906d02009-12-24 11:21:42 +0100685{
Jan Luebbe5c15c852008-12-27 15:59:25 +0000686 dbi_result result;
Harald Welte7e310b12009-03-30 20:56:32 +0000687 int i;
Holger Freytherb4064bc2009-02-23 00:50:31 +0000688
Harald Welte7e310b12009-03-30 20:56:32 +0000689 for (i = 0; i < ARRAY_SIZE(create_stmts); i++) {
690 result = dbi_conn_query(conn, create_stmts[i]);
Harald Welte0b906d02009-12-24 11:21:42 +0100691 if (!result) {
Harald Welteae1f1592009-12-24 11:39:14 +0100692 LOGP(DDB, LOGL_ERROR,
693 "Failed to create some table.\n");
Harald Welte7e310b12009-03-30 20:56:32 +0000694 return 1;
695 }
696 dbi_result_free(result);
Holger Freytherb4064bc2009-02-23 00:50:31 +0000697 }
Holger Freytherb4064bc2009-02-23 00:50:31 +0000698
Holger Hans Peter Freyther850326e2009-08-10 08:36:04 +0200699 if (check_db_revision() < 0) {
Harald Welteae1f1592009-12-24 11:39:14 +0100700 LOGP(DDB, LOGL_FATAL, "Database schema revision invalid, "
Holger Hans Peter Freyther850326e2009-08-10 08:36:04 +0200701 "please update your database schema\n");
702 return -1;
703 }
704
Jan Luebbebfbdeec2012-12-27 00:27:16 +0100705 db_configure();
706
Jan Luebbe5c15c852008-12-27 15:59:25 +0000707 return 0;
Jan Luebbe7398eb92008-12-27 00:45:41 +0000708}
709
Harald Welted1476bc2011-07-16 13:24:09 +0200710int db_fini(void)
Harald Welte0b906d02009-12-24 11:21:42 +0100711{
Jan Luebbe5c15c852008-12-27 15:59:25 +0000712 dbi_conn_close(conn);
Pau Espin Pedrolc22e54d2017-12-15 18:28:43 +0100713 dbi_shutdown_r(inst);
Holger Freytherbde36102008-12-28 22:51:39 +0000714
Harald Welte2c5f4c62011-07-16 13:22:57 +0200715 free(db_dirname);
716 free(db_basename);
Jan Luebbe5c15c852008-12-27 15:59:25 +0000717 return 0;
Jan Luebbe7398eb92008-12-27 00:45:41 +0000718}
719
Harald Welte7e310b12009-03-30 20:56:32 +0000720/* store an [unsent] SMS to the database */
721int db_sms_store(struct gsm_sms *sms)
722{
723 dbi_result result;
Holger Hans Peter Freytherca3c2562013-10-08 03:17:30 +0200724 char *q_text, *q_daddr, *q_saddr;
Vadim Yanitskiy9d61db72019-04-14 13:32:33 +0700725 unsigned char *q_udata = NULL;
Stefan Sperlingd4941622018-01-18 17:36:28 +0100726 time_t now, validity_timestamp;
Harald Welte7e310b12009-03-30 20:56:32 +0000727
728 dbi_conn_quote_string_copy(conn, (char *)sms->text, &q_text);
Harald Weltec0de14d2012-11-23 23:35:01 +0100729 dbi_conn_quote_string_copy(conn, (char *)sms->dst.addr, &q_daddr);
Holger Hans Peter Freytherca3c2562013-10-08 03:17:30 +0200730 dbi_conn_quote_string_copy(conn, (char *)sms->src.addr, &q_saddr);
Vadim Yanitskiy9d61db72019-04-14 13:32:33 +0700731
732 /* Guard against zero-length input, as this may cause
733 * buffer overruns in libdbi / libdbdsqlite3. */
734 if (sms->user_data_len > 0) {
735 dbi_conn_quote_binary_copy(conn, sms->user_data,
736 sms->user_data_len,
737 &q_udata);
738 }
Holger Hans Peter Freytherca3c2562013-10-08 03:17:30 +0200739
Stefan Sperlingd4941622018-01-18 17:36:28 +0100740 now = time(NULL);
741 validity_timestamp = now + sms->validity_minutes * 60;
742
Harald Welte7e310b12009-03-30 20:56:32 +0000743 result = dbi_conn_queryf(conn,
744 "INSERT INTO SMS "
Alexander Chemerisca7ed2d2013-10-08 03:17:32 +0200745 "(created, valid_until, "
Pablo Neira Ayuso9891dae2017-08-07 14:01:33 +0100746 "reply_path_req, status_rep_req, is_report, "
747 "msg_ref, protocol_id, data_coding_scheme, "
748 "ud_hdr_ind, "
Holger Hans Peter Freytherca3c2562013-10-08 03:17:30 +0200749 "user_data, text, "
750 "dest_addr, dest_ton, dest_npi, "
751 "src_addr, src_ton, src_npi) VALUES "
Stefan Sperling3b26f342018-01-22 18:27:25 +0100752 "(datetime('%lld', 'unixepoch'), datetime('%lld', 'unixepoch'), "
Holger Hans Peter Freytherca3c2562013-10-08 03:17:30 +0200753 "%u, %u, %u, "
Pablo Neira Ayuso9891dae2017-08-07 14:01:33 +0100754 "%u, %u, %u, "
755 "%u, "
Holger Hans Peter Freytherca3c2562013-10-08 03:17:30 +0200756 "%s, %s, "
757 "%s, %u, %u, "
758 "%s, %u, %u)",
Stefan Sperlingd4941622018-01-18 17:36:28 +0100759 (int64_t)now, (int64_t)validity_timestamp,
Pablo Neira Ayuso9891dae2017-08-07 14:01:33 +0100760 sms->reply_path_req, sms->status_rep_req, sms->is_report,
761 sms->msg_ref, sms->protocol_id, sms->data_coding_scheme,
762 sms->ud_hdr_ind,
Holger Hans Peter Freytherca3c2562013-10-08 03:17:30 +0200763 q_udata, q_text,
764 q_daddr, sms->dst.ton, sms->dst.npi,
765 q_saddr, sms->src.ton, sms->src.npi);
Harald Welte7e310b12009-03-30 20:56:32 +0000766 free(q_text);
Harald Welte76042182009-08-08 16:03:15 +0200767 free(q_udata);
Holger Hans Peter Freytherca3c2562013-10-08 03:17:30 +0200768 free(q_daddr);
769 free(q_saddr);
Harald Welte7e310b12009-03-30 20:56:32 +0000770
771 if (!result)
772 return -EIO;
773
774 dbi_result_free(result);
Neels Hofmeyra252bcd2019-11-18 07:09:18 +0100775
776 sms->id = dbi_conn_sequence_last(conn, "id");
777 LOGP(DLSMS, LOGL_INFO, "Stored SMS id=%llu in DB\n", sms->id);
Harald Welte7e310b12009-03-30 20:56:32 +0000778 return 0;
779}
780
Harald Welte2ebabca2009-08-09 19:05:21 +0200781static struct gsm_sms *sms_from_result(struct gsm_network *net, dbi_result result)
Harald Welte7e310b12009-03-30 20:56:32 +0000782{
Harald Welte76042182009-08-08 16:03:15 +0200783 struct gsm_sms *sms = sms_alloc();
Vadim Yanitskiyea24bb52019-04-02 17:52:08 +0700784 const char *daddr, *saddr;
Stefan Sperlingd4941622018-01-18 17:36:28 +0100785 time_t validity_timestamp;
Harald Welte7e310b12009-03-30 20:56:32 +0000786
Harald Welte76042182009-08-08 16:03:15 +0200787 if (!sms)
Harald Welte7e310b12009-03-30 20:56:32 +0000788 return NULL;
Harald Welte7e310b12009-03-30 20:56:32 +0000789
Harald Weltebe3e3782009-07-05 14:06:41 +0200790 sms->id = dbi_result_get_ulonglong(result, "id");
Harald Welte7e310b12009-03-30 20:56:32 +0000791
Keithc601adc2017-08-16 22:45:07 +0200792 sms->created = dbi_result_get_datetime(result, "created");
Stefan Sperlingd4941622018-01-18 17:36:28 +0100793 validity_timestamp = dbi_result_get_datetime(result, "valid_until");
794 sms->validity_minutes = (validity_timestamp - sms->created) / 60;
795 /* FIXME: those should all be get_uchar, but sqlite3 is braindead */
Holger Hans Peter Freytherb115cb62014-07-03 14:00:30 +0200796 sms->reply_path_req = dbi_result_get_ulonglong(result, "reply_path_req");
797 sms->status_rep_req = dbi_result_get_ulonglong(result, "status_rep_req");
Pablo Neira Ayuso9891dae2017-08-07 14:01:33 +0100798 sms->is_report = dbi_result_get_ulonglong(result, "is_report");
799 sms->msg_ref = dbi_result_get_ulonglong(result, "msg_ref");
Holger Hans Peter Freytherb115cb62014-07-03 14:00:30 +0200800 sms->ud_hdr_ind = dbi_result_get_ulonglong(result, "ud_hdr_ind");
801 sms->protocol_id = dbi_result_get_ulonglong(result, "protocol_id");
802 sms->data_coding_scheme = dbi_result_get_ulonglong(result,
Harald Weltef3efc592009-07-27 20:11:35 +0200803 "data_coding_scheme");
804
Holger Hans Peter Freytherb115cb62014-07-03 14:00:30 +0200805 sms->dst.npi = dbi_result_get_ulonglong(result, "dest_npi");
806 sms->dst.ton = dbi_result_get_ulonglong(result, "dest_ton");
Harald Welte76042182009-08-08 16:03:15 +0200807 daddr = dbi_result_get_string(result, "dest_addr");
Neels Hofmeyr93bafb62017-01-13 03:12:08 +0100808 if (daddr)
Max98f74672018-02-05 12:57:06 +0100809 OSMO_STRLCPY_ARRAY(sms->dst.addr, daddr);
Vadim Yanitskiye1e72472019-04-09 16:55:44 +0700810
811 if (net != NULL) /* db_sms_test passes NULL, so we need to be tolerant */
812 sms->receiver = vlr_subscr_find_by_msisdn(net->vlr, sms->dst.addr,
813 VSUB_USE_SMS_RECEIVER);
Harald Welte76042182009-08-08 16:03:15 +0200814
Holger Hans Peter Freytherb115cb62014-07-03 14:00:30 +0200815 sms->src.npi = dbi_result_get_ulonglong(result, "src_npi");
816 sms->src.ton = dbi_result_get_ulonglong(result, "src_ton");
Holger Hans Peter Freytherca3c2562013-10-08 03:17:30 +0200817 saddr = dbi_result_get_string(result, "src_addr");
Neels Hofmeyr93bafb62017-01-13 03:12:08 +0100818 if (saddr)
Max98f74672018-02-05 12:57:06 +0100819 OSMO_STRLCPY_ARRAY(sms->src.addr, saddr);
Holger Hans Peter Freytherca3c2562013-10-08 03:17:30 +0200820
Vadim Yanitskiyea24bb52019-04-02 17:52:08 +0700821 /* Parse TP-UD, TP-UDL and decoded text */
822 parse_tp_ud_from_result(sms, result);
Harald Weltebe3e3782009-07-05 14:06:41 +0200823
Harald Welte2ebabca2009-08-09 19:05:21 +0200824 return sms;
825}
826
Holger Hans Peter Freyther812dad02010-12-24 23:18:31 +0100827struct gsm_sms *db_sms_get(struct gsm_network *net, unsigned long long id)
828{
829 dbi_result result;
830 struct gsm_sms *sms;
831
832 result = dbi_conn_queryf(conn,
833 "SELECT * FROM SMS WHERE SMS.id = %llu", id);
834 if (!result)
835 return NULL;
836
Neels Hofmeyrdb827292017-12-10 14:45:15 +0100837 if (!next_row(result)) {
Holger Hans Peter Freyther812dad02010-12-24 23:18:31 +0100838 dbi_result_free(result);
839 return NULL;
840 }
841
842 sms = sms_from_result(net, result);
843
844 dbi_result_free(result);
845
846 return sms;
847}
848
Harald Welte2483f1b2016-06-19 18:06:02 +0200849struct gsm_sms *db_sms_get_next_unsent(struct gsm_network *net,
850 unsigned long long min_sms_id,
851 unsigned int max_failed)
Harald Welte2ebabca2009-08-09 19:05:21 +0200852{
853 dbi_result result;
854 struct gsm_sms *sms;
855
856 result = dbi_conn_queryf(conn,
Harald Welte2483f1b2016-06-19 18:06:02 +0200857 "SELECT * FROM SMS"
858 " WHERE sent IS NULL"
859 " AND id >= %llu"
860 " AND deliver_attempts <= %u"
861 " ORDER BY id LIMIT 1",
862 min_sms_id, max_failed);
Harald Welte2ebabca2009-08-09 19:05:21 +0200863
Sylvain Munautff1f19e2009-12-22 13:22:29 +0100864 if (!result)
865 return NULL;
866
Neels Hofmeyrdb827292017-12-10 14:45:15 +0100867 if (!next_row(result)) {
Sylvain Munautff1f19e2009-12-22 13:22:29 +0100868 dbi_result_free(result);
869 return NULL;
870 }
871
872 sms = sms_from_result(net, result);
873
874 dbi_result_free(result);
875
876 return sms;
877}
878
Sylvain Munautd5778fc2009-12-21 01:09:57 +0100879/* retrieve the next unsent SMS for a given subscriber */
Harald Welte2483f1b2016-06-19 18:06:02 +0200880struct gsm_sms *db_sms_get_unsent_for_subscr(struct vlr_subscr *vsub,
881 unsigned int max_failed)
Harald Welte2ebabca2009-08-09 19:05:21 +0200882{
Harald Welte2483f1b2016-06-19 18:06:02 +0200883 struct gsm_network *net = vsub->vlr->user_ctx;
Harald Welte2ebabca2009-08-09 19:05:21 +0200884 dbi_result result;
885 struct gsm_sms *sms;
Neels Hofmeyr50d09002017-12-05 12:39:00 +0100886 char *q_msisdn;
Harald Welte2ebabca2009-08-09 19:05:21 +0200887
Harald Welte2483f1b2016-06-19 18:06:02 +0200888 if (!vsub->lu_complete)
889 return NULL;
890
Neels Hofmeyrf6704f12017-12-05 12:34:44 +0100891 /* A subscriber having no phone number cannot possibly receive SMS. */
892 if (*vsub->msisdn == '\0')
893 return NULL;
894
Neels Hofmeyr50d09002017-12-05 12:39:00 +0100895 dbi_conn_quote_string_copy(conn, vsub->msisdn, &q_msisdn);
Harald Welte2ebabca2009-08-09 19:05:21 +0200896 result = dbi_conn_queryf(conn,
Harald Welte2483f1b2016-06-19 18:06:02 +0200897 "SELECT * FROM SMS"
898 " WHERE sent IS NULL"
Neels Hofmeyr50d09002017-12-05 12:39:00 +0100899 " AND dest_addr = %s"
Harald Welte2483f1b2016-06-19 18:06:02 +0200900 " AND deliver_attempts <= %u"
901 " ORDER BY id LIMIT 1",
Neels Hofmeyr50d09002017-12-05 12:39:00 +0100902 q_msisdn, max_failed);
903 free(q_msisdn);
904
Harald Welte2ebabca2009-08-09 19:05:21 +0200905 if (!result)
906 return NULL;
907
Neels Hofmeyrdb827292017-12-10 14:45:15 +0100908 if (!next_row(result)) {
Harald Welte2ebabca2009-08-09 19:05:21 +0200909 dbi_result_free(result);
910 return NULL;
911 }
912
Harald Welte2483f1b2016-06-19 18:06:02 +0200913 sms = sms_from_result(net, result);
914
915 dbi_result_free(result);
916
917 return sms;
918}
919
920struct gsm_sms *db_sms_get_next_unsent_rr_msisdn(struct gsm_network *net,
921 const char *last_msisdn,
922 unsigned int max_failed)
923{
924 dbi_result result;
925 struct gsm_sms *sms;
Neels Hofmeyr50d09002017-12-05 12:39:00 +0100926 char *q_last_msisdn;
Harald Welte2483f1b2016-06-19 18:06:02 +0200927
Neels Hofmeyr50d09002017-12-05 12:39:00 +0100928 dbi_conn_quote_string_copy(conn, last_msisdn, &q_last_msisdn);
Harald Welte2483f1b2016-06-19 18:06:02 +0200929 result = dbi_conn_queryf(conn,
930 "SELECT * FROM SMS"
931 " WHERE sent IS NULL"
Neels Hofmeyr50d09002017-12-05 12:39:00 +0100932 " AND dest_addr > %s"
Harald Welte2483f1b2016-06-19 18:06:02 +0200933 " AND deliver_attempts <= %u"
934 " ORDER BY dest_addr, id LIMIT 1",
Neels Hofmeyr50d09002017-12-05 12:39:00 +0100935 q_last_msisdn, max_failed);
936 free(q_last_msisdn);
937
Harald Welte2483f1b2016-06-19 18:06:02 +0200938 if (!result)
939 return NULL;
940
Neels Hofmeyrdb827292017-12-10 14:45:15 +0100941 if (!next_row(result)) {
Harald Welte2483f1b2016-06-19 18:06:02 +0200942 dbi_result_free(result);
943 return NULL;
944 }
945
946 sms = sms_from_result(net, result);
Harald Welte2ebabca2009-08-09 19:05:21 +0200947
948 dbi_result_free(result);
949
Harald Welte7e310b12009-03-30 20:56:32 +0000950 return sms;
951}
952
Alexander Chemeris1e77e3d2014-03-08 21:27:37 +0100953/* mark a given SMS as delivered */
954int db_sms_mark_delivered(struct gsm_sms *sms)
Harald Welte7e310b12009-03-30 20:56:32 +0000955{
956 dbi_result result;
957
958 result = dbi_conn_queryf(conn,
959 "UPDATE SMS "
960 "SET sent = datetime('now') "
961 "WHERE id = %llu", sms->id);
962 if (!result) {
Harald Welteae1f1592009-12-24 11:39:14 +0100963 LOGP(DDB, LOGL_ERROR, "Failed to mark SMS %llu as sent.\n", sms->id);
Harald Welte7e310b12009-03-30 20:56:32 +0000964 return 1;
965 }
966
967 dbi_result_free(result);
968 return 0;
969}
Harald Welte (local)db552c52009-08-15 20:15:14 +0200970
971/* increase the number of attempted deliveries */
972int db_sms_inc_deliver_attempts(struct gsm_sms *sms)
973{
974 dbi_result result;
975
976 result = dbi_conn_queryf(conn,
977 "UPDATE SMS "
978 "SET deliver_attempts = deliver_attempts + 1 "
979 "WHERE id = %llu", sms->id);
980 if (!result) {
Harald Welteae1f1592009-12-24 11:39:14 +0100981 LOGP(DDB, LOGL_ERROR, "Failed to inc deliver attempts for "
982 "SMS %llu.\n", sms->id);
Harald Welte (local)db552c52009-08-15 20:15:14 +0200983 return 1;
984 }
985
986 dbi_result_free(result);
987 return 0;
988}
Harald Welte (local)026531e2009-08-16 10:40:10 +0200989
Harald Welte2483f1b2016-06-19 18:06:02 +0200990/* Drop all pending SMS to or from the given extension */
991int db_sms_delete_by_msisdn(const char *msisdn)
Harald Welte (local)026531e2009-08-16 10:40:10 +0200992{
993 dbi_result result;
Neels Hofmeyr50d09002017-12-05 12:39:00 +0100994 char *q_msisdn;
Harald Welte2483f1b2016-06-19 18:06:02 +0200995 if (!msisdn || !*msisdn)
996 return 0;
Neels Hofmeyr50d09002017-12-05 12:39:00 +0100997
998 dbi_conn_quote_string_copy(conn, msisdn, &q_msisdn);
Harald Welte (local)026531e2009-08-16 10:40:10 +0200999 result = dbi_conn_queryf(conn,
Harald Welte2483f1b2016-06-19 18:06:02 +02001000 "DELETE FROM SMS WHERE src_addr=%s OR dest_addr=%s",
Neels Hofmeyr50d09002017-12-05 12:39:00 +01001001 q_msisdn, q_msisdn);
1002 free(q_msisdn);
1003
Harald Welte2483f1b2016-06-19 18:06:02 +02001004 if (!result) {
1005 LOGP(DDB, LOGL_ERROR,
1006 "Failed to delete SMS for %s\n", msisdn);
1007 return -1;
1008 }
Harald Welte (local)026531e2009-08-16 10:40:10 +02001009 dbi_result_free(result);
1010 return 0;
1011}
Harald Welteffa55a42009-12-22 19:07:32 +01001012
Stefan Sperling6ba2d5a2018-01-18 18:55:26 +01001013int db_sms_delete_sent_message_by_id(unsigned long long sms_id)
1014{
1015 dbi_result result;
1016
1017 result = dbi_conn_queryf(conn,
1018 "DELETE FROM SMS WHERE id = %llu AND sent is NOT NULL",
1019 sms_id);
1020 if (!result) {
1021 LOGP(DDB, LOGL_ERROR, "Failed to delete SMS %llu.\n", sms_id);
1022 return 1;
1023 }
1024
1025 dbi_result_free(result);
1026 return 0;
1027}
1028
Stefan Sperling14e05172018-01-22 17:31:20 +01001029
Vadim Yanitskiy570b4c82019-04-14 16:39:12 +07001030static int delete_expired_sms(unsigned long long sms_id, time_t validity_timestamp)
Stefan Sperling87cba1f2018-01-22 17:05:37 +01001031{
1032 dbi_result result;
Vadim Yanitskiy570b4c82019-04-14 16:39:12 +07001033 time_t now;
Stefan Sperling87cba1f2018-01-22 17:05:37 +01001034
1035 now = time(NULL);
Stefan Sperling87cba1f2018-01-22 17:05:37 +01001036
Vadim Yanitskiy570b4c82019-04-14 16:39:12 +07001037 /* Net yet expired */
1038 if (validity_timestamp > now)
Stefan Sperling87cba1f2018-01-22 17:05:37 +01001039 return -1;
1040
1041 result = dbi_conn_queryf(conn, "DELETE FROM SMS WHERE id = %llu", sms_id);
1042 if (!result) {
1043 LOGP(DDB, LOGL_ERROR, "Failed to delete SMS %llu.\n", sms_id);
1044 return -1;
1045 }
1046 dbi_result_free(result);
1047 return 0;
1048}
1049
Stefan Sperling14e05172018-01-22 17:31:20 +01001050int db_sms_delete_expired_message_by_id(unsigned long long sms_id)
1051{
1052 dbi_result result;
Vadim Yanitskiy570b4c82019-04-14 16:39:12 +07001053 time_t validity_timestamp;
Stefan Sperling14e05172018-01-22 17:31:20 +01001054
Vadim Yanitskiy570b4c82019-04-14 16:39:12 +07001055 result = dbi_conn_queryf(conn, "SELECT valid_until FROM SMS WHERE id = %llu", sms_id);
Stefan Sperling14e05172018-01-22 17:31:20 +01001056 if (!result)
1057 return -1;
1058 if (!next_row(result)) {
1059 dbi_result_free(result);
1060 return -1;
1061 }
1062
Stefan Sperling14e05172018-01-22 17:31:20 +01001063 validity_timestamp = dbi_result_get_datetime(result, "valid_until");
1064
1065 dbi_result_free(result);
Vadim Yanitskiy570b4c82019-04-14 16:39:12 +07001066 return delete_expired_sms(sms_id, validity_timestamp);
Stefan Sperling14e05172018-01-22 17:31:20 +01001067}
1068
1069void db_sms_delete_oldest_expired_message(void)
1070{
1071 dbi_result result;
1072
Vadim Yanitskiy570b4c82019-04-14 16:39:12 +07001073 result = dbi_conn_queryf(conn, "SELECT id,valid_until FROM SMS "
1074 "ORDER BY valid_until LIMIT 1");
Stefan Sperling14e05172018-01-22 17:31:20 +01001075 if (!result)
1076 return;
1077
1078 if (next_row(result)) {
1079 unsigned long long sms_id;
Vadim Yanitskiy570b4c82019-04-14 16:39:12 +07001080 time_t validity_timestamp;
Stefan Sperling14e05172018-01-22 17:31:20 +01001081
1082 sms_id = dbi_result_get_ulonglong(result, "id");
Stefan Sperling14e05172018-01-22 17:31:20 +01001083 validity_timestamp = dbi_result_get_datetime(result, "valid_until");
Vadim Yanitskiy570b4c82019-04-14 16:39:12 +07001084 delete_expired_sms(sms_id, validity_timestamp);
Stefan Sperling14e05172018-01-22 17:31:20 +01001085 }
1086
1087 dbi_result_free(result);
1088}