Updated mod_cdr to match changes in core to switch_channel_t, added in some new API commands, added new functionality (depends on a define and patching apr_queue.c/h) changes merged from mishehu branch.

git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@3564 d0543943-73ff-0310-b7d9-9358b9ac24b2
This commit is contained in:
Michael Jerris 2006-12-07 04:15:38 +00:00
parent 243a820dff
commit 8b87814097
20 changed files with 1267 additions and 124 deletions

View File

@ -39,6 +39,7 @@ Configuration: Section name: <csvcdr>
<param name="chanvars_fixed" value=""/> value is a comma separated list of fixed channel variables to log. This cannot be a wildcard (*). (optional) <param name="chanvars_fixed" value=""/> value is a comma separated list of fixed channel variables to log. This cannot be a wildcard (*). (optional)
<param name="chanvars_supp" value=""/> value is a comma separated list of supplemental channel variables to log. It can be a wildcard (*). (optional) <param name="chanvars_supp" value=""/> value is a comma separated list of supplemental channel variables to log. It can be a wildcard (*). (optional)
<param name="repeat_fixed_in_supp" value=""/> value is a 0 for no, 1 for yes, and is for the case that you might want any fixed channel variables to be repeated in the supplemental list. <param name="repeat_fixed_in_supp" value=""/> value is a 0 for no, 1 for yes, and is for the case that you might want any fixed channel variables to be repeated in the supplemental list.
<param name="timezone" value=""/> value is utc for utc time, local for localtime. If not specified or incorrectly specified, localtime is assumed.
Class: PddCDR, located in pddcdr.h and pddcdr.cpp Class: PddCDR, located in pddcdr.h and pddcdr.cpp
Description: This is the Perl Data Dumper output class. It writes each record to an individual text file Description: This is the Perl Data Dumper output class. It writes each record to an individual text file
@ -46,18 +47,25 @@ Description: This is the Perl Data Dumper output class. It writes each record t
Configuration: Section name: <pddcdr> Configuration: Section name: <pddcdr>
<param name="path" value=""/> value is the path where you want the output files (required) <param name="path" value=""/> value is the path where you want the output files (required)
<param name="chanvars" value=""/> value is a comma separated list of channel variables to log. It can accept a wildcard (*) (optional) <param name="chanvars" value=""/> value is a comma separated list of channel variables to log. It can accept a wildcard (*) (optional)
<param name="timezone" value=""/> value is utc for utc time, local for localtime. If not specified or incorrectly specified, localtime is assumed.
Class: XmlCDR
Description: This works very similar to the PddCDR class, only that the output is formatted as xml.
Configuration: Section name: <xmlcdr>
The configuration options are identical to PddCDR.
Class: MysqlCDR, located in mysqlcdr.h and mysqlcdr.cpp Class: MysqlCDR, located in mysqlcdr.h and mysqlcdr.cpp
Description: This class logs the call detail record to a MySQL 4.1.x or greater database using prepared Description: This class logs the call detail record to a MySQL 4.1.x or greater database using prepared
statements. This class is a little more complex than the prior two in that the fixed channel variables are treated as additional columns on the main freeswitchcdr database table for improved normalization of the database. As such, you need to specify the format of each fixed channel variable as well. If you do not specify, it will default to a varchar column type, and will not execute if the table schema does not match. Therefore it is very important to make sure that any fixed channel variables you log exist as columns on the table. The supplemental channel variables are stored in a separate table using a key / value pair linking to the callid of the call. Recommended to use at least one other logger in conjuction with this one just to be on the safe side, as failover handling of errors to write to the db are not yet implemented, and therefore there is a risk of data loss if the database connection is lost entirely (it will be limited to only those records created at the time of the loss of connection) or some other fatal error is encountered. statements. This class is a little more complex than the prior two in that the fixed channel variables are treated as additional columns on the main freeswitchcdr database table for improved normalization of the database. As such, you need to specify the format of each fixed channel variable as well. If you do not specify, it will default to a varchar column type, and will not execute if the table schema does not match the columns being operated upon. Therefore it is very important to make sure that any fixed channel variables you log exist as columns on the table. The supplemental channel variables are stored in a separate table using a key / value pair linking to the callid of the call. Recommended to use at least one other logger in conjuction with this one just to be on the safe side, as failover handling of errors to write to the db are not yet implemented, and therefore there is a risk of data loss if the database connection is lost entirely (it will be limited to only those records created at the time of the loss of connection) or some other fatal error is encountered. As of 2006-12-05, we have added a new column named "calltransferdate" to match changes in the core. Please update your schema to match.
Configuration: Section: <mysqlcdr> Configuration: Section: <mysqlcdr>
<param name="hostname" value=""/> value is the hostname or IP of the MySQL server (required) <param name="hostname" value=""/> value is the hostname or IP of the MySQL server (required)
<param name="username" value=""/> value is the username to connect to the MySQL server with (required) <param name="username" value=""/> value is the username to connect to the MySQL server with (required)
<param name="password" value=""/> value is the password to use to authenticate to the MySQL server with (required) <param name="password" value=""/> value is the password to use to authenticate to the MySQL server with (required)
<param name="dbname" value=""/> value is the name of the database to connect to (required) <param name="dbname" value=""/> value is the name of the database to connect to (required)
<param name="chanvars_fixed" value=""/> Is a comma separated list of key=type pairs. Types are x for decimal, s for string (varchar), d for double, i for integer, t for tiny. If no type is provided, it defaults that column to string (varchar). It cannot accept wildcards (*). (optional) <param name="chanvars_fixed" value=""/> Is a comma separated list of key=type pairs. Types are x for decimal, s for string (varchar), d for double, i for integer, t for tiny. If no type is provided, it defaults that column to string (varchar). It cannot accept wildcards (*). You must have a matching column of matching type in the main freeswitchcdr table. (optional)
<param name="chanvars_supp" value=""/> value is a comma separated list of supplemental channel variables to log. Can be a wildcard (*) (optional) <param name="chanvars_supp" value=""/> value is a comma separated list of supplemental channel variables to log. Can be a wildcard (*) (optional)
<param name="chanvars_supp_repeat_fixed" value=""/> value is 0 for no, 1 for yes, and determines whether or not to repeat any of the fixed channel variables as key / value pairs in the chanvars table. <param name="chanvars_supp_repeat_fixed" value=""/> value is 0 for no, 1 for yes, and determines whether or not to repeat any of the fixed channel variables as key / value pairs in the chanvars table.
<param name="timezone" value=""/> value is utc for utc time, local for localtime. If not specified or incorrectly specified, localtime is assumed.
Class: OdbcCDR, located in odbccdr.h and odbccdr.cpp Class: OdbcCDR, located in odbccdr.h and odbccdr.cpp
This class logs the call detail record to an ODBC database using prepared This class logs the call detail record to an ODBC database using prepared
@ -73,6 +81,7 @@ Configuration: Section <odbccdr>
<param name="chanvars_fixed" value=""/> Is a comma separated list of key=type pairs. Types are x for decimal, s for string (varchar), d for double, i for integer, t for tiny. If no type is provided, it defaults that column to string (varchar). It cannot accept wildcards (*). (optional) <param name="chanvars_fixed" value=""/> Is a comma separated list of key=type pairs. Types are x for decimal, s for string (varchar), d for double, i for integer, t for tiny. If no type is provided, it defaults that column to string (varchar). It cannot accept wildcards (*). (optional)
<param name="chanvars_supp" value=""/> value is a comma separated list of supplemental channel variables to log. Can be a wildcard (*) (optional) <param name="chanvars_supp" value=""/> value is a comma separated list of supplemental channel variables to log. Can be a wildcard (*) (optional)
<param name="chanvars_supp_repeat_fixed" value=""/> value is 0 for no, 1 for yes, and determines whether or not to repeat any of the fixed channel variables as key / value pairs in the chanvars table. <param name="chanvars_supp_repeat_fixed" value=""/> value is 0 for no, 1 for yes, and determines whether or not to repeat any of the fixed channel variables as key / value pairs in the chanvars table.
<param name="timezone" value=""/> value is utc for utc time, local for localtime. If not specified or incorrectly specified, localtime is assumed.
FAQ: FAQ:
@ -85,29 +94,43 @@ A: There is a distinction so that a level of uniformity can be maintain on those
Q: What would an example configuration look like? Q: What would an example configuration look like?
A: Here is an excerpt from the freeswitch XML configuration file: A: Here is an excerpt from the freeswitch XML configuration file:
Q: What about one-legged calls, such as calls to voicemail or playback?
A: With the current changes in place (as of 2006-12-01), the value of originated will always be set to 0, but destuuid will always be blank. This will provide you with a method to bill for specific applications and/or calls if you so desire.
Q: What happened to the ani2 field? Q: What happened to the ani2 field?
A: Oops, you caught us. The real name for is it aniii. You will need to update any database schemas or external handlers for this change. A: Oops, you caught us. The real name for is it aniii. You will need to update any database schemas or external handlers for this change.
Q: Are there any API commands (executable from the console) that can be run?
A: Yes, there are the following commands:
modcdr_reload - causes the freeswitch.xml to be reloaded and reruns the configure on all mod_cdr modules
modcdr_queue_pause - pauses the queue from extracting items for processing, useful if you need to free up some free space without taking the system down.
modcdr_queue_resume - resumes normal processing of items in the queue.
modcdr_show_active - displays the active backend loggers.
modcdr_show_available - displays the available (compiled-in) backend loggers.
Example configuration:
<configuration name="mod_cdr.conf" description="CDR Configuration"> <configuration name="mod_cdr.conf" description="CDR Configuration">
<pddcdr> <pddcdr>
<param name="path" value="/work/temp/pddcdr"/> <param name="path" value="/work/temp/pddcdr"/>
<param name="chanvars" value="username,accountcode"/> <!-- <param name="chanvars" value="username,accountcode"/> -->
<param name="chanvars_fixed" value="*"/> <!-- <param name="chanvars_fixed" value="foo"/> -->
</pddcdr> </pddcdr>
<csvcdr> <csvcdr>
<param name="path" value="/work/temp/csvcdr"/> <param name="path" value="/work/temp/csvcdr"/>
<param name="size_limit" value="25"/> <param name="size_limit" value="25"/>
<param name="chanvars_fixed" value="username"/> <!-- <param name="chanvars_fixed" value="username"/> -->
<param name="chanvars_supp" value="*"/> <!-- <param name="chanvars_supp" value="*"/> -->
<param name="repeat_fixed_in_supp" value="0"/> <!-- <param name="repeat_fixed_in_supp" value="0"/> -->
</csvcdr> </csvcdr>
<mysqlcdr> <mysqlcdr>
<param name="hostname" value="10.0.0.1"/> <param name="hostname" value="10.0.0.1"/>
<param name="username" value="test"/> <param name="username" value="test"/>
<param name="password" value="test"/> <param name="password" value="test"/>
<param name="dbname" value="testing"/> <param name="dbname" value="testing"/>
<!-- This following line logs username as a varchar, and The_Kow as a tiny -->
<!-- <param name="chanvars_fixed" value="username=s,The_Kow=t"/> --> <!-- <param name="chanvars_fixed" value="username=s,The_Kow=t"/> -->
<!-- This previous line logs username as a varchar, and The_Kow as a tiny --> <!-- If you do not want to log any and all chanvars to the chanvar table, comment the next 2 lines out -->
<param name="chanvars_supp" value="*"/> <param name="chanvars_supp" value="*"/>
<param name="chanvars_supp_repeat_fixed" value="0"/> <param name="chanvars_supp_repeat_fixed" value="0"/>
</mysqlcdr> </mysqlcdr>

View File

