Kannel: Open Source WAP and SMS gateway  svn-r5335
sqlbox_mysql.c
Go to the documentation of this file.
1 #include "gwlib/gwlib.h"
2 #ifdef HAVE_MYSQL
3 #include "gwlib/dbpool.h"
4 #include <mysql.h>
5 #include "sqlbox_mysql.h"
6 
7 #define sql_update mysql_update
8 #define sql_select mysql_select
9 #define MYSQL_ERR_NOSUCHFIELD 1054
10 
11 static Octstr *sqlbox_logtable;
12 static Octstr *sqlbox_insert_table;
13 
14 /*
15  * Our connection pool to mysql.
16  */
17 
18 static DBPool *pool = NULL;
19 
20 static void mysql_update(const Octstr *sql)
21 {
22  int state;
23  DBPoolConn *pc;
24 
25 #if defined(SQLBOX_TRACE)
26  debug("SQLBOX", 0, "sql: %s", octstr_get_cstr(sql));
27 #endif
28 
29  pc = dbpool_conn_consume(pool);
30  if (pc == NULL) {
31  error(0, "MYSQL: Database pool got no connection! DB update failed!");
32  return;
33  }
34 
35  state = mysql_query(pc->conn, octstr_get_cstr(sql));
36  if (state != 0)
37  error(0, "MYSQL: %s", mysql_error(pc->conn));
38  if (mysql_errno(pc->conn) == MYSQL_ERR_NOSUCHFIELD) {
39  error(0, "Try to recreate insert and log tables. The structure may have changed. See ChangeLog.");
40  }
41 
43 }
44 
45 static MYSQL_RES* mysql_select(const Octstr *sql)
46 {
47  int state;
48  MYSQL_RES *result = NULL;
49  DBPoolConn *pc;
50 
51 #if defined(SQLBOX_TRACE)
52  debug("SQLBOX", 0, "sql: %s", octstr_get_cstr(sql));
53 #endif
54 
55  pc = dbpool_conn_consume(pool);
56  if (pc == NULL) {
57  error(0, "MYSQL: Database pool got no connection! DB update failed!");
58  return NULL;
59  }
60 
61  state = mysql_query(pc->conn, octstr_get_cstr(sql));
62  if (state != 0) {
63  error(0, "MYSQL: %s", mysql_error(pc->conn));
64  if (mysql_errno(pc->conn) == MYSQL_ERR_NOSUCHFIELD) {
65  error(0, "Try to recreate insert and log tables. The structure may have changed. See ChangeLog.");
66  }
67  } else {
68  result = mysql_store_result(pc->conn);
69  }
70 
72 
73  return result;
74 }
75 
76 void sqlbox_configure_mysql(Cfg* cfg)
77 {
78  CfgGroup *grp;
79  Octstr *sql;
80 
81  if (!(grp = cfg_get_single_group(cfg, octstr_imm("sqlbox"))))
82  panic(0, "SQLBOX: MySQL: group 'sqlbox' is not specified!");
83 
84  sqlbox_logtable = cfg_get(grp, octstr_imm("sql-log-table"));
85  if (sqlbox_logtable == NULL) {
86  panic(0, "No 'sql-log-table' not configured.");
87  }
88  sqlbox_insert_table = cfg_get(grp, octstr_imm("sql-insert-table"));
89  if (sqlbox_insert_table == NULL) {
90  panic(0, "No 'sql-insert-table' not configured.");
91  }
92 
93  /* create send_sms && sent_sms tables if they do not exist */
94  sql = octstr_format(SQLBOX_MYSQL_CREATE_LOG_TABLE, sqlbox_logtable);
95  sql_update(sql);
96  octstr_destroy(sql);
97  sql = octstr_format(SQLBOX_MYSQL_CREATE_INSERT_TABLE, sqlbox_insert_table);
98  sql_update(sql);
99  octstr_destroy(sql);
100  /* end table creation */
101 }
102 
103 #define octstr_null_create(x) ((x != NULL) ? octstr_create(x) : octstr_create(""))
104 #define atol_null(x) ((x != NULL) ? atol(x) : -1)
105 Msg *mysql_fetch_msg()
106 {
107  Msg *msg = NULL;
108  Octstr *sql, *delet, *id;
109  MYSQL_RES *res;
110  MYSQL_ROW row;
111 
112  sql = octstr_format(SQLBOX_MYSQL_SELECT_QUERY, sqlbox_insert_table);
113  res = mysql_select(sql);
114  if (res == NULL) {
115  debug("sqlbox", 0, "SQL statement failed: %s", octstr_get_cstr(sql));
116  }
117  else {
118  if (mysql_num_rows(res) >= 1) {
119  row = mysql_fetch_row(res);
120  id = octstr_null_create(row[0]);
121  /* save fields in this row as msg struct */
122  msg = msg_create(sms);
123  /* we abuse the foreign_id field in the message struct for our sql_id value */
124  msg->sms.foreign_id = octstr_null_create(row[0]);
125  msg->sms.sender = octstr_null_create(row[2]);
126  msg->sms.receiver = octstr_null_create(row[3]);
127  msg->sms.udhdata = octstr_null_create(row[4]);
128  msg->sms.msgdata = octstr_null_create(row[5]);
129  msg->sms.time = atol_null(row[6]);
130  msg->sms.smsc_id = octstr_null_create(row[7]);
131  msg->sms.service = octstr_null_create(row[8]);
132  msg->sms.account = octstr_null_create(row[9]);
133  msg->sms.sms_type = atol_null(row[11]);
134  msg->sms.mclass = atol_null(row[12]);
135  msg->sms.mwi = atol_null(row[13]);
136  msg->sms.coding = atol_null(row[14]);
137  msg->sms.compress = atol_null(row[15]);
138  msg->sms.validity = atol_null(row[16]);
139  msg->sms.deferred = atol_null(row[17]);
140  msg->sms.dlr_mask = atol_null(row[18]);
141  msg->sms.dlr_url = octstr_null_create(row[19]);
142  msg->sms.pid = atol_null(row[20]);
143  msg->sms.alt_dcs = atol_null(row[21]);
144  msg->sms.rpi = atol_null(row[22]);
145  msg->sms.charset = octstr_null_create(row[23]);
146  msg->sms.binfo = octstr_null_create(row[25]);
147  msg->sms.meta_data = octstr_null_create(row[26]);
148  msg->sms.priority = atol_null(row[27]);
149  if (row[24] == NULL) {
150  msg->sms.boxc_id= octstr_duplicate(sqlbox_id);
151  }
152  else {
153  msg->sms.boxc_id= octstr_null_create(row[24]);
154  }
155  /* delete current row */
156  delet = octstr_format(SQLBOX_MYSQL_DELETE_QUERY, sqlbox_insert_table, id);
157 #if defined(SQLBOX_TRACE)
158  debug("SQLBOX", 0, "sql: %s", octstr_get_cstr(delet));
159 #endif
160  mysql_update(delet);
161  octstr_destroy(id);
162  octstr_destroy(delet);
163  }
164  mysql_free_result(res);
165  }
166  octstr_destroy(sql);
167  return msg;
168 }
169 
170 int mysql_fetch_msg_list(List *qlist, long limit)
171 {
172  Msg *msg = NULL;
173  Octstr *sql, *delet, *id;
174  MYSQL_RES *res;
175  MYSQL_ROW row;
176  int ret = 0;
177 
178  sql = octstr_format(SQLBOX_MYSQL_SELECT_LIST_QUERY, sqlbox_insert_table, limit);
179  res = mysql_select(sql);
180  if (res == NULL) {
181  debug("sqlbox", 0, "SQL statement failed: %s", octstr_get_cstr(sql));
182  }
183  else {
184  ret = mysql_num_rows(res);
185  if (ret >= 1) {
186  while (row = mysql_fetch_row(res)) {
187  /* save fields in this row as msg struct */
188  msg = msg_create(sms);
189  /* we abuse the foreign_id field in the message struct for our sql_id value */
190  msg->sms.foreign_id = octstr_null_create(row[0]);
191  msg->sms.sender = octstr_null_create(row[2]);
192  msg->sms.receiver = octstr_null_create(row[3]);
193  msg->sms.udhdata = octstr_null_create(row[4]);
194  msg->sms.msgdata = octstr_null_create(row[5]);
195  msg->sms.time = atol_null(row[6]);
196  msg->sms.smsc_id = octstr_null_create(row[7]);
197  msg->sms.service = octstr_null_create(row[8]);
198  msg->sms.account = octstr_null_create(row[9]);
199  msg->sms.sms_type = atol_null(row[11]);
200  msg->sms.mclass = atol_null(row[12]);
201  msg->sms.mwi = atol_null(row[13]);
202  msg->sms.coding = atol_null(row[14]);
203  msg->sms.compress = atol_null(row[15]);
204  msg->sms.validity = atol_null(row[16]);
205  msg->sms.deferred = atol_null(row[17]);
206  msg->sms.dlr_mask = atol_null(row[18]);
207  msg->sms.dlr_url = octstr_null_create(row[19]);
208  msg->sms.pid = atol_null(row[20]);
209  msg->sms.alt_dcs = atol_null(row[21]);
210  msg->sms.rpi = atol_null(row[22]);
211  msg->sms.charset = octstr_null_create(row[23]);
212  msg->sms.binfo = octstr_null_create(row[25]);
213  msg->sms.meta_data = octstr_null_create(row[26]);
214  msg->sms.priority = atol_null(row[27]);
215  if (row[24] == NULL) {
216  msg->sms.boxc_id= octstr_duplicate(sqlbox_id);
217  }
218  else {
219  msg->sms.boxc_id= octstr_null_create(row[24]);
220  }
221  gwlist_produce(qlist, msg);
222  }
223  }
224  mysql_free_result(res);
225  }
226  octstr_destroy(sql);
227  return ret;
228 }
229 
230 static Octstr *get_numeric_value_or_return_null(long int num)
231 {
232  if (num == -1) {
233  return octstr_create("NULL");
234  }
235  return octstr_format("%ld", num);
236 }
237 
238 static Octstr *get_string_value_or_return_null(Octstr *str)
239 {
240  if (str == NULL) {
241  return octstr_create("NULL");
242  }
243  if (octstr_compare(str, octstr_imm("")) == 0) {
244  return octstr_create("NULL");
245  }
246  /* todo: create a new string instead of inline replacing */
247  octstr_replace(str, octstr_imm("\\"), octstr_imm("\\\\"));
248  octstr_replace(str, octstr_imm("\'"), octstr_imm("\\\'"));
249  return octstr_format("\'%S\'", str);
250 }
251 
252 #define st_num(x) (stuffer[stuffcount++] = get_numeric_value_or_return_null(x))
253 #define st_str(x) (stuffer[stuffcount++] = get_string_value_or_return_null(x))
254 
255 void mysql_save_msg(Msg *msg, Octstr *momt)
256 {
257  Octstr *sql;
258  Octstr *stuffer[30];
259  int stuffcount = 0;
260 
261  sql = octstr_format(SQLBOX_MYSQL_INSERT_QUERY, sqlbox_logtable, st_str(momt), st_str(msg->sms.sender),
262  st_str(msg->sms.receiver), st_str(msg->sms.udhdata), st_str(msg->sms.msgdata), st_num(msg->sms.time),
263  st_str(msg->sms.smsc_id), st_str(msg->sms.service), st_str(msg->sms.account), st_num(msg->sms.sms_type),
264  st_num(msg->sms.mclass), st_num(msg->sms.mwi), st_num(msg->sms.coding), st_num(msg->sms.compress),
265  st_num(msg->sms.validity), st_num(msg->sms.deferred), st_num(msg->sms.dlr_mask), st_str(msg->sms.dlr_url),
266  st_num(msg->sms.pid), st_num(msg->sms.alt_dcs), st_num(msg->sms.rpi), st_str(msg->sms.charset),
267  st_str(msg->sms.boxc_id), st_str(msg->sms.binfo), st_str(msg->sms.meta_data), st_num(msg->sms.priority), st_str(msg->sms.foreign_id));
268  sql_update(sql);
269  while (stuffcount > 0) {
270  octstr_destroy(stuffer[--stuffcount]);
271  }
272  octstr_destroy(sql);
273 }
274 
275 /* save a list of messages and delete them from the insert table */
276 void mysql_save_list(List *qlist, Octstr *momt, int save_mt)
277 {
278  Octstr *sql, *values, *ids, *sep;
279  Octstr *stuffer[30];
280  int stuffcount = 0, first = 1;
281  Msg *msg;
282 
283  values = save_mt ? octstr_create("") : NULL;
284  ids = octstr_create("");
285  sep = octstr_imm("");
286  while (gwlist_len(qlist) > 0 && (msg = gwlist_consume(qlist)) != NULL) {
287  if (save_mt) {
288  /* convert into urlencoded tekst first */
289  octstr_url_encode(msg->sms.msgdata);
290  octstr_url_encode(msg->sms.udhdata);
291  octstr_format_append(values, "%S (NULL, %S, %S, %S, %S, %S, %S, %S, %S, %S, %S, %S, %S, %S, %S, %S, %S, %S, %S, %S, %S, %S, %S, %S, %S, %S, %S, %S)",
292  sep, st_str(momt), st_str(msg->sms.sender),
293  st_str(msg->sms.receiver), st_str(msg->sms.udhdata), st_str(msg->sms.msgdata), st_num(msg->sms.time),
294  st_str(msg->sms.smsc_id), st_str(msg->sms.service), st_str(msg->sms.account), st_num(msg->sms.sms_type),
295  st_num(msg->sms.mclass), st_num(msg->sms.mwi), st_num(msg->sms.coding), st_num(msg->sms.compress),
296  st_num(msg->sms.validity), st_num(msg->sms.deferred), st_num(msg->sms.dlr_mask), st_str(msg->sms.dlr_url),
297  st_num(msg->sms.pid), st_num(msg->sms.alt_dcs), st_num(msg->sms.rpi), st_str(msg->sms.charset),
298  st_str(msg->sms.boxc_id), st_str(msg->sms.binfo), st_str(msg->sms.meta_data), st_num(msg->sms.priority), st_str(msg->sms.foreign_id));
299  }
300  octstr_format_append(ids, "%S %S", sep, msg->sms.foreign_id);
301  msg_destroy(msg);
302  if (first) {
303  first = 0;
304  sep = octstr_imm(",");
305  }
306  while (stuffcount > 0) {
307  octstr_destroy(stuffer[--stuffcount]);
308  }
309  }
310  if (save_mt) {
311  sql = octstr_format(SQLBOX_MYSQL_INSERT_LIST_QUERY, sqlbox_logtable, values);
312  octstr_destroy(values);
313  sql_update(sql);
314  octstr_destroy(sql);
315  }
316  sql = octstr_format(SQLBOX_MYSQL_DELETE_LIST_QUERY, sqlbox_insert_table, ids);
317  octstr_destroy(ids);
318  sql_update(sql);
319  octstr_destroy(sql);
320 }
321 
322 void mysql_leave()
323 {
324  dbpool_destroy(pool);
325 }
326 
327 struct server_type *sqlbox_init_mysql(Cfg* cfg)
328 {
329  CfgGroup *grp;
330  List *grplist;
331  Octstr *mysql_host, *mysql_user, *mysql_pass, *mysql_db, *mysql_id;
332  Octstr *p = NULL;
333  long pool_size, mysql_port;
334  int have_port;
335  DBConf *db_conf = NULL;
336  struct server_type *res = NULL;
337 
338  /*
339  * check for all mandatory directives that specify the field names
340  * of the used MySQL table
341  */
342  if (!(grp = cfg_get_single_group(cfg, octstr_imm("sqlbox"))))
343  panic(0, "SQLBOX: MySQL: group 'sqlbox' is not specified!");
344 
345  if (!(mysql_id = cfg_get(grp, octstr_imm("id"))))
346  panic(0, "SQLBOX: MySQL: directive 'id' is not specified!");
347 
348  /*
349  * now grap the required information from the 'mysql-connection' group
350  * with the mysql-id we just obtained
351  *
352  * we have to loop through all available MySQL connection definitions
353  * and search for the one we are looking for
354  */
355 
356  grplist = cfg_get_multi_group(cfg, octstr_imm("mysql-connection"));
357  while (grplist && (grp = (CfgGroup *)gwlist_extract_first(grplist)) != NULL) {
358  p = cfg_get(grp, octstr_imm("id"));
359  if (p != NULL && octstr_compare(p, mysql_id) == 0) {
360  goto found;
361  }
362  if (p != NULL) octstr_destroy(p);
363  }
364  panic(0, "SQLBOX: MySQL: connection settings for id '%s' are not specified!",
365  octstr_get_cstr(mysql_id));
366 
367 found:
368  octstr_destroy(p);
369  gwlist_destroy(grplist, NULL);
370 
371  if (cfg_get_integer(&pool_size, grp, octstr_imm("max-connections")) == -1 || pool_size == 0)
372  pool_size = 1;
373 
374  if (!(mysql_host = cfg_get(grp, octstr_imm("host"))))
375  panic(0, "SQLBOX: MySQL: directive 'host' is not specified!");
376  if (!(mysql_user = cfg_get(grp, octstr_imm("username"))))
377  panic(0, "SQLBOX: MySQL: directive 'username' is not specified!");
378  if (!(mysql_pass = cfg_get(grp, octstr_imm("password"))))
379  panic(0, "SQLBOX: MySQL: directive 'password' is not specified!");
380  if (!(mysql_db = cfg_get(grp, octstr_imm("database"))))
381  panic(0, "SQLBOX: MySQL: directive 'database' is not specified!");
382  have_port = (cfg_get_integer(&mysql_port, grp, octstr_imm("port")) != -1);
383 
384  /*
385  * ok, ready to connect to MySQL
386  */
387  db_conf = gw_malloc(sizeof(DBConf));
388  gw_assert(db_conf != NULL);
389 
390  db_conf->mysql = gw_malloc(sizeof(MySQLConf));
391  gw_assert(db_conf->mysql != NULL);
392 
393  db_conf->mysql->host = mysql_host;
394  db_conf->mysql->username = mysql_user;
395  db_conf->mysql->password = mysql_pass;
396  db_conf->mysql->database = mysql_db;
397  if (have_port) {
398  db_conf->mysql->port = mysql_port;
399  }
400  else {
401  db_conf->mysql->port = 3306;
402  }
403 
404  pool = dbpool_create(DBPOOL_MYSQL, db_conf, pool_size);
405  gw_assert(pool != NULL);
406 
407  /*
408  * XXX should a failing connect throw panic?!
409  */
410  if (dbpool_conn_count(pool) == 0)
411  panic(0,"SQLBOX: MySQL: database pool has no connections!");
412 
413  octstr_destroy(mysql_id);
414 
415  res = gw_malloc(sizeof(struct server_type));
416  gw_assert(res != NULL);
417 
418  res->type = octstr_create("MySQL");
419  res->sql_enter = sqlbox_configure_mysql;
420  res->sql_leave = mysql_leave;
421  res->sql_fetch_msg = mysql_fetch_msg;
422  res->sql_save_msg = mysql_save_msg;
423  res->sql_fetch_msg_list = mysql_fetch_msg_list;
424  res->sql_save_list = mysql_save_list;
425  return res;
426 }
427 #endif
void error(int err, const char *fmt,...)
Definition: log.c:648
int(* sql_fetch_msg_list)(List *, long)
Definition: sqlbox_sql.h:19
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)
void gwlist_produce(List *list, void *item)
Definition: list.c:411
long gwlist_len(List *list)
Definition: list.c:166
#define cfg_get(grp, varname)
Definition: cfg.h:86
#define msg_create(type)
Definition: msg.h:136
void(* sql_save_list)(List *, Octstr *, int)
Definition: sqlbox_sql.h:20
static Cfg * cfg
Definition: opensmppbox.c:95
void(* sql_leave)()
Definition: sqlbox_sql.h:16
#define octstr_get_cstr(ostr)
Definition: octstr.h:233
static struct pid_list * found
void(* sql_enter)(Cfg *)
Definition: sqlbox_sql.h:15
void dbpool_conn_produce(DBPoolConn *conn)
Octstr * octstr_imm(const char *cstr)
Definition: octstr.c:283
Definition: msg.h:79
Definition: cfg.c:164
void * gwlist_extract_first(List *list)
Definition: list.c:305
Msg *(* sql_fetch_msg)()
Definition: sqlbox_sql.h:17
Octstr * password
Definition: dbpool.h:103
#define octstr_duplicate(ostr)
Definition: octstr.h:187
List * cfg_get_multi_group(Cfg *cfg, Octstr *name)
Definition: cfg.c:645
void msg_destroy(Msg *msg)
Definition: msg.c:132
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
Octstr * host
Definition: dbpool.h:100
Definition: dbpool.h:164
void dbpool_destroy(DBPool *p)
Definition: octstr.c:118
void * gwlist_consume(List *list)
Definition: list.c:427
MySQLConf * mysql
Definition: dbpool.h:166
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
void octstr_format_append(Octstr *os, const char *fmt,...)
Definition: octstr.c:2507
Octstr * username
Definition: dbpool.h:102
Octstr * database
Definition: dbpool.h:104
long port
Definition: dbpool.h:101
DBPoolConn * dbpool_conn_consume(DBPool *p)
Octstr * type
Definition: sqlbox_sql.h:14
void * conn
Definition: dbpool.h:95
CfgGroup * cfg_get_single_group(Cfg *cfg, Octstr *name)
Definition: cfg.c:639
Octstr * sqlbox_id
Definition: sqlbox.c:95
Definition: list.c:102
static XMLRPCDocument * msg
Definition: test_xmlrpc.c:86
void(* sql_save_msg)(Msg *, Octstr *)
Definition: sqlbox_sql.h:18
static int save_mt
Definition: sqlbox.c:89
void octstr_url_encode(Octstr *ostr)
Definition: octstr.c:1673
int octstr_compare(const Octstr *ostr1, const Octstr *ostr2)
Definition: octstr.c:871
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.