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