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