2009-01-14 02:18:51 +00:00
/*
* FreeSWITCH Modular Media Switching Software Library / Soft - Switch Application
2009-02-04 21:20:54 +00:00
* Copyright ( C ) 2005 / 2006 , Anthony Minessale II < anthm @ freeswitch . org >
2009-01-14 02:18:51 +00:00
*
* Version : MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 ( the " License " ) ; you may not use this file except in compliance with
* the License . You may obtain a copy of the License at
* http : //www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an " AS IS " basis ,
* WITHOUT WARRANTY OF ANY KIND , either express or implied . See the License
* for the specific language governing rights and limitations under the
* License .
*
* The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft - Switch Application
*
* The Initial Developer of the Original Code is
2009-02-04 21:20:54 +00:00
* Anthony Minessale II < anthm @ freeswitch . org >
2009-01-14 02:18:51 +00:00
* Portions created by the Initial Developer are Copyright ( C )
* the Initial Developer . All Rights Reserved .
*
* Contributor ( s ) :
*
* Raymond Chandler < intralanman @ gmail . com >
* Rupa Schomaker < rupa @ rupa . com >
*
* mod_lcr . c - - Least Cost Routing Module
*
*/
# include <switch.h>
# include <switch_odbc.h>
# define LCR_SYNTAX "lcr <digits> [<lcr profile>]"
2009-02-11 05:07:37 +00:00
# define LCR_ADMIN_SYNTAX "lcr_admin show profiles"
2009-01-14 02:18:51 +00:00
/* SQL Query places */
# define LCR_DIGITS_PLACE 0
# define LCR_CARRIER_PLACE 1
# define LCR_RATE_PLACE 2
# define LCR_GW_PREFIX_PLACE 3
# define LCR_GW_SUFFIX_PLACE 4
# define LCR_LSTRIP_PLACE 5
# define LCR_TSTRIP_PLACE 6
# define LCR_PREFIX_PLACE 7
# define LCR_SUFFIX_PLACE 8
2009-02-11 00:18:50 +00:00
# define LCR_QUERY_COLS 9
2009-01-14 02:18:51 +00:00
# define LCR_DIALSTRING_PLACE 3
# define LCR_HEADERS_COUNT 4
char headers [ LCR_HEADERS_COUNT ] [ 32 ] = {
" Digit Match " ,
" Carrier " ,
" Rate " ,
" Dialstring " ,
} ;
/* sql for random function */
char * db_random = NULL ;
struct odbc_obj {
switch_odbc_handle_t * handle ;
SQLHSTMT stmt ;
SQLCHAR * colbuf ;
int32_t cblen ;
SQLCHAR * code ;
int32_t codelen ;
} ;
struct lcr_obj {
char * carrier_name ;
char * gw_prefix ;
char * gw_suffix ;
char * digit_str ;
char * prefix ;
char * suffix ;
char * dialstring ;
float rate ;
char * rate_str ;
size_t lstrip ;
size_t tstrip ;
size_t digit_len ;
struct lcr_obj * next ;
} ;
struct max_obj {
size_t carrier_name ;
size_t digit_str ;
size_t rate ;
size_t dialstring ;
} ;
typedef struct odbc_obj odbc_obj_t ;
typedef odbc_obj_t * odbc_handle ;
typedef struct lcr_obj lcr_obj_t ;
typedef lcr_obj_t * lcr_route ;
typedef struct max_obj max_obj_t ;
typedef max_obj_t * max_len ;
struct profile_obj {
char * name ;
uint16_t id ;
char * order_by ;
2009-02-11 16:52:29 +00:00
char * pre_order ;
2009-02-11 00:18:50 +00:00
char * custom_sql ;
2009-01-14 02:18:51 +00:00
} ;
typedef struct profile_obj profile_t ;
struct callback_obj {
lcr_route head ;
int matches ;
2009-02-03 21:20:09 +00:00
switch_memory_pool_t * pool ;
2009-01-14 02:18:51 +00:00
char * lookup_number ;
} ;
typedef struct callback_obj callback_t ;
static struct {
switch_memory_pool_t * pool ;
char * dbname ;
char * odbc_dsn ;
switch_mutex_t * mutex ;
2009-02-03 15:59:46 +00:00
switch_mutex_t * db_mutex ;
2009-01-14 02:18:51 +00:00
switch_odbc_handle_t * master_odbc ;
switch_hash_t * profile_hash ;
profile_t * default_profile ;
void * filler1 ;
} globals ;
SWITCH_MODULE_LOAD_FUNCTION ( mod_lcr_load ) ;
SWITCH_MODULE_SHUTDOWN_FUNCTION ( mod_lcr_shutdown ) ;
SWITCH_MODULE_DEFINITION ( mod_lcr , mod_lcr_load , mod_lcr_shutdown , NULL ) ;
2009-02-03 21:20:09 +00:00
static char * get_bridge_data ( switch_memory_pool_t * pool , const char * dialed_number , lcr_route cur_route )
2009-01-15 21:51:15 +00:00
{
2009-01-14 02:18:51 +00:00
size_t lstrip ;
size_t tstrip ;
char * data = NULL ;
char * destination_number = NULL ;
char * orig_destination_number = NULL ;
2009-02-03 21:20:09 +00:00
orig_destination_number = destination_number = switch_core_strdup ( pool , dialed_number ) ;
2009-01-14 02:18:51 +00:00
tstrip = ( ( cur_route - > digit_len - cur_route - > tstrip ) + 1 ) ;
lstrip = cur_route - > lstrip ;
if ( strlen ( destination_number ) > tstrip & & cur_route - > tstrip > 0 ) {
destination_number [ tstrip ] = ' \0 ' ;
}
if ( strlen ( destination_number ) > lstrip & & cur_route - > lstrip > 0 ) {
destination_number + = lstrip ;
}
2009-02-03 21:31:41 +00:00
data = switch_core_sprintf ( pool , " %s%s%s%s%s " , cur_route - > gw_prefix , cur_route - > prefix
, destination_number , cur_route - > suffix , cur_route - > gw_suffix ) ;
2009-01-14 02:18:51 +00:00
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " Returning Dialstring %s \n " , data ) ;
return data ;
}
2009-01-15 21:51:15 +00:00
void init_max_lens ( max_len maxes )
{
2009-01-14 02:18:51 +00:00
maxes - > digit_str = ( headers [ LCR_DIGITS_PLACE ] = = NULL ? 0 : strlen ( headers [ LCR_DIGITS_PLACE ] ) ) ;
maxes - > carrier_name = ( headers [ LCR_CARRIER_PLACE ] = = NULL ? 0 : strlen ( headers [ LCR_CARRIER_PLACE ] ) ) ;
maxes - > dialstring = ( headers [ LCR_DIALSTRING_PLACE ] = = NULL ? 0 : strlen ( headers [ LCR_DIALSTRING_PLACE ] ) ) ;
maxes - > digit_str = ( headers [ LCR_DIGITS_PLACE ] = = NULL ? 0 : strlen ( headers [ LCR_DIGITS_PLACE ] ) ) ;
maxes - > rate = 8 ;
}
2009-01-15 21:51:15 +00:00
switch_status_t process_max_lengths ( max_obj_t * maxes , lcr_route routes , char * destination_number )
{
2009-01-14 02:18:51 +00:00
lcr_route current = NULL ;
if ( routes = = NULL ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_NOTICE , " no routes \n " ) ;
return SWITCH_STATUS_FALSE ;
}
if ( maxes = = NULL ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_NOTICE , " no maxes \n " ) ;
return SWITCH_STATUS_FALSE ;
}
init_max_lens ( maxes ) ;
for ( current = routes ; current ; current = current - > next ) {
size_t this_len ;
if ( current - > carrier_name ! = NULL ) {
this_len = strlen ( current - > carrier_name ) ;
if ( this_len > maxes - > carrier_name ) {
maxes - > carrier_name = this_len ;
}
}
if ( current - > dialstring ! = NULL ) {
this_len = strlen ( current - > dialstring ) ;
if ( this_len > maxes - > dialstring ) {
maxes - > dialstring = this_len ;
}
}
if ( current - > digit_str ! = NULL ) {
if ( current - > digit_len > maxes - > digit_str ) {
maxes - > digit_str = current - > digit_len ;
}
}
if ( current - > rate_str ! = NULL ) {
this_len = strlen ( current - > rate_str ) ;
if ( this_len > maxes - > rate ) {
maxes - > rate = this_len ;
}
}
}
return SWITCH_STATUS_SUCCESS ;
}
/* try each type of random until we suceed */
2009-01-15 21:51:15 +00:00
static switch_bool_t set_db_random ( )
{
2009-01-14 02:18:51 +00:00
char * sql = NULL ;
if ( globals . odbc_dsn ) {
sql = " SELECT * FROM lcr ORDER BY rand() LIMIT 1 " ;
if ( switch_odbc_handle_exec ( globals . master_odbc , sql , NULL )
= = SWITCH_ODBC_SUCCESS ) {
db_random = " rand() " ;
return SWITCH_TRUE ;
}
sql = " SELECT * FROM lcr ORDER BY random() LIMIT 1 " ;
if ( switch_odbc_handle_exec ( globals . master_odbc , sql , NULL )
= = SWITCH_ODBC_SUCCESS ) {
db_random = " random() " ;
return SWITCH_TRUE ;
}
}
return SWITCH_FALSE ;
}
2009-02-11 00:18:50 +00:00
static switch_bool_t test_lcr_sql ( const char * sql )
{
char * tsql ;
tsql = switch_mprintf ( sql , " 5555551212 " ) ;
2009-02-11 02:55:17 +00:00
switch_bool_t retval = SWITCH_FALSE ;
2009-02-11 00:18:50 +00:00
if ( globals . odbc_dsn ) {
if ( switch_odbc_handle_exec ( globals . master_odbc , tsql , NULL )
= = SWITCH_ODBC_SUCCESS ) {
retval = SWITCH_TRUE ;
} else {
retval = SWITCH_FALSE ;
}
}
switch_safe_free ( tsql ) ;
return retval ;
}
2009-01-15 20:32:25 +00:00
/* make a new string with digits only */
2009-02-03 21:20:09 +00:00
static char * string_digitsonly ( switch_memory_pool_t * pool , const char * str )
2009-01-15 21:51:15 +00:00
{
2009-01-15 20:32:25 +00:00
char * p , * np , * newstr ;
size_t len ;
p = ( char * ) str ;
len = strlen ( str ) ;
2009-02-03 21:20:09 +00:00
newstr = switch_core_alloc ( pool , len + 1 ) ;
2009-01-15 20:32:25 +00:00
np = newstr ;
while ( * p ) {
if ( switch_isdigit ( * p ) ) {
* np = * p ;
np + + ;
}
p + + ;
}
* np = ' \0 ' ;
return newstr ;
}
2009-01-15 21:51:15 +00:00
static switch_bool_t lcr_execute_sql_callback ( char * sql , switch_core_db_callback_func_t callback , void * pdata )
{
2009-02-03 15:59:46 +00:00
switch_bool_t retval = SWITCH_FALSE ;
switch_mutex_lock ( globals . db_mutex ) ;
2009-01-14 02:18:51 +00:00
if ( globals . odbc_dsn ) {
if ( switch_odbc_handle_callback_exec ( globals . master_odbc , sql , callback , pdata )
= = SWITCH_ODBC_FAIL ) {
2009-02-03 15:59:46 +00:00
retval = SWITCH_FALSE ;
2009-01-14 02:18:51 +00:00
} else {
2009-02-03 15:59:46 +00:00
retval = SWITCH_TRUE ;
2009-01-14 02:18:51 +00:00
}
}
2009-02-03 15:59:46 +00:00
switch_mutex_unlock ( globals . db_mutex ) ;
return retval ;
2009-01-14 02:18:51 +00:00
}
2009-01-15 21:51:15 +00:00
int route_add_callback ( void * pArg , int argc , char * * argv , char * * columnNames )
{
2009-01-14 02:18:51 +00:00
lcr_route additional = NULL ;
lcr_route current = NULL ;
callback_t * cbt = ( callback_t * ) pArg ;
2009-02-03 21:20:09 +00:00
switch_memory_pool_t * pool = cbt - > pool ;
2009-01-14 02:18:51 +00:00
cbt - > matches + + ;
2009-02-11 00:18:50 +00:00
if ( argc ! = LCR_QUERY_COLS ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_CRIT ,
" Unexpected number of columns returned for SQL. Returned columns are: %d. "
" If using a custom sql for this profile, verify it is correct. Otherwise file a bug report. \n " ,
argc ) ;
return SWITCH_STATUS_GENERR ;
}
2009-01-14 02:18:51 +00:00
2009-01-28 01:50:45 +00:00
if ( switch_strlen_zero ( argv [ LCR_GW_PREFIX_PLACE ] ) & & switch_strlen_zero ( argv [ LCR_GW_SUFFIX_PLACE ] ) ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_WARNING ,
" There's no way to dial this Gateway: Carrier: \" %s \" Prefix: \" %s \" , Suffix \" %s \" \n " ,
switch_str_nil ( argv [ LCR_CARRIER_PLACE ] ) ,
switch_str_nil ( argv [ LCR_GW_PREFIX_PLACE ] ) , switch_str_nil ( argv [ LCR_GW_SUFFIX_PLACE ] ) ) ;
return SWITCH_STATUS_SUCCESS ;
}
2009-02-03 21:20:09 +00:00
additional = switch_core_alloc ( pool , sizeof ( lcr_obj_t ) ) ;
2009-01-14 02:18:51 +00:00
additional - > digit_len = strlen ( argv [ LCR_DIGITS_PLACE ] ) ;
2009-02-03 21:41:48 +00:00
additional - > digit_str = switch_core_strdup ( pool , switch_str_nil ( argv [ LCR_DIGITS_PLACE ] ) ) ;
additional - > suffix = switch_core_strdup ( pool , switch_str_nil ( argv [ LCR_SUFFIX_PLACE ] ) ) ;
additional - > prefix = switch_core_strdup ( pool , switch_str_nil ( argv [ LCR_PREFIX_PLACE ] ) ) ;
additional - > carrier_name = switch_core_strdup ( pool , switch_str_nil ( argv [ LCR_CARRIER_PLACE ] ) ) ;
additional - > rate = ( float ) atof ( switch_str_nil ( argv [ LCR_RATE_PLACE ] ) ) ;
2009-02-03 21:20:09 +00:00
additional - > rate_str = switch_core_sprintf ( pool , " %0.5f " , additional - > rate ) ;
2009-02-03 21:41:48 +00:00
additional - > gw_prefix = switch_core_strdup ( pool , switch_str_nil ( argv [ LCR_GW_PREFIX_PLACE ] ) ) ;
additional - > gw_suffix = switch_core_strdup ( pool , switch_str_nil ( argv [ LCR_GW_SUFFIX_PLACE ] ) ) ;
additional - > lstrip = atoi ( switch_str_nil ( argv [ LCR_LSTRIP_PLACE ] ) ) ;
additional - > tstrip = atoi ( switch_str_nil ( argv [ LCR_TSTRIP_PLACE ] ) ) ;
2009-02-03 21:20:09 +00:00
additional - > dialstring = get_bridge_data ( pool , cbt - > lookup_number , additional ) ;
2009-01-14 02:18:51 +00:00
if ( cbt - > head = = NULL ) {
additional - > next = cbt - > head ;
cbt - > head = additional ;
return SWITCH_STATUS_SUCCESS ;
}
for ( current = cbt - > head ; current ; current = current - > next ) {
if ( ! strcmp ( current - > gw_prefix , additional - > gw_prefix ) & & ! strcmp ( current - > gw_suffix , additional - > gw_suffix ) ) {
2009-01-28 01:50:45 +00:00
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG ,
" Ignoring Duplicate route for termination point (%s:%s) \n " ,
additional - > gw_prefix , additional - > gw_suffix ) ;
2009-01-14 02:18:51 +00:00
break ;
}
if ( current - > next = = NULL ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_INFO , " adding route to end of list \n " ) ;
current - > next = additional ;
break ;
}
}
return SWITCH_STATUS_SUCCESS ;
}
2009-01-15 21:51:15 +00:00
switch_status_t lcr_do_lookup ( callback_t * cb_struct , char * digits , char * profile_name )
{
2009-01-14 02:18:51 +00:00
/* instantiate the object/struct we defined earlier */
switch_stream_handle_t sql_stream = { 0 } ;
size_t n , digit_len = strlen ( digits ) ;
char * digits_copy ;
profile_t * profile ;
switch_bool_t lookup_status ;
2009-02-11 00:18:50 +00:00
digits_copy = string_digitsonly ( cb_struct - > pool , digits ) ;
if ( switch_strlen_zero ( digits_copy ) ) {
2009-01-14 02:18:51 +00:00
return SWITCH_FALSE ;
}
/* locate the profile */
if ( switch_strlen_zero ( profile_name ) ) {
profile = globals . default_profile ;
} else if ( ! ( profile = switch_core_hash_find ( globals . profile_hash , profile_name ) ) ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " Error invalid profile %s \n " , profile_name ) ;
return SWITCH_STATUS_FALSE ;
}
2009-02-03 21:20:09 +00:00
/* SWITCH_STANDARD_STREAM doesn't use pools. but we only have to free sql_stream.data */
2009-01-14 02:18:51 +00:00
SWITCH_STANDARD_STREAM ( sql_stream ) ;
/* set up the query to be executed */
2009-02-11 00:18:50 +00:00
if ( switch_strlen_zero ( profile - > custom_sql ) ) {
sql_stream . write_function ( & sql_stream ,
" SELECT l.digits, c.carrier_name, l.rate, cg.prefix AS gw_prefix, cg.suffix AS gw_suffix, l.lead_strip, l.trail_strip, l.prefix, l.suffix "
) ;
sql_stream . write_function ( & sql_stream , " FROM lcr l JOIN carriers c ON l.carrier_id=c.id JOIN carrier_gateway cg ON c.id=cg.carrier_id WHERE c.enabled = '1' AND cg.enabled = '1' AND l.enabled = '1' AND digits IN ( " ) ;
for ( n = digit_len ; n > 0 ; n - - ) {
digits_copy [ n ] = ' \0 ' ;
sql_stream . write_function ( & sql_stream , " %s%s " , ( n = = digit_len ? " " : " , " ) , digits_copy ) ;
}
sql_stream . write_function ( & sql_stream , " ) AND CURRENT_TIMESTAMP BETWEEN date_start AND date_end " ) ;
if ( profile - > id > 0 ) {
sql_stream . write_function ( & sql_stream , " AND lcr_profile=%d " , profile - > id ) ;
}
2009-02-11 16:52:29 +00:00
sql_stream . write_function ( & sql_stream , " ORDER BY %s%s digits DESC%s " ,
profile - > pre_order ,
switch_strlen_zero ( profile - > pre_order ) ? " " : " , " ,
profile - > order_by ) ;
2009-02-11 00:18:50 +00:00
if ( db_random ) {
sql_stream . write_function ( & sql_stream , " , %s " , db_random ) ;
}
sql_stream . write_function ( & sql_stream , " ; " ) ;
} else {
char * safe_sql ;
safe_sql = switch_mprintf ( profile - > custom_sql , digits_copy ) ;
sql_stream . write_function ( & sql_stream , safe_sql ) ;
switch_safe_free ( safe_sql ) ;
2009-01-14 02:18:51 +00:00
}
2009-02-03 21:20:09 +00:00
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " %s \n " , ( char * ) sql_stream . data ) ;
2009-01-14 02:18:51 +00:00
lookup_status = lcr_execute_sql_callback ( ( char * ) sql_stream . data , route_add_callback , cb_struct ) ;
switch_safe_free ( sql_stream . data ) ;
if ( lookup_status ) {
return SWITCH_STATUS_SUCCESS ;
} else {
return SWITCH_STATUS_GENERR ;
}
}
2009-01-15 21:51:15 +00:00
static switch_status_t lcr_load_config ( )
{
2009-01-14 02:18:51 +00:00
char * cf = " lcr.conf " ;
switch_xml_t cfg , xml , settings , param , x_profile , x_profiles ;
switch_status_t status = SWITCH_STATUS_SUCCESS ;
char * odbc_user = NULL ;
char * odbc_pass = NULL ;
profile_t * profile = NULL ;
if ( ! ( xml = switch_xml_open_cfg ( cf , & cfg , NULL ) ) ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " open of %s failed \n " , cf ) ;
return SWITCH_STATUS_TERM ;
}
if ( ( settings = switch_xml_child ( cfg , " settings " ) ) ) {
for ( param = switch_xml_child ( settings , " param " ) ; param ; param = param - > next ) {
char * var = NULL ;
char * val = NULL ;
var = ( char * ) switch_xml_attr_soft ( param , " name " ) ;
val = ( char * ) switch_xml_attr_soft ( param , " value " ) ;
if ( ! strcasecmp ( var , " odbc-dsn " ) & & ! switch_strlen_zero ( val ) ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_INFO , " odbc_dsn is %s \n " , val ) ;
globals . odbc_dsn = switch_core_strdup ( globals . pool , val ) ;
if ( ( odbc_user = strchr ( globals . odbc_dsn , ' : ' ) ) ) {
* odbc_user + + = ' \0 ' ;
if ( ( odbc_pass = strchr ( odbc_user , ' : ' ) ) ) {
* odbc_pass + + = ' \0 ' ;
}
}
}
}
}
2009-02-11 00:18:50 +00:00
/* initialize sql here, 'cause we need to verify custom_sql for each profile below */
if ( globals . odbc_dsn ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_INFO
, " dsn is \" %s \" , user is \" %s \" , and password is \" %s \" \n "
, globals . odbc_dsn , odbc_user , odbc_pass
) ;
if ( ! ( globals . master_odbc = switch_odbc_handle_new ( globals . odbc_dsn , odbc_user , odbc_pass ) ) ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_CRIT , " Cannot Open ODBC Database! \n " ) ;
status = SWITCH_STATUS_FALSE ;
goto done ;
}
if ( switch_odbc_handle_connect ( globals . master_odbc ) ! = SWITCH_ODBC_SUCCESS ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_CRIT , " Cannot Open ODBC Database! \n " ) ;
status = SWITCH_STATUS_FALSE ;
goto done ;
}
}
2009-01-14 02:18:51 +00:00
/* define default profile */
profile = switch_core_alloc ( globals . pool , sizeof ( * profile ) ) ;
2009-02-11 00:18:50 +00:00
memset ( profile , 0 , sizeof ( profile_t ) ) ;
2009-01-14 02:18:51 +00:00
profile - > name = " global_default " ;
profile - > order_by = " , rate " ;
2009-02-11 16:52:29 +00:00
profile - > pre_order = " " ;
2009-01-14 02:18:51 +00:00
globals . default_profile = profile ;
switch_core_hash_init ( & globals . profile_hash , globals . pool ) ;
if ( ( x_profiles = switch_xml_child ( cfg , " profiles " ) ) ) {
for ( x_profile = switch_xml_child ( x_profiles , " profile " ) ; x_profile ; x_profile = x_profile - > next ) {
char * name = ( char * ) switch_xml_attr_soft ( x_profile , " name " ) ;
2009-02-11 16:52:29 +00:00
char * comma = " , " ;
2009-01-14 02:18:51 +00:00
switch_stream_handle_t order_by = { 0 } ;
2009-02-11 16:52:29 +00:00
switch_stream_handle_t pre_order = { 0 } ;
switch_stream_handle_t * thisorder = NULL ;
2009-01-14 02:18:51 +00:00
char * id_s = NULL ;
2009-02-11 00:18:50 +00:00
char * custom_sql = NULL ;
2009-01-14 02:18:51 +00:00
int argc , x = 0 ;
char * argv [ 4 ] = { 0 } ;
SWITCH_STANDARD_STREAM ( order_by ) ;
2009-02-11 16:52:29 +00:00
SWITCH_STANDARD_STREAM ( pre_order ) ;
2009-01-14 02:18:51 +00:00
for ( param = switch_xml_child ( x_profile , " param " ) ; param ; param = param - > next ) {
char * var , * val ;
var = ( char * ) switch_xml_attr_soft ( param , " name " ) ;
val = ( char * ) switch_xml_attr_soft ( param , " value " ) ;
2009-02-11 16:52:29 +00:00
if ( ( ! strcasecmp ( var , " order_by " ) | | ! strcasecmp ( var , " pre_order " ) ) & & ! switch_strlen_zero ( val ) ) {
if ( ! strcasecmp ( var , " order_by " ) ) {
thisorder = & order_by ;
} else if ( ! strcasecmp ( var , " pre_order " ) ) {
thisorder = & pre_order ;
comma = " " ; /* don't want leading comma */
}
2009-01-14 02:18:51 +00:00
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " param val is %s \n " , val ) ;
if ( ( argc = switch_separate_string ( val , ' , ' , argv , ( sizeof ( argv ) / sizeof ( argv [ 0 ] ) ) ) ) ) {
for ( x = 0 ; x < argc ; x + + ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " arg #%d/%d is %s \n " , x , argc , argv [ x ] ) ;
if ( ! switch_strlen_zero ( argv [ x ] ) ) {
if ( ! strcasecmp ( argv [ x ] , " quality " ) ) {
2009-02-11 16:52:29 +00:00
thisorder - > write_function ( thisorder , " %s quality DESC " , comma ) ;
2009-01-14 02:18:51 +00:00
} else if ( ! strcasecmp ( argv [ x ] , " reliability " ) ) {
2009-02-11 16:52:29 +00:00
thisorder - > write_function ( thisorder , " %s reliability DESC " , comma ) ;
2009-01-14 02:18:51 +00:00
} else {
2009-02-11 16:52:29 +00:00
thisorder - > write_function ( thisorder , " %s %s " , comma , argv [ x ] ) ;
2009-01-14 02:18:51 +00:00
}
} else {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " arg #%d is empty \n " , x ) ;
}
}
} else {
if ( ! strcasecmp ( val , " quality " ) ) {
2009-02-11 16:52:29 +00:00
thisorder - > write_function ( thisorder , " %s quality DESC " , comma ) ;
2009-01-14 02:18:51 +00:00
} else if ( ! strcasecmp ( val , " reliability " ) ) {
2009-02-11 16:52:29 +00:00
thisorder - > write_function ( thisorder , " %s reliability DESC " , comma ) ;
2009-01-14 02:18:51 +00:00
} else {
2009-02-11 16:52:29 +00:00
thisorder - > write_function ( thisorder , " %s %s " , comma , val ) ;
2009-01-14 02:18:51 +00:00
}
}
} else if ( ! strcasecmp ( var , " id " ) & & ! switch_strlen_zero ( val ) ) {
id_s = val ;
2009-02-11 00:18:50 +00:00
} else if ( ! strcasecmp ( var , " custom_sql " ) & & ! switch_strlen_zero ( val ) ) {
custom_sql = val ;
2009-01-14 02:18:51 +00:00
}
}
if ( switch_strlen_zero ( name ) ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " No name specified. \n " ) ;
} else {
profile = switch_core_alloc ( globals . pool , sizeof ( * profile ) ) ;
2009-02-11 00:18:50 +00:00
memset ( profile , 0 , sizeof ( profile_t ) ) ;
2009-01-14 02:18:51 +00:00
profile - > name = switch_core_strdup ( globals . pool , name ) ;
if ( ! switch_strlen_zero ( ( char * ) order_by . data ) ) {
profile - > order_by = switch_core_strdup ( globals . pool , ( char * ) order_by . data ) ;
} else {
/* default to rate */
profile - > order_by = " , rate " ;
}
2009-02-11 16:52:29 +00:00
if ( ! switch_strlen_zero ( ( char * ) pre_order . data ) ) {
profile - > pre_order = switch_core_strdup ( globals . pool , ( char * ) pre_order . data ) ;
} else {
/* default to rate */
profile - > order_by = " , rate " ;
}
2009-01-14 02:18:51 +00:00
if ( ! switch_strlen_zero ( id_s ) ) {
2009-01-28 01:50:45 +00:00
profile - > id = ( uint16_t ) atoi ( id_s ) ;
2009-01-14 02:18:51 +00:00
}
2009-02-11 00:18:50 +00:00
if ( ! switch_strlen_zero ( custom_sql ) ) {
if ( test_lcr_sql ( custom_sql ) = = SWITCH_TRUE ) {
profile - > custom_sql = switch_core_strdup ( globals . pool , ( char * ) custom_sql ) ;
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_INFO , " Using custom lcr sql: %s \n " , profile - > custom_sql ) ;
} else {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_CRIT , " Custom lcr sql invalid: %s \n Disabling profile: %s \n " , custom_sql , name ) ;
continue ;
}
}
2009-01-14 02:18:51 +00:00
switch_core_hash_insert ( globals . profile_hash , profile - > name , profile ) ;
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_INFO , " Loaded lcr profile %s. \n " , profile - > name ) ;
}
2009-02-03 21:20:09 +00:00
switch_safe_free ( order_by . data ) ;
2009-02-11 16:52:29 +00:00
switch_safe_free ( pre_order . data ) ;
2009-01-14 02:18:51 +00:00
}
} else {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_INFO , " No lcr profiles defined. \n " ) ;
}
done :
switch_xml_free ( xml ) ;
return status ;
}
2009-01-15 21:51:15 +00:00
SWITCH_STANDARD_DIALPLAN ( lcr_dialplan_hunt )
{
2009-01-14 02:18:51 +00:00
switch_caller_extension_t * extension = NULL ;
switch_channel_t * channel = switch_core_session_get_channel ( session ) ;
callback_t routes = { 0 } ;
lcr_route cur_route = { 0 } ;
char * lcr_profile = NULL ;
2009-02-03 21:20:09 +00:00
switch_memory_pool_t * pool = NULL ;
2009-01-14 02:18:51 +00:00
2009-02-03 21:20:09 +00:00
if ( session ) {
pool = switch_core_session_get_pool ( session ) ;
} else {
switch_core_new_memory_pool ( & pool ) ;
}
routes . pool = pool ;
2009-01-14 02:18:51 +00:00
if ( ! caller_profile ) {
caller_profile = switch_channel_get_caller_profile ( channel ) ;
}
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " LCR Lookup on %s \n " , caller_profile - > destination_number ) ;
routes . lookup_number = caller_profile - > destination_number ;
if ( lcr_do_lookup ( & routes , caller_profile - > destination_number , lcr_profile ) = = SWITCH_STATUS_SUCCESS ) {
if ( ( extension = switch_caller_extension_new ( session , caller_profile - > destination_number , caller_profile - > destination_number ) ) = = 0 ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_CRIT , " memory error! \n " ) ;
2009-02-03 21:20:09 +00:00
goto end ;
2009-01-14 02:18:51 +00:00
}
switch_channel_set_variable ( channel , SWITCH_CONTINUE_ON_FAILURE_VARIABLE , " true " ) ;
switch_channel_set_variable ( channel , SWITCH_HANGUP_AFTER_BRIDGE_VARIABLE , " true " ) ;
for ( cur_route = routes . head ; cur_route ; cur_route = cur_route - > next ) {
switch_caller_extension_add_application ( session , extension , " bridge " , cur_route - > dialstring ) ;
}
} else {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_WARNING , " LCR lookup failed for %s \n " , caller_profile - > destination_number ) ;
}
2009-02-03 21:20:09 +00:00
end :
if ( ! session ) {
switch_core_destroy_memory_pool ( & pool ) ;
}
2009-01-14 02:18:51 +00:00
return extension ;
}
2009-01-15 21:51:15 +00:00
void str_repeat ( size_t how_many , char * what , switch_stream_handle_t * str_stream )
{
2009-01-14 02:18:51 +00:00
size_t i ;
2009-01-15 22:00:43 +00:00
/*//switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "repeating %d of '%s'\n", how_many, what);*/
2009-01-14 02:18:51 +00:00
for ( i = 0 ; i < how_many ; i + + ) {
str_stream - > write_function ( str_stream , " %s " , what ) ;
}
}
SWITCH_STANDARD_APP ( lcr_app_function )
{
int argc = 0 ;
char * argv [ 4 ] = { 0 } ;
char * mydata = NULL ;
char * dest = NULL ;
char rbuf [ 1024 ] = " " ;
char vbuf [ 1024 ] = " " ;
char * rbp = rbuf ;
switch_size_t l = 0 , rbl = sizeof ( rbuf ) ;
uint32_t cnt = 1 ;
char * lcr_profile = NULL ;
switch_channel_t * channel = switch_core_session_get_channel ( session ) ;
char * last_delim = " | " ;
callback_t routes = { 0 } ;
lcr_route cur_route = { 0 } ;
2009-02-03 21:20:09 +00:00
switch_memory_pool_t * pool ;
2009-01-14 02:18:51 +00:00
if ( ! ( mydata = switch_core_session_strdup ( session , data ) ) ) {
return ;
}
2009-02-03 21:20:09 +00:00
if ( session ) {
pool = switch_core_session_get_pool ( session ) ;
} else {
switch_core_new_memory_pool ( & pool ) ;
}
routes . pool = pool ;
2009-01-14 02:18:51 +00:00
if ( ( argc = switch_separate_string ( mydata , ' ' , argv , ( sizeof ( argv ) / sizeof ( argv [ 0 ] ) ) ) ) ) {
dest = argv [ 0 ] ;
if ( argc > 1 ) {
lcr_profile = argv [ 1 ] ;
}
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " LCR Lookup on %s \n " , dest ) ;
routes . lookup_number = dest ;
if ( lcr_do_lookup ( & routes , dest , lcr_profile ) = = SWITCH_STATUS_SUCCESS ) {
for ( cur_route = routes . head ; cur_route ; cur_route = cur_route - > next ) {
switch_snprintf ( vbuf , sizeof ( vbuf ) , " lcr_route_%d " , cnt + + ) ;
switch_channel_set_variable ( channel , vbuf , cur_route - > dialstring ) ;
switch_snprintf ( rbp , rbl , " %s| " , cur_route - > dialstring ) ;
last_delim = end_of_p ( rbp ) ;
l = strlen ( cur_route - > dialstring ) + 1 ;
rbp + = l ;
rbl - = l ;
}
switch_snprintf ( vbuf , sizeof ( vbuf ) , " %d " , cnt - 1 ) ;
switch_channel_set_variable ( channel , " lcr_route_count " , vbuf ) ;
* ( rbuf + strlen ( rbuf ) - 1 ) = ' \0 ' ;
switch_channel_set_variable ( channel , " lcr_auto_route " , rbuf ) ;
} else {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_WARNING , " LCR lookup failed for %s \n " , dest ) ;
}
}
2009-02-03 21:20:09 +00:00
if ( ! session ) {
switch_core_destroy_memory_pool ( & pool ) ;
}
2009-01-14 02:18:51 +00:00
}
2009-01-15 21:51:15 +00:00
SWITCH_STANDARD_API ( dialplan_lcr_function )
{
2009-01-14 02:18:51 +00:00
char * argv [ 4 ] = { 0 } ;
int argc ;
char * mydata = NULL ;
char * dialstring = NULL ;
char * destination_number = NULL ;
char * lcr_profile = NULL ;
lcr_route current = NULL ;
max_obj_t maximum_lengths = { 0 } ;
callback_t cb_struct = { 0 } ;
2009-02-03 21:20:09 +00:00
switch_memory_pool_t * pool ;
2009-01-14 02:18:51 +00:00
switch_status_t lookup_status = SWITCH_STATUS_SUCCESS ;
2009-02-03 21:20:09 +00:00
switch_core_new_memory_pool ( & pool ) ;
2009-01-14 02:18:51 +00:00
if ( switch_strlen_zero ( cmd ) ) {
goto usage ;
}
2009-02-03 21:20:09 +00:00
mydata = switch_core_strdup ( pool , cmd ) ;
2009-01-14 02:18:51 +00:00
if ( ( argc = switch_separate_string ( mydata , ' ' , argv , ( sizeof ( argv ) / sizeof ( argv [ 0 ] ) ) ) ) ) {
2009-02-03 22:19:31 +00:00
switch_assert ( argv [ 0 ] ! = NULL ) ;
2009-02-03 21:20:09 +00:00
destination_number = switch_core_strdup ( pool , argv [ 0 ] ) ;
2009-01-14 02:18:51 +00:00
if ( argc > 1 ) {
lcr_profile = argv [ 1 ] ;
}
cb_struct . lookup_number = destination_number ;
2009-02-03 21:20:09 +00:00
cb_struct . pool = pool ;
2009-01-14 02:18:51 +00:00
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_INFO
, " data passed to lcr is [%s] \n " , cmd
) ;
lookup_status = lcr_do_lookup ( & cb_struct , destination_number , lcr_profile ) ;
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_INFO
, " lcr lookup returned [%d] \n "
, lookup_status
) ;
if ( cb_struct . head ! = NULL ) {
size_t len ;
process_max_lengths ( & maximum_lengths , cb_struct . head , destination_number ) ;
stream - > write_function ( stream , " | %s " , headers [ LCR_DIGITS_PLACE ] ) ;
if ( ( len = ( maximum_lengths . digit_str - strlen ( headers [ LCR_DIGITS_PLACE ] ) ) ) > 0 ) {
str_repeat ( len , " " , stream ) ;
}
stream - > write_function ( stream , " | %s " , headers [ LCR_CARRIER_PLACE ] ) ;
if ( ( len = ( maximum_lengths . carrier_name - strlen ( headers [ LCR_CARRIER_PLACE ] ) ) ) > 0 ) {
str_repeat ( len , " " , stream ) ;
}
stream - > write_function ( stream , " | %s " , headers [ LCR_RATE_PLACE ] ) ;
if ( ( len = ( maximum_lengths . rate - strlen ( headers [ LCR_RATE_PLACE ] ) ) ) > 0 ) {
str_repeat ( len , " " , stream ) ;
}
stream - > write_function ( stream , " | %s " , headers [ LCR_DIALSTRING_PLACE ] ) ;
if ( ( len = ( maximum_lengths . dialstring - strlen ( headers [ LCR_DIALSTRING_PLACE ] ) ) ) > 0 ) {
str_repeat ( len , " " , stream ) ;
}
stream - > write_function ( stream , " | \n " ) ;
current = cb_struct . head ;
while ( current ) {
2009-02-03 21:20:09 +00:00
dialstring = get_bridge_data ( pool , destination_number , current ) ;
2009-01-14 02:18:51 +00:00
stream - > write_function ( stream , " | %s " , current - > digit_str ) ;
str_repeat ( ( maximum_lengths . digit_str - current - > digit_len ) , " " , stream ) ;
stream - > write_function ( stream , " | %s " , current - > carrier_name ) ;
str_repeat ( ( maximum_lengths . carrier_name - strlen ( current - > carrier_name ) ) , " " , stream ) ;
stream - > write_function ( stream , " | %s " , current - > rate_str ) ;
str_repeat ( ( maximum_lengths . rate - strlen ( current - > rate_str ) ) , " " , stream ) ;
stream - > write_function ( stream , " | %s " , dialstring ) ;
str_repeat ( ( maximum_lengths . dialstring - strlen ( dialstring ) ) , " " , stream ) ;
stream - > write_function ( stream , " | \n " ) ;
current = current - > next ;
}
} else {
if ( lookup_status = = SWITCH_STATUS_SUCCESS ) {
stream - > write_function ( stream , " No Routes To Display \n " ) ;
} else {
stream - > write_function ( stream , " Error looking up routes \n " ) ;
}
}
}
2009-02-03 21:20:09 +00:00
switch_core_destroy_memory_pool ( & pool ) ;
2009-01-14 02:18:51 +00:00
return SWITCH_STATUS_SUCCESS ;
usage :
stream - > write_function ( stream , " USAGE: %s \n " , LCR_SYNTAX ) ;
return SWITCH_STATUS_SUCCESS ;
}
2009-02-11 05:07:37 +00:00
SWITCH_STANDARD_API ( dialplan_lcr_admin_function )
{
char * argv [ 4 ] = { 0 } ;
int argc ;
char * mydata = NULL ;
switch_hash_index_t * hi ;
void * val ;
profile_t * profile ;
if ( switch_strlen_zero ( cmd ) ) {
goto usage ;
}
mydata = strdup ( cmd ) ;
if ( ( argc = switch_separate_string ( mydata , ' ' , argv , ( sizeof ( argv ) / sizeof ( argv [ 0 ] ) ) ) ) ) {
if ( argc < 2 ) {
goto usage ;
}
if ( ! strcasecmp ( argv [ 0 ] , " show " ) & & ! strcasecmp ( argv [ 1 ] , " profiles " ) ) {
for ( hi = switch_hash_first ( NULL , globals . profile_hash ) ; hi ; hi = switch_hash_next ( hi ) ) {
switch_hash_this ( hi , NULL , NULL , & val ) ;
profile = ( profile_t * ) val ;
stream - > write_function ( stream , " Name: \t \t %s \n " , profile - > name ) ;
2009-02-11 16:52:29 +00:00
if ( switch_strlen_zero ( profile - > custom_sql ) ) {
stream - > write_function ( stream , " ID: \t \t %d \n " , profile - > id ) ;
stream - > write_function ( stream , " order by: \t %s \n " , profile - > order_by ) ;
if ( ! switch_strlen_zero ( profile - > pre_order ) ) {
stream - > write_function ( stream , " pre_order: \t %s \n " , profile - > pre_order ) ;
}
} else {
2009-02-11 05:07:37 +00:00
stream - > write_function ( stream , " custom sql: \t %s \n " , profile - > custom_sql ) ;
}
stream - > write_function ( stream , " \n " ) ;
}
} else {
goto usage ;
}
}
switch_safe_free ( mydata ) ;
return SWITCH_STATUS_SUCCESS ;
usage :
switch_safe_free ( mydata ) ;
stream - > write_function ( stream , " -ERR %s \n " , LCR_ADMIN_SYNTAX ) ;
return SWITCH_STATUS_SUCCESS ;
}
2009-01-15 21:51:15 +00:00
SWITCH_MODULE_LOAD_FUNCTION ( mod_lcr_load )
{
2009-01-14 02:18:51 +00:00
switch_api_interface_t * dialplan_lcr_api_interface ;
2009-02-11 05:07:37 +00:00
switch_api_interface_t * dialplan_lcr_api_admin_interface ;
2009-01-14 02:18:51 +00:00
switch_application_interface_t * app_interface ;
switch_dialplan_interface_t * dp_interface ;
* module_interface = switch_loadable_module_create_module_interface ( pool , modname ) ;
# ifndef SWITCH_HAVE_ODBC
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " You must have ODBC support in FreeSWITCH to use this module \n " ) ;
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " \t ./configure --enable-core-odbc-support \n " ) ;
return SWITCH_STATUS_FALSE ;
# endif
globals . pool = pool ;
if ( lcr_load_config ( ) ! = SWITCH_STATUS_SUCCESS ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " Unable to load lcr config file \n " ) ;
return SWITCH_STATUS_FALSE ;
}
if ( switch_mutex_init ( & globals . mutex , SWITCH_MUTEX_NESTED , globals . pool ) ! = SWITCH_STATUS_SUCCESS ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " failed to initialize mutex \n " ) ;
}
2009-02-03 15:59:46 +00:00
if ( switch_mutex_init ( & globals . db_mutex , SWITCH_MUTEX_UNNESTED , globals . pool ) ! = SWITCH_STATUS_SUCCESS ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " failed to initialize db_mutex \n " ) ;
}
2009-01-14 02:18:51 +00:00
if ( set_db_random ( ) = = SWITCH_TRUE ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_INFO , " Database RANDOM function set to %s \n " , db_random ) ;
} else {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " Unable to determine database RANDOM function \n " ) ;
} ;
SWITCH_ADD_API ( dialplan_lcr_api_interface , " lcr " , " Least Cost Routing Module " , dialplan_lcr_function , LCR_SYNTAX ) ;
2009-02-11 05:07:37 +00:00
SWITCH_ADD_API ( dialplan_lcr_api_admin_interface , " lcr_admin " , " Least Cost Routing Module Admin " , dialplan_lcr_admin_function , LCR_ADMIN_SYNTAX ) ;
2009-01-14 02:18:51 +00:00
SWITCH_ADD_APP ( app_interface , " lcr " , " Perform an LCR lookup " , " Perform an LCR lookup " ,
lcr_app_function , " <number> " , SAF_SUPPORT_NOMEDIA ) ;
SWITCH_ADD_DIALPLAN ( dp_interface , " lcr " , lcr_dialplan_hunt ) ;
/* indicate that the module should continue to be loaded */
return SWITCH_STATUS_SUCCESS ;
}
2009-01-15 21:51:15 +00:00
SWITCH_MODULE_SHUTDOWN_FUNCTION ( mod_lcr_shutdown )
{
2009-02-03 21:20:09 +00:00
switch_odbc_handle_disconnect ( globals . master_odbc ) ;
switch_odbc_handle_destroy ( & globals . master_odbc ) ;
switch_core_destroy_memory_pool ( & globals . pool ) ;
switch_core_hash_destroy ( & globals . profile_hash ) ;
2009-01-14 02:18:51 +00:00
return SWITCH_STATUS_SUCCESS ;
}
/* For Emacs:
* Local Variables :
* mode : c
* indent - tabs - mode : t
* tab - width : 4
* c - basic - offset : 4
* End :
* For VIM :
* vim : set softtabstop = 4 shiftwidth = 4 tabstop = 4 expandtab :
*/