blob: a12889bafffd4519e93764e2ff0e36d7a4add971 [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>
Jan Luebbe7398eb92008-12-27 00:45:41 +000031#include <dbi/dbi.h>
32
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
Holger Freytherbde36102008-12-28 22:51:39 +000046static char *db_basename = NULL;
47static char *db_dirname = NULL;
Holger Freyther1d506c82009-04-19 06:35:20 +000048static dbi_conn conn;
Pau Espin Pedrolc22e54d2017-12-15 18:28:43 +010049static dbi_inst inst;
Jan Luebbe7398eb92008-12-27 00:45:41 +000050
Pablo Neira Ayuso9891dae2017-08-07 14:01:33 +010051#define SCHEMA_REVISION "5"
Jan Luebbebfbdeec2012-12-27 00:27:16 +010052
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +010053enum {
54 SCHEMA_META,
55 INSERT_META,
56 SCHEMA_SUBSCRIBER,
57 SCHEMA_AUTH,
58 SCHEMA_EQUIPMENT,
59 SCHEMA_EQUIPMENT_WATCH,
60 SCHEMA_SMS,
61 SCHEMA_VLR,
62 SCHEMA_APDU,
63 SCHEMA_COUNTERS,
64 SCHEMA_RATE,
65 SCHEMA_AUTHKEY,
66 SCHEMA_AUTHLAST,
67};
68
69static const char *create_stmts[] = {
70 [SCHEMA_META] = "CREATE TABLE IF NOT EXISTS Meta ("
Harald Welte7e310b12009-03-30 20:56:32 +000071 "id INTEGER PRIMARY KEY AUTOINCREMENT, "
72 "key TEXT UNIQUE NOT NULL, "
73 "value TEXT NOT NULL"
74 ")",
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +010075 [INSERT_META] = "INSERT OR IGNORE INTO Meta "
Harald Welte7e310b12009-03-30 20:56:32 +000076 "(key, value) "
77 "VALUES "
Jan Luebbebfbdeec2012-12-27 00:27:16 +010078 "('revision', " SCHEMA_REVISION ")",
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +010079 [SCHEMA_SUBSCRIBER] = "CREATE TABLE IF NOT EXISTS Subscriber ("
Harald Welte7e310b12009-03-30 20:56:32 +000080 "id INTEGER PRIMARY KEY AUTOINCREMENT, "
81 "created TIMESTAMP NOT NULL, "
82 "updated TIMESTAMP NOT NULL, "
83 "imsi NUMERIC UNIQUE NOT NULL, "
84 "name TEXT, "
85 "extension TEXT UNIQUE, "
86 "authorized INTEGER NOT NULL DEFAULT 0, "
87 "tmsi TEXT UNIQUE, "
Jan Luebbebfbdeec2012-12-27 00:27:16 +010088 "lac INTEGER NOT NULL DEFAULT 0, "
89 "expire_lu TIMESTAMP DEFAULT NULL"
Harald Welte7e310b12009-03-30 20:56:32 +000090 ")",
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +010091 [SCHEMA_AUTH] = "CREATE TABLE IF NOT EXISTS AuthToken ("
Jan Luebbe31bef492009-08-12 14:31:14 +020092 "id INTEGER PRIMARY KEY AUTOINCREMENT, "
93 "subscriber_id INTEGER UNIQUE NOT NULL, "
94 "created TIMESTAMP NOT NULL, "
95 "token TEXT UNIQUE NOT NULL"
96 ")",
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +010097 [SCHEMA_EQUIPMENT] = "CREATE TABLE IF NOT EXISTS Equipment ("
Harald Welte7e310b12009-03-30 20:56:32 +000098 "id INTEGER PRIMARY KEY AUTOINCREMENT, "
99 "created TIMESTAMP NOT NULL, "
100 "updated TIMESTAMP NOT NULL, "
101 "name TEXT, "
Harald Weltec2e302d2009-07-05 14:08:13 +0200102 "classmark1 NUMERIC, "
103 "classmark2 BLOB, "
104 "classmark3 BLOB, "
Harald Welte7e310b12009-03-30 20:56:32 +0000105 "imei NUMERIC UNIQUE NOT NULL"
106 ")",
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100107 [SCHEMA_EQUIPMENT_WATCH] = "CREATE TABLE IF NOT EXISTS EquipmentWatch ("
Harald Welte7e310b12009-03-30 20:56:32 +0000108 "id INTEGER PRIMARY KEY AUTOINCREMENT, "
109 "created TIMESTAMP NOT NULL, "
110 "updated TIMESTAMP NOT NULL, "
111 "subscriber_id NUMERIC NOT NULL, "
112 "equipment_id NUMERIC NOT NULL, "
113 "UNIQUE (subscriber_id, equipment_id) "
114 ")",
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100115 [SCHEMA_SMS] = "CREATE TABLE IF NOT EXISTS SMS ("
Harald Welte76042182009-08-08 16:03:15 +0200116 /* metadata, not part of sms */
Harald Welte7e310b12009-03-30 20:56:32 +0000117 "id INTEGER PRIMARY KEY AUTOINCREMENT, "
118 "created TIMESTAMP NOT NULL, "
119 "sent TIMESTAMP, "
Harald Welte (local)db552c52009-08-15 20:15:14 +0200120 "deliver_attempts INTEGER NOT NULL DEFAULT 0, "
Harald Welte76042182009-08-08 16:03:15 +0200121 /* data directly copied/derived from SMS */
Harald Weltef3efc592009-07-27 20:11:35 +0200122 "valid_until TIMESTAMP, "
Harald Welte76042182009-08-08 16:03:15 +0200123 "reply_path_req INTEGER NOT NULL, "
124 "status_rep_req INTEGER NOT NULL, "
Pablo Neira Ayuso9891dae2017-08-07 14:01:33 +0100125 "is_report INTEGER NOT NULL, "
126 "msg_ref INTEGER NOT NULL, "
Harald Welte76042182009-08-08 16:03:15 +0200127 "protocol_id INTEGER NOT NULL, "
128 "data_coding_scheme INTEGER NOT NULL, "
Harald Welted0b7b772009-08-09 19:03:42 +0200129 "ud_hdr_ind INTEGER NOT NULL, "
Holger Hans Peter Freytherca3c2562013-10-08 03:17:30 +0200130 "src_addr TEXT NOT NULL, "
131 "src_ton INTEGER NOT NULL, "
132 "src_npi INTEGER NOT NULL, "
133 "dest_addr TEXT NOT NULL, "
134 "dest_ton INTEGER NOT NULL, "
135 "dest_npi INTEGER NOT NULL, "
Harald Welte76042182009-08-08 16:03:15 +0200136 "user_data BLOB, " /* TP-UD */
137 /* additional data, interpreted from SMS */
138 "header BLOB, " /* UD Header */
139 "text TEXT " /* decoded UD after UDH */
Harald Welte7e310b12009-03-30 20:56:32 +0000140 ")",
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100141 [SCHEMA_VLR] = "CREATE TABLE IF NOT EXISTS VLR ("
Holger Freytherc2995ea2009-04-19 06:35:23 +0000142 "id INTEGER PRIMARY KEY AUTOINCREMENT, "
143 "created TIMESTAMP NOT NULL, "
144 "updated TIMESTAMP NOT NULL, "
145 "subscriber_id NUMERIC UNIQUE NOT NULL, "
146 "last_bts NUMERIC NOT NULL "
147 ")",
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100148 [SCHEMA_APDU] = "CREATE TABLE IF NOT EXISTS ApduBlobs ("
Harald Welte (local)026531e2009-08-16 10:40:10 +0200149 "id INTEGER PRIMARY KEY AUTOINCREMENT, "
150 "created TIMESTAMP NOT NULL, "
151 "apdu_id_flags INTEGER NOT NULL, "
152 "subscriber_id INTEGER NOT NULL, "
153 "apdu BLOB "
154 ")",
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100155 [SCHEMA_COUNTERS] = "CREATE TABLE IF NOT EXISTS Counters ("
Harald Welteffa55a42009-12-22 19:07:32 +0100156 "id INTEGER PRIMARY KEY AUTOINCREMENT, "
157 "timestamp TIMESTAMP NOT NULL, "
Harald Weltef9a43c42009-12-22 21:40:42 +0100158 "value INTEGER NOT NULL, "
159 "name TEXT NOT NULL "
Harald Welte09f7ad02009-12-24 09:42:07 +0100160 ")",
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100161 [SCHEMA_RATE] = "CREATE TABLE IF NOT EXISTS RateCounters ("
Harald Weltec1919862010-05-13 12:55:20 +0200162 "id INTEGER PRIMARY KEY AUTOINCREMENT, "
163 "timestamp TIMESTAMP NOT NULL, "
164 "value INTEGER NOT NULL, "
165 "name TEXT NOT NULL, "
Harald Welted94d6a02010-05-14 17:38:47 +0200166 "idx INTEGER NOT NULL "
Harald Weltec1919862010-05-13 12:55:20 +0200167 ")",
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100168 [SCHEMA_AUTHKEY] = "CREATE TABLE IF NOT EXISTS AuthKeys ("
Sylvain Munaut10bf8122010-06-09 11:31:32 +0200169 "subscriber_id INTEGER PRIMARY KEY, "
Sylvain Munaut77d334a2009-12-27 19:26:12 +0100170 "algorithm_id INTEGER NOT NULL, "
Harald Welte3606cc52009-12-05 15:13:22 +0530171 "a3a8_ki BLOB "
172 ")",
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100173 [SCHEMA_AUTHLAST] = "CREATE TABLE IF NOT EXISTS AuthLastTuples ("
Sylvain Munaut10bf8122010-06-09 11:31:32 +0200174 "subscriber_id INTEGER PRIMARY KEY, "
Sylvain Munaut70881b72009-12-27 15:41:59 +0100175 "issued TIMESTAMP NOT NULL, "
176 "use_count INTEGER NOT NULL DEFAULT 0, "
177 "key_seq INTEGER NOT NULL, "
178 "rand BLOB NOT NULL, "
179 "sres BLOB NOT NULL, "
180 "kc BLOB NOT NULL "
Harald Welteffa55a42009-12-22 19:07:32 +0100181 ")",
Harald Welte7e310b12009-03-30 20:56:32 +0000182};
183
Neels Hofmeyrdb827292017-12-10 14:45:15 +0100184static inline int next_row(dbi_result result)
185{
186 if (!dbi_result_has_next_row(result))
187 return 0;
188 return dbi_result_next_row(result);
189}
190
Harald Welte0b906d02009-12-24 11:21:42 +0100191void db_error_func(dbi_conn conn, void *data)
192{
193 const char *msg;
Jan Luebbe5c15c852008-12-27 15:59:25 +0000194 dbi_conn_error(conn, &msg);
Harald Welteae1f1592009-12-24 11:39:14 +0100195 LOGP(DDB, LOGL_ERROR, "DBI: %s\n", msg);
Harald Weltec7548a12014-07-10 20:18:15 +0200196 osmo_log_backtrace(DDB, LOGL_ERROR);
Jan Luebbe7398eb92008-12-27 00:45:41 +0000197}
198
Jan Luebbebfbdeec2012-12-27 00:27:16 +0100199static int update_db_revision_2(void)
200{
201 dbi_result result;
202
203 result = dbi_conn_query(conn,
204 "ALTER TABLE Subscriber "
205 "ADD COLUMN expire_lu "
206 "TIMESTAMP DEFAULT NULL");
207 if (!result) {
208 LOGP(DDB, LOGL_ERROR,
Alexander Chemeris7e20f642014-03-07 16:59:53 +0100209 "Failed to alter table Subscriber (upgrade from rev 2).\n");
Jan Luebbebfbdeec2012-12-27 00:27:16 +0100210 return -EINVAL;
211 }
212 dbi_result_free(result);
213
214 result = dbi_conn_query(conn,
215 "UPDATE Meta "
216 "SET value = '3' "
217 "WHERE key = 'revision'");
218 if (!result) {
219 LOGP(DDB, LOGL_ERROR,
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100220 "Failed to update DB schema revision (upgrade from rev 2).\n");
Jan Luebbebfbdeec2012-12-27 00:27:16 +0100221 return -EINVAL;
222 }
223 dbi_result_free(result);
224
225 return 0;
226}
227
Vadim Yanitskiyea24bb52019-04-02 17:52:08 +0700228static void parse_tp_ud_from_result(struct gsm_sms *sms, dbi_result result)
229{
230 const unsigned char *user_data;
231 unsigned int user_data_len;
232 const char *text;
233
234 /* Retrieve TP-UDL (User-Data-Length) in octets (regardless of DCS) */
235 user_data_len = dbi_result_get_field_length(result, "user_data");
236 if (user_data_len > sizeof(sms->user_data)) {
237 LOGP(DDB, LOGL_ERROR,
238 "SMS TP-UD length %u is too big, truncating to %zu\n",
239 user_data_len, sizeof(sms->user_data));
240 user_data_len = (uint8_t) sizeof(sms->user_data);
241 }
242 sms->user_data_len = user_data_len;
243
244 /* Retrieve the TP-UD (User-Data) itself */
245 if (user_data_len > 0) {
246 user_data = dbi_result_get_binary(result, "user_data");
247 memcpy(sms->user_data, user_data, user_data_len);
248 }
249
250 /* Retrieve the text parsed from TP-UD (User-Data) */
251 text = dbi_result_get_string(result, "text");
252 if (text)
253 OSMO_STRLCPY_ARRAY(sms->text, text);
254}
255
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100256/**
257 * Copied from the normal sms_from_result_v3 to avoid having
258 * to make sure that the real routine will remain backward
259 * compatible.
260 */
261static struct gsm_sms *sms_from_result_v3(dbi_result result)
262{
263 struct gsm_sms *sms = sms_alloc();
264 long long unsigned int sender_id;
Vadim Yanitskiyea24bb52019-04-02 17:52:08 +0700265 const char *daddr;
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100266 char buf[32];
Harald Welte2483f1b2016-06-19 18:06:02 +0200267 char *quoted;
268 dbi_result result2;
269 const char *extension;
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100270
271 if (!sms)
272 return NULL;
273
274 sms->id = dbi_result_get_ulonglong(result, "id");
275
Harald Welte2483f1b2016-06-19 18:06:02 +0200276 /* find extension by id, assuming that the subscriber still exists in
277 * the db */
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100278 sender_id = dbi_result_get_ulonglong(result, "sender_id");
279 snprintf(buf, sizeof(buf), "%llu", sender_id);
Harald Welte2483f1b2016-06-19 18:06:02 +0200280
281 dbi_conn_quote_string_copy(conn, buf, &quoted);
282 result2 = dbi_conn_queryf(conn,
283 "SELECT extension FROM Subscriber "
284 "WHERE id = %s ", quoted);
285 free(quoted);
286 extension = dbi_result_get_string(result2, "extension");
287 if (extension)
Max98f74672018-02-05 12:57:06 +0100288 OSMO_STRLCPY_ARRAY(sms->src.addr, extension);
Harald Welte2483f1b2016-06-19 18:06:02 +0200289 dbi_result_free(result2);
290 /* got the extension */
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100291
Holger Hans Peter Freytherb115cb62014-07-03 14:00:30 +0200292 sms->reply_path_req = dbi_result_get_ulonglong(result, "reply_path_req");
293 sms->status_rep_req = dbi_result_get_ulonglong(result, "status_rep_req");
294 sms->ud_hdr_ind = dbi_result_get_ulonglong(result, "ud_hdr_ind");
295 sms->protocol_id = dbi_result_get_ulonglong(result, "protocol_id");
296 sms->data_coding_scheme = dbi_result_get_ulonglong(result,
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100297 "data_coding_scheme");
298
299 daddr = dbi_result_get_string(result, "dest_addr");
Neels Hofmeyr93bafb62017-01-13 03:12:08 +0100300 if (daddr)
Max98f74672018-02-05 12:57:06 +0100301 OSMO_STRLCPY_ARRAY(sms->dst.addr, daddr);
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100302
Vadim Yanitskiyea24bb52019-04-02 17:52:08 +0700303 /* Parse TP-UD, TP-UDL and decoded text */
304 parse_tp_ud_from_result(sms, result);
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100305
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100306 return sms;
307}
308
309static int update_db_revision_3(void)
310{
311 dbi_result result;
312 struct gsm_sms *sms;
313
Holger Hans Peter Freyther61144012014-03-08 16:41:37 +0100314 LOGP(DDB, LOGL_NOTICE, "Going to migrate from revision 3\n");
315
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100316 result = dbi_conn_query(conn, "BEGIN EXCLUSIVE TRANSACTION");
317 if (!result) {
318 LOGP(DDB, LOGL_ERROR,
319 "Failed to begin transaction (upgrade from rev 3)\n");
320 return -EINVAL;
321 }
322 dbi_result_free(result);
323
324 /* Rename old SMS table to be able create a new one */
325 result = dbi_conn_query(conn, "ALTER TABLE SMS RENAME TO SMS_3");
326 if (!result) {
327 LOGP(DDB, LOGL_ERROR,
328 "Failed to rename the old SMS table (upgrade from rev 3).\n");
329 goto rollback;
330 }
331 dbi_result_free(result);
332
333 /* Create new SMS table with all the bells and whistles! */
334 result = dbi_conn_query(conn, create_stmts[SCHEMA_SMS]);
335 if (!result) {
336 LOGP(DDB, LOGL_ERROR,
337 "Failed to create a new SMS table (upgrade from rev 3).\n");
338 goto rollback;
339 }
340 dbi_result_free(result);
341
342 /* Cycle through old messages and convert them to the new format */
Max5c06e402015-07-29 20:20:28 +0200343 result = dbi_conn_query(conn, "SELECT * FROM SMS_3");
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100344 if (!result) {
345 LOGP(DDB, LOGL_ERROR,
346 "Failed fetch messages from the old SMS table (upgrade from rev 3).\n");
347 goto rollback;
348 }
Neels Hofmeyrdb827292017-12-10 14:45:15 +0100349 while (next_row(result)) {
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100350 sms = sms_from_result_v3(result);
351 if (db_sms_store(sms) != 0) {
352 LOGP(DDB, LOGL_ERROR, "Failed to store message to the new SMS table(upgrade from rev 3).\n");
353 sms_free(sms);
354 dbi_result_free(result);
355 goto rollback;
356 }
357 sms_free(sms);
358 }
359 dbi_result_free(result);
360
361 /* Remove the temporary table */
362 result = dbi_conn_query(conn, "DROP TABLE SMS_3");
363 if (!result) {
364 LOGP(DDB, LOGL_ERROR,
365 "Failed to drop the old SMS table (upgrade from rev 3).\n");
366 goto rollback;
367 }
368 dbi_result_free(result);
369
370 /* We're done. Bump DB Meta revision to 4 */
371 result = dbi_conn_query(conn,
372 "UPDATE Meta "
373 "SET value = '4' "
374 "WHERE key = 'revision'");
375 if (!result) {
376 LOGP(DDB, LOGL_ERROR,
377 "Failed to update DB schema revision (upgrade from rev 3).\n");
378 goto rollback;
379 }
380 dbi_result_free(result);
381
382 result = dbi_conn_query(conn, "COMMIT TRANSACTION");
383 if (!result) {
384 LOGP(DDB, LOGL_ERROR,
385 "Failed to commit the transaction (upgrade from rev 3)\n");
386 return -EINVAL;
Alexander Couzensf480b352017-02-04 00:20:17 +0100387 } else {
388 dbi_result_free(result);
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100389 }
390
391 /* Shrink DB file size by actually wiping out SMS_3 table data */
392 result = dbi_conn_query(conn, "VACUUM");
393 if (!result)
394 LOGP(DDB, LOGL_ERROR,
395 "VACUUM failed. Ignoring it (upgrade from rev 3).\n");
396 else
397 dbi_result_free(result);
398
399 return 0;
400
401rollback:
402 result = dbi_conn_query(conn, "ROLLBACK TRANSACTION");
403 if (!result)
404 LOGP(DDB, LOGL_ERROR,
405 "Rollback failed (upgrade from rev 3).\n");
406 else
407 dbi_result_free(result);
408 return -EINVAL;
409}
410
Pablo Neira Ayuso9891dae2017-08-07 14:01:33 +0100411/* Just like v3, but there is a new message reference field for status reports,
412 * that is set to zero for existing entries since there is no way we can infer
413 * this.
414 */
415static struct gsm_sms *sms_from_result_v4(dbi_result result)
416{
417 struct gsm_sms *sms = sms_alloc();
Vadim Yanitskiyea24bb52019-04-02 17:52:08 +0700418 const char *addr;
Pablo Neira Ayuso9891dae2017-08-07 14:01:33 +0100419
420 if (!sms)
421 return NULL;
422
423 sms->id = dbi_result_get_ulonglong(result, "id");
424
425 sms->reply_path_req = dbi_result_get_ulonglong(result, "reply_path_req");
426 sms->status_rep_req = dbi_result_get_ulonglong(result, "status_rep_req");
427 sms->ud_hdr_ind = dbi_result_get_ulonglong(result, "ud_hdr_ind");
428 sms->protocol_id = dbi_result_get_ulonglong(result, "protocol_id");
429 sms->data_coding_scheme = dbi_result_get_ulonglong(result,
430 "data_coding_scheme");
431
432 addr = dbi_result_get_string(result, "src_addr");
Max98f74672018-02-05 12:57:06 +0100433 OSMO_STRLCPY_ARRAY(sms->src.addr, addr);
Pablo Neira Ayuso9891dae2017-08-07 14:01:33 +0100434 sms->src.ton = dbi_result_get_ulonglong(result, "src_ton");
435 sms->src.npi = dbi_result_get_ulonglong(result, "src_npi");
436
437 addr = dbi_result_get_string(result, "dest_addr");
Max98f74672018-02-05 12:57:06 +0100438 OSMO_STRLCPY_ARRAY(sms->dst.addr, addr);
Pablo Neira Ayuso9891dae2017-08-07 14:01:33 +0100439 sms->dst.ton = dbi_result_get_ulonglong(result, "dest_ton");
440 sms->dst.npi = dbi_result_get_ulonglong(result, "dest_npi");
441
Vadim Yanitskiyea24bb52019-04-02 17:52:08 +0700442 /* Parse TP-UD, TP-UDL and decoded text */
443 parse_tp_ud_from_result(sms, result);
Pablo Neira Ayuso9891dae2017-08-07 14:01:33 +0100444
Pablo Neira Ayuso9891dae2017-08-07 14:01:33 +0100445 return sms;
446}
447
448static int update_db_revision_4(void)
449{
450 dbi_result result;
451 struct gsm_sms *sms;
452
453 LOGP(DDB, LOGL_NOTICE, "Going to migrate from revision 4\n");
454
455 result = dbi_conn_query(conn, "BEGIN EXCLUSIVE TRANSACTION");
456 if (!result) {
457 LOGP(DDB, LOGL_ERROR,
458 "Failed to begin transaction (upgrade from rev 4)\n");
459 return -EINVAL;
460 }
461 dbi_result_free(result);
462
463 /* Rename old SMS table to be able create a new one */
464 result = dbi_conn_query(conn, "ALTER TABLE SMS RENAME TO SMS_4");
465 if (!result) {
466 LOGP(DDB, LOGL_ERROR,
467 "Failed to rename the old SMS table (upgrade from rev 4).\n");
468 goto rollback;
469 }
470 dbi_result_free(result);
471
472 /* Create new SMS table with all the bells and whistles! */
473 result = dbi_conn_query(conn, create_stmts[SCHEMA_SMS]);
474 if (!result) {
475 LOGP(DDB, LOGL_ERROR,
476 "Failed to create a new SMS table (upgrade from rev 4).\n");
477 goto rollback;
478 }
479 dbi_result_free(result);
480
481 /* Cycle through old messages and convert them to the new format */
482 result = dbi_conn_query(conn, "SELECT * FROM SMS_4");
483 if (!result) {
484 LOGP(DDB, LOGL_ERROR,
485 "Failed fetch messages from the old SMS table (upgrade from rev 4).\n");
486 goto rollback;
487 }
Neels Hofmeyrdb827292017-12-10 14:45:15 +0100488 while (next_row(result)) {
Pablo Neira Ayuso9891dae2017-08-07 14:01:33 +0100489 sms = sms_from_result_v4(result);
490 if (db_sms_store(sms) != 0) {
491 LOGP(DDB, LOGL_ERROR, "Failed to store message to the new SMS table(upgrade from rev 4).\n");
492 sms_free(sms);
493 dbi_result_free(result);
494 goto rollback;
495 }
496 sms_free(sms);
497 }
498 dbi_result_free(result);
499
500 /* Remove the temporary table */
501 result = dbi_conn_query(conn, "DROP TABLE SMS_4");
502 if (!result) {
503 LOGP(DDB, LOGL_ERROR,
504 "Failed to drop the old SMS table (upgrade from rev 4).\n");
505 goto rollback;
506 }
507 dbi_result_free(result);
508
509 /* We're done. Bump DB Meta revision to 4 */
510 result = dbi_conn_query(conn,
511 "UPDATE Meta "
512 "SET value = '5' "
513 "WHERE key = 'revision'");
514 if (!result) {
515 LOGP(DDB, LOGL_ERROR,
516 "Failed to update DB schema revision (upgrade from rev 4).\n");
517 goto rollback;
518 }
519 dbi_result_free(result);
520
521 result = dbi_conn_query(conn, "COMMIT TRANSACTION");
522 if (!result) {
523 LOGP(DDB, LOGL_ERROR,
524 "Failed to commit the transaction (upgrade from rev 4)\n");
525 return -EINVAL;
526 } else {
527 dbi_result_free(result);
528 }
529
530 /* Shrink DB file size by actually wiping out SMS_4 table data */
531 result = dbi_conn_query(conn, "VACUUM");
532 if (!result)
533 LOGP(DDB, LOGL_ERROR,
534 "VACUUM failed. Ignoring it (upgrade from rev 4).\n");
535 else
536 dbi_result_free(result);
537
538 return 0;
539
540rollback:
541 result = dbi_conn_query(conn, "ROLLBACK TRANSACTION");
542 if (!result)
543 LOGP(DDB, LOGL_ERROR,
544 "Rollback failed (upgrade from rev 4).\n");
545 else
546 dbi_result_free(result);
547 return -EINVAL;
548}
549
Harald Welted0b7b772009-08-09 19:03:42 +0200550static int check_db_revision(void)
551{
552 dbi_result result;
Jan Luebbebfbdeec2012-12-27 00:27:16 +0100553 const char *rev_s;
Vadim Yanitskiya8d8e932016-05-13 15:38:09 +0600554 int db_rev = 0;
Harald Welted0b7b772009-08-09 19:03:42 +0200555
Vadim Yanitskiya8d8e932016-05-13 15:38:09 +0600556 /* Make a query */
Harald Welted0b7b772009-08-09 19:03:42 +0200557 result = dbi_conn_query(conn,
Vadim Yanitskiya8d8e932016-05-13 15:38:09 +0600558 "SELECT value FROM Meta "
559 "WHERE key = 'revision'");
560
Harald Welted0b7b772009-08-09 19:03:42 +0200561 if (!result)
562 return -EINVAL;
563
Neels Hofmeyrdb827292017-12-10 14:45:15 +0100564 if (!next_row(result)) {
Harald Welted0b7b772009-08-09 19:03:42 +0200565 dbi_result_free(result);
566 return -EINVAL;
567 }
Vadim Yanitskiya8d8e932016-05-13 15:38:09 +0600568
569 /* Fetch the DB schema revision */
Jan Luebbebfbdeec2012-12-27 00:27:16 +0100570 rev_s = dbi_result_get_string(result, "value");
571 if (!rev_s) {
Harald Welted0b7b772009-08-09 19:03:42 +0200572 dbi_result_free(result);
573 return -EINVAL;
574 }
Vadim Yanitskiya8d8e932016-05-13 15:38:09 +0600575
576 if (!strcmp(rev_s, SCHEMA_REVISION)) {
577 /* Everything is fine */
Jan Luebbebfbdeec2012-12-27 00:27:16 +0100578 dbi_result_free(result);
Vadim Yanitskiya8d8e932016-05-13 15:38:09 +0600579 return 0;
580 }
581
582 db_rev = atoi(rev_s);
583 dbi_result_free(result);
584
585 /* Incremental migration waterfall */
586 switch (db_rev) {
587 case 2:
588 if (update_db_revision_2())
589 goto error;
Vadim Yanitskiy0cd761c2018-11-21 19:48:09 +0700590 /* fall through */
Vadim Yanitskiya8d8e932016-05-13 15:38:09 +0600591 case 3:
592 if (update_db_revision_3())
593 goto error;
Vadim Yanitskiy0cd761c2018-11-21 19:48:09 +0700594 /* fall through */
Pablo Neira Ayuso9891dae2017-08-07 14:01:33 +0100595 case 4:
596 if (update_db_revision_4())
597 goto error;
Vadim Yanitskiya8d8e932016-05-13 15:38:09 +0600598
599 /* The end of waterfall */
600 break;
601 default:
602 LOGP(DDB, LOGL_FATAL,
603 "Invalid database schema revision '%d'.\n", db_rev);
Jan Luebbebfbdeec2012-12-27 00:27:16 +0100604 return -EINVAL;
605 }
606
Jan Luebbebfbdeec2012-12-27 00:27:16 +0100607 return 0;
Vadim Yanitskiya8d8e932016-05-13 15:38:09 +0600608
609error:
610 LOGP(DDB, LOGL_FATAL, "Failed to update database "
611 "from schema revision '%d'.\n", db_rev);
612 return -EINVAL;
Jan Luebbebfbdeec2012-12-27 00:27:16 +0100613}
614
615static int db_configure(void)
616{
617 dbi_result result;
618
619 result = dbi_conn_query(conn,
620 "PRAGMA synchronous = FULL");
621 if (!result)
622 return -EINVAL;
Harald Welted0b7b772009-08-09 19:03:42 +0200623
624 dbi_result_free(result);
625 return 0;
626}
627
Harald Welte0b906d02009-12-24 11:21:42 +0100628int db_init(const char *name)
629{
Pau Espin Pedrolc22e54d2017-12-15 18:28:43 +0100630 dbi_initialize_r(NULL, &inst);
Harald Welte0b906d02009-12-24 11:21:42 +0100631
Vadim Yanitskiybaccf3a2019-04-15 20:25:57 +0700632 LOGP(DDB, LOGL_NOTICE, "Init database connection to '%s' using %s\n",
633 name, dbi_version());
634
Pau Espin Pedrolc22e54d2017-12-15 18:28:43 +0100635 conn = dbi_conn_new_r("sqlite3", inst);
Harald Welte0b906d02009-12-24 11:21:42 +0100636 if (conn == NULL) {
Stefan Sperling832046d2018-01-15 16:12:37 +0100637 LOGP(DDB, LOGL_FATAL, "Failed to create database connection to sqlite3 db '%s'; "
638 "Is the sqlite3 database driver for libdbi installed on this system?\n", name);
Jan Luebbe5c15c852008-12-27 15:59:25 +0000639 return 1;
640 }
Jan Luebbe7398eb92008-12-27 00:45:41 +0000641
Holger Freyther12aa50d2009-01-01 18:02:05 +0000642 dbi_conn_error_handler( conn, db_error_func, NULL );
Jan Luebbe7398eb92008-12-27 00:45:41 +0000643
Jan Luebbe5c15c852008-12-27 15:59:25 +0000644 /* MySQL
645 dbi_conn_set_option(conn, "host", "localhost");
646 dbi_conn_set_option(conn, "username", "your_name");
647 dbi_conn_set_option(conn, "password", "your_password");
648 dbi_conn_set_option(conn, "dbname", "your_dbname");
649 dbi_conn_set_option(conn, "encoding", "UTF-8");
650 */
Jan Luebbe7398eb92008-12-27 00:45:41 +0000651
Jan Luebbe5c15c852008-12-27 15:59:25 +0000652 /* SqLite 3 */
Holger Freyther12aa50d2009-01-01 18:02:05 +0000653 db_basename = strdup(name);
654 db_dirname = strdup(name);
Holger Freytherbde36102008-12-28 22:51:39 +0000655 dbi_conn_set_option(conn, "sqlite3_dbdir", dirname(db_dirname));
656 dbi_conn_set_option(conn, "dbname", basename(db_basename));
Jan Luebbe7398eb92008-12-27 00:45:41 +0000657
Harald Welted0b7b772009-08-09 19:03:42 +0200658 if (dbi_conn_connect(conn) < 0)
659 goto out_err;
660
Jan Luebbe5c15c852008-12-27 15:59:25 +0000661 return 0;
Harald Welted0b7b772009-08-09 19:03:42 +0200662
663out_err:
664 free(db_dirname);
665 free(db_basename);
666 db_dirname = db_basename = NULL;
667 return -1;
Jan Luebbe7398eb92008-12-27 00:45:41 +0000668}
669
Harald Welted0b7b772009-08-09 19:03:42 +0200670
Harald Welted1476bc2011-07-16 13:24:09 +0200671int db_prepare(void)
Harald Welte0b906d02009-12-24 11:21:42 +0100672{
Jan Luebbe5c15c852008-12-27 15:59:25 +0000673 dbi_result result;
Harald Welte7e310b12009-03-30 20:56:32 +0000674 int i;
Holger Freytherb4064bc2009-02-23 00:50:31 +0000675
Harald Welte7e310b12009-03-30 20:56:32 +0000676 for (i = 0; i < ARRAY_SIZE(create_stmts); i++) {
677 result = dbi_conn_query(conn, create_stmts[i]);
Harald Welte0b906d02009-12-24 11:21:42 +0100678 if (!result) {
Harald Welteae1f1592009-12-24 11:39:14 +0100679 LOGP(DDB, LOGL_ERROR,
680 "Failed to create some table.\n");
Harald Welte7e310b12009-03-30 20:56:32 +0000681 return 1;
682 }
683 dbi_result_free(result);
Holger Freytherb4064bc2009-02-23 00:50:31 +0000684 }
Holger Freytherb4064bc2009-02-23 00:50:31 +0000685
Holger Hans Peter Freyther850326e2009-08-10 08:36:04 +0200686 if (check_db_revision() < 0) {
Harald Welteae1f1592009-12-24 11:39:14 +0100687 LOGP(DDB, LOGL_FATAL, "Database schema revision invalid, "
Holger Hans Peter Freyther850326e2009-08-10 08:36:04 +0200688 "please update your database schema\n");
689 return -1;
690 }
691
Jan Luebbebfbdeec2012-12-27 00:27:16 +0100692 db_configure();
693
Jan Luebbe5c15c852008-12-27 15:59:25 +0000694 return 0;
Jan Luebbe7398eb92008-12-27 00:45:41 +0000695}
696
Harald Welted1476bc2011-07-16 13:24:09 +0200697int db_fini(void)
Harald Welte0b906d02009-12-24 11:21:42 +0100698{
Jan Luebbe5c15c852008-12-27 15:59:25 +0000699 dbi_conn_close(conn);
Pau Espin Pedrolc22e54d2017-12-15 18:28:43 +0100700 dbi_shutdown_r(inst);
Holger Freytherbde36102008-12-28 22:51:39 +0000701
Harald Welte2c5f4c62011-07-16 13:22:57 +0200702 free(db_dirname);
703 free(db_basename);
Jan Luebbe5c15c852008-12-27 15:59:25 +0000704 return 0;
Jan Luebbe7398eb92008-12-27 00:45:41 +0000705}
706
Harald Welte7e310b12009-03-30 20:56:32 +0000707/* store an [unsent] SMS to the database */
708int db_sms_store(struct gsm_sms *sms)
709{
710 dbi_result result;
Holger Hans Peter Freytherca3c2562013-10-08 03:17:30 +0200711 char *q_text, *q_daddr, *q_saddr;
Vadim Yanitskiy9d61db72019-04-14 13:32:33 +0700712 unsigned char *q_udata = NULL;
Stefan Sperlingd4941622018-01-18 17:36:28 +0100713 time_t now, validity_timestamp;
Harald Welte7e310b12009-03-30 20:56:32 +0000714
715 dbi_conn_quote_string_copy(conn, (char *)sms->text, &q_text);
Harald Weltec0de14d2012-11-23 23:35:01 +0100716 dbi_conn_quote_string_copy(conn, (char *)sms->dst.addr, &q_daddr);
Holger Hans Peter Freytherca3c2562013-10-08 03:17:30 +0200717 dbi_conn_quote_string_copy(conn, (char *)sms->src.addr, &q_saddr);
Vadim Yanitskiy9d61db72019-04-14 13:32:33 +0700718
719 /* Guard against zero-length input, as this may cause
720 * buffer overruns in libdbi / libdbdsqlite3. */
721 if (sms->user_data_len > 0) {
722 dbi_conn_quote_binary_copy(conn, sms->user_data,
723 sms->user_data_len,
724 &q_udata);
725 }
Holger Hans Peter Freytherca3c2562013-10-08 03:17:30 +0200726
Stefan Sperlingd4941622018-01-18 17:36:28 +0100727 now = time(NULL);
728 validity_timestamp = now + sms->validity_minutes * 60;
729
Harald Welte7e310b12009-03-30 20:56:32 +0000730 result = dbi_conn_queryf(conn,
731 "INSERT INTO SMS "
Alexander Chemerisca7ed2d2013-10-08 03:17:32 +0200732 "(created, valid_until, "
Pablo Neira Ayuso9891dae2017-08-07 14:01:33 +0100733 "reply_path_req, status_rep_req, is_report, "
734 "msg_ref, protocol_id, data_coding_scheme, "
735 "ud_hdr_ind, "
Holger Hans Peter Freytherca3c2562013-10-08 03:17:30 +0200736 "user_data, text, "
737 "dest_addr, dest_ton, dest_npi, "
738 "src_addr, src_ton, src_npi) VALUES "
Stefan Sperling3b26f342018-01-22 18:27:25 +0100739 "(datetime('%lld', 'unixepoch'), datetime('%lld', 'unixepoch'), "
Holger Hans Peter Freytherca3c2562013-10-08 03:17:30 +0200740 "%u, %u, %u, "
Pablo Neira Ayuso9891dae2017-08-07 14:01:33 +0100741 "%u, %u, %u, "
742 "%u, "
Holger Hans Peter Freytherca3c2562013-10-08 03:17:30 +0200743 "%s, %s, "
744 "%s, %u, %u, "
745 "%s, %u, %u)",
Stefan Sperlingd4941622018-01-18 17:36:28 +0100746 (int64_t)now, (int64_t)validity_timestamp,
Pablo Neira Ayuso9891dae2017-08-07 14:01:33 +0100747 sms->reply_path_req, sms->status_rep_req, sms->is_report,
748 sms->msg_ref, sms->protocol_id, sms->data_coding_scheme,
749 sms->ud_hdr_ind,
Holger Hans Peter Freytherca3c2562013-10-08 03:17:30 +0200750 q_udata, q_text,
751 q_daddr, sms->dst.ton, sms->dst.npi,
752 q_saddr, sms->src.ton, sms->src.npi);
Harald Welte7e310b12009-03-30 20:56:32 +0000753 free(q_text);
Harald Welte76042182009-08-08 16:03:15 +0200754 free(q_udata);
Holger Hans Peter Freytherca3c2562013-10-08 03:17:30 +0200755 free(q_daddr);
756 free(q_saddr);
Harald Welte7e310b12009-03-30 20:56:32 +0000757
758 if (!result)
759 return -EIO;
760
761 dbi_result_free(result);
762 return 0;
763}
764
Harald Welte2ebabca2009-08-09 19:05:21 +0200765static struct gsm_sms *sms_from_result(struct gsm_network *net, dbi_result result)
Harald Welte7e310b12009-03-30 20:56:32 +0000766{
Harald Welte76042182009-08-08 16:03:15 +0200767 struct gsm_sms *sms = sms_alloc();
Vadim Yanitskiyea24bb52019-04-02 17:52:08 +0700768 const char *daddr, *saddr;
Stefan Sperlingd4941622018-01-18 17:36:28 +0100769 time_t validity_timestamp;
Harald Welte7e310b12009-03-30 20:56:32 +0000770
Harald Welte76042182009-08-08 16:03:15 +0200771 if (!sms)
Harald Welte7e310b12009-03-30 20:56:32 +0000772 return NULL;
Harald Welte7e310b12009-03-30 20:56:32 +0000773
Harald Weltebe3e3782009-07-05 14:06:41 +0200774 sms->id = dbi_result_get_ulonglong(result, "id");
Harald Welte7e310b12009-03-30 20:56:32 +0000775
Keithc601adc2017-08-16 22:45:07 +0200776 sms->created = dbi_result_get_datetime(result, "created");
Stefan Sperlingd4941622018-01-18 17:36:28 +0100777 validity_timestamp = dbi_result_get_datetime(result, "valid_until");
778 sms->validity_minutes = (validity_timestamp - sms->created) / 60;
779 /* FIXME: those should all be get_uchar, but sqlite3 is braindead */
Holger Hans Peter Freytherb115cb62014-07-03 14:00:30 +0200780 sms->reply_path_req = dbi_result_get_ulonglong(result, "reply_path_req");
781 sms->status_rep_req = dbi_result_get_ulonglong(result, "status_rep_req");
Pablo Neira Ayuso9891dae2017-08-07 14:01:33 +0100782 sms->is_report = dbi_result_get_ulonglong(result, "is_report");
783 sms->msg_ref = dbi_result_get_ulonglong(result, "msg_ref");
Holger Hans Peter Freytherb115cb62014-07-03 14:00:30 +0200784 sms->ud_hdr_ind = dbi_result_get_ulonglong(result, "ud_hdr_ind");
785 sms->protocol_id = dbi_result_get_ulonglong(result, "protocol_id");
786 sms->data_coding_scheme = dbi_result_get_ulonglong(result,
Harald Weltef3efc592009-07-27 20:11:35 +0200787 "data_coding_scheme");
788
Holger Hans Peter Freytherb115cb62014-07-03 14:00:30 +0200789 sms->dst.npi = dbi_result_get_ulonglong(result, "dest_npi");
790 sms->dst.ton = dbi_result_get_ulonglong(result, "dest_ton");
Harald Welte76042182009-08-08 16:03:15 +0200791 daddr = dbi_result_get_string(result, "dest_addr");
Neels Hofmeyr93bafb62017-01-13 03:12:08 +0100792 if (daddr)
Max98f74672018-02-05 12:57:06 +0100793 OSMO_STRLCPY_ARRAY(sms->dst.addr, daddr);
Vadim Yanitskiye1e72472019-04-09 16:55:44 +0700794
795 if (net != NULL) /* db_sms_test passes NULL, so we need to be tolerant */
796 sms->receiver = vlr_subscr_find_by_msisdn(net->vlr, sms->dst.addr,
797 VSUB_USE_SMS_RECEIVER);
Harald Welte76042182009-08-08 16:03:15 +0200798
Holger Hans Peter Freytherb115cb62014-07-03 14:00:30 +0200799 sms->src.npi = dbi_result_get_ulonglong(result, "src_npi");
800 sms->src.ton = dbi_result_get_ulonglong(result, "src_ton");
Holger Hans Peter Freytherca3c2562013-10-08 03:17:30 +0200801 saddr = dbi_result_get_string(result, "src_addr");
Neels Hofmeyr93bafb62017-01-13 03:12:08 +0100802 if (saddr)
Max98f74672018-02-05 12:57:06 +0100803 OSMO_STRLCPY_ARRAY(sms->src.addr, saddr);
Holger Hans Peter Freytherca3c2562013-10-08 03:17:30 +0200804
Vadim Yanitskiyea24bb52019-04-02 17:52:08 +0700805 /* Parse TP-UD, TP-UDL and decoded text */
806 parse_tp_ud_from_result(sms, result);
Harald Weltebe3e3782009-07-05 14:06:41 +0200807
Harald Welte2ebabca2009-08-09 19:05:21 +0200808 return sms;
809}
810
Holger Hans Peter Freyther812dad02010-12-24 23:18:31 +0100811struct gsm_sms *db_sms_get(struct gsm_network *net, unsigned long long id)
812{
813 dbi_result result;
814 struct gsm_sms *sms;
815
816 result = dbi_conn_queryf(conn,
817 "SELECT * FROM SMS WHERE SMS.id = %llu", id);
818 if (!result)
819 return NULL;
820
Neels Hofmeyrdb827292017-12-10 14:45:15 +0100821 if (!next_row(result)) {
Holger Hans Peter Freyther812dad02010-12-24 23:18:31 +0100822 dbi_result_free(result);
823 return NULL;
824 }
825
826 sms = sms_from_result(net, result);
827
828 dbi_result_free(result);
829
830 return sms;
831}
832
Harald Welte2483f1b2016-06-19 18:06:02 +0200833struct gsm_sms *db_sms_get_next_unsent(struct gsm_network *net,
834 unsigned long long min_sms_id,
835 unsigned int max_failed)
Harald Welte2ebabca2009-08-09 19:05:21 +0200836{
837 dbi_result result;
838 struct gsm_sms *sms;
839
840 result = dbi_conn_queryf(conn,
Harald Welte2483f1b2016-06-19 18:06:02 +0200841 "SELECT * FROM SMS"
842 " WHERE sent IS NULL"
843 " AND id >= %llu"
844 " AND deliver_attempts <= %u"
845 " ORDER BY id LIMIT 1",
846 min_sms_id, max_failed);
Harald Welte2ebabca2009-08-09 19:05:21 +0200847
Sylvain Munautff1f19e2009-12-22 13:22:29 +0100848 if (!result)
849 return NULL;
850
Neels Hofmeyrdb827292017-12-10 14:45:15 +0100851 if (!next_row(result)) {
Sylvain Munautff1f19e2009-12-22 13:22:29 +0100852 dbi_result_free(result);
853 return NULL;
854 }
855
856 sms = sms_from_result(net, result);
857
858 dbi_result_free(result);
859
860 return sms;
861}
862
Sylvain Munautd5778fc2009-12-21 01:09:57 +0100863/* retrieve the next unsent SMS for a given subscriber */
Harald Welte2483f1b2016-06-19 18:06:02 +0200864struct gsm_sms *db_sms_get_unsent_for_subscr(struct vlr_subscr *vsub,
865 unsigned int max_failed)
Harald Welte2ebabca2009-08-09 19:05:21 +0200866{
Harald Welte2483f1b2016-06-19 18:06:02 +0200867 struct gsm_network *net = vsub->vlr->user_ctx;
Harald Welte2ebabca2009-08-09 19:05:21 +0200868 dbi_result result;
869 struct gsm_sms *sms;
Neels Hofmeyr50d09002017-12-05 12:39:00 +0100870 char *q_msisdn;
Harald Welte2ebabca2009-08-09 19:05:21 +0200871
Harald Welte2483f1b2016-06-19 18:06:02 +0200872 if (!vsub->lu_complete)
873 return NULL;
874
Neels Hofmeyrf6704f12017-12-05 12:34:44 +0100875 /* A subscriber having no phone number cannot possibly receive SMS. */
876 if (*vsub->msisdn == '\0')
877 return NULL;
878
Neels Hofmeyr50d09002017-12-05 12:39:00 +0100879 dbi_conn_quote_string_copy(conn, vsub->msisdn, &q_msisdn);
Harald Welte2ebabca2009-08-09 19:05:21 +0200880 result = dbi_conn_queryf(conn,
Harald Welte2483f1b2016-06-19 18:06:02 +0200881 "SELECT * FROM SMS"
882 " WHERE sent IS NULL"
Neels Hofmeyr50d09002017-12-05 12:39:00 +0100883 " AND dest_addr = %s"
Harald Welte2483f1b2016-06-19 18:06:02 +0200884 " AND deliver_attempts <= %u"
885 " ORDER BY id LIMIT 1",
Neels Hofmeyr50d09002017-12-05 12:39:00 +0100886 q_msisdn, max_failed);
887 free(q_msisdn);
888
Harald Welte2ebabca2009-08-09 19:05:21 +0200889 if (!result)
890 return NULL;
891
Neels Hofmeyrdb827292017-12-10 14:45:15 +0100892 if (!next_row(result)) {
Harald Welte2ebabca2009-08-09 19:05:21 +0200893 dbi_result_free(result);
894 return NULL;
895 }
896
Harald Welte2483f1b2016-06-19 18:06:02 +0200897 sms = sms_from_result(net, result);
898
899 dbi_result_free(result);
900
901 return sms;
902}
903
904struct gsm_sms *db_sms_get_next_unsent_rr_msisdn(struct gsm_network *net,
905 const char *last_msisdn,
906 unsigned int max_failed)
907{
908 dbi_result result;
909 struct gsm_sms *sms;
Neels Hofmeyr50d09002017-12-05 12:39:00 +0100910 char *q_last_msisdn;
Harald Welte2483f1b2016-06-19 18:06:02 +0200911
Neels Hofmeyr50d09002017-12-05 12:39:00 +0100912 dbi_conn_quote_string_copy(conn, last_msisdn, &q_last_msisdn);
Harald Welte2483f1b2016-06-19 18:06:02 +0200913 result = dbi_conn_queryf(conn,
914 "SELECT * FROM SMS"
915 " WHERE sent IS NULL"
Neels Hofmeyr50d09002017-12-05 12:39:00 +0100916 " AND dest_addr > %s"
Harald Welte2483f1b2016-06-19 18:06:02 +0200917 " AND deliver_attempts <= %u"
918 " ORDER BY dest_addr, id LIMIT 1",
Neels Hofmeyr50d09002017-12-05 12:39:00 +0100919 q_last_msisdn, max_failed);
920 free(q_last_msisdn);
921
Harald Welte2483f1b2016-06-19 18:06:02 +0200922 if (!result)
923 return NULL;
924
Neels Hofmeyrdb827292017-12-10 14:45:15 +0100925 if (!next_row(result)) {
Harald Welte2483f1b2016-06-19 18:06:02 +0200926 dbi_result_free(result);
927 return NULL;
928 }
929
930 sms = sms_from_result(net, result);
Harald Welte2ebabca2009-08-09 19:05:21 +0200931
932 dbi_result_free(result);
933
Harald Welte7e310b12009-03-30 20:56:32 +0000934 return sms;
935}
936
Alexander Chemeris1e77e3d2014-03-08 21:27:37 +0100937/* mark a given SMS as delivered */
938int db_sms_mark_delivered(struct gsm_sms *sms)
Harald Welte7e310b12009-03-30 20:56:32 +0000939{
940 dbi_result result;
941
942 result = dbi_conn_queryf(conn,
943 "UPDATE SMS "
944 "SET sent = datetime('now') "
945 "WHERE id = %llu", sms->id);
946 if (!result) {
Harald Welteae1f1592009-12-24 11:39:14 +0100947 LOGP(DDB, LOGL_ERROR, "Failed to mark SMS %llu as sent.\n", sms->id);
Harald Welte7e310b12009-03-30 20:56:32 +0000948 return 1;
949 }
950
951 dbi_result_free(result);
952 return 0;
953}
Harald Welte (local)db552c52009-08-15 20:15:14 +0200954
955/* increase the number of attempted deliveries */
956int db_sms_inc_deliver_attempts(struct gsm_sms *sms)
957{
958 dbi_result result;
959
960 result = dbi_conn_queryf(conn,
961 "UPDATE SMS "
962 "SET deliver_attempts = deliver_attempts + 1 "
963 "WHERE id = %llu", sms->id);
964 if (!result) {
Harald Welteae1f1592009-12-24 11:39:14 +0100965 LOGP(DDB, LOGL_ERROR, "Failed to inc deliver attempts for "
966 "SMS %llu.\n", sms->id);
Harald Welte (local)db552c52009-08-15 20:15:14 +0200967 return 1;
968 }
969
970 dbi_result_free(result);
971 return 0;
972}
Harald Welte (local)026531e2009-08-16 10:40:10 +0200973
Harald Welte2483f1b2016-06-19 18:06:02 +0200974/* Drop all pending SMS to or from the given extension */
975int db_sms_delete_by_msisdn(const char *msisdn)
Harald Welte (local)026531e2009-08-16 10:40:10 +0200976{
977 dbi_result result;
Neels Hofmeyr50d09002017-12-05 12:39:00 +0100978 char *q_msisdn;
Harald Welte2483f1b2016-06-19 18:06:02 +0200979 if (!msisdn || !*msisdn)
980 return 0;
Neels Hofmeyr50d09002017-12-05 12:39:00 +0100981
982 dbi_conn_quote_string_copy(conn, msisdn, &q_msisdn);
Harald Welte (local)026531e2009-08-16 10:40:10 +0200983 result = dbi_conn_queryf(conn,
Harald Welte2483f1b2016-06-19 18:06:02 +0200984 "DELETE FROM SMS WHERE src_addr=%s OR dest_addr=%s",
Neels Hofmeyr50d09002017-12-05 12:39:00 +0100985 q_msisdn, q_msisdn);
986 free(q_msisdn);
987
Harald Welte2483f1b2016-06-19 18:06:02 +0200988 if (!result) {
989 LOGP(DDB, LOGL_ERROR,
990 "Failed to delete SMS for %s\n", msisdn);
991 return -1;
992 }
Harald Welte (local)026531e2009-08-16 10:40:10 +0200993 dbi_result_free(result);
994 return 0;
995}
Harald Welteffa55a42009-12-22 19:07:32 +0100996
Stefan Sperling6ba2d5a2018-01-18 18:55:26 +0100997int db_sms_delete_sent_message_by_id(unsigned long long sms_id)
998{
999 dbi_result result;
1000
1001 result = dbi_conn_queryf(conn,
1002 "DELETE FROM SMS WHERE id = %llu AND sent is NOT NULL",
1003 sms_id);
1004 if (!result) {
1005 LOGP(DDB, LOGL_ERROR, "Failed to delete SMS %llu.\n", sms_id);
1006 return 1;
1007 }
1008
1009 dbi_result_free(result);
1010 return 0;
1011}
1012
Stefan Sperling14e05172018-01-22 17:31:20 +01001013
1014static int delete_expired_sms(unsigned long long sms_id, time_t created, time_t validity_timestamp)
Stefan Sperling87cba1f2018-01-22 17:05:37 +01001015{
1016 dbi_result result;
Stefan Sperling14e05172018-01-22 17:31:20 +01001017 time_t now, min_created;
Stefan Sperling87cba1f2018-01-22 17:05:37 +01001018
1019 now = time(NULL);
1020 if (validity_timestamp > now)
1021 return -1;
1022
1023 /* Our SMS expiry threshold is hard-coded to roughly 2 weeks at the moment. */
1024 min_created = now - (time_t)(60 * 60 * 24 * 7 * 2);
1025 if (min_created < 0) /* bogus system clock? */
1026 return -1;
1027 if (created >= min_created) /* not yet expired */
1028 return -1;
1029
1030 result = dbi_conn_queryf(conn, "DELETE FROM SMS WHERE id = %llu", sms_id);
1031 if (!result) {
1032 LOGP(DDB, LOGL_ERROR, "Failed to delete SMS %llu.\n", sms_id);
1033 return -1;
1034 }
1035 dbi_result_free(result);
1036 return 0;
1037}
1038
Stefan Sperling14e05172018-01-22 17:31:20 +01001039int db_sms_delete_expired_message_by_id(unsigned long long sms_id)
1040{
1041 dbi_result result;
1042 time_t created, validity_timestamp;
1043
1044 result = dbi_conn_queryf(conn, "SELECT created,valid_until FROM SMS WHERE id = %llu", sms_id);
1045 if (!result)
1046 return -1;
1047 if (!next_row(result)) {
1048 dbi_result_free(result);
1049 return -1;
1050 }
1051
1052 created = dbi_result_get_datetime(result, "created");
1053 validity_timestamp = dbi_result_get_datetime(result, "valid_until");
1054
1055 dbi_result_free(result);
1056 return delete_expired_sms(sms_id, created, validity_timestamp);
1057}
1058
1059void db_sms_delete_oldest_expired_message(void)
1060{
1061 dbi_result result;
1062
1063 result = dbi_conn_queryf(conn, "SELECT id,created,valid_until FROM SMS ORDER BY created LIMIT 1");
1064 if (!result)
1065 return;
1066
1067 if (next_row(result)) {
1068 unsigned long long sms_id;
1069 time_t created, validity_timestamp;
1070
1071 sms_id = dbi_result_get_ulonglong(result, "id");
1072 created = dbi_result_get_datetime(result, "created");
1073 validity_timestamp = dbi_result_get_datetime(result, "valid_until");
1074 delete_expired_sms(sms_id, created, validity_timestamp);
1075 }
1076
1077 dbi_result_free(result);
1078}