blob: 0cb8ff34ead057554f8ec4db1071e391a00a6c03 [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
Daniel Willmanncdeb8152015-10-08 16:10:23 +020045#include <openssl/rand.h>
46
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
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100229/**
230 * Copied from the normal sms_from_result_v3 to avoid having
231 * to make sure that the real routine will remain backward
232 * compatible.
233 */
234static struct gsm_sms *sms_from_result_v3(dbi_result result)
235{
236 struct gsm_sms *sms = sms_alloc();
237 long long unsigned int sender_id;
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100238 const char *text, *daddr;
239 const unsigned char *user_data;
240 char buf[32];
Harald Welte2483f1b2016-06-19 18:06:02 +0200241 char *quoted;
242 dbi_result result2;
243 const char *extension;
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100244
245 if (!sms)
246 return NULL;
247
248 sms->id = dbi_result_get_ulonglong(result, "id");
249
Harald Welte2483f1b2016-06-19 18:06:02 +0200250 /* find extension by id, assuming that the subscriber still exists in
251 * the db */
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100252 sender_id = dbi_result_get_ulonglong(result, "sender_id");
253 snprintf(buf, sizeof(buf), "%llu", sender_id);
Harald Welte2483f1b2016-06-19 18:06:02 +0200254
255 dbi_conn_quote_string_copy(conn, buf, &quoted);
256 result2 = dbi_conn_queryf(conn,
257 "SELECT extension FROM Subscriber "
258 "WHERE id = %s ", quoted);
259 free(quoted);
260 extension = dbi_result_get_string(result2, "extension");
261 if (extension)
262 osmo_strlcpy(sms->src.addr, extension, sizeof(sms->src.addr));
263 dbi_result_free(result2);
264 /* got the extension */
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100265
Holger Hans Peter Freytherb115cb62014-07-03 14:00:30 +0200266 sms->reply_path_req = dbi_result_get_ulonglong(result, "reply_path_req");
267 sms->status_rep_req = dbi_result_get_ulonglong(result, "status_rep_req");
268 sms->ud_hdr_ind = dbi_result_get_ulonglong(result, "ud_hdr_ind");
269 sms->protocol_id = dbi_result_get_ulonglong(result, "protocol_id");
270 sms->data_coding_scheme = dbi_result_get_ulonglong(result,
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100271 "data_coding_scheme");
272
273 daddr = dbi_result_get_string(result, "dest_addr");
Neels Hofmeyr93bafb62017-01-13 03:12:08 +0100274 if (daddr)
275 osmo_strlcpy(sms->dst.addr, daddr, sizeof(sms->dst.addr));
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100276
277 sms->user_data_len = dbi_result_get_field_length(result, "user_data");
278 user_data = dbi_result_get_binary(result, "user_data");
279 if (sms->user_data_len > sizeof(sms->user_data))
280 sms->user_data_len = (uint8_t) sizeof(sms->user_data);
281 memcpy(sms->user_data, user_data, sms->user_data_len);
282
283 text = dbi_result_get_string(result, "text");
Neels Hofmeyr93bafb62017-01-13 03:12:08 +0100284 if (text)
285 osmo_strlcpy(sms->text, text, sizeof(sms->text));
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100286 return sms;
287}
288
289static int update_db_revision_3(void)
290{
291 dbi_result result;
292 struct gsm_sms *sms;
293
Holger Hans Peter Freyther61144012014-03-08 16:41:37 +0100294 LOGP(DDB, LOGL_NOTICE, "Going to migrate from revision 3\n");
295
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100296 result = dbi_conn_query(conn, "BEGIN EXCLUSIVE TRANSACTION");
297 if (!result) {
298 LOGP(DDB, LOGL_ERROR,
299 "Failed to begin transaction (upgrade from rev 3)\n");
300 return -EINVAL;
301 }
302 dbi_result_free(result);
303
304 /* Rename old SMS table to be able create a new one */
305 result = dbi_conn_query(conn, "ALTER TABLE SMS RENAME TO SMS_3");
306 if (!result) {
307 LOGP(DDB, LOGL_ERROR,
308 "Failed to rename the old SMS table (upgrade from rev 3).\n");
309 goto rollback;
310 }
311 dbi_result_free(result);
312
313 /* Create new SMS table with all the bells and whistles! */
314 result = dbi_conn_query(conn, create_stmts[SCHEMA_SMS]);
315 if (!result) {
316 LOGP(DDB, LOGL_ERROR,
317 "Failed to create a new SMS table (upgrade from rev 3).\n");
318 goto rollback;
319 }
320 dbi_result_free(result);
321
322 /* Cycle through old messages and convert them to the new format */
Max5c06e402015-07-29 20:20:28 +0200323 result = dbi_conn_query(conn, "SELECT * FROM SMS_3");
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100324 if (!result) {
325 LOGP(DDB, LOGL_ERROR,
326 "Failed fetch messages from the old SMS table (upgrade from rev 3).\n");
327 goto rollback;
328 }
Neels Hofmeyrdb827292017-12-10 14:45:15 +0100329 while (next_row(result)) {
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100330 sms = sms_from_result_v3(result);
331 if (db_sms_store(sms) != 0) {
332 LOGP(DDB, LOGL_ERROR, "Failed to store message to the new SMS table(upgrade from rev 3).\n");
333 sms_free(sms);
334 dbi_result_free(result);
335 goto rollback;
336 }
337 sms_free(sms);
338 }
339 dbi_result_free(result);
340
341 /* Remove the temporary table */
342 result = dbi_conn_query(conn, "DROP TABLE SMS_3");
343 if (!result) {
344 LOGP(DDB, LOGL_ERROR,
345 "Failed to drop the old SMS table (upgrade from rev 3).\n");
346 goto rollback;
347 }
348 dbi_result_free(result);
349
350 /* We're done. Bump DB Meta revision to 4 */
351 result = dbi_conn_query(conn,
352 "UPDATE Meta "
353 "SET value = '4' "
354 "WHERE key = 'revision'");
355 if (!result) {
356 LOGP(DDB, LOGL_ERROR,
357 "Failed to update DB schema revision (upgrade from rev 3).\n");
358 goto rollback;
359 }
360 dbi_result_free(result);
361
362 result = dbi_conn_query(conn, "COMMIT TRANSACTION");
363 if (!result) {
364 LOGP(DDB, LOGL_ERROR,
365 "Failed to commit the transaction (upgrade from rev 3)\n");
366 return -EINVAL;
Alexander Couzensf480b352017-02-04 00:20:17 +0100367 } else {
368 dbi_result_free(result);
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100369 }
370
371 /* Shrink DB file size by actually wiping out SMS_3 table data */
372 result = dbi_conn_query(conn, "VACUUM");
373 if (!result)
374 LOGP(DDB, LOGL_ERROR,
375 "VACUUM failed. Ignoring it (upgrade from rev 3).\n");
376 else
377 dbi_result_free(result);
378
379 return 0;
380
381rollback:
382 result = dbi_conn_query(conn, "ROLLBACK TRANSACTION");
383 if (!result)
384 LOGP(DDB, LOGL_ERROR,
385 "Rollback failed (upgrade from rev 3).\n");
386 else
387 dbi_result_free(result);
388 return -EINVAL;
389}
390
Pablo Neira Ayuso9891dae2017-08-07 14:01:33 +0100391/* Just like v3, but there is a new message reference field for status reports,
392 * that is set to zero for existing entries since there is no way we can infer
393 * this.
394 */
395static struct gsm_sms *sms_from_result_v4(dbi_result result)
396{
397 struct gsm_sms *sms = sms_alloc();
398 const unsigned char *user_data;
399 const char *text, *addr;
400
401 if (!sms)
402 return NULL;
403
404 sms->id = dbi_result_get_ulonglong(result, "id");
405
406 sms->reply_path_req = dbi_result_get_ulonglong(result, "reply_path_req");
407 sms->status_rep_req = dbi_result_get_ulonglong(result, "status_rep_req");
408 sms->ud_hdr_ind = dbi_result_get_ulonglong(result, "ud_hdr_ind");
409 sms->protocol_id = dbi_result_get_ulonglong(result, "protocol_id");
410 sms->data_coding_scheme = dbi_result_get_ulonglong(result,
411 "data_coding_scheme");
412
413 addr = dbi_result_get_string(result, "src_addr");
414 osmo_strlcpy(sms->src.addr, addr, sizeof(sms->src.addr));
415 sms->src.ton = dbi_result_get_ulonglong(result, "src_ton");
416 sms->src.npi = dbi_result_get_ulonglong(result, "src_npi");
417
418 addr = dbi_result_get_string(result, "dest_addr");
419 osmo_strlcpy(sms->dst.addr, addr, sizeof(sms->dst.addr));
420 sms->dst.ton = dbi_result_get_ulonglong(result, "dest_ton");
421 sms->dst.npi = dbi_result_get_ulonglong(result, "dest_npi");
422
423 sms->user_data_len = dbi_result_get_field_length(result, "user_data");
424 user_data = dbi_result_get_binary(result, "user_data");
425 if (sms->user_data_len > sizeof(sms->user_data))
426 sms->user_data_len = (uint8_t) sizeof(sms->user_data);
427 memcpy(sms->user_data, user_data, sms->user_data_len);
428
429 text = dbi_result_get_string(result, "text");
430 if (text)
431 osmo_strlcpy(sms->text, text, sizeof(sms->text));
432 return sms;
433}
434
435static int update_db_revision_4(void)
436{
437 dbi_result result;
438 struct gsm_sms *sms;
439
440 LOGP(DDB, LOGL_NOTICE, "Going to migrate from revision 4\n");
441
442 result = dbi_conn_query(conn, "BEGIN EXCLUSIVE TRANSACTION");
443 if (!result) {
444 LOGP(DDB, LOGL_ERROR,
445 "Failed to begin transaction (upgrade from rev 4)\n");
446 return -EINVAL;
447 }
448 dbi_result_free(result);
449
450 /* Rename old SMS table to be able create a new one */
451 result = dbi_conn_query(conn, "ALTER TABLE SMS RENAME TO SMS_4");
452 if (!result) {
453 LOGP(DDB, LOGL_ERROR,
454 "Failed to rename the old SMS table (upgrade from rev 4).\n");
455 goto rollback;
456 }
457 dbi_result_free(result);
458
459 /* Create new SMS table with all the bells and whistles! */
460 result = dbi_conn_query(conn, create_stmts[SCHEMA_SMS]);
461 if (!result) {
462 LOGP(DDB, LOGL_ERROR,
463 "Failed to create a new SMS table (upgrade from rev 4).\n");
464 goto rollback;
465 }
466 dbi_result_free(result);
467
468 /* Cycle through old messages and convert them to the new format */
469 result = dbi_conn_query(conn, "SELECT * FROM SMS_4");
470 if (!result) {
471 LOGP(DDB, LOGL_ERROR,
472 "Failed fetch messages from the old SMS table (upgrade from rev 4).\n");
473 goto rollback;
474 }
Neels Hofmeyrdb827292017-12-10 14:45:15 +0100475 while (next_row(result)) {
Pablo Neira Ayuso9891dae2017-08-07 14:01:33 +0100476 sms = sms_from_result_v4(result);
477 if (db_sms_store(sms) != 0) {
478 LOGP(DDB, LOGL_ERROR, "Failed to store message to the new SMS table(upgrade from rev 4).\n");
479 sms_free(sms);
480 dbi_result_free(result);
481 goto rollback;
482 }
483 sms_free(sms);
484 }
485 dbi_result_free(result);
486
487 /* Remove the temporary table */
488 result = dbi_conn_query(conn, "DROP TABLE SMS_4");
489 if (!result) {
490 LOGP(DDB, LOGL_ERROR,
491 "Failed to drop the old SMS table (upgrade from rev 4).\n");
492 goto rollback;
493 }
494 dbi_result_free(result);
495
496 /* We're done. Bump DB Meta revision to 4 */
497 result = dbi_conn_query(conn,
498 "UPDATE Meta "
499 "SET value = '5' "
500 "WHERE key = 'revision'");
501 if (!result) {
502 LOGP(DDB, LOGL_ERROR,
503 "Failed to update DB schema revision (upgrade from rev 4).\n");
504 goto rollback;
505 }
506 dbi_result_free(result);
507
508 result = dbi_conn_query(conn, "COMMIT TRANSACTION");
509 if (!result) {
510 LOGP(DDB, LOGL_ERROR,
511 "Failed to commit the transaction (upgrade from rev 4)\n");
512 return -EINVAL;
513 } else {
514 dbi_result_free(result);
515 }
516
517 /* Shrink DB file size by actually wiping out SMS_4 table data */
518 result = dbi_conn_query(conn, "VACUUM");
519 if (!result)
520 LOGP(DDB, LOGL_ERROR,
521 "VACUUM failed. Ignoring it (upgrade from rev 4).\n");
522 else
523 dbi_result_free(result);
524
525 return 0;
526
527rollback:
528 result = dbi_conn_query(conn, "ROLLBACK TRANSACTION");
529 if (!result)
530 LOGP(DDB, LOGL_ERROR,
531 "Rollback failed (upgrade from rev 4).\n");
532 else
533 dbi_result_free(result);
534 return -EINVAL;
535}
536
Harald Welted0b7b772009-08-09 19:03:42 +0200537static int check_db_revision(void)
538{
539 dbi_result result;
Jan Luebbebfbdeec2012-12-27 00:27:16 +0100540 const char *rev_s;
Vadim Yanitskiya8d8e932016-05-13 15:38:09 +0600541 int db_rev = 0;
Harald Welted0b7b772009-08-09 19:03:42 +0200542
Vadim Yanitskiya8d8e932016-05-13 15:38:09 +0600543 /* Make a query */
Harald Welted0b7b772009-08-09 19:03:42 +0200544 result = dbi_conn_query(conn,
Vadim Yanitskiya8d8e932016-05-13 15:38:09 +0600545 "SELECT value FROM Meta "
546 "WHERE key = 'revision'");
547
Harald Welted0b7b772009-08-09 19:03:42 +0200548 if (!result)
549 return -EINVAL;
550
Neels Hofmeyrdb827292017-12-10 14:45:15 +0100551 if (!next_row(result)) {
Harald Welted0b7b772009-08-09 19:03:42 +0200552 dbi_result_free(result);
553 return -EINVAL;
554 }
Vadim Yanitskiya8d8e932016-05-13 15:38:09 +0600555
556 /* Fetch the DB schema revision */
Jan Luebbebfbdeec2012-12-27 00:27:16 +0100557 rev_s = dbi_result_get_string(result, "value");
558 if (!rev_s) {
Harald Welted0b7b772009-08-09 19:03:42 +0200559 dbi_result_free(result);
560 return -EINVAL;
561 }
Vadim Yanitskiya8d8e932016-05-13 15:38:09 +0600562
563 if (!strcmp(rev_s, SCHEMA_REVISION)) {
564 /* Everything is fine */
Jan Luebbebfbdeec2012-12-27 00:27:16 +0100565 dbi_result_free(result);
Vadim Yanitskiya8d8e932016-05-13 15:38:09 +0600566 return 0;
567 }
568
569 db_rev = atoi(rev_s);
570 dbi_result_free(result);
571
572 /* Incremental migration waterfall */
573 switch (db_rev) {
574 case 2:
575 if (update_db_revision_2())
576 goto error;
577 case 3:
578 if (update_db_revision_3())
579 goto error;
Pablo Neira Ayuso9891dae2017-08-07 14:01:33 +0100580 case 4:
581 if (update_db_revision_4())
582 goto error;
Vadim Yanitskiya8d8e932016-05-13 15:38:09 +0600583
584 /* The end of waterfall */
585 break;
586 default:
587 LOGP(DDB, LOGL_FATAL,
588 "Invalid database schema revision '%d'.\n", db_rev);
Jan Luebbebfbdeec2012-12-27 00:27:16 +0100589 return -EINVAL;
590 }
591
Jan Luebbebfbdeec2012-12-27 00:27:16 +0100592 return 0;
Vadim Yanitskiya8d8e932016-05-13 15:38:09 +0600593
594error:
595 LOGP(DDB, LOGL_FATAL, "Failed to update database "
596 "from schema revision '%d'.\n", db_rev);
597 return -EINVAL;
Jan Luebbebfbdeec2012-12-27 00:27:16 +0100598}
599
600static int db_configure(void)
601{
602 dbi_result result;
603
604 result = dbi_conn_query(conn,
605 "PRAGMA synchronous = FULL");
606 if (!result)
607 return -EINVAL;
Harald Welted0b7b772009-08-09 19:03:42 +0200608
609 dbi_result_free(result);
610 return 0;
611}
612
Harald Welte0b906d02009-12-24 11:21:42 +0100613int db_init(const char *name)
614{
Pau Espin Pedrolc22e54d2017-12-15 18:28:43 +0100615 dbi_initialize_r(NULL, &inst);
Harald Welte0b906d02009-12-24 11:21:42 +0100616
Pau Espin Pedrolc22e54d2017-12-15 18:28:43 +0100617 conn = dbi_conn_new_r("sqlite3", inst);
Harald Welte0b906d02009-12-24 11:21:42 +0100618 if (conn == NULL) {
Harald Welteae1f1592009-12-24 11:39:14 +0100619 LOGP(DDB, LOGL_FATAL, "Failed to create connection.\n");
Jan Luebbe5c15c852008-12-27 15:59:25 +0000620 return 1;
621 }
Jan Luebbe7398eb92008-12-27 00:45:41 +0000622
Holger Freyther12aa50d2009-01-01 18:02:05 +0000623 dbi_conn_error_handler( conn, db_error_func, NULL );
Jan Luebbe7398eb92008-12-27 00:45:41 +0000624
Jan Luebbe5c15c852008-12-27 15:59:25 +0000625 /* MySQL
626 dbi_conn_set_option(conn, "host", "localhost");
627 dbi_conn_set_option(conn, "username", "your_name");
628 dbi_conn_set_option(conn, "password", "your_password");
629 dbi_conn_set_option(conn, "dbname", "your_dbname");
630 dbi_conn_set_option(conn, "encoding", "UTF-8");
631 */
Jan Luebbe7398eb92008-12-27 00:45:41 +0000632
Jan Luebbe5c15c852008-12-27 15:59:25 +0000633 /* SqLite 3 */
Holger Freyther12aa50d2009-01-01 18:02:05 +0000634 db_basename = strdup(name);
635 db_dirname = strdup(name);
Holger Freytherbde36102008-12-28 22:51:39 +0000636 dbi_conn_set_option(conn, "sqlite3_dbdir", dirname(db_dirname));
637 dbi_conn_set_option(conn, "dbname", basename(db_basename));
Jan Luebbe7398eb92008-12-27 00:45:41 +0000638
Harald Welted0b7b772009-08-09 19:03:42 +0200639 if (dbi_conn_connect(conn) < 0)
640 goto out_err;
641
Jan Luebbe5c15c852008-12-27 15:59:25 +0000642 return 0;
Harald Welted0b7b772009-08-09 19:03:42 +0200643
644out_err:
645 free(db_dirname);
646 free(db_basename);
647 db_dirname = db_basename = NULL;
648 return -1;
Jan Luebbe7398eb92008-12-27 00:45:41 +0000649}
650
Harald Welted0b7b772009-08-09 19:03:42 +0200651
Harald Welted1476bc2011-07-16 13:24:09 +0200652int db_prepare(void)
Harald Welte0b906d02009-12-24 11:21:42 +0100653{
Jan Luebbe5c15c852008-12-27 15:59:25 +0000654 dbi_result result;
Harald Welte7e310b12009-03-30 20:56:32 +0000655 int i;
Holger Freytherb4064bc2009-02-23 00:50:31 +0000656
Harald Welte7e310b12009-03-30 20:56:32 +0000657 for (i = 0; i < ARRAY_SIZE(create_stmts); i++) {
658 result = dbi_conn_query(conn, create_stmts[i]);
Harald Welte0b906d02009-12-24 11:21:42 +0100659 if (!result) {
Harald Welteae1f1592009-12-24 11:39:14 +0100660 LOGP(DDB, LOGL_ERROR,
661 "Failed to create some table.\n");
Harald Welte7e310b12009-03-30 20:56:32 +0000662 return 1;
663 }
664 dbi_result_free(result);
Holger Freytherb4064bc2009-02-23 00:50:31 +0000665 }
Holger Freytherb4064bc2009-02-23 00:50:31 +0000666
Holger Hans Peter Freyther850326e2009-08-10 08:36:04 +0200667 if (check_db_revision() < 0) {
Harald Welteae1f1592009-12-24 11:39:14 +0100668 LOGP(DDB, LOGL_FATAL, "Database schema revision invalid, "
Holger Hans Peter Freyther850326e2009-08-10 08:36:04 +0200669 "please update your database schema\n");
670 return -1;
671 }
672
Jan Luebbebfbdeec2012-12-27 00:27:16 +0100673 db_configure();
674
Jan Luebbe5c15c852008-12-27 15:59:25 +0000675 return 0;
Jan Luebbe7398eb92008-12-27 00:45:41 +0000676}
677
Harald Welted1476bc2011-07-16 13:24:09 +0200678int db_fini(void)
Harald Welte0b906d02009-12-24 11:21:42 +0100679{
Jan Luebbe5c15c852008-12-27 15:59:25 +0000680 dbi_conn_close(conn);
Pau Espin Pedrolc22e54d2017-12-15 18:28:43 +0100681 dbi_shutdown_r(inst);
Holger Freytherbde36102008-12-28 22:51:39 +0000682
Harald Welte2c5f4c62011-07-16 13:22:57 +0200683 free(db_dirname);
684 free(db_basename);
Jan Luebbe5c15c852008-12-27 15:59:25 +0000685 return 0;
Jan Luebbe7398eb92008-12-27 00:45:41 +0000686}
687
Harald Welte7e310b12009-03-30 20:56:32 +0000688/* store an [unsent] SMS to the database */
689int db_sms_store(struct gsm_sms *sms)
690{
691 dbi_result result;
Holger Hans Peter Freytherca3c2562013-10-08 03:17:30 +0200692 char *q_text, *q_daddr, *q_saddr;
Harald Welte76042182009-08-08 16:03:15 +0200693 unsigned char *q_udata;
694 char *validity_timestamp = "2222-2-2";
695
696 /* FIXME: generate validity timestamp based on validity_minutes */
Harald Welte7e310b12009-03-30 20:56:32 +0000697
698 dbi_conn_quote_string_copy(conn, (char *)sms->text, &q_text);
Harald Weltec0de14d2012-11-23 23:35:01 +0100699 dbi_conn_quote_string_copy(conn, (char *)sms->dst.addr, &q_daddr);
Holger Hans Peter Freytherca3c2562013-10-08 03:17:30 +0200700 dbi_conn_quote_string_copy(conn, (char *)sms->src.addr, &q_saddr);
Harald Welte76042182009-08-08 16:03:15 +0200701 dbi_conn_quote_binary_copy(conn, sms->user_data, sms->user_data_len,
702 &q_udata);
Holger Hans Peter Freytherca3c2562013-10-08 03:17:30 +0200703
Harald Weltef3efc592009-07-27 20:11:35 +0200704 /* FIXME: correct validity period */
Harald Welte7e310b12009-03-30 20:56:32 +0000705 result = dbi_conn_queryf(conn,
706 "INSERT INTO SMS "
Alexander Chemerisca7ed2d2013-10-08 03:17:32 +0200707 "(created, valid_until, "
Pablo Neira Ayuso9891dae2017-08-07 14:01:33 +0100708 "reply_path_req, status_rep_req, is_report, "
709 "msg_ref, protocol_id, data_coding_scheme, "
710 "ud_hdr_ind, "
Holger Hans Peter Freytherca3c2562013-10-08 03:17:30 +0200711 "user_data, text, "
712 "dest_addr, dest_ton, dest_npi, "
713 "src_addr, src_ton, src_npi) VALUES "
Alexander Chemerisca7ed2d2013-10-08 03:17:32 +0200714 "(datetime('now'), %u, "
Holger Hans Peter Freytherca3c2562013-10-08 03:17:30 +0200715 "%u, %u, %u, "
Pablo Neira Ayuso9891dae2017-08-07 14:01:33 +0100716 "%u, %u, %u, "
717 "%u, "
Holger Hans Peter Freytherca3c2562013-10-08 03:17:30 +0200718 "%s, %s, "
719 "%s, %u, %u, "
720 "%s, %u, %u)",
Alexander Chemerisca7ed2d2013-10-08 03:17:32 +0200721 validity_timestamp,
Pablo Neira Ayuso9891dae2017-08-07 14:01:33 +0100722 sms->reply_path_req, sms->status_rep_req, sms->is_report,
723 sms->msg_ref, sms->protocol_id, sms->data_coding_scheme,
724 sms->ud_hdr_ind,
Holger Hans Peter Freytherca3c2562013-10-08 03:17:30 +0200725 q_udata, q_text,
726 q_daddr, sms->dst.ton, sms->dst.npi,
727 q_saddr, sms->src.ton, sms->src.npi);
Harald Welte7e310b12009-03-30 20:56:32 +0000728 free(q_text);
Harald Welte76042182009-08-08 16:03:15 +0200729 free(q_udata);
Holger Hans Peter Freytherca3c2562013-10-08 03:17:30 +0200730 free(q_daddr);
731 free(q_saddr);
Harald Welte7e310b12009-03-30 20:56:32 +0000732
733 if (!result)
734 return -EIO;
735
736 dbi_result_free(result);
737 return 0;
738}
739
Harald Welte2ebabca2009-08-09 19:05:21 +0200740static struct gsm_sms *sms_from_result(struct gsm_network *net, dbi_result result)
Harald Welte7e310b12009-03-30 20:56:32 +0000741{
Harald Welte76042182009-08-08 16:03:15 +0200742 struct gsm_sms *sms = sms_alloc();
Holger Hans Peter Freytherca3c2562013-10-08 03:17:30 +0200743 const char *text, *daddr, *saddr;
Harald Welte76042182009-08-08 16:03:15 +0200744 const unsigned char *user_data;
Harald Welte7e310b12009-03-30 20:56:32 +0000745
Harald Welte76042182009-08-08 16:03:15 +0200746 if (!sms)
Harald Welte7e310b12009-03-30 20:56:32 +0000747 return NULL;
Harald Welte7e310b12009-03-30 20:56:32 +0000748
Harald Weltebe3e3782009-07-05 14:06:41 +0200749 sms->id = dbi_result_get_ulonglong(result, "id");
Harald Welte7e310b12009-03-30 20:56:32 +0000750
Harald Weltef3efc592009-07-27 20:11:35 +0200751 /* FIXME: validity */
Harald Welte76042182009-08-08 16:03:15 +0200752 /* FIXME: those should all be get_uchar, but sqlite3 is braindead */
Keithc601adc2017-08-16 22:45:07 +0200753 sms->created = dbi_result_get_datetime(result, "created");
Holger Hans Peter Freytherb115cb62014-07-03 14:00:30 +0200754 sms->reply_path_req = dbi_result_get_ulonglong(result, "reply_path_req");
755 sms->status_rep_req = dbi_result_get_ulonglong(result, "status_rep_req");
Pablo Neira Ayuso9891dae2017-08-07 14:01:33 +0100756 sms->is_report = dbi_result_get_ulonglong(result, "is_report");
757 sms->msg_ref = dbi_result_get_ulonglong(result, "msg_ref");
Holger Hans Peter Freytherb115cb62014-07-03 14:00:30 +0200758 sms->ud_hdr_ind = dbi_result_get_ulonglong(result, "ud_hdr_ind");
759 sms->protocol_id = dbi_result_get_ulonglong(result, "protocol_id");
760 sms->data_coding_scheme = dbi_result_get_ulonglong(result,
Harald Weltef3efc592009-07-27 20:11:35 +0200761 "data_coding_scheme");
762
Holger Hans Peter Freytherb115cb62014-07-03 14:00:30 +0200763 sms->dst.npi = dbi_result_get_ulonglong(result, "dest_npi");
764 sms->dst.ton = dbi_result_get_ulonglong(result, "dest_ton");
Harald Welte76042182009-08-08 16:03:15 +0200765 daddr = dbi_result_get_string(result, "dest_addr");
Neels Hofmeyr93bafb62017-01-13 03:12:08 +0100766 if (daddr)
767 osmo_strlcpy(sms->dst.addr, daddr, sizeof(sms->dst.addr));
Harald Welte2483f1b2016-06-19 18:06:02 +0200768 sms->receiver = vlr_subscr_find_by_msisdn(net->vlr, sms->dst.addr);
Harald Welte76042182009-08-08 16:03:15 +0200769
Holger Hans Peter Freytherb115cb62014-07-03 14:00:30 +0200770 sms->src.npi = dbi_result_get_ulonglong(result, "src_npi");
771 sms->src.ton = dbi_result_get_ulonglong(result, "src_ton");
Holger Hans Peter Freytherca3c2562013-10-08 03:17:30 +0200772 saddr = dbi_result_get_string(result, "src_addr");
Neels Hofmeyr93bafb62017-01-13 03:12:08 +0100773 if (saddr)
774 osmo_strlcpy(sms->src.addr, saddr, sizeof(sms->src.addr));
Holger Hans Peter Freytherca3c2562013-10-08 03:17:30 +0200775
Harald Welte76042182009-08-08 16:03:15 +0200776 sms->user_data_len = dbi_result_get_field_length(result, "user_data");
777 user_data = dbi_result_get_binary(result, "user_data");
778 if (sms->user_data_len > sizeof(sms->user_data))
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +0200779 sms->user_data_len = (uint8_t) sizeof(sms->user_data);
Harald Welte76042182009-08-08 16:03:15 +0200780 memcpy(sms->user_data, user_data, sms->user_data_len);
Harald Weltebe3e3782009-07-05 14:06:41 +0200781
782 text = dbi_result_get_string(result, "text");
Neels Hofmeyr93bafb62017-01-13 03:12:08 +0100783 if (text)
784 osmo_strlcpy(sms->text, text, sizeof(sms->text));
Harald Welte2ebabca2009-08-09 19:05:21 +0200785 return sms;
786}
787
Holger Hans Peter Freyther812dad02010-12-24 23:18:31 +0100788struct gsm_sms *db_sms_get(struct gsm_network *net, unsigned long long id)
789{
790 dbi_result result;
791 struct gsm_sms *sms;
792
793 result = dbi_conn_queryf(conn,
794 "SELECT * FROM SMS WHERE SMS.id = %llu", id);
795 if (!result)
796 return NULL;
797
Neels Hofmeyrdb827292017-12-10 14:45:15 +0100798 if (!next_row(result)) {
Holger Hans Peter Freyther812dad02010-12-24 23:18:31 +0100799 dbi_result_free(result);
800 return NULL;
801 }
802
803 sms = sms_from_result(net, result);
804
805 dbi_result_free(result);
806
807 return sms;
808}
809
Harald Welte2483f1b2016-06-19 18:06:02 +0200810struct gsm_sms *db_sms_get_next_unsent(struct gsm_network *net,
811 unsigned long long min_sms_id,
812 unsigned int max_failed)
Harald Welte2ebabca2009-08-09 19:05:21 +0200813{
814 dbi_result result;
815 struct gsm_sms *sms;
816
817 result = dbi_conn_queryf(conn,
Harald Welte2483f1b2016-06-19 18:06:02 +0200818 "SELECT * FROM SMS"
819 " WHERE sent IS NULL"
820 " AND id >= %llu"
821 " AND deliver_attempts <= %u"
822 " ORDER BY id LIMIT 1",
823 min_sms_id, max_failed);
Harald Welte2ebabca2009-08-09 19:05:21 +0200824
Sylvain Munautff1f19e2009-12-22 13:22:29 +0100825 if (!result)
826 return NULL;
827
Neels Hofmeyrdb827292017-12-10 14:45:15 +0100828 if (!next_row(result)) {
Sylvain Munautff1f19e2009-12-22 13:22:29 +0100829 dbi_result_free(result);
830 return NULL;
831 }
832
833 sms = sms_from_result(net, result);
834
835 dbi_result_free(result);
836
837 return sms;
838}
839
Sylvain Munautd5778fc2009-12-21 01:09:57 +0100840/* retrieve the next unsent SMS for a given subscriber */
Harald Welte2483f1b2016-06-19 18:06:02 +0200841struct gsm_sms *db_sms_get_unsent_for_subscr(struct vlr_subscr *vsub,
842 unsigned int max_failed)
Harald Welte2ebabca2009-08-09 19:05:21 +0200843{
Harald Welte2483f1b2016-06-19 18:06:02 +0200844 struct gsm_network *net = vsub->vlr->user_ctx;
Harald Welte2ebabca2009-08-09 19:05:21 +0200845 dbi_result result;
846 struct gsm_sms *sms;
Neels Hofmeyr50d09002017-12-05 12:39:00 +0100847 char *q_msisdn;
Harald Welte2ebabca2009-08-09 19:05:21 +0200848
Harald Welte2483f1b2016-06-19 18:06:02 +0200849 if (!vsub->lu_complete)
850 return NULL;
851
Neels Hofmeyrf6704f12017-12-05 12:34:44 +0100852 /* A subscriber having no phone number cannot possibly receive SMS. */
853 if (*vsub->msisdn == '\0')
854 return NULL;
855
Neels Hofmeyr50d09002017-12-05 12:39:00 +0100856 dbi_conn_quote_string_copy(conn, vsub->msisdn, &q_msisdn);
Harald Welte2ebabca2009-08-09 19:05:21 +0200857 result = dbi_conn_queryf(conn,
Harald Welte2483f1b2016-06-19 18:06:02 +0200858 "SELECT * FROM SMS"
859 " WHERE sent IS NULL"
Neels Hofmeyr50d09002017-12-05 12:39:00 +0100860 " AND dest_addr = %s"
Harald Welte2483f1b2016-06-19 18:06:02 +0200861 " AND deliver_attempts <= %u"
862 " ORDER BY id LIMIT 1",
Neels Hofmeyr50d09002017-12-05 12:39:00 +0100863 q_msisdn, max_failed);
864 free(q_msisdn);
865
Harald Welte2ebabca2009-08-09 19:05:21 +0200866 if (!result)
867 return NULL;
868
Neels Hofmeyrdb827292017-12-10 14:45:15 +0100869 if (!next_row(result)) {
Harald Welte2ebabca2009-08-09 19:05:21 +0200870 dbi_result_free(result);
871 return NULL;
872 }
873
Harald Welte2483f1b2016-06-19 18:06:02 +0200874 sms = sms_from_result(net, result);
875
876 dbi_result_free(result);
877
878 return sms;
879}
880
881struct gsm_sms *db_sms_get_next_unsent_rr_msisdn(struct gsm_network *net,
882 const char *last_msisdn,
883 unsigned int max_failed)
884{
885 dbi_result result;
886 struct gsm_sms *sms;
Neels Hofmeyr50d09002017-12-05 12:39:00 +0100887 char *q_last_msisdn;
Harald Welte2483f1b2016-06-19 18:06:02 +0200888
Neels Hofmeyr50d09002017-12-05 12:39:00 +0100889 dbi_conn_quote_string_copy(conn, last_msisdn, &q_last_msisdn);
Harald Welte2483f1b2016-06-19 18:06:02 +0200890 result = dbi_conn_queryf(conn,
891 "SELECT * FROM SMS"
892 " WHERE sent IS NULL"
Neels Hofmeyr50d09002017-12-05 12:39:00 +0100893 " AND dest_addr > %s"
Harald Welte2483f1b2016-06-19 18:06:02 +0200894 " AND deliver_attempts <= %u"
895 " ORDER BY dest_addr, id LIMIT 1",
Neels Hofmeyr50d09002017-12-05 12:39:00 +0100896 q_last_msisdn, max_failed);
897 free(q_last_msisdn);
898
Harald Welte2483f1b2016-06-19 18:06:02 +0200899 if (!result)
900 return NULL;
901
Neels Hofmeyrdb827292017-12-10 14:45:15 +0100902 if (!next_row(result)) {
Harald Welte2483f1b2016-06-19 18:06:02 +0200903 dbi_result_free(result);
904 return NULL;
905 }
906
907 sms = sms_from_result(net, result);
Harald Welte2ebabca2009-08-09 19:05:21 +0200908
909 dbi_result_free(result);
910
Harald Welte7e310b12009-03-30 20:56:32 +0000911 return sms;
912}
913
Alexander Chemeris1e77e3d2014-03-08 21:27:37 +0100914/* mark a given SMS as delivered */
915int db_sms_mark_delivered(struct gsm_sms *sms)
Harald Welte7e310b12009-03-30 20:56:32 +0000916{
917 dbi_result result;
918
919 result = dbi_conn_queryf(conn,
920 "UPDATE SMS "
921 "SET sent = datetime('now') "
922 "WHERE id = %llu", sms->id);
923 if (!result) {
Harald Welteae1f1592009-12-24 11:39:14 +0100924 LOGP(DDB, LOGL_ERROR, "Failed to mark SMS %llu as sent.\n", sms->id);
Harald Welte7e310b12009-03-30 20:56:32 +0000925 return 1;
926 }
927
928 dbi_result_free(result);
929 return 0;
930}
Harald Welte (local)db552c52009-08-15 20:15:14 +0200931
932/* increase the number of attempted deliveries */
933int db_sms_inc_deliver_attempts(struct gsm_sms *sms)
934{
935 dbi_result result;
936
937 result = dbi_conn_queryf(conn,
938 "UPDATE SMS "
939 "SET deliver_attempts = deliver_attempts + 1 "
940 "WHERE id = %llu", sms->id);
941 if (!result) {
Harald Welteae1f1592009-12-24 11:39:14 +0100942 LOGP(DDB, LOGL_ERROR, "Failed to inc deliver attempts for "
943 "SMS %llu.\n", sms->id);
Harald Welte (local)db552c52009-08-15 20:15:14 +0200944 return 1;
945 }
946
947 dbi_result_free(result);
948 return 0;
949}
Harald Welte (local)026531e2009-08-16 10:40:10 +0200950
Harald Welte2483f1b2016-06-19 18:06:02 +0200951/* Drop all pending SMS to or from the given extension */
952int db_sms_delete_by_msisdn(const char *msisdn)
Harald Welte (local)026531e2009-08-16 10:40:10 +0200953{
954 dbi_result result;
Neels Hofmeyr50d09002017-12-05 12:39:00 +0100955 char *q_msisdn;
Harald Welte2483f1b2016-06-19 18:06:02 +0200956 if (!msisdn || !*msisdn)
957 return 0;
Neels Hofmeyr50d09002017-12-05 12:39:00 +0100958
959 dbi_conn_quote_string_copy(conn, msisdn, &q_msisdn);
Harald Welte (local)026531e2009-08-16 10:40:10 +0200960 result = dbi_conn_queryf(conn,
Harald Welte2483f1b2016-06-19 18:06:02 +0200961 "DELETE FROM SMS WHERE src_addr=%s OR dest_addr=%s",
Neels Hofmeyr50d09002017-12-05 12:39:00 +0100962 q_msisdn, q_msisdn);
963 free(q_msisdn);
964
Harald Welte2483f1b2016-06-19 18:06:02 +0200965 if (!result) {
966 LOGP(DDB, LOGL_ERROR,
967 "Failed to delete SMS for %s\n", msisdn);
968 return -1;
969 }
Harald Welte (local)026531e2009-08-16 10:40:10 +0200970 dbi_result_free(result);
971 return 0;
972}
Harald Welteffa55a42009-12-22 19:07:32 +0100973
Pablo Neira Ayusodfb342c2011-05-06 12:13:10 +0200974int db_store_counter(struct osmo_counter *ctr)
Harald Welteffa55a42009-12-22 19:07:32 +0100975{
976 dbi_result result;
977 char *q_name;
978
979 dbi_conn_quote_string_copy(conn, ctr->name, &q_name);
980
981 result = dbi_conn_queryf(conn,
982 "INSERT INTO Counters "
983 "(timestamp,name,value) VALUES "
984 "(datetime('now'),%s,%lu)", q_name, ctr->value);
985
986 free(q_name);
987
988 if (!result)
989 return -EIO;
990
991 dbi_result_free(result);
992 return 0;
993}
Harald Weltef2b4cd72010-05-13 11:45:07 +0200994
995static int db_store_rate_ctr(struct rate_ctr_group *ctrg, unsigned int num,
996 char *q_prefix)
997{
998 dbi_result result;
999 char *q_name;
1000
1001 dbi_conn_quote_string_copy(conn, ctrg->desc->ctr_desc[num].name,
1002 &q_name);
1003
1004 result = dbi_conn_queryf(conn,
Harald Weltec1919862010-05-13 12:55:20 +02001005 "Insert INTO RateCounters "
Harald Welted94d6a02010-05-14 17:38:47 +02001006 "(timestamp,name,idx,value) VALUES "
Harald Weltec1919862010-05-13 12:55:20 +02001007 "(datetime('now'),%s.%s,%u,%"PRIu64")",
1008 q_prefix, q_name, ctrg->idx, ctrg->ctr[num].current);
Harald Weltef2b4cd72010-05-13 11:45:07 +02001009
1010 free(q_name);
1011
1012 if (!result)
1013 return -EIO;
1014
1015 dbi_result_free(result);
1016 return 0;
1017}
1018
1019int db_store_rate_ctr_group(struct rate_ctr_group *ctrg)
1020{
1021 unsigned int i;
1022 char *q_prefix;
1023
Harald Weltec1919862010-05-13 12:55:20 +02001024 dbi_conn_quote_string_copy(conn, ctrg->desc->group_name_prefix, &q_prefix);
Harald Weltef2b4cd72010-05-13 11:45:07 +02001025
1026 for (i = 0; i < ctrg->desc->num_ctr; i++)
1027 db_store_rate_ctr(ctrg, i, q_prefix);
1028
1029 free(q_prefix);
1030
1031 return 0;
1032}