@ -76,13 +76,18 @@ BaseCDR::BaseCDR(switch_mod_cdr_newchannel_t *newchannel)
memset(aniii,0,80); memset(aniii,0,80);
memset(lastapp,0,80); memset(lastapp,0,80);
memset(lastdata,0,255); memset(lastdata,0,255);
// switch_channel_timetable_t *timetable = switch_channel_get_timetable(newchannel->channel->caller_profile);
coresession = newchannel->session; coresession = newchannel->session;
callstartdate= newchannel->timetable->created;
callanswerdate = newchannel->timetable->answered;
callenddate = newchannel->timetable->hungup;
if (newchannel->callerprofile) { if (newchannel->callerprofile)
{
callstartdate= newchannel->callerprofile->times->created;
callanswerdate = newchannel->callerprofile->times->answered;
calltransferdate = newchannel->callerprofile->times->transferred;
callenddate = newchannel->callerprofile->times->hungup;
if(newchannel->callerprofile->caller_id_name != 0) if(newchannel->callerprofile->caller_id_name != 0)
{ {
strncpy(clid,newchannel->callerprofile->caller_id_name,strlen(newchannel->callerprofile->caller_id_name)); strncpy(clid,newchannel->callerprofile->caller_id_name,strlen(newchannel->callerprofile->caller_id_name));
@ -104,34 +109,39 @@ BaseCDR::BaseCDR(switch_mod_cdr_newchannel_t *newchannel)
if(newchannel->callerprofile->network_addr != 0) if(newchannel->callerprofile->network_addr != 0)
strncpy(network_addr,newchannel->callerprofile->network_addr,strlen(newchannel->callerprofile->network_addr)); strncpy(network_addr,newchannel->callerprofile->network_addr,strlen(newchannel->callerprofile->network_addr));
} }
originated = newchannel->originate; //switch_caller_profile_t *originateprofile = switch_channel_get_originator_caller_profile(newchannel->channel->callerprofile);
if(newchannel->originateprofile && newchannel->originateprofile->uuid != 0) // Were we the receiver of the call?
strncpy(destuuid,newchannel->originateprofile->uuid,strlen(newchannel->originateprofile->uuid)); if(newchannel->callerprofile->originator_caller_profile)
// We still need to check if this is originated or not
if(originated == 0)
{ {
if (newchannel->callerprofile) { originated = 0;
if(newchannel->callerprofile->destination_number != 0) if(newchannel->callerprofile->originator_caller_profile->uuid != 0)
strncpy(destuuid,newchannel->callerprofile->originator_caller_profile->uuid,strlen(newchannel->callerprofile->originator_caller_profile->uuid));
if(newchannel->callerprofile)
{
if(newchannel->callerprofile->destination_number)
strncpy(src,newchannel->callerprofile->destination_number,strlen(newchannel->callerprofile->destination_number)); strncpy(src,newchannel->callerprofile->destination_number,strlen(newchannel->callerprofile->destination_number));
if(newchannel->callerprofile->caller_id_number != 0) if(newchannel->callerprofile->caller_id_number != 0)
strncpy(dst,newchannel->callerprofile->caller_id_number,strlen(newchannel->callerprofile->caller_id_number)); strncpy(dst,newchannel->callerprofile->caller_id_number,strlen(newchannel->callerprofile->caller_id_number));
} }
if(newchannel->originateprofile && newchannel->originateprofile->chan_name != 0)
strncpy(dstchannel,newchannel->originateprofile->chan_name,strlen(newchannel->originateprofile->chan_name));
} }
else else
{ {
if (newchannel->callerprofile) { //originateprofile = switch_channel_get_originatee_profile(newchannel->channel->callerprofile);
if(newchannel->callerprofile->caller_id_number != 0) // Or were we maybe we were the caller?
strncpy(src,newchannel->callerprofile->caller_id_number,strlen(newchannel->callerprofile->caller_id_number)); if(newchannel->callerprofile->originatee_caller_profile)
if(newchannel->callerprofile->destination_number != 0) {
strncpy(dst,newchannel->callerprofile->destination_number,strlen(newchannel->callerprofile->destination_number)); originated = 1;
if (newchannel->callerprofile) {
if(newchannel->callerprofile->caller_id_number != 0)
strncpy(src,newchannel->callerprofile->caller_id_number,strlen(newchannel->callerprofile->caller_id_number));
if(newchannel->callerprofile->destination_number != 0)
strncpy(dst,newchannel->callerprofile->destination_number,strlen(newchannel->callerprofile->destination_number));
}
if(newchannel->callerprofile->originatee_caller_profile->chan_name != 0)
strncpy(dstchannel,newchannel->callerprofile->originatee_caller_profile->chan_name,strlen(newchannel->callerprofile->originatee_caller_profile->chan_name));
} }
if(newchannel->originateprofile && newchannel->originateprofile->chan_name != 0)
strncpy(dstchannel,newchannel->originateprofile->chan_name,strlen(newchannel->originateprofile->chan_name));
} }
strncpy(myuuid,newchannel->callerprofile->uuid,strlen(newchannel->callerprofile->uuid)); strncpy(myuuid,newchannel->callerprofile->uuid,strlen(newchannel->callerprofile->uuid));
@ -140,7 +150,15 @@ BaseCDR::BaseCDR(switch_mod_cdr_newchannel_t *newchannel)
if(switch_channel_test_flag(newchannel->channel,CF_ANSWERED)) if(switch_channel_test_flag(newchannel->channel,CF_ANSWERED))
{ {
disposition=1; disposition=1;
billusec = newchannel->timetable->hungup - newchannel->timetable->answered; if(callstartdate)
billusec = callenddate - callanswerdate;
else
billusec = callenddate - calltransferdate;
}
else if(switch_channel_test_flag(newchannel->channel,CF_TRANSFER))
{
disposition=1;
billusec = callenddate - calltransferdate;
} }
else else
{ {
@ -361,7 +379,7 @@ void BaseCDR::process_channel_variables(const std::list<std::string>& stringlist
/* For Emacs: /* For Emacs:
* Local Variables: * Local Variables:
* mode:c * mode:c++
* indent-tabs-mode:nil * indent-tabs-mode:nil
* tab-width:4 * tab-width:4
* c-basic-offset:4 * c-basic-offset:4

View File

@ -50,15 +50,23 @@ struct switch_mod_cdr_newchannel_t
{ {
switch_core_session_t *session; switch_core_session_t *session;
switch_channel_t *channel; switch_channel_t *channel;
switch_channel_timetable_t *timetable; //switch_channel_timetable_t *timetable;
switch_caller_extension_t *callerextension; switch_caller_extension_t *callerextension;
switch_caller_profile_t *callerprofile; switch_caller_profile_t *callerprofile;
switch_caller_profile_t *originateprofile; //switch_caller_profile_t *originateprofile;
bool originate; //bool originate;
}; };
enum switch_mod_cdr_sql_types_t { CDR_INTEGER,CDR_STRING,CDR_DECIMAL,CDR_DOUBLE,CDR_TINY }; enum switch_mod_cdr_sql_types_t { CDR_INTEGER,CDR_STRING,CDR_DECIMAL,CDR_DOUBLE,CDR_TINY };
#ifdef WIN32
#define STDCALL __stdcall
#else
#define STDCALL
#endif
typedef apr_status_t (STDCALL *modcdr_time_convert_t)(apr_time_exp_t*,apr_time_t);
class BaseCDR { class BaseCDR {
public: public:
BaseCDR(); BaseCDR();
@ -70,6 +78,7 @@ class BaseCDR {
virtual bool is_activated() = 0; virtual bool is_activated() = 0;
virtual void tempdump_record() = 0; virtual void tempdump_record() = 0;
virtual void reread_tempdumped_records() = 0; virtual void reread_tempdumped_records() = 0;
virtual std::string get_display_name() = 0; // Get the module name
protected: protected:
void parse_channel_variables_xconfig(std::string& unparsed,std::list<std::string>& chanvarslist,bool fixed); void parse_channel_variables_xconfig(std::string& unparsed,std::list<std::string>& chanvarslist,bool fixed);
void parse_channel_variables_xconfig(std::string& unparsed,std::list<std::string>& chanvarslist,std::vector<switch_mod_cdr_sql_types_t>& chanvars_fixed_types); // Typically used for SQL types void parse_channel_variables_xconfig(std::string& unparsed,std::list<std::string>& chanvarslist,std::vector<switch_mod_cdr_sql_types_t>& chanvars_fixed_types); // Typically used for SQL types
@ -78,6 +87,7 @@ class BaseCDR {
switch_time_t callstartdate; switch_time_t callstartdate;
switch_time_t callanswerdate; switch_time_t callanswerdate;
switch_time_t callenddate; switch_time_t callenddate;
switch_time_t calltransferdate;
switch_call_cause_t hangupcause; switch_call_cause_t hangupcause;
char *hangupcause_text; char *hangupcause_text;
char clid[80]; char clid[80];
@ -108,7 +118,7 @@ class BaseCDR {
/* For Emacs: /* For Emacs:
* Local Variables: * Local Variables:
* mode:c * mode:c++
* indent-tabs-mode:nil * indent-tabs-mode:nil
* tab-width:4 * tab-width:4
* c-basic-offset:4 * c-basic-offset:4

View File

@ -82,7 +82,7 @@ BaseRegistration::BaseRegistration(basecdr_creator creator)
/* For Emacs: /* For Emacs:
* Local Variables: * Local Variables:
* mode:c * mode:c++
* indent-tabs-mode:nil * indent-tabs-mode:nil
* tab-width:4 * tab-width:4
* c-basic-offset:4 * c-basic-offset:4

View File

@ -79,7 +79,7 @@ class BaseRegistration
/* For Emacs: /* For Emacs:
* Local Variables: * Local Variables:
* mode:c * mode:c++
* indent-tabs-mode:nil * indent-tabs-mode:nil
* tab-width:4 * tab-width:4
* c-basic-offset:4 * c-basic-offset:4

View File

@ -45,8 +45,9 @@ CDRContainer::CDRContainer(switch_memory_pool_t *module_pool)
// Create the APR threadsafe queue, though I don't know if this is the current memory pool. // Create the APR threadsafe queue, though I don't know if this is the current memory pool.
switch_queue_create(&cdrqueue,5224288, module_pool); switch_queue_create(&cdrqueue,5224288, module_pool);
char *configfile = "mod_cdr.conf"; queue_paused = 0;
switch_xml_t cfg, xml, settings, param;
strcpy(configfile,"mod_cdr.conf");
switch_mod_cdr_newchannel_t *newchannel; // = new switch_mod_cdr_newchannel_t; switch_mod_cdr_newchannel_t *newchannel; // = new switch_mod_cdr_newchannel_t;
newchannel = 0; newchannel = 0;
@ -91,6 +92,119 @@ CDRContainer::~CDRContainer()
switch_console_printf(SWITCH_CHANNEL_LOG,"mod_cdr shutdown gracefully."); switch_console_printf(SWITCH_CHANNEL_LOG,"mod_cdr shutdown gracefully.");
} }
#ifdef SWITCH_QUEUE_ENHANCED
void CDRContainer::reload(switch_stream_handle_t *stream)
{
// The queue can't be paused otherwise it will never be able to reload safely.
if(queue_paused)
{
stream->write_function(stream,"The queue is currently paused, resuming it.\n");
queue_resume(stream);
}
// Something tells me I still need to figure out what to do if there are items still in queue after reload that are no longer active in the configuration.
switch_queue_isempty(cdrqueue); // Waits for the queue to be empty
switch_mod_cdr_newchannel_t *newchannel; // = new switch_mod_cdr_newchannel_t;
newchannel = 0;
const char *err;
switch_xml_t xml_root;
if ((xml_root = switch_xml_open_root(1, &err))) {
switch_console_printf(SWITCH_CHANNEL_LOG,"Reloading the XML file...\n");
switch_xml_free(xml_root);
}
if (!(xml = switch_xml_open_cfg(configfile, &cfg, NULL)))
switch_console_printf(SWITCH_CHANNEL_LOG,"open of %s failed\n", configfile);
else
{
BaseRegistry& registry(BaseRegistry::get());
for(BaseRegistry::iterator it = registry.active_begin(); it != registry.active_end(); ++it)
{
basecdr_creator func = *it;
BaseCDR* _ptr = func(newchannel);
std::auto_ptr<BaseCDR> ptr(_ptr);
ptr->disconnect();
}
registry.reset_active();
for(BaseRegistry::iterator it = registry.begin(); it != registry.end(); ++it)
{
basecdr_creator func = *it;
BaseCDR* _ptr = func(newchannel);
std::auto_ptr<BaseCDR> ptr(_ptr);
ptr->connect(cfg,xml,settings,param);
if(ptr->is_activated())
registry.add_active(it);
}
}
switch_xml_free(xml);
switch_queue_unblockpop(cdrqueue);
switch_console_printf(SWITCH_CHANNEL_LOG,"mod_cdr configuration reloaded.");
}
void CDRContainer::queue_pause(switch_stream_handle_t *stream)
{
if(queue_paused)
stream->write_function(stream,"Queue is already paused.\n");
else
{
queue_paused = 1;
switch_queue_blockpop(cdrqueue);
stream->write_function(stream,"CDR queue is now paused. Beware that this can waste resources the longer you keep it paused.\n");
}
}
void CDRContainer::queue_resume(switch_stream_handle_t *stream)
{
if(!queue_paused)
stream->write_function(stream,"Queue is currently running, no need to resume it.\n");
else
{
queue_paused = 0;
switch_queue_unblockpop(cdrqueue);
stream->write_function(stream,"CDR queue has now resumed processing CDR records.\n");
}
}
#endif
void CDRContainer::active(switch_stream_handle_t *stream)
{
switch_mod_cdr_newchannel_t *newchannel; // = new switch_mod_cdr_newchannel_t;
newchannel = 0;
stream->write_function(stream,"The following mod_cdr logging backends are currently marked as active:\n");
BaseRegistry& registry(BaseRegistry::get());
for(BaseRegistry::iterator it = registry.active_begin(); it != registry.active_end(); ++it)
{
basecdr_creator func = *it;
BaseCDR* _ptr = func(newchannel);
std::auto_ptr<BaseCDR> ptr(_ptr);
stream->write_function(stream,"%s\n",ptr->get_display_name().c_str());
}
}
void CDRContainer::available(switch_stream_handle_t *stream)
{
switch_mod_cdr_newchannel_t *newchannel; // = new switch_mod_cdr_newchannel_t;
newchannel = 0;
stream->write_function(stream,"The following mod_cdr logging backends are currently avaible for use (providing you configure them):\n");
BaseRegistry& registry(BaseRegistry::get());
for(BaseRegistry::iterator it = registry.begin(); it != registry.end(); ++it)
{
basecdr_creator func = *it;
BaseCDR* _ptr = func(newchannel);
std::auto_ptr<BaseCDR> ptr(_ptr);
stream->write_function(stream,"%s\n",ptr->get_display_name().c_str());
}
}
void CDRContainer::add_cdr(switch_core_session_t *session) void CDRContainer::add_cdr(switch_core_session_t *session)
{ {
switch_mod_cdr_newchannel_t *newchannel = new switch_mod_cdr_newchannel_t; switch_mod_cdr_newchannel_t *newchannel = new switch_mod_cdr_newchannel_t;
@ -100,56 +214,25 @@ void CDRContainer::add_cdr(switch_core_session_t *session)
assert(newchannel->channel != 0); assert(newchannel->channel != 0);
newchannel->session = session; newchannel->session = session;
newchannel->timetable = switch_channel_get_timetable(newchannel->channel);
newchannel->callerextension = switch_channel_get_caller_extension(newchannel->channel); newchannel->callerextension = switch_channel_get_caller_extension(newchannel->channel);
newchannel->callerprofile = switch_channel_get_caller_profile(newchannel->channel); newchannel->callerprofile = switch_channel_get_caller_profile(newchannel->channel);
newchannel->originateprofile = switch_channel_get_originator_caller_profile(newchannel->channel);
BaseRegistry& registry(BaseRegistry::get()); while (newchannel->callerprofile)
for(BaseRegistry::iterator it = registry.active_begin(); it != registry.active_end(); ++it)
{ {
/* BaseRegistry& registry(BaseRegistry::get());
First time it might be originator profile, or originatee. Second and for(BaseRegistry::iterator it = registry.active_begin(); it != registry.active_end(); ++it)
after is always going to be originatee profile.
*/
basecdr_creator func = *it;
if(newchannel->originateprofile != 0 )
{ {
BaseCDR* newloggerobject = func(newchannel); basecdr_creator func = *it;
switch_console_printf(SWITCH_CHANNEL_LOG,"Adding a new logger object to the queue.\n");
switch_queue_push(cdrqueue,newloggerobject);
if(newchannel->timetable->next != 0)
{
newchannel->originateprofile = switch_channel_get_originatee_caller_profile(newchannel->channel);
newchannel->originate = 1;
}
}
else
{
newchannel->originateprofile = switch_channel_get_originatee_caller_profile(newchannel->channel);
newchannel->originate = 1;
BaseCDR* newloggerobject = func(newchannel); BaseCDR* newloggerobject = func(newchannel);
switch_console_printf(SWITCH_CHANNEL_LOG,"Adding a new logger object to the queue.\n"); switch_console_printf(SWITCH_CHANNEL_LOG,"Adding a new logger object to the queue.\n");
switch_queue_push(cdrqueue,newloggerobject); switch_queue_push(cdrqueue,newloggerobject);
} }
newchannel->callerprofile = newchannel->callerprofile->next;
while (newchannel->timetable->next != 0 && newchannel->callerextension->next != 0 && newchannel->callerprofile->next != 0 && newchannel->originateprofile->next != 0 ) if(newchannel->callerextension)
{
newchannel->timetable = newchannel->timetable->next;
newchannel->callerprofile = newchannel->callerprofile->next;
newchannel->callerextension = newchannel->callerextension->next; newchannel->callerextension = newchannel->callerextension->next;
newchannel->originateprofile = newchannel->originateprofile->next;
BaseCDR* newloggerobject = func(newchannel);
switch_console_printf(SWITCH_CHANNEL_LOG,"Adding a new logger object to the queue.\n");
switch_queue_push(cdrqueue,newloggerobject);
}
} }
delete newchannel; delete newchannel;
} }
@ -166,7 +249,7 @@ void CDRContainer::process_records()
/* For Emacs: /* For Emacs:
* Local Variables: * Local Variables:
* mode:c * mode:c++
* indent-tabs-mode:nil * indent-tabs-mode:nil
* tab-width:4 * tab-width:4
* c-basic-offset:4 * c-basic-offset:4

View File

@ -58,10 +58,20 @@ class CDRContainer {
~CDRContainer(); ~CDRContainer();
void add_cdr(switch_core_session_t *session); void add_cdr(switch_core_session_t *session);
void process_records(); void process_records();
#ifdef SWITCH_QUEUE_ENHANCED
void reload(switch_stream_handle_t *stream);
void queue_pause(switch_stream_handle_t *stream);
void queue_resume(switch_stream_handle_t *stream);
#endif
void active(switch_stream_handle_t *stream);
void available(switch_stream_handle_t *stream);
protected: protected:
private: private:
switch_xml_t cfg, xml, settings, param;
switch_queue_t *cdrqueue; switch_queue_t *cdrqueue;
std::string tempfilepath; std::string tempfilepath;
char configfile[13];
bool queue_paused;
}; };
#ifdef __cplusplus #ifdef __cplusplus
@ -72,7 +82,7 @@ class CDRContainer {
/* For Emacs: /* For Emacs:
* Local Variables: * Local Variables:
* mode:c * mode:c++
* indent-tabs-mode:nil * indent-tabs-mode:nil
* tab-width:4 * tab-width:4
* c-basic-offset:4 * c-basic-offset:4

View File

@ -51,23 +51,27 @@ CsvCDR::CsvCDR(switch_mod_cdr_newchannel_t *newchannel) : BaseCDR(newchannel)
{ {
memset(formattedcallstartdate,0,100); memset(formattedcallstartdate,0,100);
memset(formattedcallanswerdate,0,100); memset(formattedcallanswerdate,0,100);
memset(formattedcalltransferdate,0,100);
memset(formattedcallenddate,0,100); memset(formattedcallenddate,0,100);
if(newchannel != 0) if(newchannel != 0)
{ {
switch_time_exp_t tempcallstart, tempcallanswer, tempcallend; switch_time_exp_t tempcallstart, tempcallanswer, tempcalltransfer, tempcallend;
memset(&tempcallstart,0,sizeof(tempcallstart)); memset(&tempcallstart,0,sizeof(tempcallstart));
memset(&tempcalltransfer,0,sizeof(tempcalltransfer));
memset(&tempcallanswer,0,sizeof(tempcallanswer)); memset(&tempcallanswer,0,sizeof(tempcallanswer));
memset(&tempcallend,0,sizeof(tempcallend)); memset(&tempcallend,0,sizeof(tempcallend));
switch_time_exp_lt(&tempcallstart, callstartdate); convert_time(&tempcallstart, callstartdate);
switch_time_exp_lt(&tempcallanswer, callanswerdate); convert_time(&tempcallanswer, callanswerdate);
switch_time_exp_lt(&tempcallend, callenddate); convert_time(&tempcalltransfer, calltransferdate);
convert_time(&tempcallend, callenddate);
// Format the times // Format the times
apr_size_t retsizecsd, retsizecad, retsizeced; //csd == callstartdate, cad == callanswerdate, ced == callenddate, ceff == callenddate_forfile apr_size_t retsizecsd, retsizecad, retsizectd, retsizeced; //csd == callstartdate, cad == callanswerdate, ced == callenddate, ceff == callenddate_forfile
char format[] = "%Y-%m-%d-%H-%M-%S"; char format[] = "%Y-%m-%d %H:%M:%S";
switch_strftime(formattedcallstartdate,&retsizecsd,sizeof(formattedcallstartdate),format,&tempcallstart); switch_strftime(formattedcallstartdate,&retsizecsd,sizeof(formattedcallstartdate),format,&tempcallstart);
switch_strftime(formattedcallanswerdate,&retsizecad,sizeof(formattedcallanswerdate),format,&tempcallanswer); switch_strftime(formattedcallanswerdate,&retsizecad,sizeof(formattedcallanswerdate),format,&tempcallanswer);
switch_strftime(formattedcalltransferdate,&retsizectd,sizeof(formattedcalltransferdate),format,&tempcalltransfer);
switch_strftime(formattedcallenddate,&retsizeced,sizeof(formattedcallenddate),format,&tempcallend); switch_strftime(formattedcallenddate,&retsizeced,sizeof(formattedcallenddate),format,&tempcallend);
process_channel_variables(chanvars_fixed_list,newchannel->channel); process_channel_variables(chanvars_fixed_list,newchannel->channel);
@ -84,11 +88,13 @@ bool CsvCDR::activated=0;
bool CsvCDR::logchanvars=0; bool CsvCDR::logchanvars=0;
bool CsvCDR::connectionstate=0; bool CsvCDR::connectionstate=0;
bool CsvCDR::repeat_fixed_in_supp=0; bool CsvCDR::repeat_fixed_in_supp=0;
modcdr_time_convert_t CsvCDR::convert_time = switch_time_exp_lt;
std::string CsvCDR::outputfile_path; std::string CsvCDR::outputfile_path;
std::ofstream CsvCDR::outputfile; std::ofstream CsvCDR::outputfile;
std::ofstream::pos_type CsvCDR::filesize_limit = (10 * 1024 * 1024); // Default file size is 10MB std::ofstream::pos_type CsvCDR::filesize_limit = (10 * 1024 * 1024); // Default file size is 10MB
std::list<std::string> CsvCDR::chanvars_fixed_list; std::list<std::string> CsvCDR::chanvars_fixed_list;
std::list<std::string> CsvCDR::chanvars_supp_list; std::list<std::string> CsvCDR::chanvars_supp_list;
std::string CsvCDR::display_name = "CsvCDR - The simple comma separated values CDR logger";
void CsvCDR::connect(switch_xml_t& cfg, switch_xml_t& xml, switch_xml_t& settings, switch_xml_t& param) void CsvCDR::connect(switch_xml_t& cfg, switch_xml_t& xml, switch_xml_t& settings, switch_xml_t& param)
{ {
@ -153,6 +159,18 @@ void CsvCDR::connect(switch_xml_t& cfg, switch_xml_t& xml, switch_xml_t& setting
std::cout << "File size limit from config file is " << filesize_limit << " byte(s)." << std::endl; std::cout << "File size limit from config file is " << filesize_limit << " byte(s)." << std::endl;
} }
} }
else if(!strcmp(var,"timezone"))
{
if(!strcmp(val,"utc"))
convert_time = switch_time_exp_gmt;
else if(!strcmp(val,"local"))
convert_time = switch_time_exp_lt;
else
{
switch_console_printf(SWITCH_CHANNEL_LOG,"Invalid configuration parameter for timezone. Possible values are utc and local. You entered: %s\nDefaulting to local.\n",val);
convert_time = switch_time_exp_lt;
}
}
} }
if(count_config_params > 0) if(count_config_params > 0)
@ -220,8 +238,13 @@ bool CsvCDR::process_record()
// Format the call record and proceed from here... // Format the call record and proceed from here...
outputfile << "\"" << callstartdate << "\",\""; outputfile << "\"" << callstartdate << "\",\"";
outputfile << formattedcallstartdate << "\",\"";
outputfile << callanswerdate << "\",\""; outputfile << callanswerdate << "\",\"";
outputfile << formattedcallanswerdate << "\",\"";
outputfile << calltransferdate << "\",\"";
outputfile << formattedcalltransferdate << "\",\"";
outputfile << callenddate << "\",\""; outputfile << callenddate << "\",\"";
outputfile << formattedcallenddate << "\",\"";
outputfile << hangupcause_text << "\",\""; outputfile << hangupcause_text << "\",\"";
outputfile << hangupcause << "\",\""; outputfile << hangupcause << "\",\"";
outputfile << clid << "\",\""; outputfile << clid << "\",\"";
@ -277,9 +300,21 @@ void CsvCDR::reread_tempdumped_records()
} }
std::string CsvCDR::get_display_name()
{
return display_name;
}
void CsvCDR::disconnect() void CsvCDR::disconnect()
{ {
outputfile.close(); outputfile.close();
activated = 0;
logchanvars = 0;
repeat_fixed_in_supp = 0;
outputfile_path.clear();
chanvars_fixed_list.clear();
chanvars_supp_list.clear();
connectionstate = 0;
switch_console_printf(SWITCH_CHANNEL_LOG,"Shutting down CsvCDR... Done!"); switch_console_printf(SWITCH_CHANNEL_LOG,"Shutting down CsvCDR... Done!");
} }
@ -287,7 +322,7 @@ AUTO_REGISTER_BASECDR(CsvCDR);
/* For Emacs: /* For Emacs:
* Local Variables: * Local Variables:
* mode:c * mode:c++
* indent-tabs-mode:nil * indent-tabs-mode:nil
* tab-width:4 * tab-width:4
* c-basic-offset:4 * c-basic-offset:4

View File

@ -56,17 +56,21 @@ class CsvCDR : public BaseCDR {
virtual bool is_activated(); virtual bool is_activated();
virtual void tempdump_record(); virtual void tempdump_record();
virtual void reread_tempdumped_records(); virtual void reread_tempdumped_records();
virtual std::string get_display_name();
private: private:
static bool activated; // Is this module activated? static bool activated; // Is this module activated?
static bool connectionstate; // What is the status of the connection? static bool connectionstate; // What is the status of the connection?
static bool logchanvars; static bool logchanvars;
static modcdr_time_convert_t convert_time;
static bool repeat_fixed_in_supp; // Repeat the fixed chanvars in the supplemental? static bool repeat_fixed_in_supp; // Repeat the fixed chanvars in the supplemental?
static std::string outputfile_path; // The directory we'll dump these into static std::string outputfile_path; // The directory we'll dump these into
static std::list<std::string> chanvars_fixed_list; // Normally this would be used, but not in this class static std::list<std::string> chanvars_fixed_list; // Normally this would be used, but not in this class
static std::list<std::string> chanvars_supp_list; // This will hold the list for all chanvars here static std::list<std::string> chanvars_supp_list; // This will hold the list for all chanvars here
static std::string display_name;
char formattedcallstartdate[100]; char formattedcallstartdate[100];
char formattedcallanswerdate[100]; char formattedcallanswerdate[100];
char formattedcalltransferdate[100];
char formattedcallenddate[100]; char formattedcallenddate[100];
static std::ofstream outputfile; static std::ofstream outputfile;
static std::ofstream::pos_type filesize_limit; static std::ofstream::pos_type filesize_limit;
@ -78,7 +82,7 @@ class CsvCDR : public BaseCDR {
/* For Emacs: /* For Emacs:
* Local Variables: * Local Variables:
* mode:c * mode:c++
* indent-tabs-mode:nil * indent-tabs-mode:nil
* tab-width:4 * tab-width:4
* c-basic-offset:4 * c-basic-offset:4

View File

@ -44,6 +44,12 @@ static int RUNNING = 0;
static CDRContainer *newcdrcontainer; static CDRContainer *newcdrcontainer;
static switch_memory_pool_t *module_pool; static switch_memory_pool_t *module_pool;
static switch_status_t my_on_hangup(switch_core_session_t *session); static switch_status_t my_on_hangup(switch_core_session_t *session);
static switch_status_t modcdr_reload(char *dest, switch_core_session_t *isession, switch_stream_handle_t *stream);
static switch_status_t modcdr_queue_pause(char *dest, switch_core_session_t *isession, switch_stream_handle_t *stream);
static switch_status_t modcdr_queue_resume(char *dest, switch_core_session_t *isession, switch_stream_handle_t *stream);
static switch_status_t modcdr_show_active(char *dest, switch_core_session_t *isession, switch_stream_handle_t *stream);
static switch_status_t modcdr_show_available(char *dest, switch_core_session_t *isession, switch_stream_handle_t *stream);
static switch_thread_rwlock_t *cdr_rwlock;
/* Now begins the glue that will tie this into the system. /* Now begins the glue that will tie this into the system.
*/ */
@ -57,18 +63,61 @@ static const switch_state_handler_table_t state_handlers = {
/*.on_transmit */ NULL /*.on_transmit */ NULL
}; };
static switch_api_interface_t modcdr_show_available_api = {
/*.interface_name */ "modcdr_show_available",
/*.desc */ "Displays the currently compiled-in mod_cdr backend loggers.",
/*.function */ modcdr_show_available,
/*.syntax */ "modcdr_queue_show_available",
/*.next */ 0
};
static switch_api_interface_t modcdr_show_active_api = {
/*.interface_name */ "modcdr_show_active",
/*.desc */ "Displays the currently active mod_cdr backend loggers.",
/*.function */ modcdr_show_active,
/*.syntax */ "modcdr_queue_show_active",
/*.next */ &modcdr_show_available_api
};
static switch_api_interface_t modcdr_queue_resume_api = {
/*.interface_name */ "modcdr_queue_resume",
/*.desc */ "Manually resumes the popping of objects from the queue.",
/*.function */ modcdr_queue_resume,
/*.syntax */ "modcdr_queue_resume",
/*.next */ &modcdr_show_active_api
};
static switch_api_interface_t modcdr_queue_pause_api = {
/*.interface_name */ "modcdr_queue_pause",
/*.desc */ "Manually pauses the popping of objects from the queue. (DANGER: Can suck your memory away rather quickly.)",
/*.function */ modcdr_queue_pause,
/*.syntax */ "modcdr_queue_pause",
/*.next */ &modcdr_queue_resume_api
};
static switch_api_interface_t modcdr_reload_interface_api = {
/*.interface_name */ "modcdr_reload",
/*.desc */ "Reload mod_cdr's configuration",
/*.function */ modcdr_reload,
/*.syntax */ "modcdr_reload",
/*.next */ &modcdr_queue_pause_api
};
static const switch_loadable_module_interface_t cdr_module_interface = { static const switch_loadable_module_interface_t cdr_module_interface = {
/*.module_name */ modname, /*.module_name */ modname,
/*.endpoint_interface */ NULL, /*.endpoint_interface */ NULL,
/*.timer_interface */ NULL, /*.timer_interface */ NULL,
/*.dialplan_interface */ NULL, /*.dialplan_interface */ NULL,
/*.codec_interface */ NULL, /*.codec_interface */ NULL,
/*.application_interface */ NULL /*.application_interface */ NULL,
/* api_interface */ &modcdr_reload_interface_api
}; };
static switch_status_t my_on_hangup(switch_core_session_t *session) static switch_status_t my_on_hangup(switch_core_session_t *session)
{ {
switch_thread_rwlock_rdlock(cdr_rwlock);
newcdrcontainer->add_cdr(session); newcdrcontainer->add_cdr(session);
switch_thread_rwlock_unlock(cdr_rwlock);
return SWITCH_STATUS_SUCCESS; return SWITCH_STATUS_SUCCESS;
} }
@ -85,6 +134,7 @@ SWITCH_MOD_DECLARE(switch_status_t) switch_module_load(const switch_loadable_mod
return SWITCH_STATUS_TERM; return SWITCH_STATUS_TERM;
} }
switch_thread_rwlock_create(&cdr_rwlock,module_pool);
newcdrcontainer = new CDRContainer(module_pool); // Instantiates the new object, automatically loads config newcdrcontainer = new CDRContainer(module_pool); // Instantiates the new object, automatically loads config
/* indicate that the module should continue to be loaded */ /* indicate that the module should continue to be loaded */
@ -100,15 +150,61 @@ SWITCH_MOD_DECLARE(switch_status_t) switch_module_runtime(void)
return RUNNING ? SWITCH_STATUS_SUCCESS : SWITCH_STATUS_TERM; return RUNNING ? SWITCH_STATUS_SUCCESS : SWITCH_STATUS_TERM;
} }
static switch_status_t modcdr_reload(char *dest=0, switch_core_session_t *isession=0, switch_stream_handle_t *stream=0)
{
#ifdef SWITCH_QUEUE_ENHANCED
switch_thread_rwlock_wrlock(cdr_rwlock);
newcdrcontainer->reload(stream);
switch_thread_rwlock_unlock(cdr_rwlock);
stream->write_function(stream, "XML Reloaded and mod_cdr reloaded.\n");
#else
stream->write_function(stream,"modcdr_reload is only supported with the apr_queue_t enhancements and SWITCH_QUEUE_ENHANCED defined.\n");
#endif
return SWITCH_STATUS_SUCCESS;
}
static switch_status_t modcdr_queue_pause(char *dest=0, switch_core_session_t *isession=0, switch_stream_handle_t *stream=0)
{
#ifdef SWITCH_QUEUE_ENHANCED
newcdrcontainer->queue_pause(stream);
#else
stream->write_function(stream,"modcdr_queue_pause is only supported with the apr_queue_t enhancements and SWITCH_QUEUE_ENHANCED defined.\n");
#endif
return SWITCH_STATUS_SUCCESS;
}
static switch_status_t modcdr_queue_resume(char *dest=0, switch_core_session_t *isession=0, switch_stream_handle_t *stream=0)
{
#ifdef SWITCH_QUEUE_ENHANCED
newcdrcontainer->queue_resume(stream);
#else
stream->write_function(stream,"modcdr_queue_pause is only supported with the apr_queue_t enhancements and SWITCH_QUEUE_ENHANCED defined.\n");
#endif
return SWITCH_STATUS_SUCCESS;
}
static switch_status_t modcdr_show_active(char *dest=0, switch_core_session_t *isession=0, switch_stream_handle_t *stream=0)
{
newcdrcontainer->active(stream);
return SWITCH_STATUS_SUCCESS;
}
static switch_status_t modcdr_show_available(char *dest=0, switch_core_session_t *isession=0, switch_stream_handle_t *stream=0)
{
newcdrcontainer->available(stream);
return SWITCH_STATUS_SUCCESS;
}
SWITCH_MOD_DECLARE(switch_status_t) switch_module_shutdown(void) SWITCH_MOD_DECLARE(switch_status_t) switch_module_shutdown(void)
{ {
delete newcdrcontainer; delete newcdrcontainer;
switch_thread_rwlock_destroy(cdr_rwlock);
return SWITCH_STATUS_SUCCESS; return SWITCH_STATUS_SUCCESS;
} }
/* For Emacs: /* For Emacs:
* Local Variables: * Local Variables:
* mode:c * mode:c++
* indent-tabs-mode:nil * indent-tabs-mode:nil
* tab-width:4 * tab-width:4
* c-basic-offset:4 * c-basic-offset:4

View File

@ -93,6 +93,8 @@ char MysqlCDR::hostname[255] = "";
char MysqlCDR::username[255] =""; char MysqlCDR::username[255] ="";
char MysqlCDR::dbname[255] = ""; char MysqlCDR::dbname[255] = "";
char MysqlCDR::password[255] = ""; char MysqlCDR::password[255] = "";
modcdr_time_convert_t MysqlCDR::convert_time = switch_time_exp_lt;
std::string MysqlCDR::display_name = "MysqlCDR - The MySQL 4.1+ CDR logger using prepared statements";
//fstream MysqlCDR::tmpfile; //fstream MysqlCDR::tmpfile;
void MysqlCDR::connect(switch_xml_t& cfg, switch_xml_t& xml, switch_xml_t& settings, switch_xml_t& param) void MysqlCDR::connect(switch_xml_t& cfg, switch_xml_t& xml, switch_xml_t& settings, switch_xml_t& param)
@ -172,6 +174,18 @@ void MysqlCDR::connect(switch_xml_t& cfg, switch_xml_t& xml, switch_xml_t& setti
repeat_fixed_in_supp = 1; repeat_fixed_in_supp = 1;
} }
} }
else if(!strcmp(var,"timezone"))
{
if(!strcmp(val,"utc"))
convert_time = switch_time_exp_gmt;
else if(!strcmp(val,"local"))
convert_time = switch_time_exp_lt;
else
{
switch_console_printf(SWITCH_CHANNEL_LOG,"Invalid configuration parameter for timezone. Possible values are utc and local. You entered: %s\nDefaulting to local.\n",val);
convert_time = switch_time_exp_lt;
}
}
} }
if (count_config_params==4) if (count_config_params==4)
@ -181,7 +195,7 @@ void MysqlCDR::connect(switch_xml_t& cfg, switch_xml_t& xml, switch_xml_t& setti
if(activated) if(activated)
{ {
tmp_sql_query = "INSERT INTO freeswitchcdr (callstartdate,callanswerdate,callenddate,originated,clid,src,dst,ani,aniii,dialplan,myuuid,destuuid,srcchannel,dstchannel,lastapp,lastdata,billusec,disposition,hangupcause,amaflags"; tmp_sql_query = "INSERT INTO freeswitchcdr (callstartdate,callanswerdate,calltransferdate,callenddate,originated,clid,src,dst,ani,aniii,dialplan,myuuid,destuuid,srcchannel,dstchannel,lastapp,lastdata,billusec,disposition,hangupcause,amaflags";
int items_appended = 0; int items_appended = 0;
@ -199,7 +213,7 @@ void MysqlCDR::connect(switch_xml_t& cfg, switch_xml_t& xml, switch_xml_t& setti
} }
} }
tmp_sql_query.append(") VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?"); tmp_sql_query.append(") VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?");
if(chanvars_fixed_list.size() > 0 ) if(chanvars_fixed_list.size() > 0 )
{ {
@ -299,25 +313,34 @@ void MysqlCDR::reread_tempdumped_records()
} }
std::string MysqlCDR::get_display_name()
{
return display_name;
}
bool MysqlCDR::process_record() bool MysqlCDR::process_record()
{ {
switch_time_exp_t tm1,tm2,tm3; // One for call start switch_time_exp_t tm1,tm2,tm3,tm4; // One for call start, answer, transfer, and end
memset(&tm1,0,sizeof(tm1)); memset(&tm1,0,sizeof(tm1));
memset(&tm2,0,sizeof(tm2)); memset(&tm2,0,sizeof(tm2));
memset(&tm3,0,sizeof(tm3)); memset(&tm3,0,sizeof(tm3));
memset(&tm4,0,sizeof(tm4));
switch_time_exp_lt(&tm1,callstartdate); convert_time(&tm1,callstartdate);
switch_time_exp_lt(&tm2,callanswerdate); convert_time(&tm2,callanswerdate);
switch_time_exp_lt(&tm3,callenddate); convert_time(&tm3,calltransferdate);
convert_time(&tm4,callenddate);
set_mysql_time(tm1,my_callstartdate); set_mysql_time(tm1,my_callstartdate);
set_mysql_time(tm2,my_callanswerdate); set_mysql_time(tm2,my_callanswerdate);
set_mysql_time(tm3,my_callenddate); set_mysql_time(tm3,my_calltransferdate);
set_mysql_time(tm4,my_calltransferdate);
// Why is this out of order? I don't know, it doesn't make sense.
add_parameter(my_callstartdate,MYSQL_TYPE_DATETIME); add_parameter(my_callstartdate,MYSQL_TYPE_DATETIME);
add_parameter(my_callanswerdate,MYSQL_TYPE_DATETIME); add_parameter(my_callanswerdate,MYSQL_TYPE_DATETIME);
add_parameter(my_callenddate,MYSQL_TYPE_DATETIME); add_parameter(my_callenddate,MYSQL_TYPE_DATETIME);
add_parameter(my_calltransferdate,MYSQL_TYPE_DATETIME);
add_parameter(originated,MYSQL_TYPE_TINY); add_parameter(originated,MYSQL_TYPE_TINY);
add_string_parameter(clid,clid_length,MYSQL_TYPE_VAR_STRING,0); add_string_parameter(clid,clid_length,MYSQL_TYPE_VAR_STRING,0);
@ -583,6 +606,11 @@ void MysqlCDR::disconnect()
chanvars_supp_list.clear(); chanvars_supp_list.clear();
chanvars_fixed_types.clear(); chanvars_fixed_types.clear();
connectionstate = 0; connectionstate = 0;
memset(hostname,0,255);
memset(username,0,255);
memset(password,0,255);
memset(dbname,0,255);
memset(sql_query,0,1024);
tmp_sql_query.clear(); tmp_sql_query.clear();
} }
@ -628,7 +656,7 @@ AUTO_REGISTER_BASECDR(MysqlCDR);
/* For Emacs: /* For Emacs:
* Local Variables: * Local Variables:
* mode:c * mode:c++
* indent-tabs-mode:nil * indent-tabs-mode:nil
* tab-width:4 * tab-width:4
* c-basic-offset:4 * c-basic-offset:4

View File

@ -53,6 +53,7 @@ class MysqlCDR : public BaseCDR {
virtual bool is_activated(); virtual bool is_activated();
virtual void tempdump_record(); virtual void tempdump_record();
virtual void reread_tempdumped_records(); virtual void reread_tempdumped_records();
virtual std::string get_display_name();
private: private:
static bool activated; static bool activated;
@ -72,11 +73,14 @@ class MysqlCDR : public BaseCDR {
static char username[255]; static char username[255];
static char dbname[255]; static char dbname[255];
static char password[255]; static char password[255];
static modcdr_time_convert_t convert_time;
static std::string display_name;
//static fstream tmpfile; //static fstream tmpfile;
std::vector<MYSQL_BIND> bindme; std::vector<MYSQL_BIND> bindme;
//MYSQL_BIND *bindme; //MYSQL_BIND *bindme;
MYSQL_TIME my_callstartdate; MYSQL_TIME my_callstartdate;
MYSQL_TIME my_callanswerdate; MYSQL_TIME my_callanswerdate;
MYSQL_TIME my_calltransferdate;
MYSQL_TIME my_callenddate; MYSQL_TIME my_callenddate;
// Why all these long unsigned int's? MySQL's prep statement API expects these to actually exist and not just be params passed to the function calls. The are to measure the length of actual data in the char* arrays. // Why all these long unsigned int's? MySQL's prep statement API expects these to actually exist and not just be params passed to the function calls. The are to measure the length of actual data in the char* arrays.
long unsigned int clid_length; long unsigned int clid_length;
@ -102,7 +106,7 @@ class MysqlCDR : public BaseCDR {
/* For Emacs: /* For Emacs:
* Local Variables: * Local Variables:
* mode:c * mode:c++
* indent-tabs-mode:nil * indent-tabs-mode:nil
* tab-width:4 * tab-width:4
* c-basic-offset:4 * c-basic-offset:4

View File

@ -0,0 +1,641 @@
/*
* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application Call Detail Recorder module
* Copyright 2006, Author: Yossi Neiman of Cartis Solutions, Inc. <freeswitch AT cartissolutions.com>
*
* 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 Call Detail Recorder module
*
* The Initial Developer of the Original Code is
* Yossi Neiman <freeswitch AT cartissolutions.com>
* Portions created by the Initial Developer are Copyright (C)
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Yossi Neiman <freeswitch AT cartissolutions.com>
*
* Description: This C++ source file describes the OdbcCDR class which handles formatting a CDR out to
* an ODBC backend using prepared statements.
*
* odbccdr.cpp
*
*/
#include "odbccdr.h"
OdbcCDR::OdbcCDR() : BaseCDR()
{
}
OdbcCDR::OdbcCDR(switch_mod_cdr_newchannel_t *newchannel) : BaseCDR(newchannel)
{
if(newchannel != 0)
{
switch_time_exp_t tempcallstart, tempcallanswer, tempcalltransfer, tempcallend;
memset(&tempcallstart,0,sizeof(tempcallstart));
memset(&tempcallanswer,0,sizeof(tempcallanswer));
memset(&tempcalltransfer,0,sizeof(tempcalltransfer));
memset(&tempcallend,0,sizeof(tempcallend));
convert_time(&tempcallstart, callstartdate);
convert_time(&tempcallanswer, callanswerdate);
convert_time(&tempcalltransfer, calltransferdate);
convert_time(&tempcallend, callenddate);
// Format the times
switch_size_t retsizecsd, retsizecad, retsizectd, retsizeced; //csd == callstartdate, cad == callanswerdate, ced == callenddate, ceff == callenddate_forfile
char format[] = "%Y-%m-%d %T";
switch_strftime(odbc_callstartdate,&retsizecsd,sizeof(odbc_callstartdate),format,&tempcallstart);
switch_strftime(odbc_callanswerdate,&retsizecad,sizeof(odbc_callanswerdate),format,&tempcallanswer);
switch_strftime(odbc_calltransferdate,&retsizectd,sizeof(odbc_calltransferdate),format,&tempcalltransfer);
switch_strftime(odbc_callenddate,&retsizeced,sizeof(odbc_callenddate),format,&tempcallend);
if(chanvars_fixed_list.size() > 0)
process_channel_variables(chanvars_fixed_list,newchannel->channel);
if(chanvars_supp_list.size() > 0)
process_channel_variables(chanvars_supp_list,chanvars_fixed_list,newchannel->channel,repeat_fixed_in_supp);
}
}
OdbcCDR::~OdbcCDR()
{
}
bool OdbcCDR::connectionstate = 0;
bool OdbcCDR::logchanvars = 0;
bool OdbcCDR::repeat_fixed_in_supp = 0;
std::string OdbCDR::display_name = "OdbcCDR - The Open Database Backend Connector CDR logger backend";
modcdr_time_convert_t OdbcCDR::convert_time;
std::list<std::string> OdbcCDR::chanvars_fixed_list;
std::list<std::string> OdbcCDR::chanvars_supp_list;
std::vector<switch_mod_cdr_sql_types_t> OdbcCDR::chanvars_fixed_types;
bool OdbcCDR::activated = 0;
char OdbcCDR::sql_query[1024] = "";
std::string OdbcCDR::tmp_sql_query;
char OdbcCDR::sql_query_chanvars[355] = "";
char OdbcCDR::sql_query_ping[10] = "";
SQLHENV OdbcCDR::ODBC_env=0;
SQLHDBC OdbcCDR::ODBC_con=0;
SQLHSTMT OdbcCDR::ODBC_stmt=0;
SQLHSTMT OdbcCDR::ODBC_stmt_chanvars = 0;
SQLHSTMT OdbcCDR::ODBC_stmt_ping = 0;
char OdbcCDR::dsn[255] = "";
char OdbcCDR::hostname[255] = "";
char OdbcCDR::username[255] ="";
char OdbcCDR::dbname[255] = "";
char OdbcCDR::password[255] = "";
char OdbcCDR::tablename[255] = "";
char OdbcCDR::tablename_chanvars[255] = "";
//fstream OdbcCDR::tmpfile;
void OdbcCDR::connect(switch_xml_t& cfg, switch_xml_t& xml, switch_xml_t& settings, switch_xml_t& param)
{
if(activated)
disconnect();
activated = 0; // Set it as inactive initially
connectionstate = 0; // Initialize it to false to show that we aren't yet connected.
int count_config_params = 0; // Need to make sure all params are set before we load
if ((settings = switch_xml_child(cfg, "odbccdr")))
{
for (param = switch_xml_child(settings, "param"); param; param = param->next)
{
char *var = (char *) switch_xml_attr_soft(param, "name");
char *val = (char *) switch_xml_attr_soft(param, "value");
if (!strcmp(var, "dsn"))
{
strncpy(dsn,val,strlen(val));
count_config_params+=4;
}
else if (!strcmp(var, "hostname"))
{
if(val != 0)
{
strncpy(hostname,val,strlen(val));
count_config_params++;
}
}
else if (!strcmp(var, "username"))
{
if(val != 0)
{
strncpy(username,val,strlen(val));
count_config_params++;
}
}
else if (!strcmp(var,"password"))
{
if(val != 0)
{
strncpy(password,val,strlen(val));
count_config_params++;
}
}
else if(!strcmp(var,"dbname"))
{
if(val != 0)
{
strncpy(dbname,val,strlen(val));
count_config_params++;
}
}
else if(!strcmp(var,"chanvars_fixed"))
{
std::string unparsed;
unparsed = val;
if(unparsed.size() > 0)
{
parse_channel_variables_xconfig(unparsed,chanvars_fixed_list,chanvars_fixed_types);
//logchanvars=1;
}
}
else if(!strcmp(var,"chanvars_supp"))
{
if(val != 0)
{
std::string unparsed = val;
bool fixed = 0;
logchanvars = 1;
parse_channel_variables_xconfig(unparsed,chanvars_supp_list,fixed);
}
}
else if(!strcmp(var,"chanvars_supp_repeat_fixed"))
{
if(val != 0)
{
std::string repeat = val;
if(repeat == "Y" || repeat == "y" || repeat == "1")
repeat_fixed_in_supp = 1;
}
}
else if(!strcmp(var,"main_db_table"))
{
if(val != 0)
strncpy(tablename,val,strlen(val));
}
else if(!strcmp(var,"supp_chanvars_db_table"))
{
if(val != 0)
strncpy(tablename_chanvars,val,strlen(val));
}
else if(!strcmp(var,"timezone"))
{
if(!strcmp(val,"utc"))
convert_time = switch_time_exp_gmt;
else if(!strcmp(val,"local"))
convert_time = switch_time_exp_lt;
else
{
switch_console_printf(SWITCH_CHANNEL_LOG,"Invalid configuration parameter for timezone. Possible values are utc and local. You entered: %s\nDefaulting to local.\n",val);
convert_time = switch_time_exp_lt;
}
}
if(strlen(tablename) == 0)
strncpy(tablename,"freeswitchcdr",13);
if(strlen(tablename_chanvars) && logchanvars)
strncpy(tablename_chanvars,"chanvars",8);
}
if (count_config_params==4)
activated = 1;
else
switch_console_printf(SWITCH_CHANNEL_LOG,"You did not specify the minimum parameters for using this module. You must specify a DSN,hostname, username, password, and database to use OdbcCDR. You only supplied %d parameters.\n",count_config_params);
if(activated)
{
tmp_sql_query = "INSERT INTO ";
tmp_sql_query.append(tablename);
tmp_sql_query.append(" (callstartdate,callanswerdate,calltransferdate,callenddate,originated,clid,src,dst,ani,aniii,dialplan,myuuid,destuuid,srcchannel,dstchannel,lastapp,lastdata,billusec,disposition,hangupcause,amaflags");
int items_appended = 0;
if(chanvars_fixed_list.size() > 0 )
{
std::list<std::string>::iterator iItr, iEnd;
for(iItr = chanvars_fixed_list.begin(), iEnd = chanvars_fixed_list.end(); iItr != iEnd; iItr++)
{
if(iItr->size() > 0)
{
tmp_sql_query.append(",");
tmp_sql_query.append(*iItr);
items_appended++;
}
}
}
tmp_sql_query.append(") VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?");
if(chanvars_fixed_list.size() > 0 )
{
for(int i = 0; i < items_appended; i++)
tmp_sql_query.append(",?");
}
tmp_sql_query.append(")");
std::string tempsql_query_chanvars = "INSERT INTO ";
tempsql_query_chanvars.append(tablename_chanvars);
tempsql_query_chanvars.append("(callid,varname,varvalue) VALUES(?,?,?)");
memset(sql_query_chanvars,0,355);
strncpy(sql_query_chanvars,tempsql_query_chanvars.c_str(),tempsql_query_chanvars.size());
strncpy(sql_query,tmp_sql_query.c_str(),tmp_sql_query.size());
strncpy(sql_query_ping,"SELECT 1;",9);
connect_to_database();
}
}
}
void OdbcCDR::connect_to_database()
{
if(connectionstate)
disconnect_stage_1();
int ODBC_res;
if (ODBC_env == SQL_NULL_HANDLE || connectionstate == 0)
{
ODBC_res = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &ODBC_env);
if ((ODBC_res != SQL_SUCCESS) && (ODBC_res != SQL_SUCCESS_WITH_INFO))
{
switch_console_printf(SWITCH_CHANNEL_LOG,"Error allocating a new ODBC handle.\n");
connectionstate = 0;
}
}
ODBC_res = SQLSetEnvAttr(ODBC_env, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3, 0);
if ((ODBC_res != SQL_SUCCESS) && (ODBC_res != SQL_SUCCESS_WITH_INFO))
{
switch_console_printf(SWITCH_CHANNEL_LOG,"Error with ODBCSetEnv\n");
SQLFreeHandle(SQL_HANDLE_ENV, ODBC_env);
connectionstate = 0;
}
ODBC_res = SQLAllocHandle(SQL_HANDLE_DBC, ODBC_env, &ODBC_con);
if ((ODBC_res != SQL_SUCCESS) && (ODBC_res != SQL_SUCCESS_WITH_INFO))
{
switch_console_printf(SWITCH_CHANNEL_LOG,"Error AllocHDB %d\n",ODBC_res);
SQLFreeHandle(SQL_HANDLE_ENV, ODBC_env);
connectionstate = 0;
}
SQLSetConnectAttr(ODBC_con, SQL_LOGIN_TIMEOUT, (SQLPOINTER *)10, 0);
/* Note that the username and password could be NULL here, but that is allowed in ODBC.
In this case, the default username and password will be used from odbc.conf */
ODBC_res = SQLConnect(ODBC_con, (SQLCHAR*)dsn, SQL_NTS, (SQLCHAR*)username, SQL_NTS, (SQLCHAR*)password, SQL_NTS);
if ((ODBC_res != SQL_SUCCESS) && (ODBC_res != SQL_SUCCESS_WITH_INFO))
{
switch_console_printf(SWITCH_CHANNEL_LOG,"Error connecting to the ODBC database on %d\n",ODBC_res);
SQLFreeHandle(SQL_HANDLE_DBC, ODBC_con);
SQLFreeHandle(SQL_HANDLE_ENV, ODBC_env);
connectionstate = 0;
}
else
{
switch_console_printf(SWITCH_CHANNEL_LOG,"Connected to %s\n", dsn);
connectionstate = 1;
}
// Turn off autocommit and have it preserve the cursors even after commit
SQLSetConnectAttr(ODBC_con, SQL_AUTOCOMMIT_OFF, NULL, 0);
SQLSetConnectAttr(ODBC_con, SQL_CB_PRESERVE, NULL, 0);
ODBC_res = SQLAllocHandle(SQL_HANDLE_STMT, ODBC_con, &ODBC_stmt);
if ((ODBC_res != SQL_SUCCESS) && (ODBC_res != SQL_SUCCESS_WITH_INFO))
{
switch_console_printf(SWITCH_CHANNEL_LOG,"Failure in allocating a prepared statement %d\n", ODBC_res);
SQLFreeHandle(SQL_HANDLE_STMT, ODBC_stmt);
}
ODBC_res = SQLPrepare(ODBC_stmt, (unsigned char *)sql_query, SQL_NTS);
if ((ODBC_res != SQL_SUCCESS) && (ODBC_res != SQL_SUCCESS_WITH_INFO))
{
switch_console_printf(SWITCH_CHANNEL_LOG,"Error in preparing a statement: %d\n", ODBC_res);
SQLFreeHandle(SQL_HANDLE_STMT, ODBC_stmt);
}
if(logchanvars)
{
ODBC_res = SQLAllocHandle(SQL_HANDLE_STMT, ODBC_con, &ODBC_stmt_chanvars);
if ((ODBC_res != SQL_SUCCESS) && (ODBC_res != SQL_SUCCESS_WITH_INFO))
{
switch_console_printf(SWITCH_CHANNEL_LOG,"Failure in allocating a prepared statement %d\n", ODBC_res);
SQLFreeHandle(SQL_HANDLE_STMT, ODBC_stmt_chanvars);
}
ODBC_res = SQLPrepare(ODBC_stmt_chanvars, (unsigned char *)sql_query_chanvars, SQL_NTS);
if ((ODBC_res != SQL_SUCCESS) && (ODBC_res != SQL_SUCCESS_WITH_INFO))
{
switch_console_printf(SWITCH_CHANNEL_LOG,"Error in preparing a statement: %d\n", ODBC_res);
SQLFreeHandle(SQL_HANDLE_STMT, ODBC_stmt);
}
}
ODBC_res = SQLAllocHandle(SQL_HANDLE_STMT, ODBC_con, &ODBC_stmt_ping);
if ((ODBC_res != SQL_SUCCESS) && (ODBC_res != SQL_SUCCESS_WITH_INFO))
{
switch_console_printf(SWITCH_CHANNEL_LOG,"Failure in allocating a prepared statement %d\n", ODBC_res);
SQLFreeHandle(SQL_HANDLE_STMT, ODBC_stmt_ping);
}
ODBC_res = SQLPrepare(ODBC_stmt_ping, (unsigned char *)sql_query_ping, SQL_NTS);
if ((ODBC_res != SQL_SUCCESS) && (ODBC_res != SQL_SUCCESS_WITH_INFO))
{
switch_console_printf(SWITCH_CHANNEL_LOG,"Error in preparing a statement: %d\n", ODBC_res);
SQLFreeHandle(SQL_HANDLE_STMT, ODBC_stmt_ping);
}
if (ODBC_res == SQL_SUCCESS || ODBC_res == SQL_SUCCESS_WITH_INFO)
connectionstate = 1;
}
bool OdbcCDR::is_activated()
{
return activated;
}
void OdbcCDR::tempdump_record()
{
}
void OdbcCDR::reread_tempdumped_records()
{
}
bool OdbcCDR::process_record()
{
for(int count=0, ODBC_res=-1; (ODBC_res != SQL_SUCCESS || ODBC_res != SQL_SUCCESS_WITH_INFO) && count < 5; count++)
{
int ODBC_res = SQLExecute(ODBC_stmt_ping);
SQLFreeStmt(ODBC_stmt_ping,SQL_CLOSE);
if(ODBC_res != SQL_SUCCESS && ODBC_res != SQL_SUCCESS_WITH_INFO)
{
// Try to reconnect and reprepare
switch_console_printf(SWITCH_CHANNEL_LOG,"Error pinging the ODBC backend. Attempt #%d to reconnect.\n",count+1);
connect_to_database();
}
}
int index = 1;
SQLBindParameter(ODBC_stmt, index++, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(odbc_callstartdate), 0, odbc_callstartdate, 0, 0);
SQLBindParameter(ODBC_stmt, index++, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(odbc_callanswerdate), 0, odbc_callanswerdate, 0, 0);
SQLBindParameter(ODBC_stmt, index++, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(odbc_calltransferdate), 0, odbc_calltransferdate, 0, 0);
SQLBindParameter(ODBC_stmt, index++, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(odbc_callenddate), 0, odbc_callenddate, 0, 0);
SQLBindParameter(ODBC_stmt, index++, SQL_PARAM_INPUT, SQL_C_SSHORT, SQL_TINYINT,0, 0,&originated, 0, 0);
SQLBindParameter(ODBC_stmt, index++, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_VARCHAR, sizeof(clid), 0, clid, 0, 0);
SQLBindParameter(ODBC_stmt, index++, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_VARCHAR, sizeof(src), 0, src, 0, 0);
SQLBindParameter(ODBC_stmt, index++, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_VARCHAR, sizeof(dst), 0, dst, 0, 0);
SQLBindParameter(ODBC_stmt, index++, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_VARCHAR, sizeof(ani), 0, ani, 0, 0);
SQLBindParameter(ODBC_stmt, index++, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_VARCHAR, sizeof(aniii), 0, aniii, 0, 0);
SQLBindParameter(ODBC_stmt, index++, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_VARCHAR, sizeof(dialplan), 0, dialplan, 0, 0);
SQLBindParameter(ODBC_stmt, index++, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(myuuid), 0, myuuid, 0, 0);
SQLBindParameter(ODBC_stmt, index++, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(destuuid), 0, destuuid, 0, 0);
SQLBindParameter(ODBC_stmt, index++, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_VARCHAR, sizeof(srcchannel), 0, srcchannel, 0, 0);
SQLBindParameter(ODBC_stmt, index++, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_VARCHAR, sizeof(dstchannel), 0, dstchannel, 0, 0);
SQLBindParameter(ODBC_stmt, index++, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_VARCHAR, sizeof(lastapp), 0, lastapp, 0, 0);
SQLBindParameter(ODBC_stmt, index++, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_VARCHAR, sizeof(lastdata), 0, lastdata, 0, 0);
SQLBindParameter(ODBC_stmt, index++, SQL_PARAM_INPUT, SQL_C_UBIGINT, SQL_BIGINT, 0, 0, &billusec, 0, 0);
SQLBindParameter(ODBC_stmt, index++, SQL_PARAM_INPUT, SQL_C_SSHORT, SQL_TINYINT, 0, 0, &disposition, 0, 0);
SQLBindParameter(ODBC_stmt, index++, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, 0, 0, &hangupcause, 0, 0);
SQLBindParameter(ODBC_stmt, index++, SQL_PARAM_INPUT, SQL_C_SSHORT, SQL_TINYINT, 0, 0, &amaflags, 0, 0);
std::list<void*> temp_chanvars_holder; // This is used for any fixed chanvars, as we don't want things out of scope
if(chanvars_fixed_list.size() > 0)
{
switch_size_t i = 0; // temporary variable, i is current spot on the string of types
std::list<std::pair<std::string,std::string> >::iterator iItr, iEnd;
for(iItr = chanvars_fixed.begin(), iEnd = chanvars_fixed.end(); iItr != iEnd; iItr++)
{
switch(chanvars_fixed_types[i])
{
case CDR_INTEGER:
{
int* x = new int;
*x = 0;
if(iItr->second.size() > 0)
{
std::istringstream istring(iItr->second);
istring >> *x;
}
temp_chanvars_holder.push_back(x);
SQLBindParameter(ODBC_stmt, index++, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_CHAR, 0, 0, x, 0, 0);
break;
}
case CDR_DOUBLE:
{
double* x = new double;
*x = 0;
if(iItr->second.size() > 0)
{
std::istringstream istring(iItr->second);
istring >> *x;
}
temp_chanvars_holder.push_back(x);
SQLBindParameter(ODBC_stmt, index++, SQL_PARAM_INPUT, SQL_C_DOUBLE, SQL_DOUBLE, 0, 0, x, 0, 0);
break;
}
case CDR_TINY:
{
short* x = new short;
*x = 0;
if(iItr->second.size() > 0)
{
std::istringstream istring(iItr->second);
istring >> *x;
}
temp_chanvars_holder.push_back(x);
SQLBindParameter(ODBC_stmt, index++, SQL_PARAM_INPUT, SQL_C_SSHORT, SQL_TINYINT, 0, 0, x, 0, 0);
break;
}
case CDR_STRING:
{
uint64_t stringlength = iItr->second.size();
char* x = new char[(stringlength+1)];
strncpy(x,iItr->second.c_str(),stringlength);
temp_chanvars_holder.push_back(x);
SQLBindParameter(ODBC_stmt, index++, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_VARCHAR, 0, 0, x, 0, 0);
break;
}
case CDR_DECIMAL:
{
uint64_t stringlength = iItr->second.size();
char* x = new char[(stringlength+1)];
strncpy(x,iItr->second.c_str(),stringlength);
SQLBindParameter(ODBC_stmt, index++, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_DECIMAL, 0, 0, x, 0, 0);
temp_chanvars_holder.push_back(x);
break;
}
default:
switch_console_printf(SWITCH_CHANNEL_LOG,"We should not get to this point in this switch/case statement.\n");
}
i++;
}
}
SQLExecute(ODBC_stmt);
if(logchanvars && chanvars_supp.size() > 0 && errorstate == 0)
{
/* Since autoincrement is a bane of the SQL rdbms industry, we have to use the myuuid
instead to link the tables. Unfortunately, this is very wasteful of space, and not
highly recommended to use on heavily loaded systems.
*/
std::map<std::string,std::string>::iterator iItr,iBeg,iEnd;
iEnd = chanvars_supp.end();
for(iItr = chanvars_supp.begin(); iItr != iEnd; iItr++)
{
std::vector<char> tempfirstvector(iItr->first.begin(), iItr->first.end());
tempfirstvector.push_back('\0');
char* varname_temp = &tempfirstvector[0];
std::vector<char> tempsecondvector(iItr->second.begin(), iItr->second.end());
tempsecondvector.push_back('\0');
char* varvalue_temp = &tempsecondvector[0];
SQLBindParameter(ODBC_stmt_chanvars, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(myuuid), 0, myuuid, 0, 0);
SQLBindParameter(ODBC_stmt_chanvars, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_VARCHAR, iItr->first.size(), 0, varname_temp, 0, 0);
SQLBindParameter(ODBC_stmt_chanvars, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_VARCHAR, iItr->second.size(), 0, varvalue_temp, 0, 0);
int ODBC_res_chanvars = SQLExecute(ODBC_stmt_chanvars);
if(ODBC_res_chanvars != SQL_SUCCESS && ODBC_res_chanvars != SQL_SUCCESS_WITH_INFO)
errorstate = 0;
else
errorstate = 1;
}
}
if(errorstate)
SQLEndTran(SQL_HANDLE_DBC,ODBC_con,SQL_ROLLBACK);
else
SQLEndTran(SQL_HANDLE_DBC,ODBC_con,SQL_COMMIT);
if(temp_chanvars_holder.size() > 0)
{
std::string::size_type i = 0, j = chanvars_fixed_types.size();
for(; i < j ; i++)
{
switch(chanvars_fixed_types[i])
{
case CDR_STRING:
case CDR_DECIMAL:
{
char* tempstring = (char*) temp_chanvars_holder.front();
temp_chanvars_holder.pop_front();
delete [] tempstring;
break;
}
case CDR_INTEGER:
{
int* tempint = (int*) temp_chanvars_holder.front();
temp_chanvars_holder.pop_front();
delete tempint;
break;
}
case CDR_DOUBLE:
{
double* tempdouble = (double*) temp_chanvars_holder.front();
temp_chanvars_holder.pop_front();
delete tempdouble;
break;
}
case CDR_TINY:
{
short* tempshort = (short*) temp_chanvars_holder.front();
temp_chanvars_holder.pop_front();
delete tempshort;
break;
}
default:
switch_console_printf(SWITCH_CHANNEL_LOG,"We should not get to this point in this switch/case statement.\n");
}
}
}
return 1;
}
void OdbcCDR::disconnect()
{
disconnect_stage_1();
activated = 0;
logchanvars = 0;
chanvars_fixed_list.clear();
chanvars_supp_list.clear();
chanvars_fixed_types.clear();
connectionstate = 0;
memset(hostname,0,255);
memset(username,0,255);
memset(password,0,255);
memset(dbname,0,255);
memset(sql_query,0,1024);
tmp_sql_query.clear();
//tmp_sql_query_chanvars.clear();
}
void OdbcCDR::disconnect_stage_1()
{
SQLFreeStmt(ODBC_stmt,SQL_UNBIND);
if(logchanvars)
SQLFreeStmt(ODBC_stmt_chanvars,SQL_UNBIND);
SQLDisconnect(ODBC_con);
SQLFreeHandle(SQL_HANDLE_DBC, ODBC_con);
SQLFreeHandle(SQL_HANDLE_ENV, ODBC_env);
connectionstate = 0;
}
std::string OdbcCDR::get_display_name()
{
return display_name;
}
AUTO_REGISTER_BASECDR(OdbcCDR);
/* For Emacs:
* Local Variables:
* mode:c++
* indent-tabs-mode:nil
* tab-width:4
* c-basic-offset:4
* End:
* For VIM:
* vim:set softtabstop=4 shiftwidth=4 tabstop=4 expandtab:
*/

