More renaming updates
[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 extern int odbc_do_query(char *sqlcmd);
47 extern int odbc_init(void);
48
49 static SQLHENV  ODBC_env = SQL_NULL_HANDLE;     /* global ODBC Environment */
50 static int      ODBC_res;                       /* global ODBC Result of Functions */
51 static SQLHDBC  ODBC_con;                       /* global ODBC Connection Handle */
52 static SQLHSTMT ODBC_stmt;                      /* global ODBC Statement Handle */
53
54 static int odbc_log(struct ast_cdr *cdr)
55 {
56         int res;
57         /*
58         long int ODBC_err, ODBC_id;
59         short int ODBC_mlen;
60         char ODBC_msg[200], ODBC_buffer[200], ODBC_stat[10];
61         */
62         struct tm tm;
63         struct timeval tv;
64         time_t t;
65         char sqlcmd[2048], timestr[128];
66         
67         ast_mutex_lock(&odbc_lock);
68
69         gettimeofday(&tv,NULL);
70         t = tv.tv_sec;
71         localtime_r(&t,&tm);
72         strftime(timestr,128,DATE_FORMAT,&tm);
73
74         memset(sqlcmd,0,2048);
75
76         if((strcmp(loguniqueid, "1") == 0) || (strcmp(loguniqueid, "yes") == 0))
77         {
78                 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, cdr->clid, cdr->src, cdr->dst, cdr->dcontext, cdr->channel, cdr->dstchannel, cdr->lastapp, cdr->lastdata, cdr->duration, cdr->billsec, cdr->disposition, cdr->amaflags, cdr->accountcode, cdr->uniqueid);
79         }
80         else
81         {
82                 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, cdr->clid, cdr->src, cdr->dst, cdr->dcontext, cdr->channel, cdr->dstchannel, cdr->lastapp, cdr->lastdata, cdr->duration, cdr->billsec, cdr->disposition, cdr->amaflags, cdr->accountcode);
83         }
84
85         if(connected)
86         {
87                 res = odbc_do_query(sqlcmd);
88                 if(res < 0)
89                 {
90                         if(option_verbose > 3)          
91                                 ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: Query FAILED Call not logged!\n");
92                         res = odbc_init();
93                         if(option_verbose > 3)
94                                 ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: Reconnecting to dsn %s\n", dsn);
95                         if(res < 0)
96                         {
97                                 if(option_verbose > 3)
98                                         ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: %s has gone away!\n", dsn);
99                                 connected = 0;
100                         }
101                         else
102                         {
103                                 if(option_verbose > 3)
104                                         ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: Trying Query again!\n");
105                                 res = odbc_do_query(sqlcmd);
106                                 if(res < 0)
107                                 {
108                                         if(option_verbose > 3)
109                                                 ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: Query FAILED Call not logged!\n");
110                                 }
111                         }
112                 }
113         }
114         else
115         {
116                 if(option_verbose > 3)
117                          ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: Reconnecting to dsn %s\n", dsn);
118                 res = odbc_init();
119                 if(res < 0)
120                 {
121                         if(option_verbose > 3)
122                         {
123                                 ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: %s has gone away!\n", dsn);
124                                 ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: Call not logged!\n");
125                         }
126                 }
127                 else
128                 {
129                         if(option_verbose > 3)
130                                 ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: Trying Query again!\n");
131                         res = odbc_do_query(sqlcmd);
132                         if(res < 0)
133                         {
134                                 if(option_verbose > 3)
135                                         ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: Query FAILED Call not logged!\n");
136                         }
137                 }
138         }
139         ast_mutex_unlock(&odbc_lock);
140         return 0;
141 }
142
143 char *description(void)
144 {
145         return desc;
146 }
147
148 static int odbc_unload_module(void)
149 {
150         if (connected)
151         {
152                 if(option_verbose > 3)
153                         ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: Disconnecting from %s\n", dsn);
154                 SQLFreeHandle(SQL_HANDLE_STMT, ODBC_stmt);
155                 SQLDisconnect(ODBC_con);
156                 SQLFreeHandle(SQL_HANDLE_DBC, ODBC_con);
157                 SQLFreeHandle(SQL_HANDLE_ENV, ODBC_env);
158                 connected = 0;
159         }
160         if (dsn && dsn_alloc)
161         {
162                 if(option_verbose > 3)
163                         ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: free dsn\n");
164                 free(dsn);
165                 dsn = NULL;
166                 dsn_alloc = 0;
167         }
168         if (username && username_alloc)
169         {
170                 if(option_verbose > 3)
171                         ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: free username\n");
172                 free(username);
173                 username = NULL;
174                 username_alloc = 0;
175         }
176         if (password && password_alloc)
177         {
178                 if(option_verbose > 3)
179                         ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: free password\n");
180                 free(password);
181                 password = NULL;
182                 password_alloc = 0;
183         }
184         ast_cdr_unregister(name);
185         return 0;
186 }
187
188 static int odbc_load_module(void)
189 {
190         int res;
191         struct ast_config *cfg;
192         struct ast_variable *var;
193         char *tmp;
194
195         cfg = ast_load(config);
196         if (!cfg)
197         {
198                 ast_log(LOG_WARNING, "cdr_odbc: Unable to load config for ODBC CDR's: %s\n", config);
199                 return 0;
200         }
201         
202         var = ast_variable_browse(cfg, "global");
203         if (!var) {
204                 /* nothing configured */
205                 return 0;
206         }
207
208         tmp = ast_variable_retrieve(cfg,"global","dsn");
209         if (tmp)
210         {
211                 dsn = malloc(strlen(tmp) + 1);
212                 if (dsn != NULL)
213                 {
214                         dsn_alloc = 1;
215                         strcpy(dsn,tmp);
216                 }
217                 else
218                 {
219                         ast_log(LOG_ERROR,"cdr_odbc: Out of memory error.\n");
220                         return -1;
221                 }
222         }
223         else
224         {
225                 ast_log(LOG_WARNING,"cdr_odbc: dsn not specified.  Assuming asteriskdb\n");
226                 dsn = "asteriskdb";
227         }
228
229         tmp = ast_variable_retrieve(cfg,"global","username");
230         if (tmp)
231         {
232                 username = malloc(strlen(tmp) + 1);
233                 if (username != NULL)
234                 {
235                         username_alloc = 1;
236                         strcpy(username,tmp);
237                 }
238                 else
239                 {
240                         ast_log(LOG_ERROR,"cdr_odbc: Out of memory error.\n");
241                         return -1;
242                 }
243         }
244         else
245         {
246                 ast_log(LOG_WARNING,"cdr_odbc: username not specified.  Assuming root\n");
247                 username = "root";
248         }
249
250         tmp = ast_variable_retrieve(cfg,"global","password");
251         if (tmp)
252         {
253                 password = malloc(strlen(tmp) + 1);
254                 if (password != NULL)
255                 {
256                         password_alloc = 1;
257                         strcpy(password,tmp);
258                 }
259                 else
260                 {
261                         ast_log(LOG_ERROR,"cdr_odbc: Out of memory error.\n");
262                         return -1;
263                 }
264         }
265         else
266         {
267                 ast_log(LOG_WARNING,"cdr_odbc: database password not specified.  Assuming blank\n");
268                 password = "";
269         }
270
271         tmp = ast_variable_retrieve(cfg,"global","loguniqueid");
272         if (tmp)
273         {
274                 loguniqueid = malloc(strlen(tmp) + 1);
275                 if (loguniqueid != NULL)
276                 {
277                         strcpy(loguniqueid,tmp);
278                         ast_log(LOG_WARNING,"cdr_odbc: Logging uniqueid\n");
279                 }
280                 else
281                 {
282                         ast_log(LOG_ERROR,"cdr_odbc: Not logging uniqueid\n");
283                 }
284         }
285         else
286         {
287                 ast_log(LOG_WARNING,"cdr_odbc: Not logging uniqueid\n");
288                 loguniqueid = NULL;
289         }
290
291         ast_destroy(cfg);
292         if(option_verbose > 3)
293         {
294                 ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: dsn is %s\n",dsn);
295                 ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: username is %s\n",username);
296                 ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: password is [secret]\n");
297
298         }
299         
300         res = odbc_init();
301         if(res < 0)
302         {
303                 ast_log(LOG_ERROR, "cdr_odbc: Unable to connect to datasource: %s\n", dsn);
304                 ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: Unable to connect to datasource: %s\n", dsn);
305         }
306
307         res = ast_cdr_register(name, desc, odbc_log);
308         if (res)
309         {
310                 ast_log(LOG_ERROR, "cdr_odbc: Unable to register ODBC CDR handling\n");
311         }
312         return res;
313 }
314
315 int odbc_do_query(char *sqlcmd)
316 {
317         long int ODBC_err;
318         short int ODBC_mlen;
319         char ODBC_msg[200], ODBC_stat[10];
320
321         ODBC_res = SQLAllocHandle(SQL_HANDLE_STMT, ODBC_con, &ODBC_stmt);
322
323         if((ODBC_res != SQL_SUCCESS) && (ODBC_res != SQL_SUCCESS_WITH_INFO))
324         {
325                 if(option_verbose > 3)
326                         ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: Failure in AllocStatement %d\n", ODBC_res);
327                 SQLGetDiagRec(SQL_HANDLE_DBC, ODBC_con, 1, ODBC_stat, &ODBC_err, ODBC_msg, 100, &ODBC_mlen);
328                 SQLFreeHandle(SQL_HANDLE_STMT, ODBC_stmt);      
329                 connected = 0;
330                 return -1;
331         }
332
333         ODBC_res = SQLPrepare(ODBC_stmt, sqlcmd, SQL_NTS);
334
335         if((ODBC_res != SQL_SUCCESS) && (ODBC_res != SQL_SUCCESS_WITH_INFO))
336         {
337                 if(option_verbose > 3)
338                         ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: Error in PREPARE %d\n", ODBC_res);
339                 SQLGetDiagRec(SQL_HANDLE_DBC, ODBC_con, 1, ODBC_stat, &ODBC_err, ODBC_msg, 100, &ODBC_mlen);
340                 SQLFreeHandle(SQL_HANDLE_STMT, ODBC_stmt);
341                 return -1;
342         }
343
344         ODBC_res = SQLExecute(ODBC_stmt);
345
346         if((ODBC_res != SQL_SUCCESS) && (ODBC_res != SQL_SUCCESS_WITH_INFO))
347         {
348                 if(option_verbose > 3)
349                         ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: Error in Query %d\n", ODBC_res);
350                 SQLGetDiagRec(SQL_HANDLE_DBC, ODBC_con, 1, ODBC_stat, &ODBC_err, ODBC_msg, 100, &ODBC_mlen);
351                 SQLFreeHandle(SQL_HANDLE_STMT, ODBC_stmt);
352                 connected = 0;
353                 return -1;
354         }
355         else
356         {
357                 if(option_verbose > 3)
358                         ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: Query Successful!\n");
359                 connected = 1;
360         }
361         return 0;
362 }
363
364 int odbc_init()
365 {
366         long int ODBC_err;
367         short int ODBC_mlen;
368         char ODBC_msg[200], ODBC_stat[10];
369
370         if ( ODBC_env == SQL_NULL_HANDLE || connected == 0)
371         {
372                 ODBC_res = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &ODBC_env);
373
374                 if((ODBC_res != SQL_SUCCESS) && (ODBC_res != SQL_SUCCESS_WITH_INFO))
375                 {
376                         if(option_verbose > 3)
377                                 ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: Error AllocHandle\n");
378                         connected = 0;
379                         return -1;
380                 }
381
382                 ODBC_res = SQLSetEnvAttr(ODBC_env, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3, 0);
383
384                 if((ODBC_res != SQL_SUCCESS) && (ODBC_res != SQL_SUCCESS_WITH_INFO))
385                 {
386                         if(option_verbose > 3)
387                                 ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: Error SetEnv\n");
388                         SQLFreeHandle(SQL_HANDLE_ENV, ODBC_env);
389                         connected = 0;
390                         return -1;
391                 }
392
393                 ODBC_res = SQLAllocHandle(SQL_HANDLE_DBC, ODBC_env, &ODBC_con);
394
395                 if((ODBC_res != SQL_SUCCESS) && (ODBC_res != SQL_SUCCESS_WITH_INFO))
396                 {
397                         if(option_verbose > 3)
398                                 ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: Error AllocHDB %d\n", ODBC_res);
399                         SQLFreeHandle(SQL_HANDLE_ENV, ODBC_env);
400                         connected = 0;
401                         return -1;
402                 }
403
404                 SQLSetConnectAttr(ODBC_con, SQL_LOGIN_TIMEOUT, (SQLPOINTER *)10, 0);    
405         }
406
407         ODBC_res = SQLConnect(ODBC_con, (SQLCHAR*)dsn, SQL_NTS, (SQLCHAR*)username, SQL_NTS, (SQLCHAR*)password, SQL_NTS);
408
409         if((ODBC_res != SQL_SUCCESS) && (ODBC_res != SQL_SUCCESS_WITH_INFO))
410         {
411                 if(option_verbose > 3)
412                         ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: Error SQLConnect %d\n", ODBC_res);
413                 SQLGetDiagRec(SQL_HANDLE_DBC, ODBC_con, 1, ODBC_stat, &ODBC_err, ODBC_msg, 100, &ODBC_mlen);
414                 SQLFreeHandle(SQL_HANDLE_ENV, ODBC_env);
415                 connected = 0;
416                 return -1;
417         }
418         else
419         {
420                 if(option_verbose > 3)
421                         ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: Connected to %s\n", dsn);
422                 connected = 1;
423         }
424
425         return 0;
426 }
427
428 int load_module(void)
429 {
430         return odbc_load_module();
431 }
432
433 int unload_module(void)
434 {
435         return odbc_unload_module();
436 }
437
438 int reload(void)
439 {
440         odbc_unload_module();
441         return odbc_load_module();
442 }
443
444 int usecount(void)
445 {
446         return connected;
447 }
448
449 char *key()
450 {
451         return ASTERISK_GPL_KEY;
452 }