00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035 #include "asterisk.h"
00036
00037 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
00038
00039 #include <sys/types.h>
00040 #include <stdio.h>
00041 #include <string.h>
00042
00043 #include <stdlib.h>
00044 #include <unistd.h>
00045 #include <time.h>
00046
00047 #ifndef __CYGWIN__
00048 #include <sql.h>
00049 #include <sqlext.h>
00050 #include <sqltypes.h>
00051 #else
00052 #include <windows.h>
00053 #include <w32api/sql.h>
00054 #include <w32api/sqlext.h>
00055 #include <w32api/sqltypes.h>
00056 #endif
00057
00058 #include "asterisk/config.h"
00059 #include "asterisk/options.h"
00060 #include "asterisk/channel.h"
00061 #include "asterisk/cdr.h"
00062 #include "asterisk/module.h"
00063 #include "asterisk/logger.h"
00064
00065 #define DATE_FORMAT "%Y-%m-%d %T"
00066
00067 static char *name = "ODBC";
00068 static char *config = "cdr_odbc.conf";
00069 static char *dsn = NULL, *username = NULL, *password = NULL, *table = NULL;
00070 static int loguniqueid = 0;
00071 static int usegmtime = 0;
00072 static int dispositionstring = 0;
00073 static int connected = 0;
00074
00075 AST_MUTEX_DEFINE_STATIC(odbc_lock);
00076
00077 static int odbc_do_query(void);
00078 static int odbc_init(void);
00079
00080 static SQLHENV ODBC_env = SQL_NULL_HANDLE;
00081 static SQLHDBC ODBC_con;
00082 static SQLHSTMT ODBC_stmt;
00083
00084 static void odbc_disconnect(void)
00085 {
00086 SQLDisconnect(ODBC_con);
00087 SQLFreeHandle(SQL_HANDLE_DBC, ODBC_con);
00088 SQLFreeHandle(SQL_HANDLE_ENV, ODBC_env);
00089 connected = 0;
00090 }
00091
00092 static int odbc_log(struct ast_cdr *cdr)
00093 {
00094 int ODBC_res;
00095 char sqlcmd[2048] = "", timestr[128];
00096 int res = 0;
00097 struct tm tm;
00098
00099 if (usegmtime)
00100 gmtime_r(&cdr->start.tv_sec,&tm);
00101 else
00102 localtime_r(&cdr->start.tv_sec,&tm);
00103
00104 ast_mutex_lock(&odbc_lock);
00105 strftime(timestr, sizeof(timestr), DATE_FORMAT, &tm);
00106 memset(sqlcmd,0,2048);
00107 if (loguniqueid) {
00108 snprintf(sqlcmd,sizeof(sqlcmd),"INSERT INTO %s "
00109 "(calldate,clid,src,dst,dcontext,channel,dstchannel,lastapp,"
00110 "lastdata,duration,billsec,disposition,amaflags,accountcode,uniqueid,userfield) "
00111 "VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)", table);
00112 } else {
00113 snprintf(sqlcmd,sizeof(sqlcmd),"INSERT INTO %s "
00114 "(calldate,clid,src,dst,dcontext,channel,dstchannel,lastapp,lastdata,"
00115 "duration,billsec,disposition,amaflags,accountcode) "
00116 "VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)", table);
00117 }
00118
00119 if (!connected) {
00120 res = odbc_init();
00121 if (res < 0) {
00122 odbc_disconnect();
00123 ast_mutex_unlock(&odbc_lock);
00124 return 0;
00125 }
00126 }
00127
00128 ODBC_res = SQLAllocHandle(SQL_HANDLE_STMT, ODBC_con, &ODBC_stmt);
00129
00130 if ((ODBC_res != SQL_SUCCESS) && (ODBC_res != SQL_SUCCESS_WITH_INFO)) {
00131 if (option_verbose > 10)
00132 ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: Failure in AllocStatement %d\n", ODBC_res);
00133 SQLFreeHandle(SQL_HANDLE_STMT, ODBC_stmt);
00134 odbc_disconnect();
00135 ast_mutex_unlock(&odbc_lock);
00136 return 0;
00137 }
00138
00139
00140
00141
00142
00143 ODBC_res = SQLPrepare(ODBC_stmt, (unsigned char *)sqlcmd, SQL_NTS);
00144
00145 if ((ODBC_res != SQL_SUCCESS) && (ODBC_res != SQL_SUCCESS_WITH_INFO)) {
00146 if (option_verbose > 10)
00147 ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: Error in PREPARE %d\n", ODBC_res);
00148 SQLFreeHandle(SQL_HANDLE_STMT, ODBC_stmt);
00149 odbc_disconnect();
00150 ast_mutex_unlock(&odbc_lock);
00151 return 0;
00152 }
00153
00154 SQLBindParameter(ODBC_stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(timestr), 0, ×tr, 0, NULL);
00155 SQLBindParameter(ODBC_stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(cdr->clid), 0, cdr->clid, 0, NULL);
00156 SQLBindParameter(ODBC_stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(cdr->src), 0, cdr->src, 0, NULL);
00157 SQLBindParameter(ODBC_stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(cdr->dst), 0, cdr->dst, 0, NULL);
00158 SQLBindParameter(ODBC_stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(cdr->dcontext), 0, cdr->dcontext, 0, NULL);
00159 SQLBindParameter(ODBC_stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(cdr->channel), 0, cdr->channel, 0, NULL);
00160 SQLBindParameter(ODBC_stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(cdr->dstchannel), 0, cdr->dstchannel, 0, NULL);
00161 SQLBindParameter(ODBC_stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(cdr->lastapp), 0, cdr->lastapp, 0, NULL);
00162 SQLBindParameter(ODBC_stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(cdr->lastdata), 0, cdr->lastdata, 0, NULL);
00163 SQLBindParameter(ODBC_stmt, 10, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, 0, 0, &cdr->duration, 0, NULL);
00164 SQLBindParameter(ODBC_stmt, 11, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, 0, 0, &cdr->billsec, 0, NULL);
00165 if (dispositionstring)
00166 SQLBindParameter(ODBC_stmt, 12, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(ast_cdr_disp2str(cdr->disposition)) + 1, 0, ast_cdr_disp2str(cdr->disposition), 0, NULL);
00167 else
00168 SQLBindParameter(ODBC_stmt, 12, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, 0, 0, &cdr->disposition, 0, NULL);
00169 SQLBindParameter(ODBC_stmt, 13, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, 0, 0, &cdr->amaflags, 0, NULL);
00170 SQLBindParameter(ODBC_stmt, 14, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(cdr->accountcode), 0, cdr->accountcode, 0, NULL);
00171
00172 if (loguniqueid) {
00173 SQLBindParameter(ODBC_stmt, 15, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(cdr->uniqueid), 0, cdr->uniqueid, 0, NULL);
00174 SQLBindParameter(ODBC_stmt, 16, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(cdr->userfield), 0, cdr->userfield, 0, NULL);
00175 }
00176
00177 if (connected) {
00178 res = odbc_do_query();
00179 if (res < 0) {
00180 if (option_verbose > 10)
00181 ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: Query FAILED Call not logged!\n");
00182 if (option_verbose > 10)
00183 ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: Reconnecting to dsn %s\n", dsn);
00184 SQLDisconnect(ODBC_con);
00185 res = odbc_init();
00186 if (res < 0) {
00187 if (option_verbose > 10)
00188 ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: %s has gone away!\n", dsn);
00189 odbc_disconnect();
00190 } else {
00191 if (option_verbose > 10)
00192 ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: Trying Query again!\n");
00193 res = odbc_do_query();
00194 if (res < 0) {
00195 if (option_verbose > 10)
00196 ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: Query FAILED Call not logged!\n");
00197 }
00198 }
00199 }
00200 } else {
00201 if (option_verbose > 10)
00202 ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: Query FAILED Call not logged!\n");
00203 }
00204 SQLFreeHandle(SQL_HANDLE_STMT, ODBC_stmt);
00205 ast_mutex_unlock(&odbc_lock);
00206 return 0;
00207 }
00208
00209 static int odbc_unload_module(void)
00210 {
00211 ast_mutex_lock(&odbc_lock);
00212 if (connected) {
00213 if (option_verbose > 10)
00214 ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: Disconnecting from %s\n", dsn);
00215 SQLFreeHandle(SQL_HANDLE_STMT, ODBC_stmt);
00216 odbc_disconnect();
00217 }
00218 if (dsn) {
00219 if (option_verbose > 10)
00220 ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: free dsn\n");
00221 free(dsn);
00222 }
00223 if (username) {
00224 if (option_verbose > 10)
00225 ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: free username\n");
00226 free(username);
00227 }
00228 if (password) {
00229 if (option_verbose > 10)
00230 ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: free password\n");
00231 free(password);
00232 }
00233 if (table) {
00234 if (option_verbose > 10)
00235 ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: free table\n");
00236 free(table);
00237 }
00238
00239 ast_cdr_unregister(name);
00240 ast_mutex_unlock(&odbc_lock);
00241 return 0;
00242 }
00243
00244 static int odbc_load_module(void)
00245 {
00246 int res = 0;
00247 struct ast_config *cfg;
00248 struct ast_variable *var;
00249 const char *tmp;
00250
00251 ast_mutex_lock(&odbc_lock);
00252
00253 cfg = ast_config_load(config);
00254 if (!cfg) {
00255 ast_log(LOG_WARNING, "cdr_odbc: Unable to load config for ODBC CDR's: %s\n", config);
00256 res = AST_MODULE_LOAD_DECLINE;
00257 goto out;
00258 }
00259
00260 var = ast_variable_browse(cfg, "global");
00261 if (!var) {
00262
00263 goto out;
00264 }
00265
00266 tmp = ast_variable_retrieve(cfg,"global","dsn");
00267 if (tmp == NULL) {
00268 ast_log(LOG_WARNING,"cdr_odbc: dsn not specified. Assuming asteriskdb\n");
00269 tmp = "asteriskdb";
00270 }
00271 dsn = strdup(tmp);
00272 if (dsn == NULL) {
00273 ast_log(LOG_ERROR,"cdr_odbc: Out of memory error.\n");
00274 res = -1;
00275 goto out;
00276 }
00277
00278 tmp = ast_variable_retrieve(cfg,"global","dispositionstring");
00279 if (tmp) {
00280 dispositionstring = ast_true(tmp);
00281 } else {
00282 dispositionstring = 0;
00283 }
00284
00285 tmp = ast_variable_retrieve(cfg,"global","username");
00286 if (tmp) {
00287 username = strdup(tmp);
00288 if (username == NULL) {
00289 ast_log(LOG_ERROR,"cdr_odbc: Out of memory error.\n");
00290 res = -1;
00291 goto out;
00292 }
00293 }
00294
00295 tmp = ast_variable_retrieve(cfg,"global","password");
00296 if (tmp) {
00297 password = strdup(tmp);
00298 if (password == NULL) {
00299 ast_log(LOG_ERROR,"cdr_odbc: Out of memory error.\n");
00300 res = -1;
00301 goto out;
00302 }
00303 }
00304
00305 tmp = ast_variable_retrieve(cfg,"global","loguniqueid");
00306 if (tmp) {
00307 loguniqueid = ast_true(tmp);
00308 if (loguniqueid) {
00309 ast_log(LOG_DEBUG,"cdr_odbc: Logging uniqueid\n");
00310 } else {
00311 ast_log(LOG_DEBUG,"cdr_odbc: Not logging uniqueid\n");
00312 }
00313 } else {
00314 ast_log(LOG_DEBUG,"cdr_odbc: Not logging uniqueid\n");
00315 loguniqueid = 0;
00316 }
00317
00318 tmp = ast_variable_retrieve(cfg,"global","usegmtime");
00319 if (tmp) {
00320 usegmtime = ast_true(tmp);
00321 if (usegmtime) {
00322 ast_log(LOG_DEBUG,"cdr_odbc: Logging in GMT\n");
00323 } else {
00324 ast_log(LOG_DEBUG,"cdr_odbc: Not logging in GMT\n");
00325 }
00326 } else {
00327 ast_log(LOG_DEBUG,"cdr_odbc: Not logging in GMT\n");
00328 usegmtime = 0;
00329 }
00330
00331 tmp = ast_variable_retrieve(cfg,"global","table");
00332 if (tmp == NULL) {
00333 ast_log(LOG_WARNING,"cdr_odbc: table not specified. Assuming cdr\n");
00334 tmp = "cdr";
00335 }
00336 table = strdup(tmp);
00337 if (table == NULL) {
00338 ast_log(LOG_ERROR,"cdr_odbc: Out of memory error.\n");
00339 res = -1;
00340 goto out;
00341 }
00342
00343 if (option_verbose > 2) {
00344 ast_verbose( VERBOSE_PREFIX_3 "cdr_odbc: dsn is %s\n",dsn);
00345 if (username)
00346 {
00347 ast_verbose( VERBOSE_PREFIX_3 "cdr_odbc: username is %s\n",username);
00348 ast_verbose( VERBOSE_PREFIX_3 "cdr_odbc: password is [secret]\n");
00349 }
00350 else
00351 ast_verbose( VERBOSE_PREFIX_3 "cdr_odbc: retreiving username and password from odbc config\n");
00352 ast_verbose( VERBOSE_PREFIX_3 "cdr_odbc: table is %s\n",table);
00353 }
00354
00355 res = odbc_init();
00356 if (res < 0) {
00357 ast_log(LOG_ERROR, "cdr_odbc: Unable to connect to datasource: %s\n", dsn);
00358 if (option_verbose > 2) {
00359 ast_verbose( VERBOSE_PREFIX_3 "cdr_odbc: Unable to connect to datasource: %s\n", dsn);
00360 }
00361 }
00362 res = ast_cdr_register(name, ast_module_info->description, odbc_log);
00363 if (res) {
00364 ast_log(LOG_ERROR, "cdr_odbc: Unable to register ODBC CDR handling\n");
00365 }
00366 out:
00367 if (cfg)
00368 ast_config_destroy(cfg);
00369 ast_mutex_unlock(&odbc_lock);
00370 return res;
00371 }
00372
00373 static int odbc_do_query(void)
00374 {
00375 int ODBC_res;
00376
00377 ODBC_res = SQLExecute(ODBC_stmt);
00378
00379 if ((ODBC_res != SQL_SUCCESS) && (ODBC_res != SQL_SUCCESS_WITH_INFO)) {
00380 if (option_verbose > 10)
00381 ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: Error in Query %d\n", ODBC_res);
00382 SQLFreeHandle(SQL_HANDLE_STMT, ODBC_stmt);
00383 odbc_disconnect();
00384 return -1;
00385 } else {
00386 if (option_verbose > 10)
00387 ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: Query Successful!\n");
00388 connected = 1;
00389 }
00390 return 0;
00391 }
00392
00393 static int odbc_init(void)
00394 {
00395 int ODBC_res;
00396
00397 if (ODBC_env == SQL_NULL_HANDLE || connected == 0) {
00398 ODBC_res = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &ODBC_env);
00399 if ((ODBC_res != SQL_SUCCESS) && (ODBC_res != SQL_SUCCESS_WITH_INFO)) {
00400 if (option_verbose > 10)
00401 ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: Error AllocHandle\n");
00402 connected = 0;
00403 return -1;
00404 }
00405
00406 ODBC_res = SQLSetEnvAttr(ODBC_env, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3, 0);
00407
00408 if ((ODBC_res != SQL_SUCCESS) && (ODBC_res != SQL_SUCCESS_WITH_INFO)) {
00409 if (option_verbose > 10)
00410 ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: Error SetEnv\n");
00411 SQLFreeHandle(SQL_HANDLE_ENV, ODBC_env);
00412 connected = 0;
00413 return -1;
00414 }
00415
00416 ODBC_res = SQLAllocHandle(SQL_HANDLE_DBC, ODBC_env, &ODBC_con);
00417
00418 if ((ODBC_res != SQL_SUCCESS) && (ODBC_res != SQL_SUCCESS_WITH_INFO)) {
00419 if (option_verbose > 10)
00420 ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: Error AllocHDB %d\n", ODBC_res);
00421 SQLFreeHandle(SQL_HANDLE_ENV, ODBC_env);
00422 connected = 0;
00423 return -1;
00424 }
00425 SQLSetConnectAttr(ODBC_con, SQL_LOGIN_TIMEOUT, (SQLPOINTER *)10, 0);
00426 }
00427
00428
00429
00430 ODBC_res = SQLConnect(ODBC_con, (SQLCHAR*)dsn, SQL_NTS, (SQLCHAR*)username, SQL_NTS, (SQLCHAR*)password, SQL_NTS);
00431
00432 if ((ODBC_res != SQL_SUCCESS) && (ODBC_res != SQL_SUCCESS_WITH_INFO)) {
00433 if (option_verbose > 10)
00434 ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: Error SQLConnect %d\n", ODBC_res);
00435 SQLFreeHandle(SQL_HANDLE_DBC, ODBC_con);
00436 SQLFreeHandle(SQL_HANDLE_ENV, ODBC_env);
00437 connected = 0;
00438 return -1;
00439 } else {
00440 if (option_verbose > 10)
00441 ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: Connected to %s\n", dsn);
00442 connected = 1;
00443 }
00444 return 0;
00445 }
00446
00447 static int load_module(void)
00448 {
00449 return odbc_load_module();
00450 }
00451
00452 static int unload_module(void)
00453 {
00454 return odbc_unload_module();
00455 }
00456
00457 static int reload(void)
00458 {
00459 odbc_unload_module();
00460 return odbc_load_module();
00461 }
00462
00463 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "ODBC CDR Backend",
00464 .load = load_module,
00465 .unload = unload_module,
00466 .reload = reload,
00467 );