@ -66,82 +66,76 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
# include <time.h>
# include <math.h>
# include <tds.h>
# include <tdsconvert.h>
# include <ctype.h>
# include "asterisk/config.h"
# include "asterisk/channel.h"
# include "asterisk/cdr.h"
# include "asterisk/module.h"
# ifdef FREETDS_PRE_0_62
# warning "You have older TDS, you should upgrade!"
# endif
# include <sqlfront.h>
# include <sybdb.h>
# define DATE_FORMAT "%Y / %m / %d %T"
static char * name = " mssql " ;
static char * name = " FreeTDS (MSSQL) " ;
static char * config = " cdr_tds.conf " ;
struct cdr_tds_config {
AST_DECLARE_STRING_FIELDS (
AST_STRING_FIELD ( hostname ) ;
AST_STRING_FIELD ( d bnam e) ;
AST_STRING_FIELD ( db user) ;
AST_STRING_FIELD ( d atabas e) ;
AST_STRING_FIELD ( username ) ;
AST_STRING_FIELD ( password ) ;
AST_STRING_FIELD ( table ) ;
AST_STRING_FIELD ( charset ) ;
AST_STRING_FIELD ( language ) ;
) ;
TDSSOCKET * tds ;
TDSLOGIN * login ;
TDSCONTEXT * context ;
DBPROCESS * dbproc ;
unsigned int connected : 1 ;
ast_mutex_t lock ;
} ;
AST_MUTEX_DEFINE_STATIC ( tds_lock ) ;
static struct cdr_tds_config * settings ;
static char * anti_injection ( const char * , int ) ;
static void get_date ( char * , struct timeval ) ;
static void get_date ( char * , size_t len , struct timeval ) ;
static int mssql_connect ( void ) ;
static int mssql_disconnect ( void ) ;
static void cdr_tds_config_destroy ( void ) ;
static int tds_log ( struct ast_cdr * cdr )
{
char s qlcmd[ 2048 ] , s tart[ 80 ] , answer [ 80 ] , end [ 80 ] ;
char s tart[ 80 ] , answer [ 80 ] , end [ 80 ] ;
char * accountcode , * src , * dst , * dcontext , * clid , * channel , * dstchannel , * lastapp , * lastdata , * uniqueid ;
int res = 0 ;
int retried = 0 ;
# ifdef FREETDS_PRE_0_62
TDS_INT result_type ;
# endif
ast_mutex_lock ( & settings - > lock ) ;
memset ( sqlcmd , 0 , 2048 ) ;
RETCODE erc ;
int res = - 1 ;
accountcode = anti_injection ( cdr - > accountcode , 20 ) ;
src = anti_injection ( cdr - > src , 80 ) ;
dst = anti_injection ( cdr - > dst , 80 ) ;
dcontext = anti_injection ( cdr - > dcontext , 80 ) ;
clid = anti_injection ( cdr - > clid , 80 ) ;
channel = anti_injection ( cdr - > channel , 80 ) ;
dstchannel = anti_injection ( cdr - > dstchannel , 80 ) ;
lastapp = anti_injection ( cdr - > lastapp , 80 ) ;
lastdata = anti_injection ( cdr - > lastdata , 80 ) ;
uniqueid = anti_injection ( cdr - > uniqueid , 32 ) ;
get_date ( start , cdr - > start ) ;
get_date ( answer , cdr - > answer ) ;
get_date ( end , cdr - > end ) ;
sprintf (
sqlcmd ,
src = anti_injection ( cdr - > src , 80 ) ;
dst = anti_injection ( cdr - > dst , 80 ) ;
dcontext = anti_injection ( cdr - > dcontext , 80 ) ;
clid = anti_injection ( cdr - > clid , 80 ) ;
channel = anti_injection ( cdr - > channel , 80 ) ;
dstchannel = anti_injection ( cdr - > dstchannel , 80 ) ;
lastapp = anti_injection ( cdr - > lastapp , 80 ) ;
lastdata = anti_injection ( cdr - > lastdata , 80 ) ;
uniqueid = anti_injection ( cdr - > uniqueid , 32 ) ;
get_date ( start , sizeof ( start ) , cdr - > start ) ;
get_date ( answer , sizeof ( answer ) , cdr - > answer ) ;
get_date ( end , sizeof ( end ) , cdr - > end ) ;
ast_mutex_lock ( & tds_lock ) ;
/* Ensure that we are connected */
if ( ! settings - > connected ) {
if ( mssql_connect ( ) ) {
/* Connect failed */
goto done ;
}
}
erc = dbfcmd ( settings - > dbproc ,
" INSERT INTO %s "
" ( "
" accountcode, "
@ -202,27 +196,26 @@ static int tds_log(struct ast_cdr *cdr)
uniqueid
) ;
do {
if ( ! settings - > connected ) {
if ( mssql_connect ( ) )
ast_log ( LOG_ERROR , " Failed to reconnect to SQL database. \n " ) ;
else
ast_log ( LOG_WARNING , " Reconnected to SQL database. \n " ) ;
if ( erc = = FAIL ) {
ast_log ( LOG_ERROR , " Failed to build INSERT statement, no CDR was logged. \n " ) ;
goto done ;
}
if ( dbsqlexec ( settings - > dbproc ) = = FAIL ) {
ast_log ( LOG_ERROR , " Failed to execute INSERT statement, no CDR was logged. \n " ) ;
goto done ;
}
retried = 1 ; /* note that we have now tried */
}
/* Consume any results we might get back (this is more of a sanity check than
* anything else , since an INSERT shouldn ' t return results ) . */
while ( dbresults ( settings - > dbproc ) ! = NO_MORE_RESULTS ) {
while ( dbnextrow ( settings - > dbproc ) ! = NO_MORE_ROWS ) ;
}
# ifdef FREETDS_PRE_0_62
if ( ! settings - > connected | | ( tds_submit_query ( settings - > tds , sqlcmd ) ! = TDS_SUCCEED ) | | ( tds_process_simple_query ( settings - > tds , & result_type ) ! = TDS_SUCCEED | | result_type ! = TDS_CMD_SUCCEED ) )
# else
if ( ! settings - > connected | | ( tds_submit_query ( settings - > tds , sqlcmd ) ! = TDS_SUCCEED ) | | ( tds_process_simple_query ( settings - > tds ) ! = TDS_SUCCEED ) )
# endif
{
ast_log ( LOG_ERROR , " Failed to insert Call Data Record into SQL database. \n " ) ;
res = 0 ;
mssql_disconnect ( ) ; /* this is ok even if we are already disconnected */
}
} while ( ! settings - > connected & & ! retried ) ;
done :
ast_mutex_unlock ( & tds_lock ) ;
ast_free ( accountcode ) ;
ast_free ( src ) ;
@ -235,9 +228,7 @@ static int tds_log(struct ast_cdr *cdr)
ast_free ( lastdata ) ;
ast_free ( uniqueid ) ;
ast_mutex_unlock ( & settings - > lock ) ;
return res ;
return 0 ;
}
static char * anti_injection ( const char * str , int len )
@ -274,39 +265,23 @@ static char *anti_injection(const char *str, int len)
return buf ;
}
static void get_date ( char * dateField , struct timeval tv )
static void get_date ( char * dateField , size_t len , struct timeval tv )
{
struct ast_tm tm ;
char buf [ 80 ] ;
/* To make sure we have date variable if not insert null to SQL */
if ( ! ast_tvzero ( tv ) ) {
struct ast_tm tm ;
ast_localtime ( & tv , & tm , NULL ) ;
ast_strftime ( buf , 80 , DATE_FORMAT , & tm ) ;
sprintf ( dateField , " '%s' " , buf ) ;
ast_strftime ( dateField , len , " ' " DATE_FORMAT " ' " , & tm ) ;
} else {
strcpy ( dateField , " null " ) ;
ast_copy_string ( dateField , " null " , len ) ;
}
}
static int mssql_disconnect ( void )
{
if ( ! settings )
return 0 ;
if ( settings - > tds ) {
tds_free_socket ( settings - > tds ) ;
settings - > tds = NULL ;
}
if ( settings - > context ) {
tds_free_context ( settings - > context ) ;
settings - > context = NULL ;
}
if ( settings - > login ) {
tds_free_login ( settings - > login ) ;
settings - > login = NULL ;
if ( settings - > dbproc ) {
dbclose ( settings - > dbproc ) ;
settings - > dbproc = NULL ;
}
settings - > connected = 0 ;
@ -316,107 +291,90 @@ static int mssql_disconnect(void)
static int mssql_connect ( void )
{
# if (defined(FREETDS_0_63) || defined(FREETDS_0_64))
TDSCONNECTION * connection = NULL ;
# else
TDSCONNECTINFO * connection = NULL ;
# endif
char query [ 128 ] ;
/* Connect to M$SQL Server */
if ( ! ( settings - > login = tds_alloc_login ( ) ) ) {
ast_log ( LOG_ERROR , " tds_alloc_login() failed. \n " ) ;
LOGINREC * login ;
if ( ( login = dblogin ( ) ) = = NULL ) {
ast_log ( LOG_ERROR , " Unable to allocate login structure for db-lib \n " ) ;
return - 1 ;
}
tds_set_server ( settings - > login , settings - > hostname ) ;
tds_set_user ( settings - > login , settings - > dbuser ) ;
tds_set_passwd ( settings - > login , settings - > password ) ;
tds_set_app ( settings - > login , " TSQL " ) ;
tds_set_library ( settings - > login , " TDS-Library " ) ;
# ifndef FREETDS_PRE_0_62
tds_set_client_charset ( settings - > login , settings - > charset ) ;
# endif
tds_set_language ( settings - > login , settings - > language ) ;
tds_set_packet ( settings - > login , 512 ) ;
tds_set_version ( settings - > login , 7 , 0 ) ;
# ifdef FREETDS_0_64
if ( ! ( settings - > context = tds_alloc_context ( NULL ) ) )
# else
if ( ! ( settings - > context = tds_alloc_context ( ) ) )
# endif
{
ast_log ( LOG_ERROR , " tds_alloc_context() failed. \n " ) ;
goto connect_fail ;
DBSETLAPP ( login , " TSQL " ) ;
DBSETLUSER ( login , settings - > username ) ;
DBSETLPWD ( login , settings - > password ) ;
DBSETLCHARSET ( login , settings - > charset ) ;
DBSETLNATLANG ( login , settings - > language ) ;
if ( ( settings - > dbproc = dbopen ( login , ( char * ) settings - > hostname ) ) = = NULL ) {
ast_log ( LOG_ERROR , " Unable to connect to %s \n " , settings - > hostname ) ;
dbloginfree ( login ) ;
return - 1 ;
}
if ( ! ( settings - > tds = tds_alloc_socket ( settings - > context , 512 ) ) ) {
ast_log ( LOG_ERROR , " tds_alloc_socket() failed. \n " ) ;
goto connect_fail ;
dbloginfree ( login ) ;
if ( dbuse ( settings - > dbproc , ( char * ) settings - > database ) = = FAIL ) {
ast_log ( LOG_ERROR , " Unable to select database %s \n " , settings - > database ) ;
goto failed ;
}
tds_set_parent ( settings - > tds , NULL ) ;
connection = tds_read_config_info ( settings - > tds , settings - > login , settings - > context - > locale ) ;
if ( ! connection ) {
ast_log ( LOG_ERROR , " tds_read_config() failed. \n " ) ;
goto connect_fail ;
if ( dbfcmd ( settings - > dbproc , " SELECT 1 FROM [%s] " , settings - > table ) = = FAIL ) {
ast_log ( LOG_ERROR , " Unable to build query while verifying the existence of table '%s' \n " , settings - > table ) ;
goto failed ;
}
if ( tds_connect ( settings - > tds , connection ) = = TDS_FAIL ) {
ast_log ( LOG_ERROR , " Failed to connect to MSSQL server. \n " ) ;
settings - > tds = NULL ; /* freed by tds_connect() on error */
# if (defined(FREETDS_0_63) || defined(FREETDS_0_64))
tds_free_connection ( connection ) ;
# else
tds_free_connect ( connection ) ;
# endif
connection = NULL ;
goto connect_fail ;
if ( dbsqlexec ( settings - > dbproc ) = = FAIL ) {
ast_log ( LOG_ERROR , " Unable to verify existence of table '%s' \n " , settings - > table ) ;
goto failed ;
}
# if (defined(FREETDS_0_63) || defined(FREETDS_0_64))
tds_free_connection ( connection ) ;
# else
tds_free_connect ( connection ) ;
# endif
connection = NULL ;
sprintf ( query , " USE %s " , settings - > dbname ) ;
# ifdef FREETDS_PRE_0_62
if ( ( tds_submit_query ( settings - > tds , query ) ! = TDS_SUCCEED ) | | ( tds_process_simple_query ( settings - > tds , & result_type ) ! = TDS_SUCCEED | | result_type ! = TDS_CMD_SUCCEED ) )
# else
if ( ( tds_submit_query ( settings - > tds , query ) ! = TDS_SUCCEED ) | | ( tds_process_simple_query ( settings - > tds ) ! = TDS_SUCCEED ) )
# endif
{
ast_log ( LOG_ERROR , " Could not change database (%s) \n " , settings - > dbname ) ;
goto connect_fail ;
/* Consume the result set (we don't really care about the result, though) */
while ( dbresults ( settings - > dbproc ) ! = NO_MORE_RESULTS ) {
while ( dbnextrow ( settings - > dbproc ) ! = NO_MORE_ROWS ) ;
}
settings - > connected = 1 ;
return 0 ;
connect_fail :
mssql_disconnect ( ) ;
failed :
dbclose ( settings - > dbproc ) ;
settings - > dbproc = NULL ;
return - 1 ;
}
static void cdr_tds_config_destroy ( void )
static int tds_unload_module ( void )
{
if ( settings )
{
ast_mutex_destroy ( & settings - > lock ) ;
if ( settings ) {
ast_mutex_lock ( & tds_lock ) ;
mssql_disconnect ( ) ;
ast_mutex_unlock ( & tds_lock ) ;
ast_string_field_free_memory ( settings ) ;
ast_free ( settings ) ;
ast_free ( settings ) ;
}
ast_cdr_unregister ( name ) ;
dbexit ( ) ;
return 0 ;
}
static int tds_unload_module ( void )
static int tds_ error_handler( DBPROCESS * dbproc , int severity , int dberr , int oserr , char * dberrstr , char * oserrstr )
{
mssql_disconnect ( ) ;
ast_log( LOG_ERROR , " %s (%d) \n " , dberrstr , dberr ) ;
ast_cdr_unregister ( name ) ;
if ( oserr ! = DBNOERR ) {
ast_log ( LOG_ERROR , " %s (%d) \n " , oserrstr , oserr ) ;
}
cdr_tds_config_destroy ( ) ;
return INT_CANCEL ;
}
static int tds_message_handler ( DBPROCESS * dbproc , DBINT msgno , int msgstate , int severity , char * msgtext , char * srvname , char * procname , int line )
{
ast_debug ( 1 , " Msg %d, Level %d, State %d, Line %d \n " , msgno , severity , msgstate , line ) ;
ast_log ( LOG_NOTICE , " %s \n " , msgtext ) ;
return 0 ;
}
@ -424,66 +382,57 @@ static int tds_unload_module(void)
static int tds_load_module ( int reload )
{
struct ast_config * cfg ;
struct ast_variable * var ;
const char * ptr = NULL ;
struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 } ;
cfg = ast_config_load ( config , config_flags ) ;
if ( ! cfg ) {
ast_log ( LOG_NOTICE , " Unable to load config for MSSQL CDRs: %s\n " , config ) ;
ast_log ( LOG_NOTICE , " Unable to load TDS config for CDRs: %s\n " , config ) ;
return 0 ;
} else if ( cfg = = CONFIG_STATUS_FILEUNCHANGED )
return 0 ;
var = ast_variable_browse ( cfg , " global " ) ;
if ( ! var ) /* nothing configured */ {
if ( ! ast_variable_browse ( cfg , " global " ) ) {
/* nothing configured */
ast_config_destroy ( cfg ) ;
return 0 ;
}
if ( reload ) {
ast_string_field_init ( settings , 0 ) ;
} else {
settings = ast_calloc ( 1 , sizeof ( * settings ) ) ;
if ( ! settings | | ast_string_field_init ( settings , 256 ) ) {
if ( settings ) {
ast_free ( settings ) ;
settings = NULL ;
}
ast_config_destroy ( cfg ) ;
return 0 ;
}
ast_mutex_lock ( & tds_lock ) ;
ast_mutex_init ( & settings - > lock ) ;
}
/* Clear out any existing settings */
ast_string_field_init ( settings , 0 ) ;
ptr = ast_variable_retrieve ( cfg , " global " , " hostname " ) ;
if ( ptr ) {
ast_string_field_set ( settings , hostname , ptr ) ;
} else {
ast_log ( LOG_ERROR , " Database server hostname not specified. \n " ) ;
ast_log ( LOG_ERROR , " Failed to connect: Database server hostname not specified. \n " ) ;
goto failed ;
}
ptr = ast_variable_retrieve ( cfg , " global " , " dbname " ) ;
if ( ptr ) {
ast_string_field_set ( settings , d bnam e, ptr ) ;
ast_string_field_set ( settings , d atabas e, ptr ) ;
} else {
ast_log ( LOG_ERROR , " Database dbname not specified. \n " ) ;
ast_log ( LOG_ERROR , " Failed to connect: Database dbname not specified. \n " ) ;
goto failed ;
}
ptr = ast_variable_retrieve ( cfg , " global " , " user " ) ;
if ( ptr ) {
ast_string_field_set ( settings , db user, ptr ) ;
ast_string_field_set ( settings , username , ptr ) ;
} else {
ast_log ( LOG_ERROR , " Database dbuser not specified. \n " ) ;
ast_log ( LOG_ERROR , " Failed to connect: Database dbuser not specified. \n " ) ;
goto failed ;
}
ptr = ast_variable_retrieve ( cfg , " global " , " password " ) ;
if ( ptr ) {
ast_string_field_set ( settings , password , ptr ) ;
} else {
ast_log ( LOG_ERROR , " Database password not specified. \n " ) ;
ast_log ( LOG_ERROR , " Failed to connect: Database password not specified. \n " ) ;
goto failed ;
}
ptr = ast_variable_retrieve ( cfg , " global " , " charset " ) ;
@ -503,19 +452,28 @@ static int tds_load_module(int reload)
ptr = ast_variable_retrieve ( cfg , " global " , " table " ) ;
if ( ptr ) {
ast_string_field_set ( settings , table , ptr ) ;
} else {
ast_ debug( 1 , " Table not specified. Assuming 'cdr' \n " ) ;
} else {
ast_ log( LOG_NOTICE , " Table name not specified, using 'cdr' by default. \n " ) ;
ast_string_field_set ( settings , table , " cdr " ) ;
}
ast_config_destroy ( cfg ) ;
ast_mutex_lock ( & settings - > lock ) ;
mssql_disconnect ( ) ;
mssql_connect ( ) ;
ast_mutex_unlock ( & settings - > lock ) ;
if ( mssql_connect ( ) ) {
/* We failed to connect (mssql_connect takes care of logging it) */
goto failed ;
}
ast_mutex_unlock ( & tds_lock ) ;
ast_config_destroy ( cfg ) ;
return 1 ;
failed :
ast_mutex_unlock ( & tds_lock ) ;
ast_config_destroy ( cfg ) ;
return 0 ;
}
static int reload ( void )
@ -525,9 +483,35 @@ static int reload(void)
static int load_module ( void )
{
if ( ! tds_load_module ( 0 ) )
if ( dbinit ( ) = = FAIL ) {
ast_log ( LOG_ERROR , " Failed to initialize FreeTDS db-lib \n " ) ;
return AST_MODULE_LOAD_DECLINE ;
}
dberrhandle ( tds_error_handler ) ;
dbmsghandle ( tds_message_handler ) ;
settings = ast_calloc ( 1 , sizeof ( * settings ) ) ;
if ( ! settings | | ast_string_field_init ( settings , 256 ) ) {
if ( settings ) {
ast_free ( settings ) ;
settings = NULL ;
}
dbexit ( ) ;
return AST_MODULE_LOAD_DECLINE ;
}
if ( ! tds_load_module ( 0 ) ) {
ast_string_field_free_memory ( settings ) ;
ast_free ( settings ) ;
settings = NULL ;
dbexit ( ) ;
return AST_MODULE_LOAD_DECLINE ;
}
ast_cdr_register ( name , ast_module_info - > description , tds_log ) ;
return AST_MODULE_LOAD_SUCCESS ;
}
@ -536,7 +520,7 @@ static int unload_module(void)
return tds_unload_module ( ) ;
}
AST_MODULE_INFO ( ASTERISK_GPL_KEY , AST_MODFLAG_DEFAULT , " MSSQL CDR Backend" ,
AST_MODULE_INFO ( ASTERISK_GPL_KEY , AST_MODFLAG_DEFAULT , " FreeTDS CDR Backend" ,
. load = load_module ,
. unload = unload_module ,
. reload = reload ,