Kannel: Open Source WAP and SMS gateway  $Revision: 5037 $
dlr_sdb.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_sdb.c
59  *
60  * Implementation of handling delivery reports (DLRs)
61  * for LibSDB.
62  *
63  * Andreas Fink <andreas@fink.org>, 18.08.2001
64  * Stipe Tolj <stolj@wapme.de>, 22.03.2002
65  * Alexander Malysh <a.malysh@centrium.de> 2003
66  * Guillaume Cottenceau 2004 (dbpool support)
67 */
68 
69 #include "gwlib/gwlib.h"
70 #include "gwlib/dbpool.h"
71 #include "dlr_p.h"
72 
73 #ifdef HAVE_SDB
74 #include <sdb.h>
75 
76 /*
77  * Our connection pool to sdb.
78  */
79 static DBPool *pool = NULL;
80 
81 /*
82  * Database fields, which we use.
83  */
84 static struct dlr_db_fields *fields = NULL;
85 
86 enum {
87  SDB_ORACLE,
88  SDB_MYSQL,
89  SDB_POSTGRES,
90  SDB_OTHER
91 };
92 
93 static long sdb_conn_type = SDB_OTHER;
94 
95 
96 static const char* sdb_get_limit_str()
97 {
98  switch (sdb_conn_type) {
99  case SDB_ORACLE:
100  return "AND ROWNUM < 2";
101  case SDB_MYSQL:
102  case SDB_POSTGRES:
103  return "LIMIT 1";
104  case SDB_OTHER:
105  default:
106  return "";
107  }
108 }
109 
110 static void dlr_sdb_shutdown()
111 {
112  dbpool_destroy(pool);
113  dlr_db_fields_destroy(fields);
114 }
115 
116 static int gw_sdb_query(char *query,
117  int (*callback)(int, char **, void *), void *closure)
118 {
119  DBPoolConn *pc;
120  int rows;
121 
122  pc = dbpool_conn_consume(pool);
123  if (pc == NULL) {
124  error(0, "SDB: Database pool got no connection!");
125  return -1;
126  }
127 
128  rows = sdb_query(pc->conn, query, callback, closure);
129 
131 
132  return rows;
133 }
134 
135 static void dlr_sdb_add(struct dlr_entry *dlr)
136 {
137  Octstr *sql;
138  int state;
139 
140  sql = octstr_format("INSERT INTO %s (%s, %s, %s, %s, %s, %s, %s, %s, %s) VALUES "
141  "('%s', '%s', '%s', '%s', '%s', '%s', '%d', '%s', '%d')",
142  octstr_get_cstr(fields->table), octstr_get_cstr(fields->field_smsc),
143  octstr_get_cstr(fields->field_ts),
147  octstr_get_cstr(fields->field_status),
150  octstr_get_cstr(dlr->service), octstr_get_cstr(dlr->url), dlr->mask,
151  octstr_get_cstr(dlr->boxc_id), 0);
152 
153 #if defined(DLR_TRACE)
154  debug("dlr.sdb", 0, "SDB: sql: %s", octstr_get_cstr(sql));
155 #endif
156 
157  state = gw_sdb_query(octstr_get_cstr(sql), NULL, NULL);
158  if (state == -1)
159  error(0, "SDB: error in inserting DLR for DST <%s>", octstr_get_cstr(dlr->destination));
160  else if (!state)
161  warning(0, "SDB: No dlr inserted for DST <%s>", octstr_get_cstr(dlr->destination));
162 
163  octstr_destroy(sql);
164  dlr_entry_destroy(dlr);
165 }
166 
167 static int sdb_callback_add(int n, char **p, void *data)
168 {
169  struct dlr_entry *res = (struct dlr_entry *) data;
170 
171  if (n != 6) {
172  debug("dlr.sdb", 0, "SDB: Result has incorrect number of columns: %d", n);
173  return 0;
174  }
175 
176 #if defined(DLR_TRACE)
177  debug("dlr.sdb", 0, "row=%s,%s,%s,%s,%s,%s",p[0],p[1],p[2],p[3],p[4],p[5]);
178 #endif
179 
180  if (res->destination != NULL) {
181  debug("dlr.sdb", 0, "SDB: Row already stored.");
182  return 0;
183  }
184 
185  res->mask = atoi(p[0]);
186  res->service = octstr_create(p[1]);
187  res->url = octstr_create(p[2]);
188  res->source = octstr_create(p[3]);
189  res->destination = octstr_create(p[4]);
190  res->boxc_id = octstr_create(p[5]);
191 
192  return 0;
193 }
194 
195 static int sdb_callback_msgs(int n, char **p, void *data)
196 {
197  long *count = (long *) data;
198 
199  if (n != 1) {
200  debug("dlr.sdb", 0, "SDB: Result has incorrect number of columns: %d", n);
201  return 0;
202  }
203 
204 #if defined(DLR_TRACE)
205  debug("dlr.sdb", 0, "SDB: messages=%s",p[0]);
206 #endif
207 
208  *count = atol(p[0]);
209 
210  return 0;
211 }
212 
213 static struct dlr_entry* dlr_sdb_get(const Octstr *smsc, const Octstr *ts, const Octstr *dst)
214 {
215  Octstr *sql, *like;
216  int state;
217  struct dlr_entry *res = dlr_entry_create();
218 
219  gw_assert(res != NULL);
220 
221  if (dst)
222  like = octstr_format("AND %S LIKE '%%%S'", fields->field_dst, dst);
223  else
224  like = octstr_imm("");
225 
226  sql = octstr_format("SELECT %S, %S, %S, %S, %S, %S FROM %S WHERE %S='%S' "
227  "AND %S='%S' %S %s", fields->field_mask, fields->field_serv,
228  fields->field_url, fields->field_src, fields->field_dst,
229  fields->field_boxc, fields->table, fields->field_smsc, smsc,
230  fields->field_ts, ts, like, sdb_get_limit_str());
231 
232 #if defined(DLR_TRACE)
233  debug("dlr.sdb", 0, "SDB: sql: %s", octstr_get_cstr(sql));
234 #endif
235 
236  state = gw_sdb_query(octstr_get_cstr(sql), sdb_callback_add, res);
237  octstr_destroy(sql);
238  octstr_destroy(like);
239  if (state == -1) {
240  error(0, "SDB: error in finding DLR");
241  goto notfound;
242  }
243  else if (state == 0) {
244  debug("dlr.sdb", 0, "SDB: no entry found for DST <%s>.", octstr_get_cstr(dst));
245  goto notfound;
246  }
247 
248  res->smsc = octstr_duplicate(smsc);
249 
250  return res;
251 
252 notfound:
253  dlr_entry_destroy(res);
254  return NULL;
255 }
256 
257 static void dlr_sdb_update(const Octstr *smsc, const Octstr *ts, const Octstr *dst, int status)
258 {
259  Octstr *sql, *like;
260  int state;
261 
262  debug("dlr.sdb", 0, "SDB: updating DLR status in database");
263 
264  if (dst)
265  like = octstr_format("AND %S LIKE '%%%S'", fields->field_dst, dst);
266  else
267  like = octstr_imm("");
268 
269  sql = octstr_format("UPDATE %S SET %S=%d WHERE %S='%S' AND %S='%S' %S %s",
270  fields->table, fields->field_status, status, fields->field_smsc,
271  smsc, fields->field_ts, ts, like, sdb_get_limit_str());
272 
273 #if defined(DLR_TRACE)
274  debug("dlr.sdb", 0, "SDB: sql: %s", octstr_get_cstr(sql));
275 #endif
276 
277  state = gw_sdb_query(octstr_get_cstr(sql), NULL, NULL);
278  octstr_destroy(sql);
279  octstr_destroy(like);
280  if (state == -1)
281  error(0, "SDB: error in updating DLR");
282  else if (!state)
283  warning(0, "SDB: No dlr to update for DST<%s> (status %d)", octstr_get_cstr(dst), status);
284 }
285 
286 static void dlr_sdb_remove(const Octstr *smsc, const Octstr *ts, const Octstr *dst)
287 {
288  Octstr *sql, *like;
289  int state;
290 
291  debug("dlr.sdb", 0, "removing DLR from database");
292  if (sdb_conn_type == SDB_POSTGRES) {
293  /*
294  * Postgres doesn't support limiting delete/update queries,
295  * thus we need to use a select subquery.
296  * - notice that for uniqueness use of `oid', postgres suggests
297  * to do vacuum regularly, even if it's virtually impossible
298  * to hit duplicates since oid's are given in a row
299  */
300  if (dst)
301  like = octstr_format("AND %S LIKE '%%%S')",
302  fields->field_dst, dst);
303  else
304  like = octstr_imm("LIMIT 1)");
305  sql = octstr_format("DELETE FROM %S WHERE oid = (SELECT oid FROM %S "
306  "WHERE %S='%S' AND %S='%S' %S LIMIT 1", fields->table,
307  fields->table, fields->field_smsc, smsc, fields->field_ts, ts,
308  like);
309  } else {
310  if (dst)
311  like = octstr_format("AND %S LIKE '%%%S'", fields->field_dst, dst);
312  else
313  like = octstr_imm("");
314 
315  sql = octstr_format("DELETE FROM %S WHERE %S='%S' AND %S='%S' %S %s",
316  fields->table, fields->field_smsc, smsc, fields->field_ts, ts,
317  like, sdb_get_limit_str());
318  }
319 
320 #if defined(DLR_TRACE)
321  debug("dlr.sdb", 0, "SDB: sql: %s", octstr_get_cstr(sql));
322 #endif
323 
324  state = gw_sdb_query(octstr_get_cstr(sql), NULL, NULL);
325  octstr_destroy(sql);
326  octstr_destroy(like);
327  if (state == -1)
328  error(0, "SDB: error in deleting DLR");
329  else if (!state)
330  warning(0, "SDB: No dlr deleted for DST<%s>", octstr_get_cstr(dst));
331 }
332 
333 static long dlr_sdb_messages(void)
334 {
335  Octstr *sql;
336  int state;
337  long res = 0;
338 
339  sql = octstr_format("SELECT count(*) FROM %s", octstr_get_cstr(fields->table));
340 
341 #if defined(DLR_TRACE)
342  debug("dlr.sdb", 0, "sql: %s", octstr_get_cstr(sql));
343 #endif
344 
345  state = gw_sdb_query(octstr_get_cstr(sql), sdb_callback_msgs, &res);
346  octstr_destroy(sql);
347  if (state == -1) {
348  error(0, "SDB: error in selecting ammount of waiting DLRs");
349  return -1;
350  }
351 
352  return res;
353 }
354 
355 static void dlr_sdb_flush(void)
356 {
357  Octstr *sql;
358  int state;
359 
360  sql = octstr_format("DELETE FROM %s", octstr_get_cstr(fields->table));
361 
362 #if defined(DLR_TRACE)
363  debug("dlr.sdb", 0, "sql: %s", octstr_get_cstr(sql));
364 #endif
365 
366  state = gw_sdb_query(octstr_get_cstr(sql), NULL, NULL);
367  octstr_destroy(sql);
368  if (state == -1) {
369  error(0, "SDB: error in flusing DLR table");
370  }
371 }
372 
373 
374 static struct dlr_storage handles = {
375  .type = "sdb",
376  .dlr_add = dlr_sdb_add,
377  .dlr_get = dlr_sdb_get,
378  .dlr_update = dlr_sdb_update,
379  .dlr_remove = dlr_sdb_remove,
380  .dlr_shutdown = dlr_sdb_shutdown,
381  .dlr_messages = dlr_sdb_messages,
382  .dlr_flush = dlr_sdb_flush
383 };
384 
386 {
387  CfgGroup *grp;
388  List *grplist;
389  Octstr *sdb_url, *sdb_id;
390  Octstr *p = NULL;
391  long pool_size;
392  DBConf *db_conf = NULL;
393 
394  /*
395  * check for all mandatory directives that specify the field names
396  * of the used table
397  */
398  if (!(grp = cfg_get_single_group(cfg, octstr_imm("dlr-db"))))
399  panic(0, "DLR: SDB: group 'dlr-db' is not specified!");
400 
401  if (!(sdb_id = cfg_get(grp, octstr_imm("id"))))
402  panic(0, "DLR: SDB: directive 'id' is not specified!");
403 
404  fields = dlr_db_fields_create(grp);
405  gw_assert(fields != NULL);
406 
407  /*
408  * now grap the required information from the 'mysql-connection' group
409  * with the sdb-id we just obtained
410  *
411  * we have to loop through all available SDB connection definitions
412  * and search for the one we are looking for
413  */
414 
415  grplist = cfg_get_multi_group(cfg, octstr_imm("sdb-connection"));
416  while (grplist && (grp = gwlist_extract_first(grplist)) != NULL) {
417  p = cfg_get(grp, octstr_imm("id"));
418  if (p != NULL && octstr_compare(p, sdb_id) == 0) {
419  goto found;
420  }
421  if (p != NULL) octstr_destroy(p);
422  }
423  panic(0, "DLR: SDB: connection settings for id '%s' are not specified!",
424  octstr_get_cstr(sdb_id));
425 
426 found:
427  octstr_destroy(p);
428  gwlist_destroy(grplist, NULL);
429 
430  if (cfg_get_integer(&pool_size, grp, octstr_imm("max-connections")) == -1 || pool_size == 0)
431  pool_size = 1;
432 
433  if (!(sdb_url = cfg_get(grp, octstr_imm("url"))))
434  panic(0, "DLR: SDB: directive 'url' is not specified!");
435 
436  if (octstr_search(sdb_url, octstr_imm("oracle:"), 0) == 0)
437  sdb_conn_type = SDB_ORACLE;
438  else if (octstr_search(sdb_url, octstr_imm("mysql:"), 0) == 0) {
439  warning(0, "DLR[sdb]: Please use native MySQL support, instead of libsdb.");
440  sdb_conn_type = SDB_MYSQL;
441  }
442  else if (octstr_search(sdb_url, octstr_imm("postgres:"), 0) == 0) {
443  sdb_conn_type = SDB_POSTGRES;
444  }
445  else
446  sdb_conn_type = SDB_OTHER;
447 
448  /*
449  * ok, ready to connect
450  */
451  info(0,"Connecting to sdb resource <%s>.", octstr_get_cstr(sdb_url));
452 
453  db_conf = gw_malloc(sizeof(DBConf));
454  gw_assert(db_conf != NULL);
455 
456  db_conf->sdb = gw_malloc(sizeof(SDBConf));
457  gw_assert(db_conf->sdb != NULL);
458 
459  db_conf->sdb->url = sdb_url;
460 
461  pool = dbpool_create(DBPOOL_SDB, db_conf, pool_size);
462  gw_assert(pool != NULL);
463 
464  /*
465  * XXX should a failing connect throw panic?!
466  */
467  if (dbpool_conn_count(pool) == 0)
468  panic(0,"DLR: SDB: database pool has no connections!");
469 
470  return &handles;
471 }
472 #else
473 /*
474  * Return NULL , so we point dlr-core that we were
475  * not compiled in.
476  */
478 {
479  return NULL;
480 }
481 #endif /* HAVE_SDB */
void error(int err, const char *fmt,...)
Definition: log.c:612
const char * type
Definition: dlr_p.h:112
void info(int err, const char *fmt,...)
Definition: log.c:636
Octstr * url
Definition: dlr_p.h:84
long dbpool_conn_count(DBPool *p)
DBPool * dbpool_create(enum db_type db_type, DBConf *conf, unsigned int connections)
Octstr * field_boxc
Definition: dlr_p.h:160
Octstr * service
Definition: dlr_p.h:83
Octstr * url
Definition: dbpool.h:124
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
Octstr * boxc_id
Definition: dlr_p.h:85
SDBConf * sdb
Definition: dbpool.h:167
long octstr_search(const Octstr *haystack, const Octstr *needle, long pos)
Definition: octstr.c:1068
#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
#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
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
gw_assert(wtls_machine->packet_to_send!=NULL)
Definition: dbpool.h:164
Octstr * field_smsc
Definition: dlr_p.h:152
static Cfg * cfg
Definition: smsbox.c:115
Octstr * destination
Definition: dlr_p.h:82
void dbpool_destroy(DBPool *p)
Definition: octstr.c:118
struct dlr_storage * dlr_init_sdb(Cfg *cfg)
Definition: dlr_sdb.c:477
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
Definition: dlr_p.h:78
DBPoolConn * dbpool_conn_consume(DBPool *p)
Octstr * smsc
Definition: dlr_p.h:79
void * conn
Definition: dbpool.h:95
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
Octstr * field_mask
Definition: dlr_p.h:158
int octstr_compare(const Octstr *ostr1, const Octstr *ostr2)
Definition: octstr.c:869
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.