stop seg when no loguniqueid is set in config file. From bkw
[asterisk/asterisk.git] / cdr / cdr_odbc.c
1 /*
2  * Asterisk -- A telephony toolkit for Linux.
3  *
4  * ODBC CDR Backend
5  * 
6  * Brian K. West <brian@bkw.org>
7  *
8  * This program is free software, distributed under the terms of
9  * the GNU General Public License.
10  *
11  * Copyright (c) 2003 Digium, Inc.
12  *
13  */
14
15 #include <sys/types.h>
16 #include <asterisk/config.h>
17 #include <asterisk/options.h>
18 #include <asterisk/channel.h>
19 #include <asterisk/cdr.h>
20 #include <asterisk/module.h>
21 #include <asterisk/logger.h>
22 #include "../asterisk.h"
23
24 #include <stdio.h>
25 #include <string.h>
26
27 #include <stdlib.h>
28 #include <unistd.h>
29 #include <time.h>
30
31 #include <sql.h>
32 #include <sqlext.h>
33 #include <sqltypes.h>
34
35 #define DATE_FORMAT "%Y-%m-%d %T"
36
37 static char *desc = "ODBC CDR Backend";
38 static char *name = "ODBC";
39 static char *config = "cdr_odbc.conf";
40 static char *dsn = NULL, *username = NULL, *password = NULL, *loguniqueid = NULL;
41 static int dsn_alloc = 0, username_alloc = 0, password_alloc = 0;
42 static int connected = 0;
43
44 static ast_mutex_t odbc_lock = AST_MUTEX_INITIALIZER;
45
46 static int odbc_do_query(char *sqlcmd);
47 static int odbc_init(void);
48 static size_t escape_string(char *to, const char *from, size_t length);
49
50 static SQLHENV  ODBC_env = SQL_NULL_HANDLE;     /* global ODBC Environment */
51 static int      ODBC_res;                       /* global ODBC Result of Functions */
52 static SQLHDBC  ODBC_con;                       /* global ODBC Connection Handle */
53 static SQLHSTMT ODBC_stmt;                      /* global ODBC Statement Handle */
54
55 static int odbc_log(struct ast_cdr *cdr)
56 {
57         int res;
58         struct tm tm;
59         struct timeval tv;
60         time_t t;
61         char sqlcmd[2048], timestr[128];
62         char *clid=NULL, *dcontext=NULL, *channel=NULL, *dstchannel=NULL, *lastapp=NULL, *lastdata=NULL, *uniqueid=NULL;
63         
64         ast_mutex_lock(&odbc_lock);
65
66         gettimeofday(&tv,NULL);
67         t = tv.tv_sec;
68         localtime_r(&t,&tm);
69         strftime(timestr,128,DATE_FORMAT,&tm);
70
71         memset(sqlcmd,0,2048);
72
73         if((clid = alloca(strlen(cdr->clid) * 2 + 1)) != NULL)
74                 escape_string(clid, cdr->clid, strlen(cdr->clid));
75         if((dcontext = alloca(strlen(cdr->dcontext) * 2 + 1)) != NULL)
76                 escape_string(dcontext, cdr->dcontext, strlen(cdr->dcontext));
77         if((channel = alloca(strlen(cdr->channel) * 2 + 1)) != NULL)
78                 escape_string(channel, cdr->channel, strlen(cdr->channel));
79         if((dstchannel = alloca(strlen(cdr->dstchannel) * 2 + 1)) != NULL)
80                 escape_string(dstchannel, cdr->dstchannel, strlen(cdr->dstchannel));
81         if((lastapp = alloca(strlen(cdr->lastapp) * 2 + 1)) != NULL)
82                 escape_string(lastapp, cdr->lastapp, strlen(cdr->lastapp));
83         if((lastdata = alloca(strlen(cdr->lastdata) * 2 + 1)) != NULL)
84                 escape_string(lastdata, cdr->lastdata, strlen(cdr->lastdata));
85         if((uniqueid = alloca(strlen(cdr->uniqueid) * 2 + 1)) != NULL)
86                 escape_string(uniqueid, cdr->uniqueid, strlen(cdr->uniqueid));
87
88         if ((!clid) || (!dcontext) || (!channel) || (!dstchannel) || (!lastapp) || (!lastdata) || (!uniqueid))
89         {
90                 ast_log(LOG_ERROR, "cdr_odbc:  Out of memory error (insert fails)\n");
91                 ast_mutex_unlock(&odbc_lock);
92                 return -1;
93         }
94
95         if((loguniqueid != NULL) && ((strcmp(loguniqueid, "1") == 0) || (strcmp(loguniqueid, "yes") == 0)))
96         {
97                 sprintf(sqlcmd,"INSERT INTO cdr (calldate,clid,src,dst,dcontext,channel,dstchannel,lastapp,lastdata,duration,billsec,disposition,amaflags,accountcode,uniqueid) VALUES ('%s','%s','%s','%s','%s','%s','%s','%s','%s',%i,%i,%i,%i,'%s','%s')", timestr, clid, cdr->src, cdr->dst, dcontext, channel, dstchannel, lastapp, lastdata, cdr->duration, cdr->billsec, cdr->disposition, cdr->amaflags, cdr->accountcode, uniqueid);
98         }
99         else
100         {
101                 sprintf(sqlcmd,"INSERT INTO cdr (calldate,clid,src,dst,dcontext,channel,dstchannel,lastapp,lastdata,duration,billsec,disposition,amaflags,accountcode) VALUES ('%s','%s','%s','%s','%s','%s','%s','%s','%s',%i,%i,%i,%i,'%s')", timestr, clid, cdr->src, cdr->dst, dcontext, channel, dstchannel, lastapp, lastdata, cdr->duration, cdr->billsec, cdr->disposition, cdr->amaflags, cdr->accountcode);
102         }
103
104         if(connected)
105         {
106                 res = odbc_do_query(sqlcmd);
107                 if(res < 0)
108                 {
109                         if(option_verbose > 3)          
110                                 ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: Query FAILED Call not logged!\n");
111                         res = odbc_init();
112                         if(option_verbose > 3)
113                                 ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: Reconnecting to dsn %s\n", dsn);
114                         if(res < 0)
115                         {
116                                 if(option_verbose > 3)
117                                         ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: %s has gone away!\n", dsn);
118                                 connected = 0;
119                         }
120                         else
121                         {
122                                 if(option_verbose > 3)
123                                         ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: Trying Query again!\n");
124                                 res = odbc_do_query(sqlcmd);
125                                 if(res < 0)
126                                 {
127                                         if(option_verbose > 3)
128                                                 ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: Query FAILED Call not logged!\n");
129                                 }
130                         }
131                 }
132         }
133         else
134         {
135                 if(option_verbose > 3)
136                          ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: Reconnecting to dsn %s\n", dsn);
137                 res = odbc_init();
138                 if(res < 0)
139                 {
140                         if(option_verbose > 3)
141                         {
142                                 ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: %s has gone away!\n", dsn);
143                                 ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: Call not logged!\n");
144                         }
145                 }
146                 else
147                 {
148                         if(option_verbose > 3)
149                                 ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: Trying Query again!\n");
150                         res = odbc_do_query(sqlcmd);
151                         if(res < 0)
152                         {
153                                 if(option_verbose > 3)
154                                         ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: Query FAILED Call not logged!\n");
155                         }
156                 }
157         }
158         ast_mutex_unlock(&odbc_lock);
159         return 0;
160 }
161
162 char *description(void)
163 {
164         return desc;
165 }
166
167 static int odbc_unload_module(void)
168 {
169         if (connected)
170         {
171                 if(option_verbose > 3)
172                         ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: Disconnecting from %s\n", dsn);
173                 SQLFreeHandle(SQL_HANDLE_STMT, ODBC_stmt);
174                 SQLDisconnect(ODBC_con);
175                 SQLFreeHandle(SQL_HANDLE_DBC, ODBC_con);
176                 SQLFreeHandle(SQL_HANDLE_ENV, ODBC_env);
177                 connected = 0;
178         }
179         if (dsn && dsn_alloc)
180         {
181                 if(option_verbose > 3)
182                         ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: free dsn\n");
183                 free(dsn);
184                 dsn = NULL;
185                 dsn_alloc = 0;
186         }
187         if (username && username_alloc)
188         {
189                 if(option_verbose > 3)
190                         ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: free username\n");
191                 free(username);
192                 username = NULL;
193                 username_alloc = 0;
194         }
195         if (password && password_alloc)
196         {
197                 if(option_verbose > 3)
198                         ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: free password\n");
199                 free(password);
200                 password = NULL;
201                 password_alloc = 0;
202         }
203         ast_cdr_unregister(name);
204         return 0;
205 }
206
207 static int odbc_load_module(void)
208 {
209         int res;
210         struct ast_config *cfg;
211         struct ast_variable *var;
212         char *tmp;
213
214         cfg = ast_load(config);
215         if (!cfg)
216         {
217                 ast_log(LOG_WARNING, "cdr_odbc: Unable to load config for ODBC CDR's: %s\n", config);
218                 return 0;
219         }
220         
221         var = ast_variable_browse(cfg, "global");
222         if (!var) {
223                 /* nothing configured */
224                 return 0;
225         }
226
227         tmp = ast_variable_retrieve(cfg,"global","dsn");
228         if (tmp)
229         {
230                 dsn = malloc(strlen(tmp) + 1);
231                 if (dsn != NULL)
232                 {
233                         dsn_alloc = 1;
234                         strcpy(dsn,tmp);
235                 }
236                 else
237                 {
238                         ast_log(LOG_ERROR,"cdr_odbc: Out of memory error.\n");
239                         return -1;
240                 }
241         }
242         else
243         {
244                 ast_log(LOG_WARNING,"cdr_odbc: dsn not specified.  Assuming asteriskdb\n");
245                 dsn = "asteriskdb";
246         }
247
248         tmp = ast_variable_retrieve(cfg,"global","username");
249         if (tmp)
250         {
251                 username = malloc(strlen(tmp) + 1);
252                 if (username != NULL)
253                 {
254                         username_alloc = 1;
255                         strcpy(username,tmp);
256                 }
257                 else
258                 {
259                         ast_log(LOG_ERROR,"cdr_odbc: Out of memory error.\n");
260                         return -1;
261                 }
262         }
263         else
264         {
265                 ast_log(LOG_WARNING,"cdr_odbc: username not specified.  Assuming root\n");
266                 username = "root";
267         }
268
269         tmp = ast_variable_retrieve(cfg,"global","password");
270         if (tmp)
271         {
272                 password = malloc(strlen(tmp) + 1);
273                 if (password != NULL)
274                 {
275                         password_alloc = 1;
276                         strcpy(password,tmp);
277                 }
278                 else
279                 {
280                         ast_log(LOG_ERROR,"cdr_odbc: Out of memory error.\n");
281                         return -1;
282                 }
283         }
284         else
285         {
286                 ast_log(LOG_WARNING,"cdr_odbc: database password not specified.  Assuming blank\n");
287                 password = "";
288         }
289
290         tmp = ast_variable_retrieve(cfg,"global","loguniqueid");
291         if (tmp)
292         {
293                 loguniqueid = malloc(strlen(tmp) + 1);
294                 if (loguniqueid != NULL)
295                 {
296                         strcpy(loguniqueid,tmp);
297                         ast_log(LOG_WARNING,"cdr_odbc: Logging uniqueid\n");
298                 }
299                 else
300                 {
301                         ast_log(LOG_ERROR,"cdr_odbc: Not logging uniqueid\n");
302                         loguniqueid = NULL;
303                 }
304         }
305         else
306         {
307                 ast_log(LOG_WARNING,"cdr_odbc: Not logging uniqueid\n");
308                 loguniqueid = NULL;
309         }
310
311         ast_destroy(cfg);
312         if(option_verbose > 3)
313         {
314                 ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: dsn is %s\n",dsn);
315                 ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: username is %s\n",username);
316                 ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: password is [secret]\n");
317
318         }
319         
320         res = odbc_init();
321         if(res < 0)
322         {
323                 ast_log(LOG_ERROR, "cdr_odbc: Unable to connect to datasource: %s\n", dsn);
324                 ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: Unable to connect to datasource: %s\n", dsn);
325         }
326
327         res = ast_cdr_register(name, desc, odbc_log);
328         if (res)
329         {
330                 ast_log(LOG_ERROR, "cdr_odbc: Unable to register ODBC CDR handling\n");
331         }
332         return res;
333 }
334
335 static int odbc_do_query(char *sqlcmd)
336 {
337         long int ODBC_err;
338         short int ODBC_mlen;
339         char ODBC_msg[200], ODBC_stat[10];
340
341         ODBC_res = SQLAllocHandle(SQL_HANDLE_STMT, ODBC_con, &ODBC_stmt);
342
343         if((ODBC_res != SQL_SUCCESS) && (ODBC_res != SQL_SUCCESS_WITH_INFO))
344         {
345                 if(option_verbose > 3)
346                         ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: Failure in AllocStatement %d\n", ODBC_res);
347                 SQLGetDiagRec(SQL_HANDLE_DBC, ODBC_con, 1, ODBC_stat, &ODBC_err, ODBC_msg, 100, &ODBC_mlen);
348                 SQLFreeHandle(SQL_HANDLE_STMT, ODBC_stmt);      
349                 connected = 0;
350                 return -1;
351         }
352
353         ODBC_res = SQLPrepare(ODBC_stmt, sqlcmd, SQL_NTS);
354
355         if((ODBC_res != SQL_SUCCESS) && (ODBC_res != SQL_SUCCESS_WITH_INFO))
356         {
357                 if(option_verbose > 3)
358                         ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: Error in PREPARE %d\n", ODBC_res);
359                 SQLGetDiagRec(SQL_HANDLE_DBC, ODBC_con, 1, ODBC_stat, &ODBC_err, ODBC_msg, 100, &ODBC_mlen);
360                 SQLFreeHandle(SQL_HANDLE_STMT, ODBC_stmt);
361                 return -1;
362         }
363
364         ODBC_res = SQLExecute(ODBC_stmt);
365
366         if((ODBC_res != SQL_SUCCESS) && (ODBC_res != SQL_SUCCESS_WITH_INFO))
367         {
368                 if(option_verbose > 3)
369                         ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: Error in Query %d\n", ODBC_res);
370                 SQLGetDiagRec(SQL_HANDLE_DBC, ODBC_con, 1, ODBC_stat, &ODBC_err, ODBC_msg, 100, &ODBC_mlen);
371                 SQLFreeHandle(SQL_HANDLE_STMT, ODBC_stmt);
372                 connected = 0;
373                 return -1;
374         }
375         else
376         {
377                 if(option_verbose > 3)
378                         ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: Query Successful!\n");
379                 connected = 1;
380         }
381         return 0;
382 }
383
384 static int odbc_init(void)
385 {
386         long int ODBC_err;
387         short int ODBC_mlen;
388         char ODBC_msg[200], ODBC_stat[10];
389
390         if ( ODBC_env == SQL_NULL_HANDLE || connected == 0)
391         {
392                 ODBC_res = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &ODBC_env);
393
394                 if((ODBC_res != SQL_SUCCESS) && (ODBC_res != SQL_SUCCESS_WITH_INFO))
395                 {
396                         if(option_verbose > 3)
397                                 ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: Error AllocHandle\n");
398                         connected = 0;
399                         return -1;
400                 }
401
402                 ODBC_res = SQLSetEnvAttr(ODBC_env, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3, 0);
403
404                 if((ODBC_res != SQL_SUCCESS) && (ODBC_res != SQL_SUCCESS_WITH_INFO))
405                 {
406                         if(option_verbose > 3)
407                                 ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: Error SetEnv\n");
408                         SQLFreeHandle(SQL_HANDLE_ENV, ODBC_env);
409                         connected = 0;
410                         return -1;
411                 }
412
413                 ODBC_res = SQLAllocHandle(SQL_HANDLE_DBC, ODBC_env, &ODBC_con);
414
415                 if((ODBC_res != SQL_SUCCESS) && (ODBC_res != SQL_SUCCESS_WITH_INFO))
416                 {
417                         if(option_verbose > 3)
418                                 ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: Error AllocHDB %d\n", ODBC_res);
419                         SQLFreeHandle(SQL_HANDLE_ENV, ODBC_env);
420                         connected = 0;
421                         return -1;
422                 }
423
424                 SQLSetConnectAttr(ODBC_con, SQL_LOGIN_TIMEOUT, (SQLPOINTER *)10, 0);    
425         }
426
427         ODBC_res = SQLConnect(ODBC_con, (SQLCHAR*)dsn, SQL_NTS, (SQLCHAR*)username, SQL_NTS, (SQLCHAR*)password, SQL_NTS);
428
429         if((ODBC_res != SQL_SUCCESS) && (ODBC_res != SQL_SUCCESS_WITH_INFO))
430         {
431                 if(option_verbose > 3)
432                         ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: Error SQLConnect %d\n", ODBC_res);
433                 SQLGetDiagRec(SQL_HANDLE_DBC, ODBC_con, 1, ODBC_stat, &ODBC_err, ODBC_msg, 100, &ODBC_mlen);
434                 SQLFreeHandle(SQL_HANDLE_ENV, ODBC_env);
435                 connected = 0;
436                 return -1;
437         }
438         else
439         {
440                 if(option_verbose > 3)
441                         ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: Connected to %s\n", dsn);
442                 connected = 1;
443         }
444
445         return 0;
446 }
447
448 static size_t escape_string(char *to, const char *from, size_t length)
449 {
450         const char *source = from;
451         char *target = to;
452         unsigned int remaining = length;
453         while (remaining > 0) {
454                 switch (*source) {
455                         case '\\':
456                                 *target = '\\';
457                                 target++;
458                                 *target = '\\';
459                                 break;
460                         case '\'':
461                                 *target = '\\';
462                                 target++;
463                                 *target = '\'';
464                                 break;
465                          case '"':
466                                 *target = '\\';
467                                 target++;
468                                 *target = '"';
469                                 break;
470                         default:
471                                 *target = *source;
472                         }
473                 source++;
474                 target++;
475                 remaining--;
476         }
477
478         *target = '\0';
479  
480         return target - to;
481 }
482
483 int load_module(void)
484 {
485         return odbc_load_module();
486 }
487
488 int unload_module(void)
489 {
490         return odbc_unload_module();
491 }
492
493 int reload(void)
494 {
495         odbc_unload_module();
496         return odbc_load_module();
497 }
498
499 int usecount(void)
500 {
501         return connected;
502 }
503
504 char *key()
505 {
506         return ASTERISK_GPL_KEY;
507 }