blob: ae7e2876bc2eafbadbdd211276b3a0ce83222505 [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
Harald Weltef2b4cd72010-05-13 11:45:07 +020032#include <openbsc/gsm_data.h>
Holger Hans Peter Freyther28dcbc52010-12-22 18:21:14 +010033#include <openbsc/gsm_subscriber.h>
Harald Weltef2b4cd72010-05-13 11:45:07 +020034#include <openbsc/gsm_04_11.h>
35#include <openbsc/db.h>
Harald Weltef2b4cd72010-05-13 11:45:07 +020036#include <openbsc/debug.h>
Harald Welte2483f1b2016-06-19 18:06:02 +020037#include <openbsc/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;
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
Harald Welte0b906d02009-12-24 11:21:42 +0100184void db_error_func(dbi_conn conn, void *data)
185{
186 const char *msg;
Jan Luebbe5c15c852008-12-27 15:59:25 +0000187 dbi_conn_error(conn, &msg);
Harald Welteae1f1592009-12-24 11:39:14 +0100188 LOGP(DDB, LOGL_ERROR, "DBI: %s\n", msg);
Harald Weltec7548a12014-07-10 20:18:15 +0200189 osmo_log_backtrace(DDB, LOGL_ERROR);
Jan Luebbe7398eb92008-12-27 00:45:41 +0000190}
191
Jan Luebbebfbdeec2012-12-27 00:27:16 +0100192static int update_db_revision_2(void)
193{
194 dbi_result result;
195
196 result = dbi_conn_query(conn,
197 "ALTER TABLE Subscriber "
198 "ADD COLUMN expire_lu "
199 "TIMESTAMP DEFAULT NULL");
200 if (!result) {
201 LOGP(DDB, LOGL_ERROR,
Alexander Chemeris7e20f642014-03-07 16:59:53 +0100202 "Failed to alter table Subscriber (upgrade from rev 2).\n");
Jan Luebbebfbdeec2012-12-27 00:27:16 +0100203 return -EINVAL;
204 }
205 dbi_result_free(result);
206
207 result = dbi_conn_query(conn,
208 "UPDATE Meta "
209 "SET value = '3' "
210 "WHERE key = 'revision'");
211 if (!result) {
212 LOGP(DDB, LOGL_ERROR,
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100213 "Failed to update DB schema revision (upgrade from rev 2).\n");
Jan Luebbebfbdeec2012-12-27 00:27:16 +0100214 return -EINVAL;
215 }
216 dbi_result_free(result);
217
218 return 0;
219}
220
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100221/**
222 * Copied from the normal sms_from_result_v3 to avoid having
223 * to make sure that the real routine will remain backward
224 * compatible.
225 */
226static struct gsm_sms *sms_from_result_v3(dbi_result result)
227{
228 struct gsm_sms *sms = sms_alloc();
229 long long unsigned int sender_id;
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100230 const char *text, *daddr;
231 const unsigned char *user_data;
232 char buf[32];
Harald Welte2483f1b2016-06-19 18:06:02 +0200233 char *quoted;
234 dbi_result result2;
235 const char *extension;
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100236
237 if (!sms)
238 return NULL;
239
240 sms->id = dbi_result_get_ulonglong(result, "id");
241
Harald Welte2483f1b2016-06-19 18:06:02 +0200242 /* find extension by id, assuming that the subscriber still exists in
243 * the db */
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100244 sender_id = dbi_result_get_ulonglong(result, "sender_id");
245 snprintf(buf, sizeof(buf), "%llu", sender_id);
Harald Welte2483f1b2016-06-19 18:06:02 +0200246
247 dbi_conn_quote_string_copy(conn, buf, &quoted);
248 result2 = dbi_conn_queryf(conn,
249 "SELECT extension FROM Subscriber "
250 "WHERE id = %s ", quoted);
251 free(quoted);
252 extension = dbi_result_get_string(result2, "extension");
253 if (extension)
254 osmo_strlcpy(sms->src.addr, extension, sizeof(sms->src.addr));
255 dbi_result_free(result2);
256 /* got the extension */
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100257
Holger Hans Peter Freytherb115cb62014-07-03 14:00:30 +0200258 sms->reply_path_req = dbi_result_get_ulonglong(result, "reply_path_req");
259 sms->status_rep_req = dbi_result_get_ulonglong(result, "status_rep_req");
260 sms->ud_hdr_ind = dbi_result_get_ulonglong(result, "ud_hdr_ind");
261 sms->protocol_id = dbi_result_get_ulonglong(result, "protocol_id");
262 sms->data_coding_scheme = dbi_result_get_ulonglong(result,
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100263 "data_coding_scheme");
264
265 daddr = dbi_result_get_string(result, "dest_addr");
Neels Hofmeyr93bafb62017-01-13 03:12:08 +0100266 if (daddr)
267 osmo_strlcpy(sms->dst.addr, daddr, sizeof(sms->dst.addr));
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100268
269 sms->user_data_len = dbi_result_get_field_length(result, "user_data");
270 user_data = dbi_result_get_binary(result, "user_data");
271 if (sms->user_data_len > sizeof(sms->user_data))
272 sms->user_data_len = (uint8_t) sizeof(sms->user_data);
273 memcpy(sms->user_data, user_data, sms->user_data_len);
274
275 text = dbi_result_get_string(result, "text");
Neels Hofmeyr93bafb62017-01-13 03:12:08 +0100276 if (text)
277 osmo_strlcpy(sms->text, text, sizeof(sms->text));
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100278 return sms;
279}
280
281static int update_db_revision_3(void)
282{
283 dbi_result result;
284 struct gsm_sms *sms;
285
Holger Hans Peter Freyther61144012014-03-08 16:41:37 +0100286 LOGP(DDB, LOGL_NOTICE, "Going to migrate from revision 3\n");
287
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100288 result = dbi_conn_query(conn, "BEGIN EXCLUSIVE TRANSACTION");
289 if (!result) {
290 LOGP(DDB, LOGL_ERROR,
291 "Failed to begin transaction (upgrade from rev 3)\n");
292 return -EINVAL;
293 }
294 dbi_result_free(result);
295
296 /* Rename old SMS table to be able create a new one */
297 result = dbi_conn_query(conn, "ALTER TABLE SMS RENAME TO SMS_3");
298 if (!result) {
299 LOGP(DDB, LOGL_ERROR,
300 "Failed to rename the old SMS table (upgrade from rev 3).\n");
301 goto rollback;
302 }
303 dbi_result_free(result);
304
305 /* Create new SMS table with all the bells and whistles! */
306 result = dbi_conn_query(conn, create_stmts[SCHEMA_SMS]);
307 if (!result) {
308 LOGP(DDB, LOGL_ERROR,
309 "Failed to create a new SMS table (upgrade from rev 3).\n");
310 goto rollback;
311 }
312 dbi_result_free(result);
313
314 /* Cycle through old messages and convert them to the new format */
Max5c06e402015-07-29 20:20:28 +0200315 result = dbi_conn_query(conn, "SELECT * FROM SMS_3");
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100316 if (!result) {
317 LOGP(DDB, LOGL_ERROR,
318 "Failed fetch messages from the old SMS table (upgrade from rev 3).\n");
319 goto rollback;
320 }
321 while (dbi_result_next_row(result)) {
322 sms = sms_from_result_v3(result);
323 if (db_sms_store(sms) != 0) {
324 LOGP(DDB, LOGL_ERROR, "Failed to store message to the new SMS table(upgrade from rev 3).\n");
325 sms_free(sms);
326 dbi_result_free(result);
327 goto rollback;
328 }
329 sms_free(sms);
330 }
331 dbi_result_free(result);
332
333 /* Remove the temporary table */
334 result = dbi_conn_query(conn, "DROP TABLE SMS_3");
335 if (!result) {
336 LOGP(DDB, LOGL_ERROR,
337 "Failed to drop the old SMS table (upgrade from rev 3).\n");
338 goto rollback;
339 }
340 dbi_result_free(result);
341
342 /* We're done. Bump DB Meta revision to 4 */
343 result = dbi_conn_query(conn,
344 "UPDATE Meta "
345 "SET value = '4' "
346 "WHERE key = 'revision'");
347 if (!result) {
348 LOGP(DDB, LOGL_ERROR,
349 "Failed to update DB schema revision (upgrade from rev 3).\n");
350 goto rollback;
351 }
352 dbi_result_free(result);
353
354 result = dbi_conn_query(conn, "COMMIT TRANSACTION");
355 if (!result) {
356 LOGP(DDB, LOGL_ERROR,
357 "Failed to commit the transaction (upgrade from rev 3)\n");
358 return -EINVAL;
Alexander Couzensf480b352017-02-04 00:20:17 +0100359 } else {
360 dbi_result_free(result);
Holger Hans Peter Freythere7cc9aa2014-03-07 18:17:22 +0100361 }
362
363 /* Shrink DB file size by actually wiping out SMS_3 table data */
364 result = dbi_conn_query(conn, "VACUUM");
365 if (!result)
366 LOGP(DDB, LOGL_ERROR,
367 "VACUUM failed. Ignoring it (upgrade from rev 3).\n");
368 else
369 dbi_result_free(result);
370
371 return 0;
372
373rollback:
374 result = dbi_conn_query(conn, "ROLLBACK TRANSACTION");
375 if (!result)
376 LOGP(DDB, LOGL_ERROR,
377 "Rollback failed (upgrade from rev 3).\n");
378 else
379 dbi_result_free(result);
380 return -EINVAL;
381}
382
Pablo Neira Ayuso9891dae2017-08-07 14:01:33 +0100383/* Just like v3, but there is a new message reference field for status reports,
384 * that is set to zero for existing entries since there is no way we can infer
385 * this.
386 */
387static struct gsm_sms *sms_from_result_v4(dbi_result result)
388{
389 struct gsm_sms *sms = sms_alloc();
390 const unsigned char *user_data;
391 const char *text, *addr;
392
393 if (!sms)
394 return NULL;
395
396 sms->id = dbi_result_get_ulonglong(result, "id");
397
398 sms->reply_path_req = dbi_result_get_ulonglong(result, "reply_path_req");
399 sms->status_rep_req = dbi_result_get_ulonglong(result, "status_rep_req");
400 sms->ud_hdr_ind = dbi_result_get_ulonglong(result, "ud_hdr_ind");
401 sms->protocol_id = dbi_result_get_ulonglong(result, "protocol_id");
402 sms->data_coding_scheme = dbi_result_get_ulonglong(result,
403 "data_coding_scheme");
404
405 addr = dbi_result_get_string(result, "src_addr");
406 osmo_strlcpy(sms->src.addr, addr, sizeof(sms->src.addr));
407 sms->src.ton = dbi_result_get_ulonglong(result, "src_ton");
408 sms->src.npi = dbi_result_get_ulonglong(result, "src_npi");
409
410 addr = dbi_result_get_string(result, "dest_addr");
411 osmo_strlcpy(sms->dst.addr, addr, sizeof(sms->dst.addr));
412 sms->dst.ton = dbi_result_get_ulonglong(result, "dest_ton");
413 sms->dst.npi = dbi_result_get_ulonglong(result, "dest_npi");
414
415 sms->user_data_len = dbi_result_get_field_length(result, "user_data");
416 user_data = dbi_result_get_binary(result, "user_data");
417 if (sms->user_data_len > sizeof(sms->user_data))
418 sms->user_data_len = (uint8_t) sizeof(sms->user_data);
419 memcpy(sms->user_data, user_data, sms->user_data_len);
420
421 text = dbi_result_get_string(result, "text");
422 if (text)
423 osmo_strlcpy(sms->text, text, sizeof(sms->text));
424 return sms;
425}
426
427static int update_db_revision_4(void)
428{
429 dbi_result result;
430 struct gsm_sms *sms;
431
432 LOGP(DDB, LOGL_NOTICE, "Going to migrate from revision 4\n");
433
434 result = dbi_conn_query(conn, "BEGIN EXCLUSIVE TRANSACTION");
435 if (!result) {
436 LOGP(DDB, LOGL_ERROR,
437 "Failed to begin transaction (upgrade from rev 4)\n");
438 return -EINVAL;
439 }
440 dbi_result_free(result);
441
442 /* Rename old SMS table to be able create a new one */
443 result = dbi_conn_query(conn, "ALTER TABLE SMS RENAME TO SMS_4");
444 if (!result) {
445 LOGP(DDB, LOGL_ERROR,
446 "Failed to rename the old SMS table (upgrade from rev 4).\n");
447 goto rollback;
448 }
449 dbi_result_free(result);
450
451 /* Create new SMS table with all the bells and whistles! */
452 result = dbi_conn_query(conn, create_stmts[SCHEMA_SMS]);
453 if (!result) {
454 LOGP(DDB, LOGL_ERROR,
455 "Failed to create a new SMS table (upgrade from rev 4).\n");
456 goto rollback;
457 }
458 dbi_result_free(result);
459
460 /* Cycle through old messages and convert them to the new format */
461 result = dbi_conn_query(conn, "SELECT * FROM SMS_4");
462 if (!result) {
463 LOGP(DDB, LOGL_ERROR,
464 "Failed fetch messages from the old SMS table (upgrade from rev 4).\n");
465 goto rollback;
466 }
467 while (dbi_result_next_row(result)) {
468 sms = sms_from_result_v4(result);
469 if (db_sms_store(sms) != 0) {
470 LOGP(DDB, LOGL_ERROR, "Failed to store message to the new SMS table(upgrade from rev 4).\n");
471 sms_free(sms);
472 dbi_result_free(result);
473 goto rollback;
474 }
475 sms_free(sms);
476 }
477 dbi_result_free(result);
478
479 /* Remove the temporary table */
480 result = dbi_conn_query(conn, "DROP TABLE SMS_4");
481 if (!result) {
482 LOGP(DDB, LOGL_ERROR,
483 "Failed to drop the old SMS table (upgrade from rev 4).\n");
484 goto rollback;
485 }
486 dbi_result_free(result);
487
488 /* We're done. Bump DB Meta revision to 4 */
489 result = dbi_conn_query(conn,
490 "UPDATE Meta "
491 "SET value = '5' "
492 "WHERE key = 'revision'");
493 if (!result) {
494 LOGP(DDB, LOGL_ERROR,
495 "Failed to update DB schema revision (upgrade from rev 4).\n");
496 goto rollback;
497 }
498 dbi_result_free(result);
499
500 result = dbi_conn_query(conn, "COMMIT TRANSACTION");
501 if (!result) {
502 LOGP(DDB, LOGL_ERROR,
503 "Failed to commit the transaction (upgrade from rev 4)\n");
504 return -EINVAL;
505 } else {
506 dbi_result_free(result);
507 }
508
509 /* Shrink DB file size by actually wiping out SMS_4 table data */
510 result = dbi_conn_query(conn, "VACUUM");
511 if (!result)
512 LOGP(DDB, LOGL_ERROR,
513 "VACUUM failed. Ignoring it (upgrade from rev 4).\n");
514 else
515 dbi_result_free(result);
516
517 return 0;
518
519rollback:
520 result = dbi_conn_query(conn, "ROLLBACK TRANSACTION");
521 if (!result)
522 LOGP(DDB, LOGL_ERROR,
523 "Rollback failed (upgrade from rev 4).\n");
524 else
525 dbi_result_free(result);
526 return -EINVAL;
527}
528
Harald Welted0b7b772009-08-09 19:03:42 +0200529static int check_db_revision(void)
530{
531 dbi_result result;
Jan Luebbebfbdeec2012-12-27 00:27:16 +0100532 const char *rev_s;
Vadim Yanitskiya8d8e932016-05-13 15:38:09 +0600533 int db_rev = 0;
Harald Welted0b7b772009-08-09 19:03:42 +0200534
Vadim Yanitskiya8d8e932016-05-13 15:38:09 +0600535 /* Make a query */
Harald Welted0b7b772009-08-09 19:03:42 +0200536 result = dbi_conn_query(conn,
Vadim Yanitskiya8d8e932016-05-13 15:38:09 +0600537 "SELECT value FROM Meta "
538 "WHERE key = 'revision'");
539
Harald Welted0b7b772009-08-09 19:03:42 +0200540 if (!result)
541 return -EINVAL;
542
543 if (!dbi_result_next_row(result)) {
544 dbi_result_free(result);
545 return -EINVAL;
546 }
Vadim Yanitskiya8d8e932016-05-13 15:38:09 +0600547
548 /* Fetch the DB schema revision */
Jan Luebbebfbdeec2012-12-27 00:27:16 +0100549 rev_s = dbi_result_get_string(result, "value");
550 if (!rev_s) {
Harald Welted0b7b772009-08-09 19:03:42 +0200551 dbi_result_free(result);
552 return -EINVAL;
553 }
Vadim Yanitskiya8d8e932016-05-13 15:38:09 +0600554
555 if (!strcmp(rev_s, SCHEMA_REVISION)) {
556 /* Everything is fine */
Jan Luebbebfbdeec2012-12-27 00:27:16 +0100557 dbi_result_free(result);
Vadim Yanitskiya8d8e932016-05-13 15:38:09 +0600558 return 0;
559 }
560
561 db_rev = atoi(rev_s);
562 dbi_result_free(result);
563
564 /* Incremental migration waterfall */
565 switch (db_rev) {
566 case 2:
567 if (update_db_revision_2())
568 goto error;
569 case 3:
570 if (update_db_revision_3())
571 goto error;
Pablo Neira Ayuso9891dae2017-08-07 14:01:33 +0100572 case 4:
573 if (update_db_revision_4())
574 goto error;
Vadim Yanitskiya8d8e932016-05-13 15:38:09 +0600575
576 /* The end of waterfall */
577 break;
578 default:
579 LOGP(DDB, LOGL_FATAL,
580 "Invalid database schema revision '%d'.\n", db_rev);
Jan Luebbebfbdeec2012-12-27 00:27:16 +0100581 return -EINVAL;
582 }
583
Jan Luebbebfbdeec2012-12-27 00:27:16 +0100584 return 0;
Vadim Yanitskiya8d8e932016-05-13 15:38:09 +0600585
586error:
587 LOGP(DDB, LOGL_FATAL, "Failed to update database "
588 "from schema revision '%d'.\n", db_rev);
589 return -EINVAL;
Jan Luebbebfbdeec2012-12-27 00:27:16 +0100590}
591
592static int db_configure(void)
593{
594 dbi_result result;
595
596 result = dbi_conn_query(conn,
597 "PRAGMA synchronous = FULL");
598 if (!result)
599 return -EINVAL;
Harald Welted0b7b772009-08-09 19:03:42 +0200600
601 dbi_result_free(result);
602 return 0;
603}
604
Harald Welte0b906d02009-12-24 11:21:42 +0100605int db_init(const char *name)
606{
Jan Luebbe5c15c852008-12-27 15:59:25 +0000607 dbi_initialize(NULL);
Harald Welte0b906d02009-12-24 11:21:42 +0100608
Jan Luebbe5c15c852008-12-27 15:59:25 +0000609 conn = dbi_conn_new("sqlite3");
Harald Welte0b906d02009-12-24 11:21:42 +0100610 if (conn == NULL) {
Harald Welteae1f1592009-12-24 11:39:14 +0100611 LOGP(DDB, LOGL_FATAL, "Failed to create connection.\n");
Jan Luebbe5c15c852008-12-27 15:59:25 +0000612 return 1;
613 }
Jan Luebbe7398eb92008-12-27 00:45:41 +0000614
Holger Freyther12aa50d2009-01-01 18:02:05 +0000615 dbi_conn_error_handler( conn, db_error_func, NULL );
Jan Luebbe7398eb92008-12-27 00:45:41 +0000616
Jan Luebbe5c15c852008-12-27 15:59:25 +0000617 /* MySQL
618 dbi_conn_set_option(conn, "host", "localhost");
619 dbi_conn_set_option(conn, "username", "your_name");
620 dbi_conn_set_option(conn, "password", "your_password");
621 dbi_conn_set_option(conn, "dbname", "your_dbname");
622 dbi_conn_set_option(conn, "encoding", "UTF-8");
623 */
Jan Luebbe7398eb92008-12-27 00:45:41 +0000624
Jan Luebbe5c15c852008-12-27 15:59:25 +0000625 /* SqLite 3 */
Holger Freyther12aa50d2009-01-01 18:02:05 +0000626 db_basename = strdup(name);
627 db_dirname = strdup(name);
Holger Freytherbde36102008-12-28 22:51:39 +0000628 dbi_conn_set_option(conn, "sqlite3_dbdir", dirname(db_dirname));
629 dbi_conn_set_option(conn, "dbname", basename(db_basename));
Jan Luebbe7398eb92008-12-27 00:45:41 +0000630
Harald Welted0b7b772009-08-09 19:03:42 +0200631 if (dbi_conn_connect(conn) < 0)
632 goto out_err;
633
Jan Luebbe5c15c852008-12-27 15:59:25 +0000634 return 0;
Harald Welted0b7b772009-08-09 19:03:42 +0200635
636out_err:
637 free(db_dirname);
638 free(db_basename);
639 db_dirname = db_basename = NULL;
640 return -1;
Jan Luebbe7398eb92008-12-27 00:45:41 +0000641}
642
Harald Welted0b7b772009-08-09 19:03:42 +0200643
Harald Welted1476bc2011-07-16 13:24:09 +0200644int db_prepare(void)
Harald Welte0b906d02009-12-24 11:21:42 +0100645{
Jan Luebbe5c15c852008-12-27 15:59:25 +0000646 dbi_result result;
Harald Welte7e310b12009-03-30 20:56:32 +0000647 int i;
Holger Freytherb4064bc2009-02-23 00:50:31 +0000648
Harald Welte7e310b12009-03-30 20:56:32 +0000649 for (i = 0; i < ARRAY_SIZE(create_stmts); i++) {
650 result = dbi_conn_query(conn, create_stmts[i]);
Harald Welte0b906d02009-12-24 11:21:42 +0100651 if (!result) {
Harald Welteae1f1592009-12-24 11:39:14 +0100652 LOGP(DDB, LOGL_ERROR,
653 "Failed to create some table.\n");
Harald Welte7e310b12009-03-30 20:56:32 +0000654 return 1;
655 }
656 dbi_result_free(result);
Holger Freytherb4064bc2009-02-23 00:50:31 +0000657 }
Holger Freytherb4064bc2009-02-23 00:50:31 +0000658
Holger Hans Peter Freyther850326e2009-08-10 08:36:04 +0200659 if (check_db_revision() < 0) {
Harald Welteae1f1592009-12-24 11:39:14 +0100660 LOGP(DDB, LOGL_FATAL, "Database schema revision invalid, "
Holger Hans Peter Freyther850326e2009-08-10 08:36:04 +0200661 "please update your database schema\n");
662 return -1;
663 }
664
Jan Luebbebfbdeec2012-12-27 00:27:16 +0100665 db_configure();
666
Jan Luebbe5c15c852008-12-27 15:59:25 +0000667 return 0;
Jan Luebbe7398eb92008-12-27 00:45:41 +0000668}
669
Harald Welted1476bc2011-07-16 13:24:09 +0200670int db_fini(void)
Harald Welte0b906d02009-12-24 11:21:42 +0100671{
Jan Luebbe5c15c852008-12-27 15:59:25 +0000672 dbi_conn_close(conn);
673 dbi_shutdown();
Holger Freytherbde36102008-12-28 22:51:39 +0000674
Harald Welte2c5f4c62011-07-16 13:22:57 +0200675 free(db_dirname);
676 free(db_basename);
Jan Luebbe5c15c852008-12-27 15:59:25 +0000677 return 0;
Jan Luebbe7398eb92008-12-27 00:45:41 +0000678}
679
Harald Welte7e310b12009-03-30 20:56:32 +0000680/* store an [unsent] SMS to the database */
681int db_sms_store(struct gsm_sms *sms)
682{
683 dbi_result result;
Holger Hans Peter Freytherca3c2562013-10-08 03:17:30 +0200684 char *q_text, *q_daddr, *q_saddr;
Harald Welte76042182009-08-08 16:03:15 +0200685 unsigned char *q_udata;
686 char *validity_timestamp = "2222-2-2";
687
688 /* FIXME: generate validity timestamp based on validity_minutes */
Harald Welte7e310b12009-03-30 20:56:32 +0000689
690 dbi_conn_quote_string_copy(conn, (char *)sms->text, &q_text);
Harald Weltec0de14d2012-11-23 23:35:01 +0100691 dbi_conn_quote_string_copy(conn, (char *)sms->dst.addr, &q_daddr);
Holger Hans Peter Freytherca3c2562013-10-08 03:17:30 +0200692 dbi_conn_quote_string_copy(conn, (char *)sms->src.addr, &q_saddr);
Harald Welte76042182009-08-08 16:03:15 +0200693 dbi_conn_quote_binary_copy(conn, sms->user_data, sms->user_data_len,
694 &q_udata);
Holger Hans Peter Freytherca3c2562013-10-08 03:17:30 +0200695
Harald Weltef3efc592009-07-27 20:11:35 +0200696 /* FIXME: correct validity period */
Harald Welte7e310b12009-03-30 20:56:32 +0000697 result = dbi_conn_queryf(conn,
698 "INSERT INTO SMS "
Alexander Chemerisca7ed2d2013-10-08 03:17:32 +0200699 "(created, valid_until, "
Pablo Neira Ayuso9891dae2017-08-07 14:01:33 +0100700 "reply_path_req, status_rep_req, is_report, "
701 "msg_ref, protocol_id, data_coding_scheme, "
702 "ud_hdr_ind, "
Holger Hans Peter Freytherca3c2562013-10-08 03:17:30 +0200703 "user_data, text, "
704 "dest_addr, dest_ton, dest_npi, "
705 "src_addr, src_ton, src_npi) VALUES "
Alexander Chemerisca7ed2d2013-10-08 03:17:32 +0200706 "(datetime('now'), %u, "
Holger Hans Peter Freytherca3c2562013-10-08 03:17:30 +0200707 "%u, %u, %u, "
Pablo Neira Ayuso9891dae2017-08-07 14:01:33 +0100708 "%u, %u, %u, "
709 "%u, "
Holger Hans Peter Freytherca3c2562013-10-08 03:17:30 +0200710 "%s, %s, "
711 "%s, %u, %u, "
712 "%s, %u, %u)",
Alexander Chemerisca7ed2d2013-10-08 03:17:32 +0200713 validity_timestamp,
Pablo Neira Ayuso9891dae2017-08-07 14:01:33 +0100714 sms->reply_path_req, sms->status_rep_req, sms->is_report,
715 sms->msg_ref, sms->protocol_id, sms->data_coding_scheme,
716 sms->ud_hdr_ind,
Holger Hans Peter Freytherca3c2562013-10-08 03:17:30 +0200717 q_udata, q_text,
718 q_daddr, sms->dst.ton, sms->dst.npi,
719 q_saddr, sms->src.ton, sms->src.npi);
Harald Welte7e310b12009-03-30 20:56:32 +0000720 free(q_text);
Harald Welte76042182009-08-08 16:03:15 +0200721 free(q_udata);
Holger Hans Peter Freytherca3c2562013-10-08 03:17:30 +0200722 free(q_daddr);
723 free(q_saddr);
Harald Welte7e310b12009-03-30 20:56:32 +0000724
725 if (!result)
726 return -EIO;
727
728 dbi_result_free(result);
729 return 0;
730}
731
Harald Welte2ebabca2009-08-09 19:05:21 +0200732static struct gsm_sms *sms_from_result(struct gsm_network *net, dbi_result result)
Harald Welte7e310b12009-03-30 20:56:32 +0000733{
Harald Welte76042182009-08-08 16:03:15 +0200734 struct gsm_sms *sms = sms_alloc();
Holger Hans Peter Freytherca3c2562013-10-08 03:17:30 +0200735 const char *text, *daddr, *saddr;
Harald Welte76042182009-08-08 16:03:15 +0200736 const unsigned char *user_data;
Harald Welte7e310b12009-03-30 20:56:32 +0000737
Harald Welte76042182009-08-08 16:03:15 +0200738 if (!sms)
Harald Welte7e310b12009-03-30 20:56:32 +0000739 return NULL;
Harald Welte7e310b12009-03-30 20:56:32 +0000740
Harald Weltebe3e3782009-07-05 14:06:41 +0200741 sms->id = dbi_result_get_ulonglong(result, "id");
Harald Welte7e310b12009-03-30 20:56:32 +0000742
Harald Weltef3efc592009-07-27 20:11:35 +0200743 /* FIXME: validity */
Harald Welte76042182009-08-08 16:03:15 +0200744 /* FIXME: those should all be get_uchar, but sqlite3 is braindead */
Keithc601adc2017-08-16 22:45:07 +0200745 sms->created = dbi_result_get_datetime(result, "created");
Holger Hans Peter Freytherb115cb62014-07-03 14:00:30 +0200746 sms->reply_path_req = dbi_result_get_ulonglong(result, "reply_path_req");
747 sms->status_rep_req = dbi_result_get_ulonglong(result, "status_rep_req");
Pablo Neira Ayuso9891dae2017-08-07 14:01:33 +0100748 sms->is_report = dbi_result_get_ulonglong(result, "is_report");
749 sms->msg_ref = dbi_result_get_ulonglong(result, "msg_ref");
Holger Hans Peter Freytherb115cb62014-07-03 14:00:30 +0200750 sms->ud_hdr_ind = dbi_result_get_ulonglong(result, "ud_hdr_ind");
751 sms->protocol_id = dbi_result_get_ulonglong(result, "protocol_id");
752 sms->data_coding_scheme = dbi_result_get_ulonglong(result,
Harald Weltef3efc592009-07-27 20:11:35 +0200753 "data_coding_scheme");
754
Holger Hans Peter Freytherb115cb62014-07-03 14:00:30 +0200755 sms->dst.npi = dbi_result_get_ulonglong(result, "dest_npi");
756 sms->dst.ton = dbi_result_get_ulonglong(result, "dest_ton");
Harald Welte76042182009-08-08 16:03:15 +0200757 daddr = dbi_result_get_string(result, "dest_addr");
Neels Hofmeyr93bafb62017-01-13 03:12:08 +0100758 if (daddr)
759 osmo_strlcpy(sms->dst.addr, daddr, sizeof(sms->dst.addr));
Harald Welte2483f1b2016-06-19 18:06:02 +0200760 sms->receiver = vlr_subscr_find_by_msisdn(net->vlr, sms->dst.addr);
Harald Welte76042182009-08-08 16:03:15 +0200761
Holger Hans Peter Freytherb115cb62014-07-03 14:00:30 +0200762 sms->src.npi = dbi_result_get_ulonglong(result, "src_npi");
763 sms->src.ton = dbi_result_get_ulonglong(result, "src_ton");
Holger Hans Peter Freytherca3c2562013-10-08 03:17:30 +0200764 saddr = dbi_result_get_string(result, "src_addr");
Neels Hofmeyr93bafb62017-01-13 03:12:08 +0100765 if (saddr)
766 osmo_strlcpy(sms->src.addr, saddr, sizeof(sms->src.addr));
Holger Hans Peter Freytherca3c2562013-10-08 03:17:30 +0200767
Harald Welte76042182009-08-08 16:03:15 +0200768 sms->user_data_len = dbi_result_get_field_length(result, "user_data");
769 user_data = dbi_result_get_binary(result, "user_data");
770 if (sms->user_data_len > sizeof(sms->user_data))
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +0200771 sms->user_data_len = (uint8_t) sizeof(sms->user_data);
Harald Welte76042182009-08-08 16:03:15 +0200772 memcpy(sms->user_data, user_data, sms->user_data_len);
Harald Weltebe3e3782009-07-05 14:06:41 +0200773
774 text = dbi_result_get_string(result, "text");
Neels Hofmeyr93bafb62017-01-13 03:12:08 +0100775 if (text)
776 osmo_strlcpy(sms->text, text, sizeof(sms->text));
Harald Welte2ebabca2009-08-09 19:05:21 +0200777 return sms;
778}
779
Holger Hans Peter Freyther812dad02010-12-24 23:18:31 +0100780struct gsm_sms *db_sms_get(struct gsm_network *net, unsigned long long id)
781{
782 dbi_result result;
783 struct gsm_sms *sms;
784
785 result = dbi_conn_queryf(conn,
786 "SELECT * FROM SMS WHERE SMS.id = %llu", id);
787 if (!result)
788 return NULL;
789
790 if (!dbi_result_next_row(result)) {
791 dbi_result_free(result);
792 return NULL;
793 }
794
795 sms = sms_from_result(net, result);
796
797 dbi_result_free(result);
798
799 return sms;
800}
801
Harald Welte2483f1b2016-06-19 18:06:02 +0200802struct gsm_sms *db_sms_get_next_unsent(struct gsm_network *net,
803 unsigned long long min_sms_id,
804 unsigned int max_failed)
Harald Welte2ebabca2009-08-09 19:05:21 +0200805{
806 dbi_result result;
807 struct gsm_sms *sms;
808
809 result = dbi_conn_queryf(conn,
Harald Welte2483f1b2016-06-19 18:06:02 +0200810 "SELECT * FROM SMS"
811 " WHERE sent IS NULL"
812 " AND id >= %llu"
813 " AND deliver_attempts <= %u"
814 " ORDER BY id LIMIT 1",
815 min_sms_id, max_failed);
Harald Welte2ebabca2009-08-09 19:05:21 +0200816
Sylvain Munautff1f19e2009-12-22 13:22:29 +0100817 if (!result)
818 return NULL;
819
820 if (!dbi_result_next_row(result)) {
821 dbi_result_free(result);
822 return NULL;
823 }
824
825 sms = sms_from_result(net, result);
826
827 dbi_result_free(result);
828
829 return sms;
830}
831
Sylvain Munautd5778fc2009-12-21 01:09:57 +0100832/* retrieve the next unsent SMS for a given subscriber */
Harald Welte2483f1b2016-06-19 18:06:02 +0200833struct gsm_sms *db_sms_get_unsent_for_subscr(struct vlr_subscr *vsub,
834 unsigned int max_failed)
Harald Welte2ebabca2009-08-09 19:05:21 +0200835{
Harald Welte2483f1b2016-06-19 18:06:02 +0200836 struct gsm_network *net = vsub->vlr->user_ctx;
Harald Welte2ebabca2009-08-09 19:05:21 +0200837 dbi_result result;
838 struct gsm_sms *sms;
839
Harald Welte2483f1b2016-06-19 18:06:02 +0200840 if (!vsub->lu_complete)
841 return NULL;
842
Harald Welte2ebabca2009-08-09 19:05:21 +0200843 result = dbi_conn_queryf(conn,
Harald Welte2483f1b2016-06-19 18:06:02 +0200844 "SELECT * FROM SMS"
845 " WHERE sent IS NULL"
846 " AND dest_addr=%s"
847 " AND deliver_attempts <= %u"
848 " ORDER BY id LIMIT 1",
849 vsub->msisdn, max_failed);
Harald Welte2ebabca2009-08-09 19:05:21 +0200850 if (!result)
851 return NULL;
852
853 if (!dbi_result_next_row(result)) {
854 dbi_result_free(result);
855 return NULL;
856 }
857
Harald Welte2483f1b2016-06-19 18:06:02 +0200858 sms = sms_from_result(net, result);
859
860 dbi_result_free(result);
861
862 return sms;
863}
864
865struct gsm_sms *db_sms_get_next_unsent_rr_msisdn(struct gsm_network *net,
866 const char *last_msisdn,
867 unsigned int max_failed)
868{
869 dbi_result result;
870 struct gsm_sms *sms;
871
872 result = dbi_conn_queryf(conn,
873 "SELECT * FROM SMS"
874 " WHERE sent IS NULL"
875 " AND dest_addr > '%s'"
876 " AND deliver_attempts <= %u"
877 " ORDER BY dest_addr, id LIMIT 1",
878 last_msisdn, max_failed);
879 if (!result)
880 return NULL;
881
882 if (!dbi_result_next_row(result)) {
883 dbi_result_free(result);
884 return NULL;
885 }
886
887 sms = sms_from_result(net, result);
Harald Welte2ebabca2009-08-09 19:05:21 +0200888
889 dbi_result_free(result);
890
Harald Welte7e310b12009-03-30 20:56:32 +0000891 return sms;
892}
893
Alexander Chemeris1e77e3d2014-03-08 21:27:37 +0100894/* mark a given SMS as delivered */
895int db_sms_mark_delivered(struct gsm_sms *sms)
Harald Welte7e310b12009-03-30 20:56:32 +0000896{
897 dbi_result result;
898
899 result = dbi_conn_queryf(conn,
900 "UPDATE SMS "
901 "SET sent = datetime('now') "
902 "WHERE id = %llu", sms->id);
903 if (!result) {
Harald Welteae1f1592009-12-24 11:39:14 +0100904 LOGP(DDB, LOGL_ERROR, "Failed to mark SMS %llu as sent.\n", sms->id);
Harald Welte7e310b12009-03-30 20:56:32 +0000905 return 1;
906 }
907
908 dbi_result_free(result);
909 return 0;
910}
Harald Welte (local)db552c52009-08-15 20:15:14 +0200911
912/* increase the number of attempted deliveries */
913int db_sms_inc_deliver_attempts(struct gsm_sms *sms)
914{
915 dbi_result result;
916
917 result = dbi_conn_queryf(conn,
918 "UPDATE SMS "
919 "SET deliver_attempts = deliver_attempts + 1 "
920 "WHERE id = %llu", sms->id);
921 if (!result) {
Harald Welteae1f1592009-12-24 11:39:14 +0100922 LOGP(DDB, LOGL_ERROR, "Failed to inc deliver attempts for "
923 "SMS %llu.\n", sms->id);
Harald Welte (local)db552c52009-08-15 20:15:14 +0200924 return 1;
925 }
926
927 dbi_result_free(result);
928 return 0;
929}
Harald Welte (local)026531e2009-08-16 10:40:10 +0200930
Harald Welte2483f1b2016-06-19 18:06:02 +0200931/* Drop all pending SMS to or from the given extension */
932int db_sms_delete_by_msisdn(const char *msisdn)
Harald Welte (local)026531e2009-08-16 10:40:10 +0200933{
934 dbi_result result;
Harald Welte2483f1b2016-06-19 18:06:02 +0200935 if (!msisdn || !*msisdn)
936 return 0;
Harald Welte (local)026531e2009-08-16 10:40:10 +0200937 result = dbi_conn_queryf(conn,
Harald Welte2483f1b2016-06-19 18:06:02 +0200938 "DELETE FROM SMS WHERE src_addr=%s OR dest_addr=%s",
939 msisdn, msisdn);
940 if (!result) {
941 LOGP(DDB, LOGL_ERROR,
942 "Failed to delete SMS for %s\n", msisdn);
943 return -1;
944 }
Harald Welte (local)026531e2009-08-16 10:40:10 +0200945 dbi_result_free(result);
946 return 0;
947}
Harald Welteffa55a42009-12-22 19:07:32 +0100948
Pablo Neira Ayusodfb342c2011-05-06 12:13:10 +0200949int db_store_counter(struct osmo_counter *ctr)
Harald Welteffa55a42009-12-22 19:07:32 +0100950{
951 dbi_result result;
952 char *q_name;
953
954 dbi_conn_quote_string_copy(conn, ctr->name, &q_name);
955
956 result = dbi_conn_queryf(conn,
957 "INSERT INTO Counters "
958 "(timestamp,name,value) VALUES "
959 "(datetime('now'),%s,%lu)", q_name, ctr->value);
960
961 free(q_name);
962
963 if (!result)
964 return -EIO;
965
966 dbi_result_free(result);
967 return 0;
968}
Harald Weltef2b4cd72010-05-13 11:45:07 +0200969
970static int db_store_rate_ctr(struct rate_ctr_group *ctrg, unsigned int num,
971 char *q_prefix)
972{
973 dbi_result result;
974 char *q_name;
975
976 dbi_conn_quote_string_copy(conn, ctrg->desc->ctr_desc[num].name,
977 &q_name);
978
979 result = dbi_conn_queryf(conn,
Harald Weltec1919862010-05-13 12:55:20 +0200980 "Insert INTO RateCounters "
Harald Welted94d6a02010-05-14 17:38:47 +0200981 "(timestamp,name,idx,value) VALUES "
Harald Weltec1919862010-05-13 12:55:20 +0200982 "(datetime('now'),%s.%s,%u,%"PRIu64")",
983 q_prefix, q_name, ctrg->idx, ctrg->ctr[num].current);
Harald Weltef2b4cd72010-05-13 11:45:07 +0200984
985 free(q_name);
986
987 if (!result)
988 return -EIO;
989
990 dbi_result_free(result);
991 return 0;
992}
993
994int db_store_rate_ctr_group(struct rate_ctr_group *ctrg)
995{
996 unsigned int i;
997 char *q_prefix;
998
Harald Weltec1919862010-05-13 12:55:20 +0200999 dbi_conn_quote_string_copy(conn, ctrg->desc->group_name_prefix, &q_prefix);
Harald Weltef2b4cd72010-05-13 11:45:07 +02001000
1001 for (i = 0; i < ctrg->desc->num_ctr; i++)
1002 db_store_rate_ctr(ctrg, i, q_prefix);
1003
1004 free(q_prefix);
1005
1006 return 0;
1007}