View File

@ -0,0 +1,115 @@
/*
* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application Call Detail Recorder module
* Copyright 2006, Author: Yossi Neiman of Cartis Solutions, Inc. <freeswitch AT cartissolutions.com>
*
* 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 Call Detail Recorder module
*
* The Initial Developer of the Original Code is
* Yossi Neiman <freeswitch AT cartissolutions.com>
* Portions created by the Initial Developer are Copyright (C)
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Yossi Neiman <freeswitch AT cartissolutions.com>
*
* Description: This C++ header file describes the OdbcCDR class which handles formatting a CDR out to
* an ODBC backend using prepared statements.
*
* odbccdr.h
*
*/
#include "baseregistry.h"
#include <switch.h>
#include <iostream>
#include <list>
#include <sstream>
#ifndef __CYGWIN__
#include <sql.h>
#include <sqlext.h>
#include <sqltypes.h>
#else
#include <windows.h>
#include <w32api/sql.h>
#include <w32api/sqlext.h>
#include <w32api/sqltypes.h>
#endif
#ifndef ODBCCDR
#define ODBCCDR
class OdbcCDR : public BaseCDR {
public:
OdbcCDR();
OdbcCDR(switch_mod_cdr_newchannel_t *newchannel);
//OdbcCDR(const MysqlCDR& copyFrom);
virtual ~OdbcCDR();
virtual bool process_record();
virtual void connect(switch_xml_t& cfg, switch_xml_t& xml, switch_xml_t& settings, switch_xml_t& param);
virtual void disconnect();
virtual bool is_activated();
virtual void tempdump_record();
virtual void reread_tempdumped_records();
virtual std::string get_display_name();
private:
static bool activated;
static char sql_query[1024];
static modcdr_time_convert_t convert_time;
static std::string display_name;
static std::string tmp_sql_query; // Object must exist to bind the statement, this used for generating the sql
static char sql_query_chanvars[355];
static char sql_query_ping[10];
static bool connectionstate;
static bool logchanvars;
static SQLHENV ODBC_env; /* global ODBC Environment */
static SQLHDBC ODBC_con; /* global ODBC Connection Handle */
static SQLHSTMT ODBC_stmt;
static SQLHSTMT ODBC_stmt_chanvars;
static SQLHSTMT ODBC_stmt_ping;
static std::list<std::string> chanvars_fixed_list;
static std::vector<switch_mod_cdr_sql_types_t> chanvars_fixed_types;
static std::list<std::string> chanvars_supp_list; // The supplemental list
static bool repeat_fixed_in_supp;
static char dsn[255];
static char hostname[255];
static char username[255];
static char dbname[255];
static char password[255];
static char tablename[255];
static char tablename_chanvars[255];
//static fstream tmpfile;
char odbc_callstartdate[128];
char odbc_callanswerdate[128];
char odbc_calltransferdate[128];
char odbc_callenddate[128];
void disconnect_stage_1();
void connect_to_database();
};
#endif
/* For Emacs:
* Local Variables:
* mode:c++
* indent-tabs-mode:nil
* tab-width:4
* c-basic-offset:4
* End:
* For VIM:
* vim:set softtabstop=4 shiftwidth=4 tabstop=4 expandtab:
*/

