Kannel: Open Source WAP and SMS gateway  $Revision: 5037 $
dlr_redis.c
Go to the documentation of this file.
1 /* ====================================================================
2  * The Kannel Software License, Version 1.0
3  *
4  * Copyright (c) 2001-2016 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 /*
74  * Some SMSCs (such as the Logica SMPP simulator when bound multiple times
75  * under high load) erroneously return duplicate message IDs. Before writing
76  * the DLR, check to ensure that an existing DLR with the same message ID
77  * doesn't already exist (HMSET of an existing key overwrites it silently
78  * and we want the first message to win, not the erroneous one).
79  * We issue a HSETNX first to ensure that the key doesn't already exist.
80  * Only if it succeeds do we proceed to update it with full DLR info.
81  * Define the following macro in case you want this extra handling.
82  */
83 /* #define REDIS_PRECHECK 1 */
84 
85 /*
86  * Our connection pool to redis.
87  */
88 static DBPool *pool = NULL;
89 
90 /*
91  * Database-centric DLR definition (common across all engines)
92  */
93 static struct dlr_db_fields *fields = NULL;
94 
95 static void dlr_redis_shutdown()
96 {
97  dbpool_destroy(pool);
98  dlr_db_fields_destroy(fields);
99 }
100 
101 static void dlr_redis_add(struct dlr_entry *entry)
102 {
103  Octstr *key, *sql, *os;
104  DBPoolConn *pconn;
105  List *binds;
106  int res, len;
107 
108  debug("dlr.redis", 0, "Adding DLR into keystore");
109 
110  pconn = dbpool_conn_consume(pool);
111  /* just for sure */
112  if (pconn == NULL) {
113  error(0, "DLR: REDIS: No connection available - dropping DLR");
114  dlr_entry_destroy(entry);
115  return;
116  }
117 
118  if (entry->use_dst && entry->destination) {
119  Octstr *dst_min;
120 
121  /* keep a shorten version for the key part */
122  dst_min = octstr_duplicate(entry->destination);
123  len = octstr_len(dst_min);
124  if (len > MIN_DST_LEN)
125  octstr_delete(dst_min, 0, len - MIN_DST_LEN);
126 
127  key = octstr_format("%S:%S:%S:%S", fields->table,
128  entry->smsc,
129  entry->timestamp,
130  dst_min);
131 
132  octstr_destroy(dst_min);
133  } else {
134  key = octstr_format("%S:%S:%S", fields->table,
135  entry->smsc,
136  entry->timestamp);
137  }
138 
139 #ifdef REDIS_PRECHECK
140  binds = gwlist_create();
141  sql = octstr_format("HSETNX %S %S ?", key, fields->field_smsc);
142  if (dbpool_conn_update(pconn, sql, binds) != 1) {
143  error(0, "DLR: REDIS: DLR for %s already exists! Duplicate Message ID?",
144  octstr_get_cstr(key));
145 
146  octstr_destroy(sql);
147  octstr_destroy(key);
148  octstr_destroy(tsclean);
149  octstr_destroy(dstclean);
150  octstr_destroy(srcclean);
151  gwlist_destroy(binds, NULL);
152  dbpool_conn_produce(pconn);
153  return;
154  }
155  octstr_destroy(sql);
156  gwlist_destroy(binds, NULL);
157 #endif
158 
159  binds = gwlist_create();
160  sql = octstr_create("");
161  gwlist_append(binds, octstr_imm("HMSET"));
162  gwlist_append(binds, key);
163  gwlist_append(binds, fields->field_smsc);
164  gwlist_append(binds, entry->smsc);
165  gwlist_append(binds, fields->field_ts);
166  gwlist_append(binds, entry->timestamp);
167  gwlist_append(binds, fields->field_src);
168  gwlist_append(binds, entry->source);
169  gwlist_append(binds, fields->field_dst);
170  gwlist_append(binds, entry->destination);
171  gwlist_append(binds, fields->field_serv);
172  gwlist_append(binds, entry->service);
173  gwlist_append(binds, fields->field_url);
174  octstr_url_encode(entry->url);
175  gwlist_append(binds, entry->url);
176  gwlist_append(binds, fields->field_mask);
177  os = octstr_format("%d", entry->mask);
178  gwlist_append(binds, os);
179  gwlist_append(binds, fields->field_boxc);
180  gwlist_append(binds, entry->boxc_id);
181 
182  res = dbpool_conn_update(pconn, sql, binds);
183 
184  if (res == -1) {
185  error(0, "DLR: REDIS: Error while adding dlr entry %s",
186  octstr_get_cstr(key));
187  }
188  else {
189  /* HMSET returned OK. Set EXPIRE if applicable and then
190  * increment the DLR counter */
191  if (fields->ttl) {
192  gwlist_destroy(binds, NULL);
193  binds = gwlist_create();
194  gwlist_append(binds, octstr_imm("EXPIRE"));
195  gwlist_append(binds, key);
196  octstr_destroy(os);
197  os = octstr_format("%ld", fields->ttl);
198  gwlist_append(binds, os);
199  res = dbpool_conn_update(pconn, sql, binds);
200  }
201  /* We are not performing an 'INCR <table>:Count'
202  * operation here, since we can't be accurate due
203  * to TTL'ed expiration. Rather use 'DBSIZE' based
204  * on seperated databases in redis. */
205  }
206 
207  dbpool_conn_produce(pconn);
208  octstr_destroy(sql);
209  octstr_destroy(key);
210  octstr_destroy(os);
211  gwlist_destroy(binds, NULL);
212  dlr_entry_destroy(entry);
213 }
214 
215 static inline void get_octstr_value(Octstr **os, const List *r, const int i)
216 {
217  *os = octstr_duplicate(gwlist_get((List*)r, i));
218  if (octstr_str_compare(*os, "_NULL_") == 0) {
219  octstr_destroy(*os);
220  *os = NULL;
221  }
222 }
223 
224 static struct dlr_entry *dlr_redis_get(const Octstr *smsc, const Octstr *ts, const Octstr *dst)
225 {
226  Octstr *key, *sql;
227  DBPoolConn *pconn;
228  List *binds = gwlist_create();
229  List *result = NULL, *row;
230  struct dlr_entry *res = NULL;
231 
232  pconn = dbpool_conn_consume(pool);
233  if (pconn == NULL) {
234  error(0, "DLR: REDIS: No connection available");
235  gwlist_destroy(binds, NULL);
236  dbpool_conn_produce(pconn);
237  return NULL;
238  }
239 
240  /* If the destination address is not NULL, then
241  * it has been shortened by the abstractive layer. */
242  if (dst)
243  key = octstr_format("%S:%S:%S:%S", fields->table,
244  (Octstr*) smsc, (Octstr*) ts, (Octstr*) dst);
245  else
246  key = octstr_format("%S:%S:%S", fields->table,
247  (Octstr*) smsc, (Octstr*) ts);
248 
249  sql = octstr_create("");
250  gwlist_append(binds, octstr_imm("HMGET"));
251  gwlist_append(binds, key);
252  gwlist_append(binds, fields->field_mask);
253  gwlist_append(binds, fields->field_serv);
254  gwlist_append(binds, fields->field_url);
255  gwlist_append(binds, fields->field_src);
256  gwlist_append(binds, fields->field_dst);
257  gwlist_append(binds, fields->field_boxc);
258 
259  if (dbpool_conn_select(pconn, sql, binds, &result) != 0) {
260  error(0, "DLR: REDIS: Failed to fetch DLR for %s", octstr_get_cstr(key));
261  octstr_destroy(sql);
262  octstr_destroy(key);
263  gwlist_destroy(binds, NULL);
264  dbpool_conn_produce(pconn);
265  return NULL;
266  }
267 
268  dbpool_conn_produce(pconn);
269  octstr_destroy(sql);
270  octstr_destroy(key);
271  gwlist_destroy(binds, NULL);
272 
273 #define LO2CSTR(r, i) octstr_get_cstr(gwlist_get(r, i))
274 
275  if (gwlist_len(result) > 0) {
276  row = gwlist_extract_first(result);
277 
278  /*
279  * If we get an empty set back from redis, this is
280  * still an array with "" values, representing (nil).
281  * If the mask is empty then this can't be a valid
282  * set, therefore bail out.
283  */
284  if (octstr_len(gwlist_get(row, 0)) > 0) {
285  res = dlr_entry_create();
286  gw_assert(res != NULL);
287  res->mask = atoi(octstr_get_cstr(gwlist_get(row, 0)));
288  get_octstr_value(&res->service, row, 1);
289  get_octstr_value(&res->url, row, 2);
290  octstr_url_decode(res->url);
291  get_octstr_value(&res->source, row, 3);
292  get_octstr_value(&res->destination, row, 4);
293  get_octstr_value(&res->boxc_id, row, 5);
294  res->smsc = octstr_duplicate(smsc);
295  }
297  }
298  gwlist_destroy(result, NULL);
299 
300 #undef LO2CSTR
301 
302  return res;
303 }
304 
305 static void dlr_redis_remove(const Octstr *smsc, const Octstr *ts, const Octstr *dst)
306 {
307  Octstr *key, *sql;
308  DBPoolConn *pconn;
309  List *binds = gwlist_create();
310  int res;
311 
312  debug("dlr.redis", 0, "Removing DLR from keystore");
313 
314  pconn = dbpool_conn_consume(pool);
315  /* just for sure */
316  if (pconn == NULL) {
317  error(0, "DLR: REDIS: No connection available");
318  gwlist_destroy(binds, NULL);
319  return;
320  }
321 
322  if (dst)
323  key = octstr_format("%S:%S:%S:%S", fields->table,
324  (Octstr*) smsc, (Octstr*) ts, (Octstr*) dst);
325  else
326  key = octstr_format("%S:%S:%S", fields->table,
327  (Octstr*) smsc, (Octstr*) ts);
328 
329  sql = octstr_create("");
330  gwlist_append(binds, octstr_imm("DEL"));
331  gwlist_append(binds, key);
332 
333  res = dbpool_conn_update(pconn, sql, binds);
334 
335  /*
336  * Redis DEL returns the number of keys deleted
337  */
338  if (res != 1) {
339  /*
340  * We may fail to delete a DLR that was successfully retrieved
341  * just above due to race conditions when duplicate message IDs
342  * are received. This happens frequently when testing via the
343  * Logica SMPP emulator due to its duplicate message ID bugs.
344  */
345  error(0, "DLR: REDIS: Error while removing dlr entry for %s",
346  octstr_get_cstr(key));
347  }
348  /* We don't perform 'DECR <table>:Count', since we have TTL'ed
349  * expirations, which can't be handled with manual counters. */
350 
351  dbpool_conn_produce(pconn);
352  octstr_destroy(sql);
353  octstr_destroy(key);
354  gwlist_destroy(binds, NULL);
355 }
356 
357 static void dlr_redis_update(const Octstr *smsc, const Octstr *ts, const Octstr *dst, int status)
358 {
359  Octstr *key, *sql, *os_status;
360  DBPoolConn *pconn;
361  List *binds = gwlist_create();
362  int res;
363 
364  debug("dlr.redis", 0, "Updating DLR status in keystore");
365 
366  pconn = dbpool_conn_consume(pool);
367  /* just for sure */
368  if (pconn == NULL) {
369  error(0, "DLR: REDIS: No connection available");
370  gwlist_destroy(binds, NULL);
371  return;
372  }
373 
374  os_status = octstr_format("%d", status);
375 
376  key = octstr_format((dst ? "%S:?:?:?" : "%S:?:?"), fields->table);
377 
378  sql = octstr_format("HSET %S %S ?", key, fields->field_status);
379  gwlist_append(binds, (Octstr*)smsc);
380  gwlist_append(binds, (Octstr*)ts);
381  if (dst != NULL)
382  gwlist_append(binds, (Octstr*)dst);
383  gwlist_append(binds, os_status);
384 
385  if ((res = dbpool_conn_update(pconn, sql, binds)) == -1) {
386  error(0, "DLR: REDIS: Error while updating dlr entry for %s",
387  octstr_get_cstr(key));
388  }
389  else if (!res) {
390  warning(0, "DLR: REDIS: No dlr found to update for %s",
391  octstr_get_cstr(key));
392  }
393 
394  dbpool_conn_produce(pconn);
395  octstr_destroy(os_status);
396  octstr_destroy(key);
397  octstr_destroy(sql);
398  gwlist_destroy(binds, NULL);
399 }
400 
401 static long dlr_redis_messages(void)
402 {
403  List *result, *row;
404  DBPoolConn *conn;
405  long msgs = -1;
406 
407  conn = dbpool_conn_consume(pool);
408  if (conn == NULL)
409  return -1;
410 
411  if (dbpool_conn_select(conn, octstr_imm("DBSIZE"), NULL, &result) != 0) {
412  dbpool_conn_produce(conn);
413  return 0;
414  }
415 
416  dbpool_conn_produce(conn);
417 
418  if (gwlist_len(result) > 0) {
419  row = gwlist_extract_first(result);
420  msgs = atol(octstr_get_cstr(gwlist_get(row, 0)));
422 
423  while ((row = gwlist_extract_first(result)) != NULL)
425  }
426  gwlist_destroy(result, NULL);
427 
428  return msgs;
429 }
430 
431 static void dlr_redis_flush(void)
432 {
433  Octstr *sql;
434  DBPoolConn *pconn;
435  int rows;
436 
437  pconn = dbpool_conn_consume(pool);
438  /* just for sure */
439  if (pconn == NULL) {
440  error(0, "DLR: REDIS: No connection available");
441  return;
442  }
443 
444  sql = octstr_imm("FLUSHDB");
445  rows = dbpool_conn_update(pconn, sql, NULL);
446  if (rows == -1)
447  error(0, "DLR: REDIS: Error while flushing dlr entries from database");
448  else
449  debug("dlr.redis", 0, "Flushed %d DLR entries from database", rows);
450  dbpool_conn_produce(pconn);
451  octstr_destroy(sql);
452 }
453 
454 static struct dlr_storage handles = {
455  .type = "redis",
456  .dlr_add = dlr_redis_add,
457  .dlr_get = dlr_redis_get,
458  .dlr_update = dlr_redis_update,
459  .dlr_remove = dlr_redis_remove,
460  .dlr_shutdown = dlr_redis_shutdown,
461  .dlr_messages = dlr_redis_messages,
462  .dlr_flush = dlr_redis_flush
463 };
464 
466 {
467  CfgGroup *grp;
468  List *grplist;
469  Octstr *redis_host, *redis_pass, *redis_id;
470  long redis_port = 0, redis_database = -1, redis_idle_timeout = -1;
471  Octstr *p = NULL;
472  long pool_size;
473  DBConf *db_conf = NULL;
474 
475  /*
476  * Check for all mandatory directives that specify the field names
477  * of the used Redis key
478  */
479  if (!(grp = cfg_get_single_group(cfg, octstr_imm("dlr-db"))))
480  panic(0, "DLR: Redis: group 'dlr-db' is not specified!");
481 
482  if (!(redis_id = cfg_get(grp, octstr_imm("id"))))
483  panic(0, "DLR: Redis: directive 'id' is not specified!");
484 
485  fields = dlr_db_fields_create(grp);
486  gw_assert(fields != NULL);
487 
488  /*
489  * Escaping special quotes for field/table names
490  */
491  octstr_replace(fields->table, octstr_imm("`"), octstr_imm("``"));
492  octstr_replace(fields->field_smsc, octstr_imm("`"), octstr_imm("``"));
493  octstr_replace(fields->field_ts, octstr_imm("`"), octstr_imm("``"));
494  octstr_replace(fields->field_src, octstr_imm("`"), octstr_imm("``"));
495  octstr_replace(fields->field_dst, octstr_imm("`"), octstr_imm("``"));
496  octstr_replace(fields->field_serv, octstr_imm("`"), octstr_imm("``"));
497  octstr_replace(fields->field_url, octstr_imm("`"), octstr_imm("``"));
498  octstr_replace(fields->field_mask, octstr_imm("`"), octstr_imm("``"));
499  octstr_replace(fields->field_status, octstr_imm("`"), octstr_imm("``"));
500  octstr_replace(fields->field_boxc, octstr_imm("`"), octstr_imm("``"));
501 
502  /*
503  * Now grab the required information from the 'redis-connection' group
504  * with the redis-id we just obtained.
505  *
506  * We have to loop through all available Redis connection definitions
507  * and search for the one we are looking for.
508  */
509  grplist = cfg_get_multi_group(cfg, octstr_imm("redis-connection"));
510  while (grplist && (grp = gwlist_extract_first(grplist)) != NULL) {
511  p = cfg_get(grp, octstr_imm("id"));
512  if (p != NULL && octstr_compare(p, redis_id) == 0) {
513  goto found;
514  }
515  if (p != NULL)
516  octstr_destroy(p);
517  }
518  panic(0, "DLR: Redis: connection settings for id '%s' are not specified!",
519  octstr_get_cstr(redis_id));
520 
521 found:
522  octstr_destroy(p);
523  gwlist_destroy(grplist, NULL);
524 
525  if (cfg_get_integer(&pool_size, grp, octstr_imm("max-connections")) == -1 || pool_size == 0)
526  pool_size = 1;
527 
528  if (!(redis_host = cfg_get(grp, octstr_imm("host"))))
529  panic(0, "DLR: Redis: directive 'host' is not specified!");
530  if (cfg_get_integer(&redis_port, grp, octstr_imm("port")) == -1)
531  panic(0, "DLR: Redis: directive 'port' is not specified!");
532  redis_pass = cfg_get(grp, octstr_imm("password"));
533  cfg_get_integer(&redis_database, grp, octstr_imm("database"));
534  cfg_get_integer(&redis_idle_timeout, grp, octstr_imm("idle-timeout"));
535 
536  /*
537  * Ok, ready to connect to Redis
538  */
539  db_conf = gw_malloc(sizeof(DBConf));
540  gw_assert(db_conf != NULL);
541 
542  db_conf->redis = gw_malloc(sizeof(RedisConf));
543  gw_assert(db_conf->redis != NULL);
544 
545  db_conf->redis->host = redis_host;
546  db_conf->redis->port = redis_port;
547  db_conf->redis->password = redis_pass;
548  db_conf->redis->database = redis_database;
549  db_conf->redis->idle_timeout = redis_idle_timeout;
550 
551  pool = dbpool_create(DBPOOL_REDIS, db_conf, pool_size);
552  gw_assert(pool != NULL);
553 
554  /*
555  * Panic on failure to connect. Should we just try to reconnect?
556  */
557  if (dbpool_conn_count(pool) == 0)
558  panic(0,"DLR: Redis: database pool has no connections!");
559 
560  octstr_destroy(redis_id);
561 
562  return &handles;
563 }
564 #else
565 /*
566  * Return NULL, so we point dlr-core that we were
567  * not compiled in.
568  */
570 {
571  return NULL;
572 }
573 #endif /* HAVE_REDIS */
void error(int err, const char *fmt,...)
Definition: log.c:612
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:2647
long dbpool_conn_count(DBPool *p)
DBPool * dbpool_create(enum db_type db_type, DBConf *conf, unsigned int connections)
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:1744
#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
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:281
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:1525
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:642
void warning(int err, const char *fmt,...)
Definition: log.c:624
Octstr * timestamp
Definition: dlr_p.h:80
Octstr * field_serv
Definition: dlr_p.h:156
Octstr * octstr_format(const char *fmt,...)
Definition: octstr.c:2462
void octstr_destroy(Octstr *ostr)
Definition: octstr.c:322
#define octstr_create(cstr)
Definition: octstr.h:125
void octstr_destroy_item(void *os)
Definition: octstr.c:334
gw_assert(wtls_machine->packet_to_send!=NULL)
long database
Definition: dbpool.h:151
Definition: dbpool.h:164
Octstr * field_smsc
Definition: dlr_p.h:152
static Cfg * cfg
Definition: smsbox.c:115
long octstr_len(const Octstr *ostr)
Definition: octstr.c:340
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:690
int cfg_get_integer(long *n, CfgGroup *grp, Octstr *varname)
Definition: cfg.c:739
#define panic
Definition: log.h:87
Definition: cfg.c:73
int octstr_str_compare(const Octstr *ostr, const char *str)
Definition: octstr.c:971
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:569
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:636
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:1671
long ttl
Definition: dlr_p.h:151
int octstr_compare(const Octstr *ostr1, const Octstr *ostr2)
Definition: octstr.c:869
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.