blob: e80ef530afcc1f50b2225e3c4237a9e337ba0bd7 [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>
Jan Luebbe7398eb92008-12-27 00:45:41 +000030#include <dbi/dbi.h>
31
Neels Hofmeyr90843962017-09-04 15:04:35 +020032#include <osmocom/msc/gsm_data.h>
33#include <osmocom/msc/gsm_subscriber.h>
34#include <osmocom/msc/gsm_04_11.h>
35#include <osmocom/msc/db.h>
36#include <osmocom/msc/debug.h>
37#include <osmocom/msc/vlr.h>
Holger Hans Peter Freytherc5faf662010-12-22 18:16:01 +010038
Harald Welted3fa84d2016-04-20 17:50:17 +020039#include <osmocom/gsm/protocol/gsm_23_003.h>
Pablo Neira Ayuso136f4532011-03-22 16:47:59 +010040#include <osmocom/core/talloc.h>
41#include <osmocom/core/statistics.h>
42#include <osmocom/core/rate_ctr.h>
Neels Hofmeyr93bafb62017-01-13 03:12:08 +010043#include <osmocom/core/utils.h>
Harald Weltef2b4cd72010-05-13 11:45:07 +020044
Holger Freytherbde36102008-12-28 22:51:39 +000045static char *db_basename = NULL;
46static char *db_dirname = NULL;
Holger Freyther1d506c82009-04-19 06:35:20 +000047static dbi_conn conn;
Pau Espin Pedrolc22e54d2017-12-15 18:28:43 +010048static dbi_inst inst;
Jan Luebbe7398eb92008-12-27 00:45:41 +000049
Pablo Neira Ayuso9891dae2017-08-07 14:01:33 +010050#define SCHEMA_REVISION "5"
Jan Luebbebfbdeec2012-12-27 00:27:16 +010051
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +010052enum {
53 SCHEMA_META,
54 INSERT_META,
55 SCHEMA_SUBSCRIBER,
56 SCHEMA_AUTH,
57 SCHEMA_EQUIPMENT,
58 SCHEMA_EQUIPMENT_WATCH,
59 SCHEMA_SMS,
60 SCHEMA_VLR,
61 SCHEMA_APDU,
62 SCHEMA_COUNTERS,
63 SCHEMA_RATE,
64 SCHEMA_AUTHKEY,
65 SCHEMA_AUTHLAST,
66};
67
68static const char *create_stmts[] = {
69 [SCHEMA_META] = "CREATE TABLE IF NOT EXISTS Meta ("
Harald Welte7e310b12009-03-30 20:56:32 +000070 "id INTEGER PRIMARY KEY AUTOINCREMENT, "
71 "key TEXT UNIQUE NOT NULL, "
72 "value TEXT NOT NULL"
73 ")",
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +010074 [INSERT_META] = "INSERT OR IGNORE INTO Meta "
Harald Welte7e310b12009-03-30 20:56:32 +000075 "(key, value) "
76 "VALUES "
Jan Luebbebfbdeec2012-12-27 00:27:16 +010077 "('revision', " SCHEMA_REVISION ")",
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +010078 [SCHEMA_SUBSCRIBER] = "CREATE TABLE IF NOT EXISTS Subscriber ("
Harald Welte7e310b12009-03-30 20:56:32 +000079 "id INTEGER PRIMARY KEY AUTOINCREMENT, "
80 "created TIMESTAMP NOT NULL, "
81 "updated TIMESTAMP NOT NULL, "
82 "imsi NUMERIC UNIQUE NOT NULL, "
83 "name TEXT, "
84 "extension TEXT UNIQUE, "
85 "authorized INTEGER NOT NULL DEFAULT 0, "
86 "tmsi TEXT UNIQUE, "
Jan Luebbebfbdeec2012-12-27 00:27:16 +010087 "lac INTEGER NOT NULL DEFAULT 0, "
88 "expire_lu TIMESTAMP DEFAULT NULL"
Harald Welte7e310b12009-03-30 20:56:32 +000089 ")",
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +010090 [SCHEMA_AUTH] = "CREATE TABLE IF NOT EXISTS AuthToken ("
Jan Luebbe31bef492009-08-12 14:31:14 +020091 "id INTEGER PRIMARY KEY AUTOINCREMENT, "
92 "subscriber_id INTEGER UNIQUE NOT NULL, "
93 "created TIMESTAMP NOT NULL, "
94 "token TEXT UNIQUE NOT NULL"
95 ")",
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +010096 [SCHEMA_EQUIPMENT] = "CREATE TABLE IF NOT EXISTS Equipment ("
Harald Welte7e310b12009-03-30 20:56:32 +000097 "id INTEGER PRIMARY KEY AUTOINCREMENT, "
98 "created TIMESTAMP NOT NULL, "
99 "updated TIMESTAMP NOT NULL, "
100 "name TEXT, "
Harald Weltec2e302d2009-07-05 14:08:13 +0200101 "classmark1 NUMERIC, "
102 "classmark2 BLOB, "
103 "classmark3 BLOB, "
Harald Welte7e310b12009-03-30 20:56:32 +0000104 "imei NUMERIC UNIQUE NOT NULL"
105 ")",
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100106 [SCHEMA_EQUIPMENT_WATCH] = "CREATE TABLE IF NOT EXISTS EquipmentWatch ("
Harald Welte7e310b12009-03-30 20:56:32 +0000107 "id INTEGER PRIMARY KEY AUTOINCREMENT, "
108 "created TIMESTAMP NOT NULL, "
109 "updated TIMESTAMP NOT NULL, "
110 "subscriber_id NUMERIC NOT NULL, "
111 "equipment_id NUMERIC NOT NULL, "
112 "UNIQUE (subscriber_id, equipment_id) "
113 ")",
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100114 [SCHEMA_SMS] = "CREATE TABLE IF NOT EXISTS SMS ("
Harald Welte76042182009-08-08 16:03:15 +0200115 /* metadata, not part of sms */
Harald Welte7e310b12009-03-30 20:56:32 +0000116 "id INTEGER PRIMARY KEY AUTOINCREMENT, "
117 "created TIMESTAMP NOT NULL, "
118 "sent TIMESTAMP, "
Harald Welte (local)db552c52009-08-15 20:15:14 +0200119 "deliver_attempts INTEGER NOT NULL DEFAULT 0, "
Harald Welte76042182009-08-08 16:03:15 +0200120 /* data directly copied/derived from SMS */
Harald Weltef3efc592009-07-27 20:11:35 +0200121 "valid_until TIMESTAMP, "
Harald Welte76042182009-08-08 16:03:15 +0200122 "reply_path_req INTEGER NOT NULL, "
123 "status_rep_req INTEGER NOT NULL, "
Pablo Neira Ayuso9891dae2017-08-07 14:01:33 +0100124 "is_report INTEGER NOT NULL, "
125 "msg_ref INTEGER NOT NULL, "
Harald Welte76042182009-08-08 16:03:15 +0200126 "protocol_id INTEGER NOT NULL, "
127 "data_coding_scheme INTEGER NOT NULL, "
Harald Welted0b7b772009-08-09 19:03:42 +0200128 "ud_hdr_ind INTEGER NOT NULL, "
Holger Hans Peter Freytherca3c2562013-10-08 03:17:30 +0200129 "src_addr TEXT NOT NULL, "
130 "src_ton INTEGER NOT NULL, "
131 "src_npi INTEGER NOT NULL, "
132 "dest_addr TEXT NOT NULL, "
133 "dest_ton INTEGER NOT NULL, "
134 "dest_npi INTEGER NOT NULL, "
Harald Welte76042182009-08-08 16:03:15 +0200135 "user_data BLOB, " /* TP-UD */
136 /* additional data, interpreted from SMS */
137 "header BLOB, " /* UD Header */
138 "text TEXT " /* decoded UD after UDH */
Harald Welte7e310b12009-03-30 20:56:32 +0000139 ")",
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100140 [SCHEMA_VLR] = "CREATE TABLE IF NOT EXISTS VLR ("
Holger Freytherc2995ea2009-04-19 06:35:23 +0000141 "id INTEGER PRIMARY KEY AUTOINCREMENT, "
142 "created TIMESTAMP NOT NULL, "
143 "updated TIMESTAMP NOT NULL, "
144 "subscriber_id NUMERIC UNIQUE NOT NULL, "
145 "last_bts NUMERIC NOT NULL "
146 ")",
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100147 [SCHEMA_APDU] = "CREATE TABLE IF NOT EXISTS ApduBlobs ("
Harald Welte (local)026531e2009-08-16 10:40:10 +0200148 "id INTEGER PRIMARY KEY AUTOINCREMENT, "
149 "created TIMESTAMP NOT NULL, "
150 "apdu_id_flags INTEGER NOT NULL, "
151 "subscriber_id INTEGER NOT NULL, "
152 "apdu BLOB "
153 ")",
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100154 [SCHEMA_COUNTERS] = "CREATE TABLE IF NOT EXISTS Counters ("
Harald Welteffa55a42009-12-22 19:07:32 +0100155 "id INTEGER PRIMARY KEY AUTOINCREMENT, "
156 "timestamp TIMESTAMP NOT NULL, "
Harald Weltef9a43c42009-12-22 21:40:42 +0100157 "value INTEGER NOT NULL, "
158 "name TEXT NOT NULL "
Harald Welte09f7ad02009-12-24 09:42:07 +0100159 ")",
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100160 [SCHEMA_RATE] = "CREATE TABLE IF NOT EXISTS RateCounters ("
Harald Weltec1919862010-05-13 12:55:20 +0200161 "id INTEGER PRIMARY KEY AUTOINCREMENT, "
162 "timestamp TIMESTAMP NOT NULL, "
163 "value INTEGER NOT NULL, "
164 "name TEXT NOT NULL, "
Harald Welted94d6a02010-05-14 17:38:47 +0200165 "idx INTEGER NOT NULL "
Harald Weltec1919862010-05-13 12:55:20 +0200166 ")",
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100167 [SCHEMA_AUTHKEY] = "CREATE TABLE IF NOT EXISTS AuthKeys ("
Sylvain Munaut10bf8122010-06-09 11:31:32 +0200168 "subscriber_id INTEGER PRIMARY KEY, "
Sylvain Munaut77d334a2009-12-27 19:26:12 +0100169 "algorithm_id INTEGER NOT NULL, "
Harald Welte3606cc52009-12-05 15:13:22 +0530170 "a3a8_ki BLOB "
171 ")",
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100172 [SCHEMA_AUTHLAST] = "CREATE TABLE IF NOT EXISTS AuthLastTuples ("
Sylvain Munaut10bf8122010-06-09 11:31:32 +0200173 "subscriber_id INTEGER PRIMARY KEY, "
Sylvain Munaut70881b72009-12-27 15:41:59 +0100174 "issued TIMESTAMP NOT NULL, "
175 "use_count INTEGER NOT NULL DEFAULT 0, "
176 "key_seq INTEGER NOT NULL, "
177 "rand BLOB NOT NULL, "
178 "sres BLOB NOT NULL, "
179 "kc BLOB NOT NULL "
Harald Welteffa55a42009-12-22 19:07:32 +0100180 ")",
Harald Welte7e310b12009-03-30 20:56:32 +0000181};
182
Neels Hofmeyrdb827292017-12-10 14:45:15 +0100183static inline int next_row(dbi_result result)
184{
185 if (!dbi_result_has_next_row(result))
186 return 0;
187 return dbi_result_next_row(result);
188}
189
Harald Welte0b906d02009-12-24 11:21:42 +0100190void db_error_func(dbi_conn conn, void *data)
191{
192 const char *msg;
Jan Luebbe5c15c852008-12-27 15:59:25 +0000193 dbi_conn_error(conn, &msg);
Harald Welteae1f1592009-12-24 11:39:14 +0100194 LOGP(DDB, LOGL_ERROR, "DBI: %s\n", msg);
Harald Weltec7548a12014-07-10 20:18:15 +0200195 osmo_log_backtrace(DDB, LOGL_ERROR);
Jan Luebbe7398eb92008-12-27 00:45:41 +0000196}
197
Jan Luebbebfbdeec2012-12-27 00:27:16 +0100198static int update_db_revision_2(void)
199{
200 dbi_result result;
201
202 result = dbi_conn_query(conn,
203 "ALTER TABLE Subscriber "
204 "ADD COLUMN expire_lu "
205 "TIMESTAMP DEFAULT NULL");
206 if (!result) {
207 LOGP(DDB, LOGL_ERROR,
Alexander Chemeris7e20f642014-03-07 16:59:53 +0100208 "Failed to alter table Subscriber (upgrade from rev 2).\n");
Jan Luebbebfbdeec2012-12-27 00:27:16 +0100209 return -EINVAL;
210 }
211 dbi_result_free(result);
212
213 result = dbi_conn_query(conn,
214 "UPDATE Meta "
215 "SET value = '3' "
216 "WHERE key = 'revision'");
217 if (!result) {
218 LOGP(DDB, LOGL_ERROR,
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100219 "Failed to update DB schema revision (upgrade from rev 2).\n");
Jan Luebbebfbdeec2012-12-27 00:27:16 +0100220 return -EINVAL;
221 }
222 dbi_result_free(result);
223
224 return 0;
225}
226
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100227/**
228 * Copied from the normal sms_from_result_v3 to avoid having
229 * to make sure that the real routine will remain backward
230 * compatible.
231 */
232static struct gsm_sms *sms_from_result_v3(dbi_result result)
233{
234 struct gsm_sms *sms = sms_alloc();
235 long long unsigned int sender_id;
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100236 const char *text, *daddr;
237 const unsigned char *user_data;
238 char buf[32];
Harald Welte2483f1b2016-06-19 18:06:02 +0200239 char *quoted;
240 dbi_result result2;
241 const char *extension;
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100242
243 if (!sms)
244 return NULL;
245
246 sms->id = dbi_result_get_ulonglong(result, "id");
247
Harald Welte2483f1b2016-06-19 18:06:02 +0200248 /* find extension by id, assuming that the subscriber still exists in
249 * the db */
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100250 sender_id = dbi_result_get_ulonglong(result, "sender_id");
251 snprintf(buf, sizeof(buf), "%llu", sender_id);
Harald Welte2483f1b2016-06-19 18:06:02 +0200252
253 dbi_conn_quote_string_copy(conn, buf, &quoted);
254 result2 = dbi_conn_queryf(conn,
255 "SELECT extension FROM Subscriber "
256 "WHERE id = %s ", quoted);
257 free(quoted);
258 extension = dbi_result_get_string(result2, "extension");
259 if (extension)
260 osmo_strlcpy(sms->src.addr, extension, sizeof(sms->src.addr));
261 dbi_result_free(result2);
262 /* got the extension */
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100263
Holger Hans Peter Freytherb115cb62014-07-03 14:00:30 +0200264 sms->reply_path_req = dbi_result_get_ulonglong(result, "reply_path_req");
265 sms->status_rep_req = dbi_result_get_ulonglong(result, "status_rep_req");
266 sms->ud_hdr_ind = dbi_result_get_ulonglong(result, "ud_hdr_ind");
267 sms->protocol_id = dbi_result_get_ulonglong(result, "protocol_id");
268 sms->data_coding_scheme = dbi_result_get_ulonglong(result,
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100269 "data_coding_scheme");
270
271 daddr = dbi_result_get_string(result, "dest_addr");
Neels Hofmeyr93bafb62017-01-13 03:12:08 +0100272 if (daddr)
273 osmo_strlcpy(sms->dst.addr, daddr, sizeof(sms->dst.addr));
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100274
275 sms->user_data_len = dbi_result_get_field_length(result, "user_data");
276 user_data = dbi_result_get_binary(result, "user_data");
277 if (sms->user_data_len > sizeof(sms->user_data))
278 sms->user_data_len = (uint8_t) sizeof(sms->user_data);
279 memcpy(sms->user_data, user_data, sms->user_data_len);
280
281 text = dbi_result_get_string(result, "text");
Neels Hofmeyr93bafb62017-01-13 03:12:08 +0100282 if (text)
283 osmo_strlcpy(sms->text, text, sizeof(sms->text));
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100284 return sms;
285}
286
287static int update_db_revision_3(void)
288{
289 dbi_result result;
290 struct gsm_sms *sms;
291
Holger Hans Peter Freyther61144012014-03-08 16:41:37 +0100292 LOGP(DDB, LOGL_NOTICE, "Going to migrate from revision 3\n");
293
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100294 result = dbi_conn_query(conn, "BEGIN EXCLUSIVE TRANSACTION");
295 if (!result) {
296 LOGP(DDB, LOGL_ERROR,
297 "Failed to begin transaction (upgrade from rev 3)\n");
298 return -EINVAL;
299 }
300 dbi_result_free(result);
301
302 /* Rename old SMS table to be able create a new one */
303 result = dbi_conn_query(conn, "ALTER TABLE SMS RENAME TO SMS_3");
304 if (!result) {
305 LOGP(DDB, LOGL_ERROR,
306 "Failed to rename the old SMS table (upgrade from rev 3).\n");
307 goto rollback;
308 }
309 dbi_result_free(result);
310
311 /* Create new SMS table with all the bells and whistles! */
312 result = dbi_conn_query(conn, create_stmts[SCHEMA_SMS]);
313 if (!result) {
314 LOGP(DDB, LOGL_ERROR,
315 "Failed to create a new SMS table (upgrade from rev 3).\n");
316 goto rollback;
317 }
318 dbi_result_free(result);
319
320 /* Cycle through old messages and convert them to the new format */
Max5c06e402015-07-29 20:20:28 +0200321 result = dbi_conn_query(conn, "SELECT * FROM SMS_3");
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100322 if (!result) {
323 LOGP(DDB, LOGL_ERROR,
324 "Failed fetch messages from the old SMS table (upgrade from rev 3).\n");
325 goto rollback;
326 }
Neels Hofmeyrdb827292017-12-10 14:45:15 +0100327 while (next_row(result)) {
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100328 sms = sms_from_result_v3(result);
329 if (db_sms_store(sms) != 0) {
330 LOGP(DDB, LOGL_ERROR, "Failed to store message to the new SMS table(upgrade from rev 3).\n");
331 sms_free(sms);
332 dbi_result_free(result);
333 goto rollback;
334 }
335 sms_free(sms);
336 }
337 dbi_result_free(result);
338
339 /* Remove the temporary table */
340 result = dbi_conn_query(conn, "DROP TABLE SMS_3");
341 if (!result) {
342 LOGP(DDB, LOGL_ERROR,
343 "Failed to drop the old SMS table (upgrade from rev 3).\n");
344 goto rollback;
345 }
346 dbi_result_free(result);
347
348 /* We're done. Bump DB Meta revision to 4 */
349 result = dbi_conn_query(conn,
350 "UPDATE Meta "
351 "SET value = '4' "
352 "WHERE key = 'revision'");
353 if (!result) {
354 LOGP(DDB, LOGL_ERROR,
355 "Failed to update DB schema revision (upgrade from rev 3).\n");
356 goto rollback;
357 }
358 dbi_result_free(result);
359
360 result = dbi_conn_query(conn, "COMMIT TRANSACTION");
361 if (!result) {
362 LOGP(DDB, LOGL_ERROR,
363 "Failed to commit the transaction (upgrade from rev 3)\n");
364 return -EINVAL;
Alexander Couzensf480b352017-02-04 00:20:17 +0100365 } else {
366 dbi_result_free(result);
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100367 }
368
369 /* Shrink DB file size by actually wiping out SMS_3 table data */
370 result = dbi_conn_query(conn, "VACUUM");
371 if (!result)
372 LOGP(DDB, LOGL_ERROR,
373 "VACUUM failed. Ignoring it (upgrade from rev 3).\n");
374 else
375 dbi_result_free(result);
376
377 return 0;
378
379rollback:
380 result = dbi_conn_query(conn, "ROLLBACK TRANSACTION");
381 if (!result)
382 LOGP(DDB, LOGL_ERROR,
383 "Rollback failed (upgrade from rev 3).\n");
384 else
385 dbi_result_free(result);
386 return -EINVAL;
387}
388
Pablo Neira Ayuso9891dae2017-08-07 14:01:33 +0100389/* Just like v3, but there is a new message reference field for status reports,
390 * that is set to zero for existing entries since there is no way we can infer
391 * this.
392 */
393static struct gsm_sms *sms_from_result_v4(dbi_result result)
394{
395 struct gsm_sms *sms = sms_alloc();
396 const unsigned char *user_data;
397 const char *text, *addr;
398
399 if (!sms)
400 return NULL;
401
402 sms->id = dbi_result_get_ulonglong(result, "id");
403
404 sms->reply_path_req = dbi_result_get_ulonglong(result, "reply_path_req");
405 sms->status_rep_req = dbi_result_get_ulonglong(result, "status_rep_req");
406 sms->ud_hdr_ind = dbi_result_get_ulonglong(result, "ud_hdr_ind");
407 sms->protocol_id = dbi_result_get_ulonglong(result, "protocol_id");
408 sms->data_coding_scheme = dbi_result_get_ulonglong(result,
409 "data_coding_scheme");
410
411 addr = dbi_result_get_string(result, "src_addr");
412 osmo_strlcpy(sms->src.addr, addr, sizeof(sms->src.addr));
413 sms->src.ton = dbi_result_get_ulonglong(result, "src_ton");
414 sms->src.npi = dbi_result_get_ulonglong(result, "src_npi");
415
416 addr = dbi_result_get_string(result, "dest_addr");
417 osmo_strlcpy(sms->dst.addr, addr, sizeof(sms->dst.addr));
418 sms->dst.ton = dbi_result_get_ulonglong(result, "dest_ton");
419 sms->dst.npi = dbi_result_get_ulonglong(result, "dest_npi");
420
421 sms->user_data_len = dbi_result_get_field_length(result, "user_data");
422 user_data = dbi_result_get_binary(result, "user_data");
423 if (sms->user_data_len > sizeof(sms->user_data))
424 sms->user_data_len = (uint8_t) sizeof(sms->user_data);
425 memcpy(sms->user_data, user_data, sms->user_data_len);
426
427 text = dbi_result_get_string(result, "text");
428 if (text)
429 osmo_strlcpy(sms->text, text, sizeof(sms->text));
430 return sms;
431}
432
433static int update_db_revision_4(void)
434{
435 dbi_result result;
436 struct gsm_sms *sms;
437
438 LOGP(DDB, LOGL_NOTICE, "Going to migrate from revision 4\n");
439
440 result = dbi_conn_query(conn, "BEGIN EXCLUSIVE TRANSACTION");
441 if (!result) {
442 LOGP(DDB, LOGL_ERROR,
443 "Failed to begin transaction (upgrade from rev 4)\n");
444 return -EINVAL;
445 }
446 dbi_result_free(result);
447
448 /* Rename old SMS table to be able create a new one */
449 result = dbi_conn_query(conn, "ALTER TABLE SMS RENAME TO SMS_4");
450 if (!result) {
451 LOGP(DDB, LOGL_ERROR,
452 "Failed to rename the old SMS table (upgrade from rev 4).\n");
453 goto rollback;
454 }
455 dbi_result_free(result);
456
457 /* Create new SMS table with all the bells and whistles! */
458 result = dbi_conn_query(conn, create_stmts[SCHEMA_SMS]);
459 if (!result) {
460 LOGP(DDB, LOGL_ERROR,
461 "Failed to create a new SMS table (upgrade from rev 4).\n");
462 goto rollback;
463 }
464 dbi_result_free(result);
465
466 /* Cycle through old messages and convert them to the new format */
467 result = dbi_conn_query(conn, "SELECT * FROM SMS_4");
468 if (!result) {
469 LOGP(DDB, LOGL_ERROR,
470 "Failed fetch messages from the old SMS table (upgrade from rev 4).\n");
471 goto rollback;
472 }
Neels Hofmeyrdb827292017-12-10 14:45:15 +0100473 while (next_row(result)) {
Pablo Neira Ayuso9891dae2017-08-07 14:01:33 +0100474 sms = sms_from_result_v4(result);
475 if (db_sms_store(sms) != 0) {
476 LOGP(DDB, LOGL_ERROR, "Failed to store message to the new SMS table(upgrade from rev 4).\n");
477 sms_free(sms);
478 dbi_result_free(result);
479 goto rollback;
480 }
481 sms_free(sms);
482 }
483 dbi_result_free(result);
484
485 /* Remove the temporary table */
486 result = dbi_conn_query(conn, "DROP TABLE SMS_4");
487 if (!result) {
488 LOGP(DDB, LOGL_ERROR,
489 "Failed to drop the old SMS table (upgrade from rev 4).\n");
490 goto rollback;
491 }
492 dbi_result_free(result);
493
494 /* We're done. Bump DB Meta revision to 4 */
495 result = dbi_conn_query(conn,
496 "UPDATE Meta "
497 "SET value = '5' "
498 "WHERE key = 'revision'");
499 if (!result) {
500 LOGP(DDB, LOGL_ERROR,
501 "Failed to update DB schema revision (upgrade from rev 4).\n");
502 goto rollback;
503 }
504 dbi_result_free(result);
505
506 result = dbi_conn_query(conn, "COMMIT TRANSACTION");
507 if (!result) {
508 LOGP(DDB, LOGL_ERROR,
509 "Failed to commit the transaction (upgrade from rev 4)\n");
510 return -EINVAL;
511 } else {
512 dbi_result_free(result);
513 }
514
515 /* Shrink DB file size by actually wiping out SMS_4 table data */
516 result = dbi_conn_query(conn, "VACUUM");
517 if (!result)
518 LOGP(DDB, LOGL_ERROR,
519 "VACUUM failed. Ignoring it (upgrade from rev 4).\n");
520 else
521 dbi_result_free(result);
522
523 return 0;
524
525rollback:
526 result = dbi_conn_query(conn, "ROLLBACK TRANSACTION");
527 if (!result)
528 LOGP(DDB, LOGL_ERROR,
529 "Rollback failed (upgrade from rev 4).\n");
530 else
531 dbi_result_free(result);
532 return -EINVAL;
533}
534
Harald Welted0b7b772009-08-09 19:03:42 +0200535static int check_db_revision(void)
536{
537 dbi_result result;
Jan Luebbebfbdeec2012-12-27 00:27:16 +0100538 const char *rev_s;
Vadim Yanitskiya8d8e932016-05-13 15:38:09 +0600539 int db_rev = 0;
Harald Welted0b7b772009-08-09 19:03:42 +0200540
Vadim Yanitskiya8d8e932016-05-13 15:38:09 +0600541 /* Make a query */
Harald Welted0b7b772009-08-09 19:03:42 +0200542 result = dbi_conn_query(conn,
Vadim Yanitskiya8d8e932016-05-13 15:38:09 +0600543 "SELECT value FROM Meta "
544 "WHERE key = 'revision'");
545
Harald Welted0b7b772009-08-09 19:03:42 +0200546 if (!result)
547 return -EINVAL;
548
Neels Hofmeyrdb827292017-12-10 14:45:15 +0100549 if (!next_row(result)) {
Harald Welted0b7b772009-08-09 19:03:42 +0200550 dbi_result_free(result);
551 return -EINVAL;
552 }
Vadim Yanitskiya8d8e932016-05-13 15:38:09 +0600553
554 /* Fetch the DB schema revision */
Jan Luebbebfbdeec2012-12-27 00:27:16 +0100555 rev_s = dbi_result_get_string(result, "value");
556 if (!rev_s) {
Harald Welted0b7b772009-08-09 19:03:42 +0200557 dbi_result_free(result);
558 return -EINVAL;
559 }
Vadim Yanitskiya8d8e932016-05-13 15:38:09 +0600560
561 if (!strcmp(rev_s, SCHEMA_REVISION)) {
562 /* Everything is fine */
Jan Luebbebfbdeec2012-12-27 00:27:16 +0100563 dbi_result_free(result);
Vadim Yanitskiya8d8e932016-05-13 15:38:09 +0600564 return 0;
565 }
566
567 db_rev = atoi(rev_s);
568 dbi_result_free(result);
569
570 /* Incremental migration waterfall */
571 switch (db_rev) {
572 case 2:
573 if (update_db_revision_2())
574 goto error;
575 case 3:
576 if (update_db_revision_3())
577 goto error;
Pablo Neira Ayuso9891dae2017-08-07 14:01:33 +0100578 case 4:
579 if (update_db_revision_4())
580 goto error;
Vadim Yanitskiya8d8e932016-05-13 15:38:09 +0600581
582 /* The end of waterfall */
583 break;
584 default:
585 LOGP(DDB, LOGL_FATAL,
586 "Invalid database schema revision '%d'.\n", db_rev);
Jan Luebbebfbdeec2012-12-27 00:27:16 +0100587 return -EINVAL;
588 }
589
Jan Luebbebfbdeec2012-12-27 00:27:16 +0100590 return 0;
Vadim Yanitskiya8d8e932016-05-13 15:38:09 +0600591
592error:
593 LOGP(DDB, LOGL_FATAL, "Failed to update database "
594 "from schema revision '%d'.\n", db_rev);
595 return -EINVAL;
Jan Luebbebfbdeec2012-12-27 00:27:16 +0100596}
597
598static int db_configure(void)
599{
600 dbi_result result;
601
602 result = dbi_conn_query(conn,
603 "PRAGMA synchronous = FULL");
604 if (!result)
605 return -EINVAL;
Harald Welted0b7b772009-08-09 19:03:42 +0200606
607 dbi_result_free(result);
608 return 0;
609}
610
Harald Welte0b906d02009-12-24 11:21:42 +0100611int db_init(const char *name)
612{
Pau Espin Pedrolc22e54d2017-12-15 18:28:43 +0100613 dbi_initialize_r(NULL, &inst);
Harald Welte0b906d02009-12-24 11:21:42 +0100614
Pau Espin Pedrolc22e54d2017-12-15 18:28:43 +0100615 conn = dbi_conn_new_r("sqlite3", inst);
Harald Welte0b906d02009-12-24 11:21:42 +0100616 if (conn == NULL) {
Stefan Sperling832046d2018-01-15 16:12:37 +0100617 LOGP(DDB, LOGL_FATAL, "Failed to create database connection to sqlite3 db '%s'; "
618 "Is the sqlite3 database driver for libdbi installed on this system?\n", name);
Jan Luebbe5c15c852008-12-27 15:59:25 +0000619 return 1;
620 }
Jan Luebbe7398eb92008-12-27 00:45:41 +0000621
Holger Freyther12aa50d2009-01-01 18:02:05 +0000622 dbi_conn_error_handler( conn, db_error_func, NULL );
Jan Luebbe7398eb92008-12-27 00:45:41 +0000623
Jan Luebbe5c15c852008-12-27 15:59:25 +0000624 /* MySQL
625 dbi_conn_set_option(conn, "host", "localhost");
626 dbi_conn_set_option(conn, "username", "your_name");
627 dbi_conn_set_option(conn, "password", "your_password");
628 dbi_conn_set_option(conn, "dbname", "your_dbname");
629 dbi_conn_set_option(conn, "encoding", "UTF-8");
630 */
Jan Luebbe7398eb92008-12-27 00:45:41 +0000631
Jan Luebbe5c15c852008-12-27 15:59:25 +0000632 /* SqLite 3 */
Holger Freyther12aa50d2009-01-01 18:02:05 +0000633 db_basename = strdup(name);
634 db_dirname = strdup(name);
Holger Freytherbde36102008-12-28 22:51:39 +0000635 dbi_conn_set_option(conn, "sqlite3_dbdir", dirname(db_dirname));
636 dbi_conn_set_option(conn, "dbname", basename(db_basename));
Jan Luebbe7398eb92008-12-27 00:45:41 +0000637
Harald Welted0b7b772009-08-09 19:03:42 +0200638 if (dbi_conn_connect(conn) < 0)
639 goto out_err;
640
Jan Luebbe5c15c852008-12-27 15:59:25 +0000641 return 0;
Harald Welted0b7b772009-08-09 19:03:42 +0200642
643out_err:
644 free(db_dirname);
645 free(db_basename);
646 db_dirname = db_basename = NULL;
647 return -1;
Jan Luebbe7398eb92008-12-27 00:45:41 +0000648}
649
Harald Welted0b7b772009-08-09 19:03:42 +0200650
Harald Welted1476bc2011-07-16 13:24:09 +0200651int db_prepare(void)
Harald Welte0b906d02009-12-24 11:21:42 +0100652{
Jan Luebbe5c15c852008-12-27 15:59:25 +0000653 dbi_result result;
Harald Welte7e310b12009-03-30 20:56:32 +0000654 int i;
Holger Freytherb4064bc2009-02-23 00:50:31 +0000655
Harald Welte7e310b12009-03-30 20:56:32 +0000656 for (i = 0; i < ARRAY_SIZE(create_stmts); i++) {
657 result = dbi_conn_query(conn, create_stmts[i]);
Harald Welte0b906d02009-12-24 11:21:42 +0100658 if (!result) {
Harald Welteae1f1592009-12-24 11:39:14 +0100659 LOGP(DDB, LOGL_ERROR,
660 "Failed to create some table.\n");
Harald Welte7e310b12009-03-30 20:56:32 +0000661 return 1;
662 }
663 dbi_result_free(result);
Holger Freytherb4064bc2009-02-23 00:50:31 +0000664 }
Holger Freytherb4064bc2009-02-23 00:50:31 +0000665
Holger Hans Peter Freyther850326e2009-08-10 08:36:04 +0200666 if (check_db_revision() < 0) {
Harald Welteae1f1592009-12-24 11:39:14 +0100667 LOGP(DDB, LOGL_FATAL, "Database schema revision invalid, "
Holger Hans Peter Freyther850326e2009-08-10 08:36:04 +0200668 "please update your database schema\n");
669 return -1;
670 }
671
Jan Luebbebfbdeec2012-12-27 00:27:16 +0100672 db_configure();
673
Jan Luebbe5c15c852008-12-27 15:59:25 +0000674 return 0;
Jan Luebbe7398eb92008-12-27 00:45:41 +0000675}
676
Harald Welted1476bc2011-07-16 13:24:09 +0200677int db_fini(void)
Harald Welte0b906d02009-12-24 11:21:42 +0100678{
Jan Luebbe5c15c852008-12-27 15:59:25 +0000679 dbi_conn_close(conn);
Pau Espin Pedrolc22e54d2017-12-15 18:28:43 +0100680 dbi_shutdown_r(inst);
Holger Freytherbde36102008-12-28 22:51:39 +0000681
Harald Welte2c5f4c62011-07-16 13:22:57 +0200682 free(db_dirname);
683 free(db_basename);
Jan Luebbe5c15c852008-12-27 15:59:25 +0000684 return 0;
Jan Luebbe7398eb92008-12-27 00:45:41 +0000685}
686
Harald Welte7e310b12009-03-30 20:56:32 +0000687/* store an [unsent] SMS to the database */
688int db_sms_store(struct gsm_sms *sms)
689{
690 dbi_result result;
Holger Hans Peter Freytherca3c2562013-10-08 03:17:30 +0200691 char *q_text, *q_daddr, *q_saddr;
Harald Welte76042182009-08-08 16:03:15 +0200692 unsigned char *q_udata;
693 char *validity_timestamp = "2222-2-2";
694
695 /* FIXME: generate validity timestamp based on validity_minutes */
Harald Welte7e310b12009-03-30 20:56:32 +0000696
697 dbi_conn_quote_string_copy(conn, (char *)sms->text, &q_text);
Harald Weltec0de14d2012-11-23 23:35:01 +0100698 dbi_conn_quote_string_copy(conn, (char *)sms->dst.addr, &q_daddr);
Holger Hans Peter Freytherca3c2562013-10-08 03:17:30 +0200699 dbi_conn_quote_string_copy(conn, (char *)sms->src.addr, &q_saddr);
Harald Welte76042182009-08-08 16:03:15 +0200700 dbi_conn_quote_binary_copy(conn, sms->user_data, sms->user_data_len,
701 &q_udata);
Holger Hans Peter Freytherca3c2562013-10-08 03:17:30 +0200702
Harald Weltef3efc592009-07-27 20:11:35 +0200703 /* FIXME: correct validity period */
Harald Welte7e310b12009-03-30 20:56:32 +0000704 result = dbi_conn_queryf(conn,
705 "INSERT INTO SMS "
Alexander Chemerisca7ed2d2013-10-08 03:17:32 +0200706 "(created, valid_until, "
Pablo Neira Ayuso9891dae2017-08-07 14:01:33 +0100707 "reply_path_req, status_rep_req, is_report, "
708 "msg_ref, protocol_id, data_coding_scheme, "
709 "ud_hdr_ind, "
Holger Hans Peter Freytherca3c2562013-10-08 03:17:30 +0200710 "user_data, text, "
711 "dest_addr, dest_ton, dest_npi, "
712 "src_addr, src_ton, src_npi) VALUES "
Alexander Chemerisca7ed2d2013-10-08 03:17:32 +0200713 "(datetime('now'), %u, "
Holger Hans Peter Freytherca3c2562013-10-08 03:17:30 +0200714 "%u, %u, %u, "
Pablo Neira Ayuso9891dae2017-08-07 14:01:33 +0100715 "%u, %u, %u, "
716 "%u, "
Holger Hans Peter Freytherca3c2562013-10-08 03:17:30 +0200717 "%s, %s, "
718 "%s, %u, %u, "
719 "%s, %u, %u)",
Alexander Chemerisca7ed2d2013-10-08 03:17:32 +0200720 validity_timestamp,
Pablo Neira Ayuso9891dae2017-08-07 14:01:33 +0100721 sms->reply_path_req, sms->status_rep_req, sms->is_report,
722 sms->msg_ref, sms->protocol_id, sms->data_coding_scheme,
723 sms->ud_hdr_ind,
Holger Hans Peter Freytherca3c2562013-10-08 03:17:30 +0200724 q_udata, q_text,
725 q_daddr, sms->dst.ton, sms->dst.npi,
726 q_saddr, sms->src.ton, sms->src.npi);
Harald Welte7e310b12009-03-30 20:56:32 +0000727 free(q_text);
Harald Welte76042182009-08-08 16:03:15 +0200728 free(q_udata);
Holger Hans Peter Freytherca3c2562013-10-08 03:17:30 +0200729 free(q_daddr);
730 free(q_saddr);
Harald Welte7e310b12009-03-30 20:56:32 +0000731
732 if (!result)
733 return -EIO;
734
735 dbi_result_free(result);
736 return 0;
737}
738
Harald Welte2ebabca2009-08-09 19:05:21 +0200739static struct gsm_sms *sms_from_result(struct gsm_network *net, dbi_result result)
Harald Welte7e310b12009-03-30 20:56:32 +0000740{
Harald Welte76042182009-08-08 16:03:15 +0200741 struct gsm_sms *sms = sms_alloc();
Holger Hans Peter Freytherca3c2562013-10-08 03:17:30 +0200742 const char *text, *daddr, *saddr;
Harald Welte76042182009-08-08 16:03:15 +0200743 const unsigned char *user_data;
Harald Welte7e310b12009-03-30 20:56:32 +0000744
Harald Welte76042182009-08-08 16:03:15 +0200745 if (!sms)
Harald Welte7e310b12009-03-30 20:56:32 +0000746 return NULL;
Harald Welte7e310b12009-03-30 20:56:32 +0000747
Harald Weltebe3e3782009-07-05 14:06:41 +0200748 sms->id = dbi_result_get_ulonglong(result, "id");
Harald Welte7e310b12009-03-30 20:56:32 +0000749
Harald Weltef3efc592009-07-27 20:11:35 +0200750 /* FIXME: validity */
Harald Welte76042182009-08-08 16:03:15 +0200751 /* FIXME: those should all be get_uchar, but sqlite3 is braindead */
Keithc601adc2017-08-16 22:45:07 +0200752 sms->created = dbi_result_get_datetime(result, "created");
Holger Hans Peter Freytherb115cb62014-07-03 14:00:30 +0200753 sms->reply_path_req = dbi_result_get_ulonglong(result, "reply_path_req");
754 sms->status_rep_req = dbi_result_get_ulonglong(result, "status_rep_req");
Pablo Neira Ayuso9891dae2017-08-07 14:01:33 +0100755 sms->is_report = dbi_result_get_ulonglong(result, "is_report");
756 sms->msg_ref = dbi_result_get_ulonglong(result, "msg_ref");
Holger Hans Peter Freytherb115cb62014-07-03 14:00:30 +0200757 sms->ud_hdr_ind = dbi_result_get_ulonglong(result, "ud_hdr_ind");
758 sms->protocol_id = dbi_result_get_ulonglong(result, "protocol_id");
759 sms->data_coding_scheme = dbi_result_get_ulonglong(result,
Harald Weltef3efc592009-07-27 20:11:35 +0200760 "data_coding_scheme");
761
Holger Hans Peter Freytherb115cb62014-07-03 14:00:30 +0200762 sms->dst.npi = dbi_result_get_ulonglong(result, "dest_npi");
763 sms->dst.ton = dbi_result_get_ulonglong(result, "dest_ton");
Harald Welte76042182009-08-08 16:03:15 +0200764 daddr = dbi_result_get_string(result, "dest_addr");
Neels Hofmeyr93bafb62017-01-13 03:12:08 +0100765 if (daddr)
766 osmo_strlcpy(sms->dst.addr, daddr, sizeof(sms->dst.addr));
Harald Welte2483f1b2016-06-19 18:06:02 +0200767 sms->receiver = vlr_subscr_find_by_msisdn(net->vlr, sms->dst.addr);
Harald Welte76042182009-08-08 16:03:15 +0200768
Holger Hans Peter Freytherb115cb62014-07-03 14:00:30 +0200769 sms->src.npi = dbi_result_get_ulonglong(result, "src_npi");
770 sms->src.ton = dbi_result_get_ulonglong(result, "src_ton");
Holger Hans Peter Freytherca3c2562013-10-08 03:17:30 +0200771 saddr = dbi_result_get_string(result, "src_addr");
Neels Hofmeyr93bafb62017-01-13 03:12:08 +0100772 if (saddr)
773 osmo_strlcpy(sms->src.addr, saddr, sizeof(sms->src.addr));
Holger Hans Peter Freytherca3c2562013-10-08 03:17:30 +0200774
Harald Welte76042182009-08-08 16:03:15 +0200775 sms->user_data_len = dbi_result_get_field_length(result, "user_data");
776 user_data = dbi_result_get_binary(result, "user_data");
777 if (sms->user_data_len > sizeof(sms->user_data))
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +0200778 sms->user_data_len = (uint8_t) sizeof(sms->user_data);
Harald Welte76042182009-08-08 16:03:15 +0200779 memcpy(sms->user_data, user_data, sms->user_data_len);
Harald Weltebe3e3782009-07-05 14:06:41 +0200780
781 text = dbi_result_get_string(result, "text");
Neels Hofmeyr93bafb62017-01-13 03:12:08 +0100782 if (text)
783 osmo_strlcpy(sms->text, text, sizeof(sms->text));
Harald Welte2ebabca2009-08-09 19:05:21 +0200784 return sms;
785}
786
Holger Hans Peter Freyther812dad02010-12-24 23:18:31 +0100787struct gsm_sms *db_sms_get(struct gsm_network *net, unsigned long long id)
788{
789 dbi_result result;
790 struct gsm_sms *sms;
791
792 result = dbi_conn_queryf(conn,
793 "SELECT * FROM SMS WHERE SMS.id = %llu", id);
794 if (!result)
795 return NULL;
796
Neels Hofmeyrdb827292017-12-10 14:45:15 +0100797 if (!next_row(result)) {
Holger Hans Peter Freyther812dad02010-12-24 23:18:31 +0100798 dbi_result_free(result);
799 return NULL;
800 }
801
802 sms = sms_from_result(net, result);
803
804 dbi_result_free(result);
805
806 return sms;
807}
808
Harald Welte2483f1b2016-06-19 18:06:02 +0200809struct gsm_sms *db_sms_get_next_unsent(struct gsm_network *net,
810 unsigned long long min_sms_id,
811 unsigned int max_failed)
Harald Welte2ebabca2009-08-09 19:05:21 +0200812{
813 dbi_result result;
814 struct gsm_sms *sms;
815
816 result = dbi_conn_queryf(conn,
Harald Welte2483f1b2016-06-19 18:06:02 +0200817 "SELECT * FROM SMS"
818 " WHERE sent IS NULL"
819 " AND id >= %llu"
820 " AND deliver_attempts <= %u"
821 " ORDER BY id LIMIT 1",
822 min_sms_id, max_failed);
Harald Welte2ebabca2009-08-09 19:05:21 +0200823
Sylvain Munautff1f19e2009-12-22 13:22:29 +0100824 if (!result)
825 return NULL;
826
Neels Hofmeyrdb827292017-12-10 14:45:15 +0100827 if (!next_row(result)) {
Sylvain Munautff1f19e2009-12-22 13:22:29 +0100828 dbi_result_free(result);
829 return NULL;
830 }
831
832 sms = sms_from_result(net, result);
833
834 dbi_result_free(result);
835
836 return sms;
837}
838
Sylvain Munautd5778fc2009-12-21 01:09:57 +0100839/* retrieve the next unsent SMS for a given subscriber */
Harald Welte2483f1b2016-06-19 18:06:02 +0200840struct gsm_sms *db_sms_get_unsent_for_subscr(struct vlr_subscr *vsub,
841 unsigned int max_failed)
Harald Welte2ebabca2009-08-09 19:05:21 +0200842{
Harald Welte2483f1b2016-06-19 18:06:02 +0200843 struct gsm_network *net = vsub->vlr->user_ctx;
Harald Welte2ebabca2009-08-09 19:05:21 +0200844 dbi_result result;
845 struct gsm_sms *sms;
Neels Hofmeyr50d09002017-12-05 12:39:00 +0100846 char *q_msisdn;
Harald Welte2ebabca2009-08-09 19:05:21 +0200847
Harald Welte2483f1b2016-06-19 18:06:02 +0200848 if (!vsub->lu_complete)
849 return NULL;
850
Neels Hofmeyrf6704f12017-12-05 12:34:44 +0100851 /* A subscriber having no phone number cannot possibly receive SMS. */
852 if (*vsub->msisdn == '\0')
853 return NULL;
854
Neels Hofmeyr50d09002017-12-05 12:39:00 +0100855 dbi_conn_quote_string_copy(conn, vsub->msisdn, &q_msisdn);
Harald Welte2ebabca2009-08-09 19:05:21 +0200856 result = dbi_conn_queryf(conn,
Harald Welte2483f1b2016-06-19 18:06:02 +0200857 "SELECT * FROM SMS"
858 " WHERE sent IS NULL"
Neels Hofmeyr50d09002017-12-05 12:39:00 +0100859 " AND dest_addr = %s"
Harald Welte2483f1b2016-06-19 18:06:02 +0200860 " AND deliver_attempts <= %u"
861 " ORDER BY id LIMIT 1",
Neels Hofmeyr50d09002017-12-05 12:39:00 +0100862 q_msisdn, max_failed);
863 free(q_msisdn);
864
Harald Welte2ebabca2009-08-09 19:05:21 +0200865 if (!result)
866 return NULL;
867
Neels Hofmeyrdb827292017-12-10 14:45:15 +0100868 if (!next_row(result)) {
Harald Welte2ebabca2009-08-09 19:05:21 +0200869 dbi_result_free(result);
870 return NULL;
871 }
872
Harald Welte2483f1b2016-06-19 18:06:02 +0200873 sms = sms_from_result(net, result);
874
875 dbi_result_free(result);
876
877 return sms;
878}
879
880struct gsm_sms *db_sms_get_next_unsent_rr_msisdn(struct gsm_network *net,
881 const char *last_msisdn,
882 unsigned int max_failed)
883{
884 dbi_result result;
885 struct gsm_sms *sms;
Neels Hofmeyr50d09002017-12-05 12:39:00 +0100886 char *q_last_msisdn;
Harald Welte2483f1b2016-06-19 18:06:02 +0200887
Neels Hofmeyr50d09002017-12-05 12:39:00 +0100888 dbi_conn_quote_string_copy(conn, last_msisdn, &q_last_msisdn);
Harald Welte2483f1b2016-06-19 18:06:02 +0200889 result = dbi_conn_queryf(conn,
890 "SELECT * FROM SMS"
891 " WHERE sent IS NULL"
Neels Hofmeyr50d09002017-12-05 12:39:00 +0100892 " AND dest_addr > %s"
Harald Welte2483f1b2016-06-19 18:06:02 +0200893 " AND deliver_attempts <= %u"
894 " ORDER BY dest_addr, id LIMIT 1",
Neels Hofmeyr50d09002017-12-05 12:39:00 +0100895 q_last_msisdn, max_failed);
896 free(q_last_msisdn);
897
Harald Welte2483f1b2016-06-19 18:06:02 +0200898 if (!result)
899 return NULL;
900
Neels Hofmeyrdb827292017-12-10 14:45:15 +0100901 if (!next_row(result)) {
Harald Welte2483f1b2016-06-19 18:06:02 +0200902 dbi_result_free(result);
903 return NULL;
904 }
905
906 sms = sms_from_result(net, result);
Harald Welte2ebabca2009-08-09 19:05:21 +0200907
908 dbi_result_free(result);
909
Harald Welte7e310b12009-03-30 20:56:32 +0000910 return sms;
911}
912
Alexander Chemeris1e77e3d2014-03-08 21:27:37 +0100913/* mark a given SMS as delivered */
914int db_sms_mark_delivered(struct gsm_sms *sms)
Harald Welte7e310b12009-03-30 20:56:32 +0000915{
916 dbi_result result;
917
918 result = dbi_conn_queryf(conn,
919 "UPDATE SMS "
920 "SET sent = datetime('now') "
921 "WHERE id = %llu", sms->id);
922 if (!result) {
Harald Welteae1f1592009-12-24 11:39:14 +0100923 LOGP(DDB, LOGL_ERROR, "Failed to mark SMS %llu as sent.\n", sms->id);
Harald Welte7e310b12009-03-30 20:56:32 +0000924 return 1;
925 }
926
927 dbi_result_free(result);
928 return 0;
929}
Harald Welte (local)db552c52009-08-15 20:15:14 +0200930
931/* increase the number of attempted deliveries */
932int db_sms_inc_deliver_attempts(struct gsm_sms *sms)
933{
934 dbi_result result;
935
936 result = dbi_conn_queryf(conn,
937 "UPDATE SMS "
938 "SET deliver_attempts = deliver_attempts + 1 "
939 "WHERE id = %llu", sms->id);
940 if (!result) {
Harald Welteae1f1592009-12-24 11:39:14 +0100941 LOGP(DDB, LOGL_ERROR, "Failed to inc deliver attempts for "
942 "SMS %llu.\n", sms->id);
Harald Welte (local)db552c52009-08-15 20:15:14 +0200943 return 1;
944 }
945
946 dbi_result_free(result);
947 return 0;
948}
Harald Welte (local)026531e2009-08-16 10:40:10 +0200949
Harald Welte2483f1b2016-06-19 18:06:02 +0200950/* Drop all pending SMS to or from the given extension */
951int db_sms_delete_by_msisdn(const char *msisdn)
Harald Welte (local)026531e2009-08-16 10:40:10 +0200952{
953 dbi_result result;
Neels Hofmeyr50d09002017-12-05 12:39:00 +0100954 char *q_msisdn;
Harald Welte2483f1b2016-06-19 18:06:02 +0200955 if (!msisdn || !*msisdn)
956 return 0;
Neels Hofmeyr50d09002017-12-05 12:39:00 +0100957
958 dbi_conn_quote_string_copy(conn, msisdn, &q_msisdn);
Harald Welte (local)026531e2009-08-16 10:40:10 +0200959 result = dbi_conn_queryf(conn,
Harald Welte2483f1b2016-06-19 18:06:02 +0200960 "DELETE FROM SMS WHERE src_addr=%s OR dest_addr=%s",
Neels Hofmeyr50d09002017-12-05 12:39:00 +0100961 q_msisdn, q_msisdn);
962 free(q_msisdn);
963
Harald Welte2483f1b2016-06-19 18:06:02 +0200964 if (!result) {
965 LOGP(DDB, LOGL_ERROR,
966 "Failed to delete SMS for %s\n", msisdn);
967 return -1;
968 }
Harald Welte (local)026531e2009-08-16 10:40:10 +0200969 dbi_result_free(result);
970 return 0;
971}
Harald Welteffa55a42009-12-22 19:07:32 +0100972
Stefan Sperling6ba2d5a2018-01-18 18:55:26 +0100973int db_sms_delete_sent_message_by_id(unsigned long long sms_id)
974{
975 dbi_result result;
976
977 result = dbi_conn_queryf(conn,
978 "DELETE FROM SMS WHERE id = %llu AND sent is NOT NULL",
979 sms_id);
980 if (!result) {
981 LOGP(DDB, LOGL_ERROR, "Failed to delete SMS %llu.\n", sms_id);
982 return 1;
983 }
984
985 dbi_result_free(result);
986 return 0;
987}
988
Pablo Neira Ayusodfb342c2011-05-06 12:13:10 +0200989int db_store_counter(struct osmo_counter *ctr)
Harald Welteffa55a42009-12-22 19:07:32 +0100990{
991 dbi_result result;
992 char *q_name;
993
994 dbi_conn_quote_string_copy(conn, ctr->name, &q_name);
995
996 result = dbi_conn_queryf(conn,
997 "INSERT INTO Counters "
998 "(timestamp,name,value) VALUES "
999 "(datetime('now'),%s,%lu)", q_name, ctr->value);
1000
1001 free(q_name);
1002
1003 if (!result)
1004 return -EIO;
1005
1006 dbi_result_free(result);
1007 return 0;
1008}
Harald Weltef2b4cd72010-05-13 11:45:07 +02001009
1010static int db_store_rate_ctr(struct rate_ctr_group *ctrg, unsigned int num,
1011 char *q_prefix)
1012{
1013 dbi_result result;
1014 char *q_name;
1015
1016 dbi_conn_quote_string_copy(conn, ctrg->desc->ctr_desc[num].name,
1017 &q_name);
1018
1019 result = dbi_conn_queryf(conn,
Harald Weltec1919862010-05-13 12:55:20 +02001020 "Insert INTO RateCounters "
Harald Welted94d6a02010-05-14 17:38:47 +02001021 "(timestamp,name,idx,value) VALUES "
Harald Weltec1919862010-05-13 12:55:20 +02001022 "(datetime('now'),%s.%s,%u,%"PRIu64")",
1023 q_prefix, q_name, ctrg->idx, ctrg->ctr[num].current);
Harald Weltef2b4cd72010-05-13 11:45:07 +02001024
1025 free(q_name);
1026
1027 if (!result)
1028 return -EIO;
1029
1030 dbi_result_free(result);
1031 return 0;
1032}
1033
1034int db_store_rate_ctr_group(struct rate_ctr_group *ctrg)
1035{
1036 unsigned int i;
1037 char *q_prefix;
1038
Harald Weltec1919862010-05-13 12:55:20 +02001039 dbi_conn_quote_string_copy(conn, ctrg->desc->group_name_prefix, &q_prefix);
Harald Weltef2b4cd72010-05-13 11:45:07 +02001040
1041 for (i = 0; i < ctrg->desc->num_ctr; i++)
1042 db_store_rate_ctr(ctrg, i, q_prefix);
1043
1044 free(q_prefix);
1045
1046 return 0;
1047}