View File

@ -47,22 +47,26 @@ PddCDR::PddCDR(switch_mod_cdr_newchannel_t *newchannel) : BaseCDR(newchannel)
memset(formattedcallstartdate,0,100); memset(formattedcallstartdate,0,100);
memset(formattedcallanswerdate,0,100); memset(formattedcallanswerdate,0,100);
memset(formattedcallenddate,0,100); memset(formattedcallenddate,0,100);
memset(formattedcalltransferdate,0,100);
if(newchannel != 0) if(newchannel != 0)
{ {
switch_time_exp_t tempcallstart, tempcallanswer, tempcallend; switch_time_exp_t tempcallstart, tempcallanswer, tempcalltransfer, tempcallend;
memset(&tempcallstart,0,sizeof(tempcallstart)); memset(&tempcallstart,0,sizeof(tempcallstart));
memset(&tempcallanswer,0,sizeof(tempcallanswer)); memset(&tempcallanswer,0,sizeof(tempcallanswer));
memset(&tempcalltransfer,0,sizeof(tempcalltransfer));
memset(&tempcallend,0,sizeof(tempcallend)); memset(&tempcallend,0,sizeof(tempcallend));
switch_time_exp_lt(&tempcallstart, callstartdate); convert_time(&tempcallstart, callstartdate);
switch_time_exp_lt(&tempcallanswer, callanswerdate); convert_time(&tempcallanswer, callanswerdate);
switch_time_exp_lt(&tempcallend, callenddate); convert_time(&tempcalltransfer, calltransferdate);
convert_time(&tempcallend, callenddate);
// Format the times // Format the times
size_t retsizecsd, retsizecad, retsizeced; //csd == callstartdate, cad == callanswerdate, ced == callenddate, ceff == callenddate_forfile size_t retsizecsd, retsizecad, retsizectd, retsizeced; //csd == callstartdate, cad == callanswerdate, ced == callenddate, ceff == callenddate_forfile
char format[] = "%Y-%m-%d-%H-%M-%S"; char format[] = "%Y-%m-%d %H:%M:%S";
switch_strftime(formattedcallstartdate,&retsizecsd,sizeof(formattedcallstartdate),format,&tempcallstart); switch_strftime(formattedcallstartdate,&retsizecsd,sizeof(formattedcallstartdate),format,&tempcallstart);
switch_strftime(formattedcallanswerdate,&retsizecad,sizeof(formattedcallanswerdate),format,&tempcallanswer); switch_strftime(formattedcallanswerdate,&retsizecad,sizeof(formattedcallanswerdate),format,&tempcallanswer);
switch_strftime(formattedcalltransferdate,&retsizectd,sizeof(formattedcalltransferdate),format,&tempcalltransfer);
switch_strftime(formattedcallenddate,&retsizeced,sizeof(formattedcallenddate),format,&tempcallend); switch_strftime(formattedcallenddate,&retsizeced,sizeof(formattedcallenddate),format,&tempcallend);
std::ostringstream ostring; std::ostringstream ostring;
@ -74,10 +78,7 @@ PddCDR::PddCDR(switch_mod_cdr_newchannel_t *newchannel) : BaseCDR(newchannel)
outputfile_name.append(callenddate_forfile); // Make sorting a bit easier, kinda like Maildir does outputfile_name.append(callenddate_forfile); // Make sorting a bit easier, kinda like Maildir does
outputfile_name.append("."); outputfile_name.append(".");
outputfile_name.append(myuuid); // The goal is to have a resulting filename of "/path/to/myuuid" outputfile_name.append(myuuid); // The goal is to have a resulting filename of "/path/to/myuuid"
outputfile_name.append(".pdd"); // .pdd - "perl data dumper" outputfile_name.append(".pdd"); // .pdd - "perl data dumper"
outputfile.open(outputfile_name.c_str());
bool repeat = 1; bool repeat = 1;
process_channel_variables(chanvars_supp_list,chanvars_fixed_list,newchannel->channel,repeat); process_channel_variables(chanvars_supp_list,chanvars_fixed_list,newchannel->channel,repeat);
} }
@ -91,9 +92,11 @@ PddCDR::~PddCDR()
bool PddCDR::activated=0; bool PddCDR::activated=0;
bool PddCDR::logchanvars=0; bool PddCDR::logchanvars=0;
bool PddCDR::connectionstate=0; bool PddCDR::connectionstate=0;
modcdr_time_convert_t PddCDR::convert_time = switch_time_exp_lt;
std::string PddCDR::outputfile_path; std::string PddCDR::outputfile_path;
std::list<std::string> PddCDR::chanvars_fixed_list; std::list<std::string> PddCDR::chanvars_fixed_list;
std::list<std::string> PddCDR::chanvars_supp_list; std::list<std::string> PddCDR::chanvars_supp_list;
std::string PddCDR::display_name = "PddCDR - Perl Data Dumper CDR logger";
void PddCDR::connect(switch_xml_t& cfg, switch_xml_t& xml, switch_xml_t& settings, switch_xml_t& param) void PddCDR::connect(switch_xml_t& cfg, switch_xml_t& xml, switch_xml_t& settings, switch_xml_t& param)
{ {
@ -137,6 +140,18 @@ void PddCDR::connect(switch_xml_t& cfg, switch_xml_t& xml, switch_xml_t& setting
{ {
switch_console_printf(SWITCH_CHANNEL_LOG,"PddCDR has no need for a fixed or supplemental list of channel variables due to the nature of the format. Please use the setting parameter of \"chanvars\" instead and try again.\n"); switch_console_printf(SWITCH_CHANNEL_LOG,"PddCDR has no need for a fixed or supplemental list of channel variables due to the nature of the format. Please use the setting parameter of \"chanvars\" instead and try again.\n");
} }
else if(!strcmp(var,"timezone"))
{
if(!strcmp(val,"utc"))
convert_time = switch_time_exp_gmt;
else if(!strcmp(val,"local"))
convert_time = switch_time_exp_lt;
else
{
switch_console_printf(SWITCH_CHANNEL_LOG,"Invalid configuration parameter for timezone. Possible values are utc and local. You entered: %s\nDefaulting to local.\n",val);
convert_time = switch_time_exp_lt;
}
}
} }
if(count_config_params > 0) if(count_config_params > 0)
@ -148,6 +163,8 @@ void PddCDR::connect(switch_xml_t& cfg, switch_xml_t& xml, switch_xml_t& setting
bool PddCDR::process_record() bool PddCDR::process_record()
{ {
outputfile.open(outputfile_name.c_str());
bool retval = 0; bool retval = 0;
if(!outputfile) if(!outputfile)
switch_console_printf(SWITCH_CHANNEL_LOG, "PddCDR::process_record(): Unable to open file %s to commit the call record to. Invalid path name, invalid permissions, or no space available?\n",outputfile_name.c_str()); switch_console_printf(SWITCH_CHANNEL_LOG, "PddCDR::process_record(): Unable to open file %s to commit the call record to. Invalid path name, invalid permissions, or no space available?\n",outputfile_name.c_str());
@ -156,8 +173,13 @@ bool PddCDR::process_record()
// Format the call record and proceed from here... // Format the call record and proceed from here...
outputfile << "$VAR1 = {" << std::endl; outputfile << "$VAR1 = {" << std::endl;
outputfile << "\t\'callstartdate\' = \'" << callstartdate << "\'," << std::endl; outputfile << "\t\'callstartdate\' = \'" << callstartdate << "\'," << std::endl;
outputfile << "\t\'formattedcallstartdate\' = \'" << formattedcallstartdate << "\'," << std::endl;
outputfile << "\t\'callanswerdate\' = \'" << callanswerdate << "\'," << std::endl; outputfile << "\t\'callanswerdate\' = \'" << callanswerdate << "\'," << std::endl;
outputfile << "\t\'formattedcallanswerdate\' = \'" << formattedcallanswerdate << "\'," << std::endl;
outputfile << "\t\'calltransferdate\' = \'" << calltransferdate << "\'," << std::endl;
outputfile << "\t\'formattedcalltransferdate\' = \'" << formattedcalltransferdate << "\'," << std::endl;
outputfile << "\t\'callenddate\' = \'" << callenddate << "\'," << std::endl; outputfile << "\t\'callenddate\' = \'" << callenddate << "\'," << std::endl;
outputfile << "\t\'formatcallenddate\' = \'" << formattedcallenddate << "\'," << std::endl;
outputfile << "\t\'hangupcause\' = \'" << hangupcause_text << "\'," << std::endl; outputfile << "\t\'hangupcause\' = \'" << hangupcause_text << "\'," << std::endl;
outputfile << "\t\'hangupcausecode\' = \'" << hangupcause << "\'," << std::endl; outputfile << "\t\'hangupcausecode\' = \'" << hangupcause << "\'," << std::endl;
outputfile << "\t\'clid\' = \'" << clid << "\'," << std::endl; outputfile << "\t\'clid\' = \'" << clid << "\'," << std::endl;
@ -208,8 +230,18 @@ void PddCDR::reread_tempdumped_records()
} }
std::string PddCDR::get_display_name()
{
return display_name;
}
void PddCDR::disconnect() void PddCDR::disconnect()
{ {
activated = 0;
connectionstate = 0;
logchanvars = 0;
outputfile_path.clear();
chanvars_supp_list.clear();
switch_console_printf(SWITCH_CHANNEL_LOG,"Shutting down PddCDR... Done!"); switch_console_printf(SWITCH_CHANNEL_LOG,"Shutting down PddCDR... Done!");
} }
@ -217,7 +249,7 @@ AUTO_REGISTER_BASECDR(PddCDR);
/* For Emacs: /* For Emacs:
* Local Variables: * Local Variables:
* mode:c * mode:c++
* indent-tabs-mode:nil * indent-tabs-mode:nil
* tab-width:4 * tab-width:4
* c-basic-offset:4 * c-basic-offset:4

View File

@ -53,16 +53,20 @@ class PddCDR : public BaseCDR {
virtual bool is_activated(); virtual bool is_activated();
virtual void tempdump_record(); virtual void tempdump_record();
virtual void reread_tempdumped_records(); virtual void reread_tempdumped_records();
virtual std::string get_display_name();
private: private:
static bool activated; // Is this module activated? static bool activated; // Is this module activated?
static bool connectionstate; // What is the status of the connection? static bool connectionstate; // What is the status of the connection?
static bool logchanvars; static bool logchanvars;
static modcdr_time_convert_t convert_time;
static std::string outputfile_path; // The directory we'll dump these into static std::string outputfile_path; // The directory we'll dump these into
static std::list<std::string> chanvars_fixed_list; // Normally this would be used, but not in this class static std::list<std::string> chanvars_fixed_list; // Normally this would be used, but not in this class
static std::list<std::string> chanvars_supp_list; // This will hold the list for all chanvars here static std::list<std::string> chanvars_supp_list; // This will hold the list for all chanvars here
static std::string display_name;
char formattedcallstartdate[100]; char formattedcallstartdate[100];
char formattedcallanswerdate[100]; char formattedcallanswerdate[100];
char formattedcalltransferdate[100];
char formattedcallenddate[100]; char formattedcallenddate[100];
std::string outputfile_name; std::string outputfile_name;
std::ofstream outputfile; std::ofstream outputfile;
@ -72,7 +76,7 @@ class PddCDR : public BaseCDR {
/* For Emacs: /* For Emacs:
* Local Variables: * Local Variables:
* mode:c * mode:c++
* indent-tabs-mode:nil * indent-tabs-mode:nil
* tab-width:4 * tab-width:4
* c-basic-offset:4 * c-basic-offset:4

View File

@ -0,0 +1,3 @@
ALTER TABLE freeswitchcdr DROP callid;
ALTER TABLE chanvars DROP callid;
ALTER TABLE chanvars ADD myuuid char(36) NOT NULL FIRST;

View File

@ -2,6 +2,7 @@ create table freeswitchcdr (
callid bigint unsigned default 0 primary key, /* This will need to be handled specially for auto increment, as that might not be standard */ callid bigint unsigned default 0 primary key, /* This will need to be handled specially for auto increment, as that might not be standard */
callstartdate datetime NOT NULL, callstartdate datetime NOT NULL,
callanswerdate datetime NOT NULL, callanswerdate datetime NOT NULL,
calltransferdate datetime NOT NULL,
callenddate datetime NOT NULL, callenddate datetime NOT NULL,
originated tinyint default 0, originated tinyint default 0,
clid varchar(80) default "Freeswitch - Unknown", clid varchar(80) default "Freeswitch - Unknown",

View File

@ -51,19 +51,22 @@ XmlCDR::XmlCDR(switch_mod_cdr_newchannel_t *newchannel) : BaseCDR(newchannel)
if(newchannel != 0) if(newchannel != 0)
{ {
switch_time_exp_t tempcallstart, tempcallanswer, tempcallend; switch_time_exp_t tempcallstart, tempcallanswer, tempcalltransfer, tempcallend;
memset(&tempcallstart,0,sizeof(tempcallstart)); memset(&tempcallstart,0,sizeof(tempcallstart));
memset(&tempcallanswer,0,sizeof(tempcallanswer)); memset(&tempcallanswer,0,sizeof(tempcallanswer));
memset(&tempcallend,0,sizeof(tempcallend)); memset(&tempcallend,0,sizeof(tempcallend));
switch_time_exp_lt(&tempcallstart, callstartdate); memset(&tempcalltransfer,0,sizeof(tempcalltransfer));
switch_time_exp_lt(&tempcallanswer, callanswerdate); convert_time(&tempcallstart, callstartdate);
switch_time_exp_lt(&tempcallend, callenddate); convert_time(&tempcallanswer, callanswerdate);
convert_time(&tempcalltransfer, calltransferdate);
convert_time(&tempcallend, callenddate);
// Format the times // Format the times
size_t retsizecsd, retsizecad, retsizeced; //csd == callstartdate, cad == callanswerdate, ced == callenddate, ceff == callenddate_forfile size_t retsizecsd, retsizecad, retsizectd, retsizeced; //csd == callstartdate, cad == callanswerdate, ced == callenddate, ceff == callenddate_forfile
char format[] = "%Y-%m-%d-%H-%M-%S"; char format[] = "%Y-%m-%d %H:%M:%S";
switch_strftime(formattedcallstartdate,&retsizecsd,sizeof(formattedcallstartdate),format,&tempcallstart); switch_strftime(formattedcallstartdate,&retsizecsd,sizeof(formattedcallstartdate),format,&tempcallstart);
switch_strftime(formattedcallanswerdate,&retsizecad,sizeof(formattedcallanswerdate),format,&tempcallanswer); switch_strftime(formattedcallanswerdate,&retsizecad,sizeof(formattedcallanswerdate),format,&tempcallanswer);
switch_strftime(formattedcalltransferdate,&retsizectd,sizeof(formattedcalltransferdate),format,&tempcalltransfer);
switch_strftime(formattedcallenddate,&retsizeced,sizeof(formattedcallenddate),format,&tempcallend); switch_strftime(formattedcallenddate,&retsizeced,sizeof(formattedcallenddate),format,&tempcallend);
std::ostringstream ostring; std::ostringstream ostring;
@ -76,9 +79,7 @@ XmlCDR::XmlCDR(switch_mod_cdr_newchannel_t *newchannel) : BaseCDR(newchannel)
outputfile_name.append("."); outputfile_name.append(".");
outputfile_name.append(myuuid); // The goal is to have a resulting filename of "/path/to/myuuid" outputfile_name.append(myuuid); // The goal is to have a resulting filename of "/path/to/myuuid"
outputfile_name.append(".xml"); // .xml - "XML Data Dumper" outputfile_name.append(".xml"); // .xml - "XML Data Dumper"
outputfile.open(outputfile_name.c_str());
bool repeat = 1; bool repeat = 1;
process_channel_variables(chanvars_supp_list,chanvars_fixed_list,newchannel->channel,repeat); process_channel_variables(chanvars_supp_list,chanvars_fixed_list,newchannel->channel,repeat);
} }
@ -92,9 +93,11 @@ XmlCDR::~XmlCDR()
bool XmlCDR::activated=0; bool XmlCDR::activated=0;
bool XmlCDR::logchanvars=0; bool XmlCDR::logchanvars=0;
bool XmlCDR::connectionstate=0; bool XmlCDR::connectionstate=0;
modcdr_time_convert_t XmlCDR::convert_time = switch_time_exp_lt;
std::string XmlCDR::outputfile_path; std::string XmlCDR::outputfile_path;
std::list<std::string> XmlCDR::chanvars_fixed_list; std::list<std::string> XmlCDR::chanvars_fixed_list;
std::list<std::string> XmlCDR::chanvars_supp_list; std::list<std::string> XmlCDR::chanvars_supp_list;
std::string XmlCDR::display_name = "XmlCDR - The rough implementation of XML CDR logger";
void XmlCDR::connect(switch_xml_t& cfg, switch_xml_t& xml, switch_xml_t& settings, switch_xml_t& param) void XmlCDR::connect(switch_xml_t& cfg, switch_xml_t& xml, switch_xml_t& settings, switch_xml_t& param)
{ {
@ -138,6 +141,18 @@ void XmlCDR::connect(switch_xml_t& cfg, switch_xml_t& xml, switch_xml_t& setting
{ {
switch_console_printf(SWITCH_CHANNEL_LOG,"XmlCDR has no need for a fixed or supplemental list of channel variables due to the nature of the format. Please use the setting parameter of \"chanvars\" instead and try again.\n"); switch_console_printf(SWITCH_CHANNEL_LOG,"XmlCDR has no need for a fixed or supplemental list of channel variables due to the nature of the format. Please use the setting parameter of \"chanvars\" instead and try again.\n");
} }
else if(!strcmp(var,"timezone"))
{
if(!strcmp(val,"utc"))
convert_time = switch_time_exp_gmt;
else if(!strcmp(val,"local"))
convert_time = switch_time_exp_lt;
else
{
switch_console_printf(SWITCH_CHANNEL_LOG,"Invalid configuration parameter for timezone. Possible values are utc and local. You entered: %s\nDefaulting to local.\n",val);
convert_time = switch_time_exp_lt;
}
}
} }
if(count_config_params > 0) if(count_config_params > 0)
@ -150,6 +165,8 @@ void XmlCDR::connect(switch_xml_t& cfg, switch_xml_t& xml, switch_xml_t& setting
bool XmlCDR::process_record() bool XmlCDR::process_record()
{ {
bool retval = 0; bool retval = 0;
outputfile.open(outputfile_name.c_str());
if(!outputfile) if(!outputfile)
switch_console_printf(SWITCH_CHANNEL_LOG, "XmlCDR::process_record(): Unable to open file %s to commit the call record to. Invalid path name, invalid permissions, or no space available?\n",outputfile_name.c_str()); switch_console_printf(SWITCH_CHANNEL_LOG, "XmlCDR::process_record(): Unable to open file %s to commit the call record to. Invalid path name, invalid permissions, or no space available?\n",outputfile_name.c_str());
else else
@ -159,8 +176,13 @@ bool XmlCDR::process_record()
outputfile << "<?xml version=\"1.0\"?>" << std::endl; outputfile << "<?xml version=\"1.0\"?>" << std::endl;
outputfile << "<document type=\"freeswitch-cdr/xml\">" << std::endl; outputfile << "<document type=\"freeswitch-cdr/xml\">" << std::endl;
outputfile << "\t<callstartdate data=\"" << callstartdate << "\" />" << std::endl; outputfile << "\t<callstartdate data=\"" << callstartdate << "\" />" << std::endl;
outputfile << "\t<formattedcallstartdate data=\"" << formattedcallstartdate << "\" />" << std::endl;
outputfile << "\t<callanswerdate data=\"" << callanswerdate << "\" />" << std::endl; outputfile << "\t<callanswerdate data=\"" << callanswerdate << "\" />" << std::endl;
outputfile << "\t<formattedcallanswerdate data=\"" << formattedcallanswerdate << "\" />" << std::endl;
outputfile << "\t<calltransferdate data=\"" << calltransferdate << "\" />" << std::endl;
outputfile << "\t<formattedcalltransferdate data=\"" << formattedcalltransferdate << "\" />" << std::endl;
outputfile << "\t<callenddate data=\"" << callenddate << "\" />" << std::endl; outputfile << "\t<callenddate data=\"" << callenddate << "\" />" << std::endl;
outputfile << "\t<formattedcallenddate data=\"" << formattedcallenddate << "\" />" << std::endl;
outputfile << "\t<hangupcause data=\"" << hangupcause_text << "\" />" << std::endl; outputfile << "\t<hangupcause data=\"" << hangupcause_text << "\" />" << std::endl;
outputfile << "\t<hangupcausecode data=\"" << hangupcause << "\" />" << std::endl; outputfile << "\t<hangupcausecode data=\"" << hangupcause << "\" />" << std::endl;
outputfile << "\t<clid data=\"" << clid << "\" />" << std::endl; outputfile << "\t<clid data=\"" << clid << "\" />" << std::endl;
@ -213,8 +235,18 @@ void XmlCDR::reread_tempdumped_records()
} }
std::string XmlCDR::get_display_name()
{
return display_name;
}
void XmlCDR::disconnect() void XmlCDR::disconnect()
{ {
activated = 0;
connectionstate = 0;
logchanvars = 0;
outputfile_path.clear();
chanvars_supp_list.clear();
switch_console_printf(SWITCH_CHANNEL_LOG,"Shutting down XmlCDR... Done!"); switch_console_printf(SWITCH_CHANNEL_LOG,"Shutting down XmlCDR... Done!");
} }
@ -222,7 +254,7 @@ AUTO_REGISTER_BASECDR(XmlCDR);
/* For Emacs: /* For Emacs:
* Local Variables: * Local Variables:
* mode:c * mode:c++
* indent-tabs-mode:nil * indent-tabs-mode:nil
* tab-width:4 * tab-width:4
* c-basic-offset:4 * c-basic-offset:4

View File

@ -54,16 +54,20 @@ class XmlCDR : public BaseCDR {
virtual bool is_activated(); virtual bool is_activated();
virtual void tempdump_record(); virtual void tempdump_record();
virtual void reread_tempdumped_records(); virtual void reread_tempdumped_records();
virtual std::string get_display_name();
private: private:
static bool activated; // Is this module activated? static bool activated; // Is this module activated?
static bool connectionstate; // What is the status of the connection? static bool connectionstate; // What is the status of the connection?
static bool logchanvars; static bool logchanvars;
static modcdr_time_convert_t convert_time;
static std::string outputfile_path; // The directory we'll dump these into static std::string outputfile_path; // The directory we'll dump these into
static std::list<std::string> chanvars_fixed_list; // Normally this would be used, but not in this class static std::list<std::string> chanvars_fixed_list; // Normally this would be used, but not in this class
static std::list<std::string> chanvars_supp_list; // This will hold the list for all chanvars here static std::list<std::string> chanvars_supp_list; // This will hold the list for all chanvars here
static std::string display_name;
char formattedcallstartdate[100]; char formattedcallstartdate[100];
char formattedcallanswerdate[100]; char formattedcallanswerdate[100];
char formattedcalltransferdate[100];
char formattedcallenddate[100]; char formattedcallenddate[100];
std::string outputfile_name; std::string outputfile_name;
std::ofstream outputfile; std::ofstream outputfile;
@ -73,7 +77,7 @@ class XmlCDR : public BaseCDR {
/* For Emacs: /* For Emacs:
* Local Variables: * Local Variables:
* mode:c * mode:c++
* indent-tabs-mode:nil * indent-tabs-mode:nil
* tab-width:4 * tab-width:4
* c-basic-offset:4 * c-basic-offset:4