Kannel: Open Source WAP and SMS gateway  svn-r5335
dlr_redis.c
Go to the documentation of this file.
1 /* ====================================================================
2  * The Kannel Software License, Version 1.0
3  *
4  * Copyright (c) 2001-2018 Kannel Group
5  * Copyright (c) 1998-2001 WapIT Ltd.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  * notice, this list of conditions and the following disclaimer.
14  *
15  * 2. Redistributions in binary form must reproduce the above copyright
16  * notice, this list of conditions and the following disclaimer in
17  * the documentation and/or other materials provided with the
18  * distribution.
19  *
20  * 3. The end-user documentation included with the redistribution,
21  * if any, must include the following acknowledgment:
22  * "This product includes software developed by the
23  * Kannel Group (http://www.kannel.org/)."
24  * Alternately, this acknowledgment may appear in the software itself,
25  * if and wherever such third-party acknowledgments normally appear.
26  *
27  * 4. The names "Kannel" and "Kannel Group" must not be used to
28  * endorse or promote products derived from this software without
29  * prior written permission. For written permission, please
30  * contact org@kannel.org.
31  *
32  * 5. Products derived from this software may not be called "Kannel",
33  * nor may "Kannel" appear in their name, without prior written
34  * permission of the Kannel Group.
35  *
36  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
37  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
38  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
39  * DISCLAIMED. IN NO EVENT SHALL THE KANNEL GROUP OR ITS CONTRIBUTORS
40  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
41  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
42  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
43  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
44  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
45  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
46  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
47  * ====================================================================
48  *
49  * This software consists of voluntary contributions made by many
50  * individuals on behalf of the Kannel Group. For more information on
51  * the Kannel Group, please see <http://www.kannel.org/>.
52  *
53  * Portions of this software are based upon software originally written at
54  * WapIT Ltd., Helsinki, Finland for the Kannel project.
55  */
56 
57 /*
58  * dlr_redis.c
59  *
60  * Implementation of handling delivery reports (DLRs)
61  * for the Redis keystore
62  *
63  * Toby Phipps <toby.phipps at nexmedia.com.sg>, 2011-08-23
64  * Stipe Tolj <stolj at kannel.org>, 2013-12-12
65  */
66 
67 #include "gwlib/gwlib.h"
68 #include "gwlib/dbpool.h"
69 #include "dlr_p.h"
70 
71 #ifdef HAVE_REDIS
72 
73 #define REDIS_DEFAULT_PORT 6379
74 
75 /*
76  * Some SMSCs (such as the Logica SMPP simulator when bound multiple times
77  * under high load) erroneously return duplicate message IDs. Before writing
78  * the DLR, check to ensure that an existing DLR with the same message ID
79  * doesn't already exist (HMSET of an existing key overwrites it silently
80  * and we want the first message to win, not the erroneous one).
81  * We issue a HSETNX first to ensure that the key doesn't already exist.
82  * Only if it succeeds do we proceed to update it with full DLR info.
83  * Define the following macro in case you want this extra handling.
84  */
85 /* #define REDIS_PRECHECK 1 */
86 
87 /*
88  * Our connection pool to redis.
89  */
90 static DBPool *pool = NULL;
91 
92 /*
93  * Database-centric DLR definition (common across all engines)
94  */
95 static struct dlr_db_fields *fields = NULL;
96 
97 static void dlr_redis_shutdown()
98 {
99  dbpool_destroy(pool);
100  dlr_db_fields_destroy(fields);
101 }
102 
103 static void dlr_redis_add(struct dlr_entry *entry)
104 {
105  Octstr *key, *sql, *os;
106  DBPoolConn *pconn;
107  List *binds;
108  int res, len;
109 
110  debug("dlr.redis", 0, "Adding DLR into keystore");
111 
112  pconn = dbpool_conn_consume(pool);
113  /* just for sure */
114  if (pconn == NULL) {
115  error(0, "DLR: REDIS: No connection available - dropping DLR");
116  dlr_entry_destroy(entry);
117  return;
118  }
119 
120  if (entry->use_dst && entry->destination) {
121  Octstr *dst_min;
122 
123  /* keep a shorten version for the key part */
124  dst_min = octstr_duplicate(entry->destination);
125  len = octstr_len(dst_min);
126  if (len > MIN_DST_LEN)
127  octstr_delete(dst_min, 0, len - MIN_DST_LEN);
128 
129  key = octstr_format("%S:%S:%S:%S", fields->table,
130  entry->smsc,
131  entry->timestamp,
132  dst_min);
133 
134  octstr_destroy(dst_min);
135  } else {
136  key = octstr_format("%S:%S:%S", fields->table,
137  entry->smsc,
138  entry->timestamp);
139  }
140 
141 #ifdef REDIS_PRECHECK
142  binds = gwlist_create();
143  sql = octstr_format("HSETNX %S %S ?", key, fields->field_smsc);
144  if (dbpool_conn_update(pconn, sql, binds) != 1) {
145  error(0, "DLR: REDIS: DLR for %s already exists! Duplicate Message ID?",
146  octstr_get_cstr(key));
147 
148  octstr_destroy(sql);
149  octstr_destroy(key);
150  octstr_destroy(tsclean);
151  octstr_destroy(dstclean);
152  octstr_destroy(srcclean);
153  gwlist_destroy(binds, NULL);
154  dbpool_conn_produce(pconn);
155  return;
156  }
157  octstr_destroy(sql);
158  gwlist_destroy(binds, NULL);
159 #endif
160 
161  binds = gwlist_create();
162  sql = octstr_create("");
163  gwlist_append(binds, octstr_imm("HMSET"));
164  gwlist_append(binds, key);
165  gwlist_append(binds, fields->field_smsc);
166  gwlist_append(binds, entry->smsc);
167  gwlist_append(binds, fields->field_ts);
168  gwlist_append(binds, entry->timestamp);
169  gwlist_append(binds, fields->field_src);
170  gwlist_append(binds, entry->source);
171  gwlist_append(binds, fields->field_dst);
172  gwlist_append(binds, entry->destination);
173  gwlist_append(binds, fields->field_serv);
174  gwlist_append(binds, entry->service);
175  gwlist_append(binds, fields->field_url);
176  octstr_url_encode(entry->url);
177  gwlist_append(binds, entry->url);
178  gwlist_append(binds, fields->field_mask);
179  os = octstr_format("%d", entry->mask);
180  gwlist_append(binds, os);
181  gwlist_append(binds, fields->field_boxc);
182  gwlist_append(binds, entry->boxc_id);
183  gwlist_append(binds, fields->field_status);
184  gwlist_append(binds, octstr_imm("0"));
185 
186  res = dbpool_conn_update(pconn, sql, binds);
187 
188  if (res == -1) {
189  error(0, "DLR: REDIS: Error while adding dlr entry %s",
190  octstr_get_cstr(key));
191  }
192  else {
193  /* HMSET returned OK. Set EXPIRE if applicable and then
194  * increment the DLR counter */
195  if (fields->ttl) {
196  gwlist_destroy(binds, NULL);
197  binds = gwlist_create();
198  gwlist_append(binds, octstr_imm("EXPIRE"));
199  gwlist_append(binds, key);
200  octstr_destroy(os);
201  os = octstr_format("%ld", fields->ttl);
202  gwlist_append(binds, os);
203  res = dbpool_conn_update(pconn, sql, binds);
204  }
205  /* We are not performing an 'INCR <table>:Count'
206  * operation here, since we can't be accurate due
207  * to TTL'ed expiration. Rather use 'DBSIZE' based
208  * on seperated databases in redis. */
209  }
210 
211  dbpool_conn_produce(pconn);
212  octstr_destroy(sql);
213  octstr_destroy(key);
214  octstr_destroy(os);
215  gwlist_destroy(binds, NULL);
216  dlr_entry_destroy(entry);
217 }
218 
219 static inline void get_octstr_value(Octstr **os, const List *r, const int i)
220 {
221  *os = octstr_duplicate(gwlist_get((List*)r, i));
222  if (octstr_str_compare(*os, "_NULL_") == 0) {
223  octstr_destroy(*os);
224  *os = NULL;
225  }
226 }
227 
228 static struct dlr_entry *dlr_redis_get(const Octstr *smsc, const Octstr *ts, const Octstr *dst)
229 {
230  Octstr *key, *sql;
231  DBPoolConn *pconn;
232  List *binds = gwlist_create();
233  List *result = NULL, *row;
234  struct dlr_entry *res = NULL;
235 
236  pconn = dbpool_conn_consume(pool);
237  if (pconn == NULL) {
238  error(0, "DLR: REDIS: No connection available");
239  gwlist_destroy(binds, NULL);
240  dbpool_conn_produce(pconn);
241  return NULL;
242  }
243 
244  /* If the destination address is not NULL, then
245  * it has been shortened by the abstractive layer. */
246  if (dst)
247  key = octstr_format("%S:%S:%S:%S", fields->table,
248  (Octstr*) smsc, (Octstr*) ts, (Octstr*) dst);
249  else
250  key = octstr_format("%S:%S:%S", fields->table,
251  (Octstr*) smsc, (Octstr*) ts);
252 
253  sql = octstr_create("");
254  gwlist_append(binds, octstr_imm("HMGET"));
255  gwlist_append(binds, key);
256  gwlist_append(binds, fields->field_mask);
257  gwlist_append(binds, fields->field_serv);
258  gwlist_append(binds, fields->field_url);
259  gwlist_append(binds, fields->field_src);
260  gwlist_append(binds, fields->field_dst);
261  gwlist_append(binds, fields->field_boxc);
262 
263  if (dbpool_conn_select(pconn, sql, binds, &result) != 0) {
264  error(0, "DLR: REDIS: Failed to fetch DLR for %s", octstr_get_cstr(key));
265  octstr_destroy(sql);
266  octstr_destroy(key);
267  gwlist_destroy(binds, NULL);
268  dbpool_conn_produce(pconn);
269  return NULL;
270  }
271 
272  dbpool_conn_produce(pconn);
273  octstr_destroy(sql);
274  octstr_destroy(key);
275  gwlist_destroy(binds, NULL);
276 
277 #define LO2CSTR(r, i) octstr_get_cstr(gwlist_get(r, i))
278 
279  if (gwlist_len(result) > 0) {
280  row = gwlist_extract_first(result);
281 
282  /*
283  * If we get an empty set back from redis, this is
284  * still an array with "" values, representing (nil).
285  * If the mask is empty then this can't be a valid
286  * set, therefore bail out.
287  */
288  if (octstr_len(gwlist_get(row, 0)) > 0) {
289  res = dlr_entry_create();
290  gw_assert(res != NULL);
291  res->mask = atoi(octstr_get_cstr(gwlist_get(row, 0)));
292  get_octstr_value(&res->service, row, 1);
293  get_octstr_value(&res->url, row, 2);
294  octstr_url_decode(res->url);
295  get_octstr_value(&res->source, row, 3);
296  get_octstr_value(&res->destination, row, 4);
297  get_octstr_value(&res->boxc_id, row, 5);
298  res->smsc = octstr_duplicate(smsc);
299  }
301  }
302  gwlist_destroy(result, NULL);
303 
304 #undef LO2CSTR
305 
306  return res;
307 }
308 
309 static void dlr_redis_remove(const Octstr *smsc, const Octstr *ts, const Octstr *dst)
310 {
311  Octstr *key, *sql;
312  DBPoolConn *pconn;
313  List *binds = gwlist_create();
314  int res;
315 
316  debug("dlr.redis", 0, "Removing DLR from keystore");
317 
318  pconn = dbpool_conn_consume(pool);
319  /* just for sure */
320  if (pconn == NULL) {
321  error(0, "DLR: REDIS: No connection available");
322  gwlist_destroy(binds, NULL);
323  return;
324  }
325 
326  if (dst)
327  key = octstr_format("%S:%S:%S:%S", fields->table,
328  (Octstr*) smsc, (Octstr*) ts, (Octstr*) dst);
329  else
330  key = octstr_format("%S:%S:%S", fields->table,
331  (Octstr*) smsc, (Octstr*) ts);
332 
333  sql = octstr_create("");
334  gwlist_append(binds, octstr_imm("DEL"));
335  gwlist_append(binds, key);
336 
337  res = dbpool_conn_update(pconn, sql, binds);
338 
339  /*
340  * Redis DEL returns the number of keys deleted
341  */
342  if (res != 1) {
343  /*
344  * We may fail to delete a DLR that was successfully retrieved
345  * just above due to race conditions when duplicate message IDs
346  * are received. This happens frequently when testing via the
347  * Logica SMPP emulator due to its duplicate message ID bugs.
348  */
349  error(0, "DLR: REDIS: Error while removing dlr entry for %s",
350  octstr_get_cstr(key));
351  }
352  /* We don't perform 'DECR <table>:Count', since we have TTL'ed
353  * expirations, which can't be handled with manual counters. */
354 
355  dbpool_conn_produce(pconn);
356  octstr_destroy(sql);
357  octstr_destroy(key);
358  gwlist_destroy(binds, NULL);
359 }
360 
361 static void dlr_redis_update(const Octstr *smsc, const Octstr *ts, const Octstr *dst, int status)
362 {
363  Octstr *key, *sql, *os_status;
364  DBPoolConn *pconn;
365  List *binds = gwlist_create();
366  int res;
367 
368  debug("dlr.redis", 0, "Updating DLR status in keystore");
369 
370  pconn = dbpool_conn_consume(pool);
371  /* just for sure */
372  if (pconn == NULL) {
373  error(0, "DLR: REDIS: No connection available");
374  gwlist_destroy(binds, NULL);
375  return;
376  }
377 
378  os_status = octstr_format("%d", status);
379 
380  /* If the destination address is not NULL, then
381  * it has been shortened by the abstractive layer. */
382  if (dst)
383  key = octstr_format("%S:%S:%S:%S", fields->table,
384  (Octstr*) smsc, (Octstr*) ts, (Octstr*) dst);
385  else
386  key = octstr_format("%S:%S:%S", fields->table,
387  (Octstr*) smsc, (Octstr*) ts);
388 
389  sql = octstr_create("");
390  gwlist_append(binds, octstr_imm("HMSET"));
391  gwlist_append(binds, key);
392  gwlist_append(binds, fields->field_status);
393  gwlist_append(binds, os_status);
394 
395  if ((res = dbpool_conn_update(pconn, sql, binds)) == -1) {
396  error(0, "DLR: REDIS: Error while updating dlr entry for %s",
397  octstr_get_cstr(key));
398  }
399 
400  dbpool_conn_produce(pconn);
401  octstr_destroy(os_status);
402  octstr_destroy(key);
403  octstr_destroy(sql);
404  gwlist_destroy(binds, NULL);
405 }
406 
407 static long dlr_redis_messages(void)
408 {
409  List *result, *row;
410  DBPoolConn *conn;
411  long msgs = -1;
412 
413  conn = dbpool_conn_consume(pool);
414  if (conn == NULL)
415  return -1;
416 
417  if (dbpool_conn_select(conn, octstr_imm("DBSIZE"), NULL, &result) != 0) {
418  dbpool_conn_produce(conn);
419  return 0;
420  }
421 
422  dbpool_conn_produce(conn);
423 
424  if (gwlist_len(result) > 0) {
425  row = gwlist_extract_first(result);
426  msgs = atol(octstr_get_cstr(gwlist_get(row, 0)));
428 
429  while ((row = gwlist_extract_first(result)) != NULL)
431  }
432  gwlist_destroy(result, NULL);
433 
434  return msgs;
435 }
436 
437 static void dlr_redis_flush(void)
438 {
439  Octstr *sql;
440  DBPoolConn *pconn;
441  int rows;
442 
443  pconn = dbpool_conn_consume(pool);
444  /* just for sure */
445  if (pconn == NULL) {
446  error(0, "DLR: REDIS: No connection available");
447  return;
448  }
449 
450  sql = octstr_imm("FLUSHDB");
451  rows = dbpool_conn_update(pconn, sql, NULL);
452  if (rows == -1)
453  error(0, "DLR: REDIS: Error while flushing dlr entries from database");
454  else
455  debug("dlr.redis", 0, "Flushed %d DLR entries from database", rows);
456  dbpool_conn_produce(pconn);
457  octstr_destroy(sql);
458 }
459 
460 static struct dlr_storage handles = {
461  .type = "redis",
462  .dlr_add = dlr_redis_add,
463  .dlr_get = dlr_redis_get,
464  .dlr_update = dlr_redis_update,
465  .dlr_remove = dlr_redis_remove,
466  .dlr_shutdown = dlr_redis_shutdown,
467  .dlr_messages = dlr_redis_messages,
468  .dlr_flush = dlr_redis_flush
469 };
470 
472 {
473  CfgGroup *grp;
474  List *grplist;
475  Octstr *redis_host, *redis_pass, *redis_id;
476  long redis_port = 0, redis_database = -1, redis_idle_timeout = -1;
477  Octstr *p = NULL;
478  long pool_size;
479  DBConf *db_conf = NULL;
480 
481  /*
482  * Check for all mandatory directives that specify the field names
483  * of the used Redis key
484  */
485  if (!(grp = cfg_get_single_group(cfg, octstr_imm("dlr-db"))))
486  panic(0, "DLR: Redis: group 'dlr-db' is not specified!");
487 
488  if (!(redis_id = cfg_get(grp, octstr_imm("id"))))
489  panic(0, "DLR: Redis: directive 'id' is not specified!");
490 
491  fields = dlr_db_fields_create(grp);
492  gw_assert(fields != NULL);
493 
494  /*
495  * Escaping special quotes for field/table names
496  */
497  octstr_replace(fields->table, octstr_imm("`"), octstr_imm("``"));
498  octstr_replace(fields->field_smsc, octstr_imm("`"), octstr_imm("``"));
499  octstr_replace(fields->field_ts, octstr_imm("`"), octstr_imm("``"));
500  octstr_replace(fields->field_src, octstr_imm("`"), octstr_imm("``"));
501  octstr_replace(fields->field_dst, octstr_imm("`"), octstr_imm("``"));
502  octstr_replace(fields->field_serv, octstr_imm("`"), octstr_imm("``"));
503  octstr_replace(fields->field_url, octstr_imm("`"), octstr_imm("``"));
504  octstr_replace(fields->field_mask, octstr_imm("`"), octstr_imm("``"));
505  octstr_replace(fields->field_status, octstr_imm("`"), octstr_imm("``"));
506  octstr_replace(fields->field_boxc, octstr_imm("`"), octstr_imm("``"));
507 
508  /*
509  * Now grab the required information from the 'redis-connection' group
510  * with the redis-id we just obtained.
511  *
512  * We have to loop through all available Redis connection definitions
513  * and search for the one we are looking for.
514  */
515  grplist = cfg_get_multi_group(cfg, octstr_imm("redis-connection"));
516  while (grplist && (grp = gwlist_extract_first(grplist)) != NULL) {
517  p = cfg_get(grp, octstr_imm("id"));
518  if (p != NULL && octstr_compare(p, redis_id) == 0) {
519  goto found;
520  }
521  if (p != NULL)
522  octstr_destroy(p);
523  }
524  panic(0, "DLR: Redis: connection settings for id '%s' are not specified!",
525  octstr_get_cstr(redis_id));
526 
527 found:
528  octstr_destroy(p);
529  gwlist_destroy(grplist, NULL);
530 
531  if (cfg_get_integer(&pool_size, grp, octstr_imm("max-connections")) == -1 || pool_size == 0)
532  pool_size = 1;
533 
534  if (!(redis_host = cfg_get(grp, octstr_imm("host"))))
535  panic(0, "DLR: Redis: directive 'host' is not specified!");
536  if (cfg_get_integer(&redis_port, grp, octstr_imm("port")) == -1) {
537  redis_port = REDIS_DEFAULT_PORT;
538  }
539  redis_pass = cfg_get(grp, octstr_imm("password"));
540  cfg_get_integer(&redis_database, grp, octstr_imm("database"));
541  cfg_get_integer(&redis_idle_timeout, grp, octstr_imm("idle-timeout"));
542 
543  /*
544  * Ok, ready to connect to Redis
545  */
546  db_conf = gw_malloc(sizeof(DBConf));
547  gw_assert(db_conf != NULL);
548 
549  db_conf->redis = gw_malloc(sizeof(RedisConf));
550  gw_assert(db_conf->redis != NULL);
551 
552  db_conf->redis->host = redis_host;
553  db_conf->redis->port = redis_port;
554  db_conf->redis->password = redis_pass;
555  db_conf->redis->database = redis_database;
556  db_conf->redis->idle_timeout = redis_idle_timeout;
557 
558  pool = dbpool_create(DBPOOL_REDIS, db_conf, pool_size);
559  gw_assert(pool != NULL);
560 
561  /*
562  * Panic on failure to connect. Should we just try to reconnect?
563  */
564  if (dbpool_conn_count(pool) == 0)
565  panic(0,"DLR: Redis: database pool has no connections!");
566 
567  octstr_destroy(redis_id);
568 
569  return &handles;
570 }
571 #else
572 /*
573  * Return NULL, so we point dlr-core that we were
574  * not compiled in.
575  */
577 {
578  return NULL;
579 }
580 #endif /* HAVE_REDIS */
void error(int err, const char *fmt,...)
Definition: log.c:648
const char * type
Definition: dlr_p.h:112
Octstr * url
Definition: dlr_p.h:84
void octstr_replace(Octstr *haystack, Octstr *needle, Octstr *repl)
Definition: octstr.c:2649
long dbpool_conn_count(DBPool *p)
DBPool * dbpool_create(enum db_type db_type, DBConf *conf, unsigned int connections)
gw_assert(wtls_machine->packet_to_send !=NULL)
RedisConf * redis
Definition: dbpool.h:172
Octstr * field_boxc
Definition: dlr_p.h:160
void gwlist_append(List *list, void *item)
Definition: list.c:179
Octstr * service
Definition: dlr_p.h:83
void dlr_db_fields_destroy(struct dlr_db_fields *fields)
Definition: dlr.c:204
struct dlr_entry * dlr_entry_create(void)
Definition: dlr.c:103
long gwlist_len(List *list)
Definition: list.c:166
Octstr * boxc_id
Definition: dlr_p.h:85
void * gwlist_get(List *list, long pos)
Definition: list.c:292
int octstr_url_decode(Octstr *ostr)
Definition: octstr.c:1746
#define MIN_DST_LEN
Definition: dlr_p.h:74
#define cfg_get(grp, varname)
Definition: cfg.h:86
Octstr * field_src
Definition: dlr_p.h:154
Octstr * field_url
Definition: dlr_p.h:157
static Cfg * cfg
Definition: opensmppbox.c:95
Octstr * password
Definition: dbpool.h:150
#define octstr_get_cstr(ostr)
Definition: octstr.h:233
static struct pid_list * found
Octstr * field_status
Definition: dlr_p.h:159
void dbpool_conn_produce(DBPoolConn *conn)
static struct dlr_storage * handles
Definition: dlr.c:97
Octstr * table
Definition: dlr_p.h:150
Octstr * octstr_imm(const char *cstr)
Definition: octstr.c:283
Definition: cfg.c:164
void * gwlist_extract_first(List *list)
Definition: list.c:305
void octstr_delete(Octstr *ostr1, long pos, long len)
Definition: octstr.c:1527
Octstr * source
Definition: dlr_p.h:81
#define octstr_duplicate(ostr)
Definition: octstr.h:187
List * cfg_get_multi_group(Cfg *cfg, Octstr *name)
Definition: cfg.c:645
Octstr * timestamp
Definition: dlr_p.h:80
Octstr * field_serv
Definition: dlr_p.h:156
Octstr * octstr_format(const char *fmt,...)
Definition: octstr.c:2464
void octstr_destroy(Octstr *ostr)
Definition: octstr.c:324
#define octstr_create(cstr)
Definition: octstr.h:125
void octstr_destroy_item(void *os)
Definition: octstr.c:336
long database
Definition: dbpool.h:151
Definition: dbpool.h:164
Octstr * field_smsc
Definition: dlr_p.h:152
long octstr_len(const Octstr *ostr)
Definition: octstr.c:342
Octstr * destination
Definition: dlr_p.h:82
void dbpool_destroy(DBPool *p)
int dbpool_conn_update(DBPoolConn *conn, const Octstr *sql, List *binds)
Definition: octstr.c:118
long port
Definition: dbpool.h:149
void dlr_entry_destroy(struct dlr_entry *dlr)
Definition: dlr.c:142
void debug(const char *place, int err, const char *fmt,...)
Definition: log.c:726
int cfg_get_integer(long *n, CfgGroup *grp, Octstr *varname)
Definition: cfg.c:742
#define panic
Definition: log.h:87
Definition: cfg.c:73
int octstr_str_compare(const Octstr *ostr, const char *str)
Definition: octstr.c:973
int dbpool_conn_select(DBPoolConn *conn, const Octstr *sql, List *binds, List **result)
#define gwlist_create()
Definition: list.h:136
struct dlr_storage * dlr_init_redis(Cfg *cfg)
Definition: dlr_redis.c:576
Definition: dlr_p.h:78
DBPoolConn * dbpool_conn_consume(DBPool *p)
Octstr * smsc
Definition: dlr_p.h:79
Octstr * host
Definition: dbpool.h:148
int mask
Definition: dlr_p.h:86
CfgGroup * cfg_get_single_group(Cfg *cfg, Octstr *name)
Definition: cfg.c:639
struct dlr_db_fields * dlr_db_fields_create(CfgGroup *grp)
Definition: dlr.c:169
Octstr * field_ts
Definition: dlr_p.h:153
Definition: list.c:102
Octstr * field_dst
Definition: dlr_p.h:155
int use_dst
Definition: dlr_p.h:87
Octstr * field_mask
Definition: dlr_p.h:158
void octstr_url_encode(Octstr *ostr)
Definition: octstr.c:1673
long ttl
Definition: dlr_p.h:151
int octstr_compare(const Octstr *ostr1, const Octstr *ostr2)
Definition: octstr.c:871
long idle_timeout
Definition: dbpool.h:152
void gwlist_destroy(List *list, gwlist_item_destructor_t *destructor)
Definition: list.c:145
See file LICENSE for details about the license agreement for using, modifying, copying or deriving work from this software.