git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@6326 d0543943-73ff-0310-b7d9-9358b9ac24b2
This commit is contained in:
Brian West 2007-11-19 15:22:55 +00:00
parent 073a49c94c
commit 840208ff70
27 changed files with 0 additions and 5520 deletions

View File

@ -1,42 +0,0 @@
AM_CFLAGS = $(SWITCH_AM_CFLAGS)
AM_CPPFLAGS = $(SWITCH_AM_CXXFLAGS)
AM_LDFLAGS = $(SWITCH_AM_LDFLAGS)
#we should set all these vars from configure, no reason to have these in each Makefile.am
LIBTOOL = echo "`link=\`echo $@|grep .la;echo $@|grep .so;echo $@|grep .dll\`;if test -n "$$link"; then echo Creating $@;fi`";`if test -z "$(VERBOSE)" ; \
then echo $(SHELL) $(switch_builddir)/quiet_libtool ;else echo $(SHELL) $(switch_builddir)/libtool; fi`
AM_MAKEFLAGS=`test -n "$(VERBOSE)" || echo -s`
# Dirty trick to override the link output
LIBS+=> $(MODNAME).log || error="yes";if test -n "$(VERBOSE)" -o "$$error" = "yes";then cat $(MODNAME).log;fi;if test "$$error" = "yes";then exit 1;fi
moddir=$(prefix)/mod
MODNAME=mod_cdr
mod_LTLIBRARIES = mod_cdr.la
mod_cdr_la_SOURCES = mod_cdr.cpp cdrcontainer.cpp basecdr.cpp baseregistry.cpp pddcdr.cpp csvcdr.cpp xmlcdr.cpp sqlitecdr.cpp
mod_cdr_la_CFLAGS = $(AM_CFLAGS)
mod_cdr_la_CPPFLAGS = $(AM_CPPFLAGS)
mod_cdr_la_LIBADD=$(switch_builddir)/libfreeswitch.la
mod_cdr_la_LDFLAGS=-module -avoid-version -no-undefined -export-symbols-regex ^switch_module_.*$ -rpath $(PREFIX)/$(libdir)
#Build mysqlcdr if we have mysql client
if HAVE_MYSQL
mod_cdr_la_CFLAGS += $(MYSQL_CFLAGS)
mod_cdr_la_CPPFLAGS += $(MYSQL_CFLAGS)
mod_cdr_la_LDFLAGS += $(MYSQL_LDFLAGS)
mod_cdr_la_SOURCES += mysqlcdr.cpp
endif
#mod_cdr_la_CFLAGS += -DSWITCH_QUEUE_ENHANCED
#mod_cdr_la_LDFLAGS += -lcurl
#Override the linstall target so we just install the .so/.dylib
install-data-am: $(DESTDIR)$(PREFIX)/$(moddir)/$(MODNAME).$(DYNAMIC_LIB_EXTEN)
$(DESTDIR)$(PREFIX)/$(moddir)/$(MODNAME).$(DYNAMIC_LIB_EXTEN): $(MODNAME).la
@echo installing $(MODNAME).$(DYNAMIC_LIB_EXTEN)
@if [ -f .libs/$(MODNAME).$(DYNAMIC_LIB_EXTEN) ] ; then \
$(LIBTOOL) --mode=install $(INSTALL) .libs/$(MODNAME).$(DYNAMIC_LIB_EXTEN) $(DESTDIR)$(PREFIX)/$(moddir) >/dev/null ; \
else \
$(LIBTOOL) --mode=install $(INSTALL) $(MODNAME).$(DYNAMIC_LIB_EXTEN) $(DESTDIR)$(PREFIX)/$(moddir) >/dev/null ; \
fi

View File

@ -1,145 +0,0 @@
/*
* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application Call Detail Recorder module
*
* Copyright 2006, Author: Yossi Neiman, president of Cartis Solutions, Inc.
* <freeswitch AT cartissolutions.com>
*
* Description: We don't need any documentation, do we?
*
* 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 Core API is the FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application by
* Anthony Minnesale II <anthmct@yahoo.com>
*
* README
*
*/
mod_cdr has been written in C++ using class inheritence and virtual methods, and registers all classes that
have been built in at load time. It then calls up the connect() method on each particular object and if successful at initializing the object, it adds it to a data structure that contains 1 instance of every sucessfully configured and connected object. This means that at any time after loading, it will only try to create objects that are functional, skipping any that are built in but are not desired for use at the current time.
In order to use mod_cdr, you must uncomment this line in the freeswitch.xml configuration file located in the conf directory: <load module="mod_cdr"/> in the <modules> section. Then below this section, you need to uncomment this section <configuration name="mod_cdr.conf" description="CDR Configuration"> and input the per class configurations. All class specific configurations go within this parent section as a subsection.
There are currently the following types of loggers:
Class: CsvCDR, located in csvcdr.h and csvcdr.cpp
Description: This is the standard CSV output class. It has a default list of CDR elements that it always
logs, and the user can specify channel variables to be logged in a fixed order (chanvars_fixed) starting after the last default value to be logged. Additionally, the user can specify additional channel variables to be logged after the fixed order channel variables as key=value pairs.
Configuration: Section name: <csvcdr>
<param name="path" value=""/> value is the location to put the files (required) <param name="size_limit" value=""/> value is the size limit of the output file, default value of 10MB (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="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
Description: This is the Perl Data Dumper output class. It writes each record to an individual text file
containing all the information. It does not distinguish between fixed and supplemental channel variables. If you are running freeswitch in a high volume environment, it is important to note that these individual files are small, often times between 750 and 1500 bytes. It is therefore recommended to use a partition with small cluster sizes or (on systems supporting) on reiserfs or reiser4 filesystems which are both excellent handlers of large numbers of small files.
Configuration: Section name: <pddcdr>
<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="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
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 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>
<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="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="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_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
This class logs the call detail record to an ODBC database using prepared
statements. This class is similar to MysqlCDR 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 myuuid 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. Additionally, since there is no uniform method to handling columns of type sequence/autoincrement/autonumber in the rdbms world, it should be noted that each row in the chanvars (supplemental channel variables) table will cost 36 bytes for the myuuid to be posted to it instead of 8 bytes for a bigint as in MysqlCDR.
Configuration: Section <odbccdr>
<param name="dsn" value=""/> - Not yet implemented, but meant for future use in ease of config
<param name="hostname" value=""/> - The hostname or IP of the backend server
<param name="username" value=""/> - The username used to authenticate to the backend server
<param name="password" value=""/> - The password used to authenticate to the backend server
<param name="dbname" value=""/> - The database to use for logging
<param name="main_db_table" value=""/> - Optional: the name of the main logging table (defaults to freeswitchcdr)
<param name="supp_chanvars_db_table" value=""/> - Optional: the name of the supplemental chanvars table (defaults to chanvars)
<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_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: SqliteCDR, located in sqlitecdr.h and sqlitecdr.cpp
This class uses the Sqlite3 library as included with FreeSWITCH's core. Sqlite is not strict in its handling of column types, and in theory you can toss any sort of data at any sort of column. That being said, this logger was designed to at least attempt to match the column types to the type of data being sent to it. It will warn you if the schema does not match what chanvars_fixed types you specify, but it will not fail on them. However, due to the use of prepared statements, it is likely that you will have unexpected results if you mismatch your chanvars_fixed logging to db schema. The is the first SQL logger to automatically create the db schema and even update it on its own.
Configuration: Section <sqlitecdr>
<param name="path" value=""/> value is the path to the database to open or create (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 (*). You must have a matching column of matching type in the main freeswitchcdr table. The logger will attempt to automatically update the schema, but it cannot if a column by that name already exists.(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.
FAQ:
Q: Why C++?
A: Why not? It works well in this design.
Q: Why is there a distinction between fixed and supplemental channel variables?
A: There is a distinction so that a level of uniformity can be maintain on those loggers that require uniformity. For example, if you expect that channel variable FOO will always been at a specific location on a CSV row, then it is required that it be a fixed channel variable. The fixed means that it is a fixed position, and needs to be logged even if it is blank. Supplemental channel variables, on the other hand, are more dynamic, and will only log those channel variables that actually exist on that given leg of the call. That means that if you supply a wildcard, it will simply iterate all the channel variables. If you do not want to repeat the fixed channel variables in the supplemental channel variables, it will then remove the fixed channel variables from the data structure holding the supplemental channel variables even before the process_record() method is called up.
Q: What would an example configuration look like?
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?
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">
<pddcdr>
<param name="path" value="/work/temp/pddcdr"/>
<!-- <param name="chanvars" value="username,accountcode"/> -->
<!-- <param name="chanvars_fixed" value="foo"/> -->
</pddcdr>
<csvcdr>
<param name="path" value="/work/temp/csvcdr"/>
<param name="size_limit" value="25"/>
<!-- <param name="chanvars_fixed" value="username"/> -->
<!-- <param name="chanvars_supp" value="*"/> -->
<!-- <param name="repeat_fixed_in_supp" value="0"/> -->
</csvcdr>
<mysqlcdr>
<param name="hostname" value="10.0.0.1"/>
<param name="username" value="test"/>
<param name="password" value="test"/>
<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"/> -->
<!-- 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_repeat_fixed" value="0"/>
</mysqlcdr>
</configuration>

View File

@ -1,409 +0,0 @@
/*
* 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>
* Marcel Barbulescu <marcelbarbulescu@gmail.com>
*
* Description: This C++ source file describes the BaseCDR class that all other CDR classes inherit from.
* It handles the bulk of the processing of data from the switch_channel_t objects.
*
* basecdr.cpp
*
*/
#include "basecdr.h"
#include <iostream>
#include <string>
#include <cstring>
#include <switch.h>
BaseCDR::BaseCDR()
{
callstartdate = 0;
callanswerdate = 0;
callenddate = 0;
strncpy(clid,"Unknown",7);
strncpy(src,"-1",2);
strncpy(dst,"-1",2);
strncpy(srcchannel,"-1",2);
strncpy(dstchannel,"-1",2);
strncpy(lastapp,"-1",2);
billusec = 0;
disposition=0;
amaflags = 0;
errorstate = 0;
}
BaseCDR::BaseCDR(switch_mod_cdr_newchannel_t *newchannel)
{
if(newchannel != 0)
{
errorstate = 0;
originated=1; // One-legged calls are always considered the originator
memset(clid,0,80);
memset(dialplan,0,80);
memset(myuuid,0,37);
memset(destuuid,0,37);
memset(dialplan,0,80);
memset(&hangupcause,0,sizeof(hangupcause));
hangupcause_text = 0;
memset(src,0,80);
memset(dst,0,80);
memset(srcchannel,0,80);
memset(dstchannel,0,80);
memset(network_addr,0,40);
memset(ani,0,80);
memset(aniii,0,80);
memset(lastapp,0,80);
memset(lastdata,0,255);
// switch_channel_timetable_t *timetable = switch_channel_get_timetable(newchannel->channel->caller_profile);
coresession = newchannel->session;
if (newchannel->callerprofile)
{
callstartdate= newchannel->callerprofile->times->created;
callanswerdate = newchannel->callerprofile->times->answered;
calltransferdate = newchannel->callerprofile->times->transferred;
callenddate = newchannel->callerprofile->times->hungup;
if(!switch_strlen_zero(newchannel->callerprofile->caller_id_name))
strncpy(clid,newchannel->callerprofile->caller_id_name,strlen(newchannel->callerprofile->caller_id_name));
// Get the ANI information if it's set
if(!switch_strlen_zero(newchannel->callerprofile->ani))
strncpy(ani,newchannel->callerprofile->ani,strlen(newchannel->callerprofile->ani));
if(!switch_strlen_zero(newchannel->callerprofile->aniii))
strncpy(aniii,newchannel->callerprofile->aniii,strlen(newchannel->callerprofile->aniii));
if(!switch_strlen_zero(newchannel->callerprofile->dialplan))
strncpy(dialplan,newchannel->callerprofile->dialplan,strlen(newchannel->callerprofile->dialplan));
if(!switch_strlen_zero(newchannel->callerprofile->network_addr))
strncpy(network_addr,newchannel->callerprofile->network_addr,strlen(newchannel->callerprofile->network_addr));
// Were we the receiver of the call?
if(newchannel->callerprofile->originator_caller_profile)
{
originated = 0;
if(!switch_strlen_zero(newchannel->callerprofile->originator_caller_profile->uuid))
strncpy(destuuid,newchannel->callerprofile->originator_caller_profile->uuid,strlen(newchannel->callerprofile->originator_caller_profile->uuid));
if(!switch_strlen_zero(newchannel->callerprofile->destination_number))
strncpy(src,newchannel->callerprofile->destination_number,strlen(newchannel->callerprofile->destination_number));
if(!switch_strlen_zero(newchannel->callerprofile->caller_id_number))
strncpy(dst,newchannel->callerprofile->caller_id_number,strlen(newchannel->callerprofile->caller_id_number));
}
else
{
//originateprofile = switch_channel_get_originatee_profile(newchannel->channel->callerprofile);
// Or were we maybe we were the caller?
if(newchannel->callerprofile->originatee_caller_profile)
{
if(!switch_strlen_zero(newchannel->callerprofile->caller_id_number))
strncpy(src,newchannel->callerprofile->caller_id_number,strlen(newchannel->callerprofile->caller_id_number));
if(!switch_strlen_zero(newchannel->callerprofile->destination_number))
strncpy(dst,newchannel->callerprofile->destination_number,strlen(newchannel->callerprofile->destination_number));
if(!switch_strlen_zero(newchannel->callerprofile->originatee_caller_profile->chan_name))
strncpy(dstchannel,newchannel->callerprofile->originatee_caller_profile->chan_name,strlen(newchannel->callerprofile->originatee_caller_profile->chan_name));
}
}
}
if(!switch_strlen_zero(newchannel->callerprofile->uuid))
strncpy(myuuid,newchannel->callerprofile->uuid,strlen(newchannel->callerprofile->uuid));
if(!switch_strlen_zero(newchannel->callerprofile->chan_name))
strncpy(srcchannel,newchannel->callerprofile->chan_name,strlen(newchannel->callerprofile->chan_name));
if(switch_channel_test_flag(newchannel->channel,CF_ANSWERED))
{
disposition=1;
if(callstartdate && callanswerdate)
{
if(callenddate)
billusec = callenddate - callanswerdate;
else if(calltransferdate)
billusec = calltransferdate - callanswerdate;
}
}
else if(switch_channel_test_flag(newchannel->channel,CF_TRANSFER))
{
disposition=1;
if(callanswerdate && calltransferdate)
billusec = calltransferdate - callanswerdate;
}
else
{
disposition=0;
billusec = 0;
}
// What was the hangup cause?
hangupcause = switch_channel_get_cause(newchannel->channel);
hangupcause_text = switch_channel_cause2str(hangupcause);
if(newchannel->callerextension)
if(!newchannel->callerextension->last_application)
{
if(!switch_strlen_zero(newchannel->callerextension->last_application->application_name))
strncpy(lastapp,newchannel->callerextension->last_application->application_name,strlen(newchannel->callerextension->last_application->application_name));
if(!switch_strlen_zero(newchannel->callerextension->last_application->application_data))
strncpy(lastdata,newchannel->callerextension->last_application->application_data,strlen(newchannel->callerextension->last_application->application_data));
}
amaflags=0;
}
}
BaseCDR::~BaseCDR()
{
}
/* bool fixed is for checking if this is the fixed or supplemental list */
void BaseCDR::parse_channel_variables_xconfig(std::string& unparsed,std::list<std::string>& chanvarlist,bool fixed)
{
std::string tempstring;
for(std::string::size_type i = 0, j = 0; j != std::string::npos; )
{
j = unparsed.find(',',i);
if(j > 0)
{
tempstring = unparsed.substr(i,(j-i));
chanvarlist.push_back(tempstring);
i =j+1;
}
else
{
tempstring = unparsed.substr(i);
chanvarlist.push_back(tempstring);
}
}
// Now we need to clean up in case somebody put in a '*' in the chanvars fixed list
std::list<std::string>::iterator iBeg,iItr,iEnd;
iBeg = chanvarlist.begin();
iEnd = chanvarlist.end();
bool exitcode = 1;
for(iItr = chanvarlist.begin(); iItr != iEnd && exitcode ; )
{
if(iItr->find('*',0) != std::string::npos)
{
if(fixed)
{
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Wildcards are not allow in the fixed chanvars list. Item removed.\n");
iItr = chanvarlist.erase(iItr);
}
else
{
if(chanvarlist.size() > 1)
chanvarlist.clear();
std::string all = "*";
chanvarlist.push_back(all);
exitcode = 0;
}
}
else
iItr++;
}
}
void BaseCDR::parse_channel_variables_xconfig(std::string& unparsed,std::list<std::string>& chanvarlist,std::vector<switch_mod_cdr_sql_types_t>& chanvars_fixed_types)
{
bool fixed = 1;
std::string tempstring, tempstring2;
switch_mod_cdr_sql_types_t sql_type;
parse_channel_variables_xconfig(unparsed,chanvarlist,fixed);
std::list<std::string> tempchanvarlist; // = chanvarlist;
std::list<std::string>::iterator iEnd, iItr;
for(iItr = chanvarlist.begin(), iEnd = chanvarlist.end() ; iItr != iEnd ; iItr++)
{
sql_type = CDR_STRING;
std::string::size_type i = 0, j = 0;
j = iItr->find('=',i);
if(j > 0 )
{
tempstring = iItr->substr(i,(j-i));
i =j+1;
tempstring2 = iItr->substr(i);
if(tempstring2.size() == 1)
{
switch(tempstring2[0])
{
case 'I':
case 'i':
sql_type = CDR_INTEGER;
break;
case 'S':
case 's':
sql_type = CDR_STRING;
break;
case 'D':
case 'd':
sql_type = CDR_DOUBLE;
break;
case 'T':
case 't':
sql_type = CDR_TINY;
break;
default:
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Valid fixed channel variable types are x (decimal), d (double), i (integer), t (tiny), s (string). You tried to give a type of %s to chanvar %s.\nReverting this chanvar type to a string type.\n", tempstring2.c_str(), tempstring.c_str());
sql_type = CDR_STRING;
}
}
else
{
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Valid fixed channel variable types are x (decimal), d (double), i (integer), t (tiny), s (string). You tried to give a type of %s to chanvar %s.\nReverting this chanvar type to a string type.\n", tempstring2.c_str(), tempstring.c_str());
sql_type = CDR_STRING;
}
}
else
{
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "No parameter set, for channel variable %s, using default type of string.\n", iItr->c_str());
sql_type = CDR_STRING;
tempstring = *iItr;
}
tempchanvarlist.push_back(tempstring);
chanvars_fixed_types.push_back(sql_type);
}
chanvarlist.clear();
chanvarlist = tempchanvarlist;
}
// This is for processing the fixed channel variables
void BaseCDR::process_channel_variables(const std::list<std::string>& stringlist,switch_channel_t *channel)
{
std::list<std::string>::const_iterator iItr,iEnd;
iEnd = stringlist.end();
for(iItr = stringlist.begin(); iItr != iEnd; iItr++)
{
std::vector<char> tempstringvector(iItr->begin(), iItr->end());
tempstringvector.push_back('\0');
char* tempstring= &tempstringvector[0];
const char *tempvariable;
tempvariable = switch_channel_get_variable(channel,tempstring);
std::pair<std::string,std::string> temppair;
temppair.first = *iItr;
if(!switch_strlen_zero(tempvariable))
temppair.second = tempvariable;
chanvars_fixed.push_back(temppair);
}
}
// This one is for processing of supplemental chanvars
void BaseCDR::process_channel_variables(const std::list<std::string>& stringlist, const std::list<std::string>& fixedlist, switch_channel_t *channel,bool repeat)
{
if(stringlist.front() == "*") {
switch_event_header_t *hi;
switch_memory_pool_t *sessionpool;
sessionpool = switch_core_session_get_pool(coresession);
if ((hi = switch_channel_variable_first(channel))) {
for (; hi; hi = hi->next) {
std::string name, value;
name = hi->name;
value = hi->value;
chanvars_supp[name] = value;
}
switch_channel_variable_last(channel);
}
} else {
std::list<std::string>::const_iterator iItr,iEnd;
iEnd = stringlist.end();
for (iItr = stringlist.begin(); iItr != iEnd; iItr++) {
std::vector<char> tempstringvector(iItr->begin(), iItr->end());
tempstringvector.push_back('\0');
char* tempstring= &tempstringvector[0];
const char *tempvariable;
tempvariable = switch_channel_get_variable(channel,tempstring);
if (!switch_strlen_zero(tempvariable))
chanvars_supp[*iItr] = tempvariable;
}
}
if (!repeat) {
std::map<std::string,std::string>::iterator MapItr;
std::list<std::string>::const_iterator iItr,iEnd;
for (iItr = fixedlist.begin(), iEnd = fixedlist.end(); iItr != iEnd; iItr++) {
MapItr = chanvars_supp.find(*iItr);
if (MapItr != chanvars_supp.end() )
chanvars_supp.erase(MapItr);
}
}
}
void BaseCDR::escape_string(std::string& src)
{
std::string::size_type pos = 0;
// find all occurences of ' and replace them with \'
while ( ( pos = src.find( "'", pos ) ) != std::string::npos )
{
src.replace( pos, 1, "\\'" );
pos += 2;
}
}
std::string BaseCDR::escape_chararray(char* src)
{
std::string src_string = src;
std::string::size_type pos = 0;
// find all occurences of ' and replace them with \'
while ( ( pos = src_string.find( "'", pos ) ) != std::string::npos )
{
src_string.replace( pos, 1, "\\'" );
pos += 2;
}
return src_string;
}
/* For Emacs:
* Local Variables:
* mode:c++
* indent-tabs-mode:t
* tab-width:4
* c-basic-offset:4
* End:
* For VIM:
* vim:set softtabstop=4 shiftwidth=4 tabstop=4 expandtab:
*/

View File

@ -1,129 +0,0 @@
/*
* 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 header describes the switch_mod_cdr_newchannel_t struct, the BaseCDR base class that all
* CDR loggers will inherit from, and the switch_mod_cdr_sql_types_t enum for use with the SQL type loggers
* (i.e. MySQL). This is part of the mod_cdr module for Freeswitch by Anthony Minnesale and friends.
*
* basecdr.h
*
*/
#ifndef BASECDR
#define BASECDR
#ifdef __cplusplus
#include <vector>
#include <list>
#include <vector>
#include <map>
#include <switch.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
struct switch_mod_cdr_newchannel_t {
switch_core_session_t *session;
switch_channel_t *channel;
//switch_channel_timetable_t *timetable;
switch_caller_extension_t *callerextension;
switch_caller_profile_t *callerprofile;
//switch_caller_profile_t *originateprofile;
//bool originate;
};
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 switch_status_t (STDCALL * modcdr_time_convert_t) (switch_time_exp_t *, switch_time_t);
class BaseCDR {
public:
BaseCDR();
virtual ~ BaseCDR();
BaseCDR(switch_mod_cdr_newchannel_t * newchannel);
virtual void connect(switch_xml_t & cfg, switch_xml_t & xml, switch_xml_t & settings, switch_xml_t & param) = 0;
virtual void disconnect() = 0;
virtual bool process_record() = 0;
virtual bool is_activated() = 0;
virtual void tempdump_record() = 0;
virtual void reread_tempdumped_records() = 0;
virtual std::string get_display_name() = 0; // Get the module name
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, std::vector < switch_mod_cdr_sql_types_t > &chanvars_fixed_types); // Typically used for SQL types
void process_channel_variables(const std::list < std::string > &stringlist, const std::list < std::string > &fixedlist, switch_channel_t *channel, bool repeat = 1); //This is used for supplemental chanvars
void process_channel_variables(const std::list < std::string > &stringlist, switch_channel_t *channel); // This is used for fixed chanvars
void escape_string(std::string & src);
std::string escape_chararray(char *src);
switch_time_t callstartdate;
switch_time_t callanswerdate;
switch_time_t callenddate;
switch_time_t calltransferdate;
switch_call_cause_t hangupcause;
const char *hangupcause_text;
char clid[80];
bool originated; // Did they originate this call?
char dialplan[80];
char myuuid[37]; // 36 + 1 to hold \0
char destuuid[37];
char src[80];
char dst[80];
char srcchannel[80];
char dstchannel[80];
char ani[80];
char aniii[80];
char network_addr[40];
char lastapp[80];
char lastdata[255];
switch_time_t billusec; // Yes, you heard me, we're measuring in microseconds
int disposition; // Currently 0 = Busy/Unanswered, 1 = Answered
int amaflags;
switch_core_session_t *coresession;
std::list < std::pair < std::string, std::string > >chanvars_fixed;
std::map < std::string, std::string > chanvars_supp;
bool errorstate; // True if there is an error writing the log
};
#endif
#endif
/* For Emacs:
* Local Variables:
* mode:c++
* indent-tabs-mode:t
* tab-width:4
* c-basic-offset:4
* End:
* For VIM:
* vim:set softtabstop=4 shiftwidth=4 tabstop=4 expandtab:
*/

View File

@ -1,92 +0,0 @@
/*
* 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 describes the BaseRegistry and BaseRegistration classes that implement
* the factory routine with the aid of the templated base_factory() function.
*
* baseregistry.cpp
*
*/
#include "baseregistry.h"
BaseRegistry& BaseRegistry::get()
{
static BaseRegistry instance;
return instance;
}
void BaseRegistry::add(basecdr_creator creator)
{
m_bases.push_back(creator);
}
BaseRegistry::iterator BaseRegistry::begin()
{
return m_bases.begin();
}
BaseRegistry::iterator BaseRegistry::end()
{
return m_bases.end();
}
void BaseRegistry::reset_active()
{
active_bases.clear();
}
void BaseRegistry::add_active(iterator tempobject)
{
active_bases.push_back(*tempobject);
}
BaseRegistry::iterator BaseRegistry::active_begin()
{
return active_bases.begin();
}
BaseRegistry::iterator BaseRegistry::active_end()
{
return active_bases.end();
}
BaseRegistration::BaseRegistration(basecdr_creator creator)
{
BaseRegistry::get().add(creator);
}
/* For Emacs:
* Local Variables:
* mode:c++
* indent-tabs-mode:t
* tab-width:4
* c-basic-offset:4
* End:
* For VIM:
* vim:set softtabstop=4 shiftwidth=4 tabstop=4 expandtab:
*/

View File

@ -1,87 +0,0 @@
/*
* 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: BaseRegistry and BaseRegistration classes and AUTO_REGISTER_BASECDR macro provided in part by David
* Terrell from his blog posting http://meat.net/2006/03/cpp-runtime-class-registration/ Much thanks to him
* for his help.
*
* baseregistry.h
*
*/
#include "basecdr.h"
#include <iostream>
#ifndef BASECDRREGISTRY
#define BASECDRREGISTRY
#ifdef __cplusplus
#include <vector>
template < class T > BaseCDR * basecdr_factory(switch_mod_cdr_newchannel_t * newchannel)
{
return new T(newchannel);
}
typedef BaseCDR *(*basecdr_creator) (switch_mod_cdr_newchannel_t * newchannel);
class BaseRegistry {
private:
std::vector < basecdr_creator > m_bases; // Stores all modules
std::vector < basecdr_creator > active_bases; // Stores only active modules
public:
typedef std::vector < basecdr_creator >::iterator iterator;
static BaseRegistry & get();
void add(basecdr_creator);
void reset_active(); // Clears the active vector for reloading of configuration.
void add_active(iterator);
iterator begin();
iterator end();
iterator active_begin();
iterator active_end();
};
class BaseRegistration {
public:
BaseRegistration(basecdr_creator);
};
#define AUTO_REGISTER_BASECDR(basecdr) BaseRegistration _basecdr_registration_ ## basecdr(&basecdr_factory<basecdr>);
#endif // ifdef __cplusplus
#endif
/* For Emacs:
* Local Variables:
* mode:c++
* indent-tabs-mode:t
* tab-width:4
* c-basic-offset:4
* End:
* For VIM:
* vim:set softtabstop=4 shiftwidth=4 tabstop=4 expandtab:
*/

View File

@ -1,260 +0,0 @@
/*
* 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>
* Marcel Barbulescu <marcelbarbulescu@gmail.com>
*
* Description: This C++ source file describes the CDRContainer singleton object used by mod_cdr to control
* the creation, processing, and destruction of various CDR logger objects.
*
* cdrcontainer.cpp
*
*/
#include "cdrcontainer.h"
#include "baseregistry.h"
CDRContainer::CDRContainer()
{
}
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.
switch_queue_create(&cdrqueue,5224288, module_pool);
queue_paused = 0;
strcpy(configfile,"cdr.conf");
switch_mod_cdr_newchannel_t *newchannel; // = new switch_mod_cdr_newchannel_t;
newchannel = 0;
if (!(xml = switch_xml_open_cfg(configfile, &cfg, NULL)))
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "open of %s failed\n", configfile);
else
{
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);
ptr->connect(cfg,xml,settings,param);
if(ptr->is_activated())
registry.add_active(it);
}
}
switch_xml_free(xml);
}
CDRContainer::~CDRContainer()
{
if(switch_queue_size(cdrqueue) > 0)
process_records();
switch_mod_cdr_newchannel_t *newchannel; //= new switch_mod_cdr_newchannel_t;
newchannel = 0;
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();
}
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "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_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Reloading the XML file...\n");
switch_xml_free(xml_root);
}
if (!(xml = switch_xml_open_cfg(configfile, &cfg, NULL)))
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "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_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "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)
{
switch_mod_cdr_newchannel_t *newchannel = new switch_mod_cdr_newchannel_t;
memset(newchannel,0,sizeof(*newchannel));
newchannel->channel = switch_core_session_get_channel(session);
assert(newchannel->channel != 0);
newchannel->session = session;
newchannel->callerextension = switch_channel_get_caller_extension(newchannel->channel);
newchannel->callerprofile = switch_channel_get_caller_profile(newchannel->channel);
while (newchannel->callerprofile)
{
BaseRegistry& registry(BaseRegistry::get());
for(BaseRegistry::iterator it = registry.active_begin(); it != registry.active_end(); ++it)
{
basecdr_creator func = *it;
BaseCDR* newloggerobject = func(newchannel);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Adding a new logger object to the queue.\n");
switch_queue_push(cdrqueue,newloggerobject);
}
newchannel->callerprofile = newchannel->callerprofile->next;
if(newchannel->callerextension)
newchannel->callerextension = newchannel->callerextension->next;
}
delete newchannel;
}
void CDRContainer::process_records()
{
BaseCDR *tempnewloggerobject = 0, *newloggerobject = 0;
while(switch_queue_pop(cdrqueue,reinterpret_cast< void** > (&tempnewloggerobject))== SWITCH_STATUS_SUCCESS)
{
newloggerobject = tempnewloggerobject;
newloggerobject->process_record();
delete newloggerobject;
}
}
/* For Emacs:
* Local Variables:
* mode:c++
* indent-tabs-mode:t
* tab-width:4
* c-basic-offset:4
* End:
* For VIM:
* vim:set softtabstop=4 shiftwidth=4 tabstop=4 expandtab:
*/

View File

@ -1,90 +0,0 @@
/*
* 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 CDRContainer singleton object used by mod_cdr to control
* the creation, processing, and destruction of various CDR logger objects.
*
* cdrcontainer.h
*
*/
#ifndef CDRCONTAINER
#define CDRCONTAINER
#ifdef __cplusplus
#include <cstring>
#include <iostream>
#endif
#include "basecdr.h"
#include <switch.h>
/*
The CDRContainer Class will serve as a central receptacle for all CDR methods. CDRContainer::CDRContainer() will initialize the system, and CDRContainer::add_cdr() will add a new object of each CDR type to a queue for "later processing" by the other thread.
CDRContainer::process_records() will iterate thru the queue and commit the records to the respective backends.
*/
#ifdef __cplusplus
extern "C" {
#endif
class CDRContainer {
public:
CDRContainer();
CDRContainer(switch_memory_pool_t *module_pool);
~CDRContainer();
void add_cdr(switch_core_session_t *session);
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:
private:
switch_xml_t cfg, xml, settings, param;
switch_queue_t *cdrqueue;
std::string tempfilepath;
char configfile[13];
bool queue_paused;
};
#ifdef __cplusplus
}
#endif
#endif
/* For Emacs:
* Local Variables:
* mode:c++
* indent-tabs-mode:t
* tab-width:4
* c-basic-offset:4
* End:
* For VIM:
* vim:set softtabstop=4 shiftwidth=4 tabstop=4 expandtab:
*/

View File

@ -1,350 +0,0 @@
/*
* 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>
* Marcel Barbulescu <marcelbarbulescu@gmail.com>
*
* Description: This C++ source file describes the CsvCDR class that handles processing CDRs to a CSV format.
* This is the standard CSV module, and has a list of predefined variables to log out which can be
* added to, but never have the default ones removed. If you want to use one that allows you to explicity
* set all data variables to be logged and in what order, then this is not the class you want to use, and
* one will be coming in the future to do just that.
*
* csvcdr.cpp
*
*/
#include <switch.h>
#include "csvcdr.h"
#include <string>
CsvCDR::CsvCDR() : BaseCDR()
{
memset(formattedcallstartdate,0,100);
memset(formattedcallanswerdate,0,100);
memset(formattedcallenddate,0,100);
}
CsvCDR::CsvCDR(switch_mod_cdr_newchannel_t *newchannel) : BaseCDR(newchannel)
{
memset(formattedcallstartdate,0,100);
memset(formattedcallanswerdate,0,100);
memset(formattedcalltransferdate,0,100);
memset(formattedcallenddate,0,100);
if(newchannel != 0)
{
switch_time_exp_t tempcallstart, tempcallanswer, tempcalltransfer, tempcallend;
memset(&tempcallstart,0,sizeof(tempcallstart));
memset(&tempcalltransfer,0,sizeof(tempcalltransfer));
memset(&tempcallanswer,0,sizeof(tempcallanswer));
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 %H:%M:%S";
switch_strftime(formattedcallstartdate,&retsizecsd,sizeof(formattedcallstartdate),format,&tempcallstart);
switch_strftime(formattedcallanswerdate,&retsizecad,sizeof(formattedcallanswerdate),format,&tempcallanswer);
switch_strftime(formattedcalltransferdate,&retsizectd,sizeof(formattedcalltransferdate),format,&tempcalltransfer);
switch_strftime(formattedcallenddate,&retsizeced,sizeof(formattedcallenddate),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);
}
}
CsvCDR::~CsvCDR()
{
}
bool CsvCDR::activated=0;
bool CsvCDR::logchanvars=0;
bool CsvCDR::connectionstate=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::ofstream CsvCDR::outputfile;
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_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)
{
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "CsvCDR::connect() - Loading configuration file.\n");
activated = 0; // Set it as inactive initially
connectionstate = 0; // Initialize it to false to show that we aren't yet connected.
if ((settings = switch_xml_child(cfg, "csvcdr")))
{
int count_config_params = 0; // Need to make sure all params are set before we load
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, "path"))
{
if(val != 0)
outputfile_path = val;
count_config_params++;
}
else if (!strcmp(var, "chanvars_supp"))
{
if(val != 0)
{
std::string unparsed;
unparsed = val;
if(unparsed.size() > 0)
{
bool fixed = 0;
parse_channel_variables_xconfig(unparsed,chanvars_supp_list,fixed);
logchanvars=1;
}
}
}
else if (!strcmp(var, "chanvars_fixed"))
{
if(val != 0)
{
std::string unparsed;
unparsed = val;
if(unparsed.size() > 0)
{
bool fixed = 1;
parse_channel_variables_xconfig(unparsed,chanvars_fixed_list,fixed);
logchanvars=1;
}
}
}
else if (!strcmp(var, "repeat_fixed_in_supp"))
{
if(!strcmp(val,"1"))
repeat_fixed_in_supp = 1;
else if (!strcmp(val,"y") || !strcmp(val,"y"))
repeat_fixed_in_supp = 0;
}
else if (!strcmp(var, "size_limit"))
{
if(val != 0)
{
filesize_limit = atoi(val) * 1024 * 1024; // Value is in MB
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_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "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)
{
open_file();
if(outputfile.good())
{
activated = 1;
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "CsvCDR activated, log rotation will occur at or after %d MB\n", (int)(filesize_limit >> 20));
}
}
else
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "CsvCDR::connect(): You did not specify the minimum parameters for using this module. You must specify at least a path to have the records logged to.\n");
}
}
void CsvCDR::check_file_size_and_open()
{
// Test if the file has been opened or not
if(outputfile)
{
if(outputfile.tellp() > filesize_limit)
outputfile.close();
}
if (!outputfile)
{
open_file();
}
}
void CsvCDR::open_file()
{
switch_time_t now = switch_time_now();
switch_time_exp_t now_converted;
memset(&now_converted,0,sizeof(now_converted));
switch_time_exp_lt(&now_converted,now);
switch_size_t retsize;
char format[] = "%Y-%m-%d-%H-%M-%S";
char formatteddate[100];
memset(formatteddate,0,100);
switch_strftime(formatteddate,&retsize,100,format,&now_converted);
std::string filename = outputfile_path;
filename.append(SWITCH_PATH_SEPARATOR);
filename.append(formatteddate);
filename.append(".csv");
outputfile.clear();
outputfile.open(filename.c_str(),std::ios_base::app|std::ios_base::binary);
if(outputfile.fail())
{
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Could not open the CSV file %s . CsvCDR logger will not be functional until this is resolved and a reload is issued. Failbit is set to %d.\n", filename.c_str(), outputfile.fail());
activated = 0;
}
else
{
outputfile << "callstartdate,formattedcallstartdate,callanswerdate,formattedcallanswerdate,calltransferdate,formattedcalltransferdate,callendate,formattedcallenddate,hangupcause_text,hangupcause,clid,originated,dialplan,myuuid,destuuid,src,dst,srcchannel,dstchannel,ani,aniii,network_address,lastapp,lastdata,billusec,disposition,amaflags";
if(chanvars_fixed_list.size())
{
std::list<std::string>::iterator iItr, iEnd;
for(iItr = chanvars_fixed_list.begin(),iEnd = chanvars_fixed_list.end(); iItr != iEnd; iItr++)
outputfile << "," << *iItr;
}
if(logchanvars)
outputfile << ",chanvars_supp";
outputfile << std::endl;
}
}
bool CsvCDR::process_record()
{
check_file_size_and_open();
bool retval = 0;
// Format the call record and proceed from here...
outputfile << "\"" << callstartdate << "\",\"";
outputfile << formattedcallstartdate << "\",\"";
outputfile << callanswerdate << "\",\"";
outputfile << formattedcallanswerdate << "\",\"";
outputfile << calltransferdate << "\",\"";
outputfile << formattedcalltransferdate << "\",\"";
outputfile << callenddate << "\",\"";
outputfile << formattedcallenddate << "\",\"";
outputfile << hangupcause_text << "\",\"";
outputfile << hangupcause << "\",\"";
outputfile << clid << "\",\"";
outputfile << originated << "\",\"";
outputfile << dialplan << "\",\"";
outputfile << myuuid << "\",\"";
outputfile << destuuid << "\",\"";
outputfile << src << "\",\"";
outputfile << dst << "\",\"";
outputfile << srcchannel << "\",\"";
outputfile << dstchannel << "\",\"";
outputfile << ani << "\",\"";
outputfile << aniii << "\",\"";
outputfile << network_addr << "\",\"";
outputfile << lastapp << "\",\"";
outputfile << lastdata << "\",\"";
outputfile << billusec << "\",\"";
outputfile << disposition << "\",\"";
outputfile << amaflags << "\"";
// Now to process chanvars, fixed ones first
if(chanvars_fixed.size() > 0 )
{
std::list<std::pair<std::string,std::string> >::iterator iItr, iEnd;
for(iItr = chanvars_fixed.begin(), iEnd = chanvars_fixed.end(); iItr != iEnd; iItr++)
outputfile << ",\"" << iItr->second << "\"";
}
if(chanvars_supp.size() > 0 )
{
std::map<std::string,std::string>::iterator iItr,iEnd;
for(iItr = chanvars_supp.begin(), iEnd = chanvars_supp.end() ; iItr != iEnd; iItr++)
outputfile << ",\"" << iItr->first << "=" << iItr->second << "\"";
}
outputfile << std::endl;
retval = 1;
return retval;
}
bool CsvCDR::is_activated()
{
return activated;
}
void CsvCDR::tempdump_record()
{
}
void CsvCDR::reread_tempdumped_records()
{
}
std::string CsvCDR::get_display_name()
{
return display_name;
}
void CsvCDR::disconnect()
{
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_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Shutting down CsvCDR... Done!\n");
}
AUTO_REGISTER_BASECDR(CsvCDR);
/* For Emacs:
* Local Variables:
* mode:c++
* indent-tabs-mode:t
* tab-width:4
* c-basic-offset:4
* End:
* For VIM:
* vim:set softtabstop=4 shiftwidth=4 tabstop=4 expandtab:
*/

View File

@ -1,92 +0,0 @@
/*
* 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 CsvCDR class that handles processing CDRs to a CSV format.
* This is the standard CSV module, and has a list of predefined variables to log out which can be
* added to, but never have the default ones removed. If you want to use one that allows you to explicity
* set all data variables to be logged and in what order, then this is not the class you want to use, and
* one will be coming in the future to do just that.
*
* csvcdr.h
*
*/
#include "baseregistry.h"
#include <switch.h>
#include <iostream>
#include <fstream>
#include <list>
#ifndef CSVCDR
#define CSVCDR
class CsvCDR:public BaseCDR {
public:
CsvCDR();
CsvCDR(switch_mod_cdr_newchannel_t * newchannel);
//CsvCDR(const CsvCDR& copyFrom);
virtual ~ CsvCDR();
virtual bool process_record();
virtual void connect(switch_xml_t & cfg, switch_xml_t & xml, switch_xml_t & settings, switch_xml_t & param); // connect and disconnect need to be static because we're persisting connections until shutdown
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; // Is this module activated?
static bool connectionstate; // What is the status of the connection?
static bool logchanvars;
static modcdr_time_convert_t convert_time;
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::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::string display_name;
char formattedcallstartdate[100];
char formattedcallanswerdate[100];
char formattedcalltransferdate[100];
char formattedcallenddate[100];
static std::ofstream outputfile;
static std::ofstream::pos_type filesize_limit;
void check_file_size_and_open(); // Checks the size of the file, and if it's greater than size allowed, rotates it.
void open_file();
};
#endif
/* For Emacs:
* Local Variables:
* mode:c++
* indent-tabs-mode:t
* tab-width:4
* c-basic-offset:4
* End:
* For VIM:
* vim:set softtabstop=4 shiftwidth=4 tabstop=4 expandtab:
*/

View File

@ -1,388 +0,0 @@
/*
* 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
*
* This code is largely derived from csvcdr.cpp with minor code snippets from mod_xml_curl.c and edited by
* Bret McDanel <trixter AT 0xdecafbad.com>
* 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>
* Bret McDanel <trixter AT 0xdecafbad.com>
* Anthony Minessale II <anthmct@yahoo.com>
* Marcel Barbulescu <marcelbarbulescu@gmail.com>
*
* Description: This C++ source file describes the CurlCDR class that handles processing CDRs to HTTP endpoint.
* This is the standard Curl module, and has a list of predefined variables to log out which can be
* added to, but never have the default ones removed. If you want to use one that allows you to explicity
* set all data variables to be logged and in what order, then this is not the class you want to use, and
* one will be coming in the future to do just that.
*
* curlcdr.cpp
*
*/
#include <switch.h>
#include "curlcdr.h"
#include <string>
#include <curl/curl.h>
CurlCDR::CurlCDR() : BaseCDR()
{
memset(formattedcallstartdate,0,100);
memset(formattedcallanswerdate,0,100);
memset(formattedcallenddate,0,100);
}
CurlCDR::CurlCDR(switch_mod_cdr_newchannel_t *newchannel) : BaseCDR(newchannel)
{
memset(formattedcallstartdate,0,100);
memset(formattedcallanswerdate,0,100);
memset(formattedcalltransferdate,0,100);
memset(formattedcallenddate,0,100);
if(newchannel != 0)
{
switch_time_exp_t tempcallstart, tempcallanswer, tempcalltransfer, tempcallend;
memset(&tempcallstart,0,sizeof(tempcallstart));
memset(&tempcalltransfer,0,sizeof(tempcalltransfer));
memset(&tempcallanswer,0,sizeof(tempcallanswer));
memset(&tempcallend,0,sizeof(tempcallend));
convert_time(&tempcallstart, callstartdate);
convert_time(&tempcallanswer, callanswerdate);
convert_time(&tempcalltransfer, calltransferdate);
convert_time(&tempcallend, callenddate);
// Format the times
apr_size_t retsizecsd, retsizecad, retsizectd, retsizeced; //csd == callstartdate, cad == callanswerdate, ced == callenddate, ceff == callenddate_forfile
char format[] = "%Y-%m-%d %H:%M:%S";
switch_strftime(formattedcallstartdate,&retsizecsd,sizeof(formattedcallstartdate),format,&tempcallstart);
switch_strftime(formattedcallanswerdate,&retsizecad,sizeof(formattedcallanswerdate),format,&tempcallanswer);
switch_strftime(formattedcalltransferdate,&retsizectd,sizeof(formattedcalltransferdate),format,&tempcalltransfer);
switch_strftime(formattedcallenddate,&retsizeced,sizeof(formattedcallenddate),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,0);
}
}
CurlCDR::~CurlCDR()
{
}
bool CurlCDR::activated=0;
bool CurlCDR::logchanvars=0;
bool CurlCDR::connectionstate=0;
modcdr_time_convert_t CurlCDR::convert_time = switch_time_exp_lt;
const char *CurlCDR::gateway_url;
const char *CurlCDR::gateway_credentials;
std::list<std::string> CurlCDR::chanvars_fixed_list;
std::list<std::string> CurlCDR::chanvars_supp_list;
std::string CurlCDR::display_name = "CurlCDR - The HTTP CDR logger";
std::string CurlCDR::postdata;
void CurlCDR::connect(switch_xml_t& cfg, switch_xml_t& xml, switch_xml_t& settings, switch_xml_t& param)
{
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "CurlCDR::connect() - Loading configuration file.\n");
activated = 0; // Set it as inactive initially
connectionstate = 0; // Initialize it to false to show that we aren't yet connected.
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Checking to see if curlcdr is valid\n");
if ((settings = switch_xml_child(cfg, "curlcdr")))
{
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "curlcdr appears to be!!!\n");
int count_config_params = 0; // Need to make sure all params are set before we load
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, "gateway_url"))
{
if(val != 0)
gateway_url = val;
count_config_params++;
}
else if (!strcmp(var, "gateway_credentials"))
{
if(val != 0)
gateway_credentials=val;
count_config_params++;
}
else if (!strcmp(var, "chanvars_supp"))
{
if(val != 0)
{
std::string unparsed;
unparsed = val;
if(unparsed.size() > 0)
{
bool fixed = 0;
parse_channel_variables_xconfig(unparsed,chanvars_supp_list,fixed);
logchanvars=1;
}
}
}
else if (!strcmp(var, "chanvars_fixed"))
{
if(val != 0)
{
std::string unparsed;
unparsed = val;
if(unparsed.size() > 0)
{
bool fixed = 1;
parse_channel_variables_xconfig(unparsed,chanvars_fixed_list,fixed);
logchanvars=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_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "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(strlen(gateway_url))
{
activated = 1;
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "CurlCDR activated");
}
else
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "CurlCDR::connect(): You must specify a gateway_url to have the records logged to.\n");
}
else
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "CurlCDR::connect(): You did not specify the minimum parameters for using this module. You must specify at least a gateway_url to have the records logged to.\n");
}
}
// from Bjarne Stroustrup's page (the guy who designed and implemented C++)
// surely his code has to be better than mine - ok he did say this was the
// *easiest* way and made no comment on it being best :)
std::string CurlCDR::itos(int i)
{
std::stringstream s;
s << i;
return s.str();
}
std::string CurlCDR::lltos(long long ll)
{
std::stringstream s;
s << ll;
return s.str();
}
bool CurlCDR::process_record()
{
CURL *curl_handle = NULL;
static char curl_errorstr[CURL_ERROR_SIZE];
bool retval = 0;
char *curlescaped;
curl_handle = curl_easy_init();
if(!strncasecmp(gateway_url,"https",5)) {
curl_easy_setopt(curl_handle,CURLOPT_SSL_VERIFYPEER,0);
curl_easy_setopt(curl_handle,CURLOPT_SSL_VERIFYHOST,0);
}
// build the HTTP POST data block
// WARNING - We need to make sure that the data added is properly escaped, else some sneaky person could set
// their CLID to "My Name&billusec=0" for example, which would result in a post of
// clid=My+Name&billusec=0
// Simply calculating the billusec in the web app wont solve this because they could play the same
// game with callstarttime and all of that.
//
// The solution I came up with was to use curl_easy_escape() which does a strlen() for every value,
// as well as a malloc of some sort, and thus requires a curl_free(). This is *not* efficient, if
// anyone has a better idea, that would be great. We cant rely on this after the string is assembled
// however because things like the real & would be escaped and that would break things.
//
// To compensate I only do this where its likely to be a problem. Preformatted dates are not likely
// to ever have such data in them.
postdata.append("callstartdate=" + lltos(callstartdate));
postdata.append("&formattedcallstartdate=");
postdata.append(formattedcallstartdate);
postdata.append("&callanswerdate=" + lltos(callanswerdate));
postdata.append("&formattedcallanswerdate=");
postdata.append(formattedcallanswerdate);
postdata.append("&calltransferdate=" + lltos(calltransferdate));
postdata.append("&formattedcalltransferdate=");
postdata.append(formattedcalltransferdate);
postdata.append("&callenddate=" + lltos(callenddate));
postdata.append("&formattedcallenddate=");
postdata.append(formattedcallenddate);
postdata.append("&hangupcause_text=");
postdata.append(hangupcause_text);
postdata.append("&hangupcause=" + itos(hangupcause));
postdata.append("&clid=");
curlescaped = curl_easy_escape(curl_handle,clid,0);
postdata.append(curlescaped);
curl_free(curlescaped);
postdata.append(originated==FALSE?"&originated=0":"&originated=1");
postdata.append("&dialplan=");
curlescaped = curl_easy_escape(curl_handle,dialplan,0); // admin may have used 'bad chars' in the dialplan name
postdata.append(curlescaped);
curl_free(curlescaped);
postdata.append("&myuuid=");
postdata.append(myuuid);
postdata.append("&destuuid=");
postdata.append(destuuid);
postdata.append("&src=");
curlescaped = curl_easy_escape(curl_handle,src,0);
postdata.append(curlescaped);
curl_free(curlescaped);
postdata.append("&dst=");
curlescaped = curl_easy_escape(curl_handle,dst,0);
postdata.append(curlescaped);
curl_free(curlescaped);
postdata.append("&srcchannel=");
curlescaped = curl_easy_escape(curl_handle,srcchannel,0);
postdata.append(curlescaped);
curl_free(curlescaped);
postdata.append("&dstchannel=");
curlescaped = curl_easy_escape(curl_handle,dstchannel,0);
postdata.append(curlescaped);
curl_free(curlescaped);
postdata.append("&ani=");
postdata.append(ani); // this may need to be escaped but is likely to be safe since it should be numeric
postdata.append("&aniii=");
postdata.append(aniii); // this has to be numeric to be valid
postdata.append("&network_addr=");
postdata.append(network_addr);
postdata.append("&lastapp=");
curlescaped = curl_easy_escape(curl_handle,lastapp,0);
postdata.append(curlescaped);
curl_free(curlescaped);
postdata.append("&lastdata=");
curlescaped = curl_easy_escape(curl_handle,lastdata,0);
postdata.append(curlescaped);
curl_free(curlescaped);
postdata.append("&billusec=" + lltos(billusec));
postdata.append("&disposition=" + itos(disposition));
postdata.append("&amaflags=" + itos(amaflags));
// Now to process chanvars, fixed ones first
if(chanvars_fixed.size() > 0 ) {
std::list<std::pair<std::string,std::string> >::iterator iItr, iEnd;
for(iItr = chanvars_fixed.begin(), iEnd = chanvars_fixed.end(); iItr != iEnd; iItr++) {
curlescaped = curl_easy_escape(curl_handle,iItr->second.c_str(),0);
if(curlescaped != (char *)NULL) {
postdata.append("&" + iItr->first + "=" + curlescaped);
curl_free(curlescaped);
}
}
}
if(chanvars_supp.size() > 0 ) {
std::map<std::string,std::string>::iterator iItr,iEnd;
for(iItr = chanvars_supp.begin(), iEnd = chanvars_supp.end() ; iItr != iEnd; iItr++) {
curlescaped = curl_easy_escape(curl_handle,iItr->second.c_str(),0);
if(curlescaped != (char *)NULL) {
postdata.append("&" + iItr->first + "=");
postdata.append(curlescaped);
curl_free(curlescaped);
}
}
}
if(!switch_strlen_zero(gateway_credentials)) {
curl_easy_setopt(curl_handle,CURLOPT_HTTPAUTH,CURLAUTH_ANY);
curl_easy_setopt(curl_handle,CURLOPT_USERPWD,gateway_credentials);
}
curl_easy_setopt(curl_handle,CURLOPT_POST,1);
curl_easy_setopt(curl_handle,CURLOPT_POSTFIELDS,postdata.c_str());
curl_easy_setopt(curl_handle,CURLOPT_URL,gateway_url);
curl_easy_setopt(curl_handle,CURLOPT_USERAGENT,"freeswitch-xml/1.0");
curl_easy_setopt(curl_handle,CURLOPT_ERRORBUFFER,curl_errorstr);
if(curl_easy_perform(curl_handle)) {
switch_log_printf(SWITCH_CHANNEL_LOG,SWITCH_LOG_ERROR,"CurlCDR::process_record() - Error logging CDR record - %s\n",curl_errorstr);
}
curl_easy_cleanup(curl_handle);
retval = 1;
return retval;
}
bool CurlCDR::is_activated()
{
return activated;
}
void CurlCDR::tempdump_record()
{
}
void CurlCDR::reread_tempdumped_records()
{
}
std::string CurlCDR::get_display_name()
{
return display_name;
}
void CurlCDR::disconnect()
{
activated = 0;
logchanvars = 0;
chanvars_fixed_list.clear();
chanvars_supp_list.clear();
connectionstate = 0;
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Shutting down CurlCDR... Done!");
}
AUTO_REGISTER_BASECDR(CurlCDR);
/* 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

@ -1,97 +0,0 @@
/*
* 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
*
* This code is largely derived from csvcdr.cpp with minor code snippets from mod_xml_curl.c and edited by
* Bret McDanel <trixter AT 0xdecafbad.com>
* 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>
* Bret McDanel <trixter AT 0xdecafbad.com>
* Anthony Minessale II <anthmct@yahoo.com>
*
* Description: This C++ source file describes the CurlCDR class that handles processing CDRs to HTTP endpoint.
* This is the standard Curl module, and has a list of predefined variables to log out which can be
* added to, but never have the default ones removed. If you want to use one that allows you to explicity
* set all data variables to be logged and in what order, then this is not the class you want to use, and
* one will be coming in the future to do just that.
*
* curlcdr.h
*
*/
#include "baseregistry.h"
#include <switch.h>
#include <iostream>
#include <fstream>
#include <list>
#include <sstream>
#ifndef CURLCDR
#define CURLCDR
class CurlCDR:public BaseCDR {
public:
CurlCDR();
CurlCDR(switch_mod_cdr_newchannel_t * newchannel);
//CurlCDR(const CurlCDR& copyFrom);
virtual ~ CurlCDR();
virtual bool process_record();
virtual void connect(switch_xml_t & cfg, switch_xml_t & xml, switch_xml_t & settings, switch_xml_t & param); // connect and disconnect need to be static because we're persisting connections until shutdown
virtual void disconnect();
virtual bool is_activated();
virtual void tempdump_record();
virtual void reread_tempdumped_records();
virtual std::string get_display_name();
virtual std::string itos(int i);
virtual std::string lltos(long long ll);
private:
static bool activated; // Is this module activated?
static bool connectionstate; // What is the status of the connection?
static bool logchanvars;
static modcdr_time_convert_t convert_time;
static const char *gateway_url; // The URL to send data to
static const char *gateway_credentials; // The credentials for http auth
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::string display_name;
static std::string postdata;
char formattedcallstartdate[100];
char formattedcallanswerdate[100];
char formattedcalltransferdate[100];
char formattedcallenddate[100];
};
#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

@ -1,176 +0,0 @@
/*
* 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>
* Marcel Barbulescu <marcelbarbulescu@gmail.com>
*
* Description: This source file describes the most basic portions of the CDR module. These are the functions
* and structures that the Freeswitch core looks for when opening up the DSO file to create the load, shutdown
* and runtime threads as necessary.
*
* mod_cdr.cpp
*
*/
#include "cdrcontainer.h"
#include <switch.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
SWITCH_MODULE_LOAD_FUNCTION(mod_cdr_load);
SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_cdr_shutdown);
SWITCH_MODULE_RUNTIME_FUNCTION(mod_cdr_runtime);
SWITCH_MODULE_DEFINITION(mod_cdr, mod_cdr_load, mod_cdr_shutdown, mod_cdr_runtime);
static int RUNNING = 0;
static CDRContainer *newcdrcontainer;
static switch_memory_pool_t *module_pool;
static switch_status_t my_on_hangup(switch_core_session_t *session);
static switch_status_t modcdr_reload(const char *dest, switch_core_session_t *isession, switch_stream_handle_t *stream);
static switch_status_t modcdr_queue_pause(const char *dest, switch_core_session_t *isession, switch_stream_handle_t *stream);
static switch_status_t modcdr_queue_resume(const char *dest, switch_core_session_t *isession, switch_stream_handle_t *stream);
static switch_status_t modcdr_show_active(const char *dest, switch_core_session_t *isession, switch_stream_handle_t *stream);
static switch_status_t modcdr_show_available(const 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.
*/
static const switch_state_handler_table_t state_handlers = {
/*.on_init */ NULL,
/*.on_ring */ NULL,
/*.on_execute */ NULL,
/*.on_hangup */ my_on_hangup,
/*.on_loopback */ NULL,
/*.on_transmit */ NULL
};
static switch_status_t my_on_hangup(switch_core_session_t *session)
{
switch_thread_rwlock_rdlock(cdr_rwlock);
newcdrcontainer->add_cdr(session);
switch_thread_rwlock_unlock(cdr_rwlock);
return SWITCH_STATUS_SUCCESS;
}
#define AVAIL_DESCR "Displays the currently compiled-in mod_cdr backend loggers."
#define ACTIVE_DESCR "Displays the currently active mod_cdr backend loggers."
#define RESUME_DESCR "Manually resumes the popping of objects from the queue."
#define PAUSE_DESCR "Manually pauses the popping of objects from the queue. (DANGER: Can suck your memory away rather quickly.)"
SWITCH_MODULE_LOAD_FUNCTION(mod_cdr_load)
{
switch_api_interface_t *api_interface;
/* connect my internal structure to the blank pointer passed to me */
*module_interface = switch_loadable_module_create_module_interface(pool, modname);
SWITCH_ADD_API(api_interface, "modcdr_reload", "Reload mod_cdr's configuration", modcdr_reload, "");
SWITCH_ADD_API(api_interface, "modcdr_queue_pause", PAUSE_DESCR, modcdr_queue_pause, "");
SWITCH_ADD_API(api_interface, "modcdr_queue_resume", RESUME_DESCR, modcdr_queue_resume, "");
SWITCH_ADD_API(api_interface, "modcdr_show_active", ACTIVE_DESCR, modcdr_show_active, "");
SWITCH_ADD_API(api_interface, "modcdr_show_available", AVAIL_DESCR, modcdr_show_available, "");
switch_core_add_state_handler(&state_handlers);
module_pool = pool;
switch_thread_rwlock_create(&cdr_rwlock,module_pool);
newcdrcontainer = new CDRContainer(module_pool); // Instantiates the new object, automatically loads config
/* indicate that the module should continue to be loaded */
return SWITCH_STATUS_SUCCESS;
}
SWITCH_MODULE_RUNTIME_FUNCTION(mod_cdr_runtime)
{
RUNNING = 1;
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "mod_cdr made it to runtime. Wee!\n");
newcdrcontainer->process_records();
return RUNNING ? SWITCH_STATUS_SUCCESS : SWITCH_STATUS_TERM;
}
SWITCH_STANDARD_API(modcdr_reload)
{
#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;
}
SWITCH_STANDARD_API(modcdr_queue_pause)
{
#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;
}
SWITCH_STANDARD_API(modcdr_queue_resume)
{
#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;
}
SWITCH_STANDARD_API(modcdr_show_active)
{
newcdrcontainer->active(stream);
return SWITCH_STATUS_SUCCESS;
}
SWITCH_STANDARD_API(modcdr_show_available)
{
newcdrcontainer->available(stream);
return SWITCH_STATUS_SUCCESS;
}
SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_cdr_shutdown)
{
delete newcdrcontainer;
switch_thread_rwlock_destroy(cdr_rwlock);
return SWITCH_STATUS_SUCCESS;
}
/* For Emacs:
* Local Variables:
* mode:c++
* indent-tabs-mode:t
* tab-width:4
* c-basic-offset:4
* End:
* For VIM:
* vim:set softtabstop=4 shiftwidth=4 tabstop=4 expandtab:
*/

View File

@ -1,179 +0,0 @@
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="8.00"
Name="mod_cdr"
ProjectGUID="{3D1EED36-A510-4EDB-B4D9-4E0F4A5EC2A8}"
RootNamespace="mod_cdr"
Keyword="Win32Proj"
>
<Platforms>
<Platform
Name="Win32"
/>
</Platforms>
<ToolFiles>
</ToolFiles>
<Configurations>
<Configuration
Name="Debug|Win32"
ConfigurationType="2"
InheritedPropertySheets="..\..\..\..\w32\module_debug.vsprops"
CharacterSet="2"
>
<Tool
Name="VCCLCompilerTool"
AdditionalIncludeDirectories="&quot;C:\Program Files\MySQL\MySQL Server 5.0\include&quot;"
UsePrecompiledHeader="0"
/>
</Configuration>
<Configuration
Name="Release|Win32"
ConfigurationType="2"
InheritedPropertySheets="..\..\..\..\w32\module_release.vsprops"
CharacterSet="2"
>
<Tool
Name="VCCLCompilerTool"
AdditionalIncludeDirectories="&quot;C:\Program Files\MySQL\MySQL Server 5.0\include&quot;"
UsePrecompiledHeader="0"
/>
</Configuration>
<Configuration
Name="Debug with MySql|Win32"
ConfigurationType="2"
InheritedPropertySheets="..\..\..\..\w32\module_debug.vsprops"
CharacterSet="2"
>
<Tool
Name="VCCLCompilerTool"
AdditionalIncludeDirectories="&quot;C:\Program Files\MySQL\MySQL Server 5.0\include&quot;"
UsePrecompiledHeader="0"
/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="mysqlclient.lib ws2_32.lib"
AdditionalLibraryDirectories="&quot;C:\Program Files\MySQL\MySQL Server 5.0\lib\debug&quot;"
/>
</Configuration>
<Configuration
Name="Release with MySql|Win32"
ConfigurationType="2"
InheritedPropertySheets="..\..\..\..\w32\module_release.vsprops"
CharacterSet="2"
>
<Tool
Name="VCCLCompilerTool"
AdditionalIncludeDirectories="&quot;C:\Program Files\MySQL\MySQL Server 5.0\include&quot;"
UsePrecompiledHeader="0"
/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="mysqlclient.lib ws2_32.lib"
AdditionalLibraryDirectories="&quot;C:\Program Files\MySQL\MySQL Server 5.0\lib\opt&quot;"
/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<Filter
Name="Source Files"
Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx"
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
>
<File
RelativePath=".\basecdr.cpp"
>
</File>
<File
RelativePath=".\baseregistry.cpp"
>
</File>
<File
RelativePath=".\cdrcontainer.cpp"
>
</File>
<File
RelativePath=".\csvcdr.cpp"
>
</File>
<File
RelativePath=".\mod_cdr.cpp"
>
</File>
<File
RelativePath=".\mysqlcdr.cpp"
>
<FileConfiguration
Name="Debug|Win32"
ExcludedFromBuild="true"
>
<Tool
Name="VCCLCompilerTool"
/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32"
ExcludedFromBuild="true"
>
<Tool
Name="VCCLCompilerTool"
/>
</FileConfiguration>
</File>
<File
RelativePath=".\pddcdr.cpp"
>
</File>
<File
RelativePath=".\sqlitecdr.cpp"
>
</File>
<File
RelativePath=".\xmlcdr.cpp"
>
</File>
</Filter>
<Filter
Name="Header Files"
Filter="h;hpp;hxx;hm;inl;inc;xsd"
UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
>
<File
RelativePath=".\basecdr.h"
>
</File>
<File
RelativePath=".\baseregistry.h"
>
</File>
<File
RelativePath=".\cdrcontainer.h"
>
</File>
<File
RelativePath=".\csvcdr.h"
>
</File>
<File
RelativePath=".\mysqlcdr.h"
>
</File>
<File
RelativePath=".\pddcdr.h"
>
</File>
<File
RelativePath=".\sqlitecdr.h"
>
</File>
<File
RelativePath=".\xmlcdr.h"
>
</File>
</Filter>
</Files>
<Globals>
</Globals>
</VisualStudioProject>

View File

@ -1,672 +0,0 @@
/*
* 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>
* Marcel Barbulescu <marcelbarbulescu@gmail.com>
*
* Description: This C++ source file describes the MysqlCDR class which handles formatting a CDR out to
* a MySQL 4.1.x or greater server using prepared statements.
*
* mysqlcdr.cpp
*
*/
#ifdef WIN32
#include <Winsock2.h>
#endif
#include <mysql.h>
#include <switch.h>
#include <cstring>
#include <iostream>
#include "mysqlcdr.h"
MysqlCDR::MysqlCDR() : BaseCDR()
{
}
MysqlCDR::MysqlCDR(switch_mod_cdr_newchannel_t *newchannel) : BaseCDR(newchannel)
{
if(newchannel != 0)
{
clid_length = (long unsigned int)strlen(clid);
src_length = (long unsigned int)strlen(src);
dst_length = (long unsigned int)strlen(dst);
ani_length = (long unsigned int)strlen(ani);
aniii_length = (long unsigned int)strlen(aniii);
dialplan_length = (long unsigned int)strlen(dialplan);
myuuid_length = (long unsigned int)strlen(myuuid);
destuuid_length = (long unsigned int)strlen(destuuid);
srcchannel_length = (long unsigned int)strlen(srcchannel);
dstchannel_length = (long unsigned int)strlen(dstchannel);
lastapp_length = (long unsigned int)strlen(lastapp);
lastdata_length = (long unsigned int)strlen(lastdata);
network_addr_length = (long unsigned int)strlen(network_addr);
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);
}
}
MysqlCDR::~MysqlCDR()
{
}
bool MysqlCDR::connectionstate = 0;
bool MysqlCDR::logchanvars = 0;
bool MysqlCDR::repeat_fixed_in_supp = 0;
std::list<std::string> MysqlCDR::chanvars_fixed_list;
std::list<std::string> MysqlCDR::chanvars_supp_list;
std::vector<switch_mod_cdr_sql_types_t> MysqlCDR::chanvars_fixed_types;
bool MysqlCDR::activated = 0;
char MysqlCDR::sql_query[1024] = "";
std::string MysqlCDR::tmp_sql_query;
char MysqlCDR::sql_query_chanvars[100] = "";
MYSQL* MysqlCDR::conn = 0;
MYSQL_STMT* MysqlCDR::stmt=0;
MYSQL_STMT* MysqlCDR::stmt_chanvars=0;
char MysqlCDR::hostname[255] = "";
char MysqlCDR::username[255] ="";
char MysqlCDR::dbname[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;
void MysqlCDR::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, "mysqlcdr")))
{
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, "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,"timezone"))
{
if(!strcmp(val,"utc"))
convert_time = switch_time_exp_gmt;
else if(!strcmp(val,"local"))
convert_time = switch_time_exp_lt;
else
{
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "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)
activated = 1;
else
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "You did not specify the minimum parameters for using this module. You must specify a hostname, username, password, and database to use MysqlCDR. You only supplied %d parameters.\n", count_config_params);
if(activated)
{
tmp_sql_query = "INSERT INTO freeswitchcdr (callstartdate,callanswerdate,calltransferdate,callenddate,originated,clid,src,dst,ani,aniii,dialplan,myuuid,destuuid,srcchannel,dstchannel,network_addr,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(")");
char tempsql_query_chanvars[] = "INSERT INTO chanvars (callid,varname,varvalue) VALUES(?,?,?)";
memset(sql_query_chanvars,0,100);
strncpy(sql_query_chanvars,tempsql_query_chanvars,strlen(tempsql_query_chanvars));
strncpy(sql_query,tmp_sql_query.c_str(),tmp_sql_query.size());
connect_to_database();
}
}
}
void MysqlCDR::connect_to_database()
{
conn = mysql_init(NULL);
mysql_options(conn, MYSQL_READ_DEFAULT_FILE, "");
if(mysql_real_connect(conn,hostname,username,password,dbname,0,NULL,0) == NULL)
{
const char *error1 = mysql_error(conn);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Cannot connect to MySQL Server. The error was: %s\n", error1);
}
else
{
connectionstate = 1;
conn->reconnect = false;
}
mysql_autocommit(conn,0);
stmt = mysql_stmt_init(conn);
mysql_stmt_prepare(stmt,sql_query,(long unsigned int)strlen(sql_query));
if(logchanvars)
{
stmt_chanvars = mysql_stmt_init(conn);
mysql_stmt_prepare(stmt_chanvars,sql_query_chanvars,(long unsigned int)strlen(sql_query_chanvars));
}
}
bool MysqlCDR::is_activated()
{
return activated;
}
template <typename T>
void MysqlCDR::add_parameter(T& param, enum_field_types type, bool *is_null)
{
MYSQL_BIND temp_bind;
memset(&temp_bind,0,sizeof(temp_bind));
temp_bind.buffer_type = type;
if(is_null != 0)
{
if(*is_null)
temp_bind.is_null = (my_bool*) is_null;
else
temp_bind.buffer = &param;
}
else
temp_bind.buffer = &param;
bindme.push_back(temp_bind);
}
template <>
void MysqlCDR::add_parameter<MYSQL_TIME>(MYSQL_TIME& param, enum_field_types type, bool* is_null)
{
MYSQL_BIND temp_bind;
memset(&temp_bind,0,sizeof(temp_bind));
temp_bind.buffer_type = type;
if(is_null != 0)
{
if(*is_null)
temp_bind.is_null = (my_bool*) is_null;
else
temp_bind.buffer = &param;
}
else
temp_bind.buffer = &param;
bindme.push_back(temp_bind);
}
void MysqlCDR::tempdump_record()
{
}
void MysqlCDR::reread_tempdumped_records()
{
}
std::string MysqlCDR::get_display_name()
{
return display_name;
}
bool MysqlCDR::process_record()
{
switch_time_exp_t tm1,tm2,tm3,tm4; // One for call start, answer, transfer, and end
memset(&tm1,0,sizeof(tm1));
memset(&tm2,0,sizeof(tm2));
memset(&tm3,0,sizeof(tm3));
memset(&tm4,0,sizeof(tm4));
convert_time(&tm1,callstartdate);
convert_time(&tm2,callanswerdate);
convert_time(&tm3,calltransferdate);
convert_time(&tm4,callenddate);
set_mysql_time(tm1,my_callstartdate);
set_mysql_time(tm2,my_callanswerdate);
set_mysql_time(tm3,my_calltransferdate);
set_mysql_time(tm4,my_callenddate);
// 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_callanswerdate,MYSQL_TYPE_DATETIME);
add_parameter(my_calltransferdate,MYSQL_TYPE_DATETIME);
add_parameter(my_callenddate,MYSQL_TYPE_DATETIME);
add_parameter(originated,MYSQL_TYPE_TINY);
add_string_parameter(clid,clid_length,MYSQL_TYPE_VAR_STRING,0);
add_string_parameter(src,src_length,MYSQL_TYPE_VAR_STRING,0);
add_string_parameter(dst,dst_length,MYSQL_TYPE_VAR_STRING,0);
add_string_parameter(ani,ani_length,MYSQL_TYPE_VAR_STRING,0);
add_string_parameter(aniii,aniii_length,MYSQL_TYPE_VAR_STRING,0);
add_string_parameter(dialplan,dialplan_length,MYSQL_TYPE_VAR_STRING,0);
add_string_parameter(myuuid,myuuid_length,MYSQL_TYPE_VAR_STRING,0);
add_string_parameter(destuuid,destuuid_length,MYSQL_TYPE_VAR_STRING,0);
add_string_parameter(srcchannel,srcchannel_length,MYSQL_TYPE_VAR_STRING,0);
add_string_parameter(dstchannel,dstchannel_length,MYSQL_TYPE_VAR_STRING,0);
add_string_parameter(network_addr,network_addr_length,MYSQL_TYPE_VAR_STRING,0);
add_string_parameter(lastapp,lastapp_length,MYSQL_TYPE_VAR_STRING,0);
add_string_parameter(lastdata,lastdata_length,MYSQL_TYPE_VAR_STRING,0);
add_parameter(billusec,MYSQL_TYPE_LONGLONG,0);
add_parameter(disposition,MYSQL_TYPE_TINY,0);
add_parameter(hangupcause,MYSQL_TYPE_LONG,0);
add_parameter(amaflags,MYSQL_TYPE_TINY,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;
bool* is_null = new bool;
*is_null = 0;
if(iItr->second.size() > 0)
{
std::istringstream istring(iItr->second);
istring >> *x;
}
else
*is_null = 1;
temp_chanvars_holder.push_back(x);
temp_chanvars_holder.push_back(is_null);
add_parameter(*x,MYSQL_TYPE_LONG,is_null);
break;
}
case CDR_DOUBLE:
{
double* x = new double;
*x = 0;
bool* is_null = new bool;
*is_null = 0;
if(iItr->second.size() > 0)
{
std::istringstream istring(iItr->second);
istring >> *x;
}
else
*is_null = 1;
temp_chanvars_holder.push_back(x);
temp_chanvars_holder.push_back(is_null);
add_parameter(*x,MYSQL_TYPE_DOUBLE,is_null);
break;
}
case CDR_TINY:
{
short* x = new short;
*x = 0;
bool* is_null = new bool;
*is_null = 0;
if(iItr->second.size() > 0)
{
std::istringstream istring(iItr->second);
istring >> *x;
}
else
*is_null = 1;
temp_chanvars_holder.push_back(x);
temp_chanvars_holder.push_back(is_null);
add_parameter(*x,MYSQL_TYPE_TINY,is_null);
break;
}
case CDR_STRING:
case CDR_DECIMAL:
{
long unsigned int* stringlength = new long unsigned int;
*stringlength = (long unsigned int)(iItr->second.size());
char* x = new char[(*stringlength+1)];
strncpy(x,iItr->second.c_str(),*stringlength);
bool* is_null = new bool;
*is_null = 0;
add_string_parameter(x,*stringlength,MYSQL_TYPE_VAR_STRING,is_null);
temp_chanvars_holder.push_back(stringlength);
temp_chanvars_holder.push_back(x);
temp_chanvars_holder.push_back(is_null);
break;
}
default:
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "We should not get to this point in this switch/case statement.\n");
}
i++;
}
}
MYSQL_BIND *bindmetemp;
bindmetemp = new MYSQL_BIND[bindme.size()];
copy(bindme.begin(), bindme.end(), bindmetemp);
for(int mysql_ping_result = -1, count = 0, mysql_stmt_error_code = -1; mysql_ping_result != 0 && count < 5 && mysql_stmt_error_code != 0 ; count++)
{
mysql_ping_result = mysql_ping(conn);
if(mysql_ping_result)
{
switch(mysql_ping_result)
{
case CR_SERVER_GONE_ERROR:
case CR_SERVER_LOST:
{
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "We lost connection to the MySQL server. Trying to reconnect.\n");
connect_to_database();
break;
}
default:
{
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "We have encountered an unknown error when pinging the MySQL server. Attempting to reconnect anyways.\n");
connect_to_database();
}
}
}
else
{
mysql_stmt_bind_param(stmt,bindmetemp);
mysql_stmt_error_code = mysql_stmt_execute(stmt);
if(mysql_stmt_error_code != 0)
{
errorstate = 1;
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "MysqlCDR::process_record() - Statement executed? Error: %d\n", mysql_stmt_error_code);
const char* mysql_stmt_error_string = mysql_stmt_error(stmt);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "MySQL encountered error: %s\n", mysql_stmt_error_string);
}
else
errorstate = 0;
if(logchanvars && chanvars_supp.size() > 0 && errorstate == 0)
{
long long insertid = mysql_stmt_insert_id(stmt);
std::map<std::string,std::string>::iterator iItr,iBeg,iEnd;
iEnd = chanvars_supp.end();
for(iItr = chanvars_supp.begin(); iItr != iEnd; iItr++)
{
MYSQL_BIND bindme_chanvars[3];
memset(bindme_chanvars,0,sizeof(bindme_chanvars));
bindme_chanvars[0].buffer_type = MYSQL_TYPE_LONGLONG;
bindme_chanvars[0].buffer = &insertid;
std::vector<char> tempfirstvector(iItr->first.begin(), iItr->first.end());
tempfirstvector.push_back('\0');
char* varname_temp = &tempfirstvector[0];
bindme_chanvars[1].buffer_type = MYSQL_TYPE_VAR_STRING;
long unsigned int varname_length = (long unsigned int)(iItr->first.size());
bindme_chanvars[1].length = &varname_length;
bindme_chanvars[1].buffer_length = varname_length;
bindme_chanvars[1].buffer = varname_temp;
std::vector<char> tempsecondvector(iItr->second.begin(), iItr->second.end());
tempsecondvector.push_back('\0');
char* varvalue_temp = &tempsecondvector[0];
bindme_chanvars[2].buffer_type = MYSQL_TYPE_VAR_STRING;
if(iItr->second.size() == 0)
bindme_chanvars[2].is_null = (my_bool*)1;
else
{
long unsigned int varvalue_length = (long unsigned int)(iItr->second.size());
bindme_chanvars[2].length = &varvalue_length;
bindme_chanvars[2].buffer_length = varvalue_length;
bindme_chanvars[2].buffer = varvalue_temp;
}
mysql_stmt_bind_param(stmt_chanvars,bindme_chanvars);
mysql_stmt_execute(stmt_chanvars);
}
}
if(errorstate == 0)
mysql_commit(conn);
else
mysql_rollback(conn);
}
}
delete [] bindmetemp;
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:
{
long unsigned int* stringlength = (long unsigned int*)temp_chanvars_holder.front();
temp_chanvars_holder.pop_front();
delete stringlength;
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_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "We should not get to this point in this switch/case statement.\n");
}
bool* tempbool = (bool*) temp_chanvars_holder.front();
temp_chanvars_holder.pop_front();
delete tempbool;
}
}
return 1;
}
void MysqlCDR::disconnect()
{
mysql_stmt_close(stmt);
if(logchanvars)
mysql_stmt_close(stmt_chanvars);
mysql_close(conn);
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();
}
void MysqlCDR::add_string_parameter(char* param, long unsigned int& param_length, enum_field_types type, bool *is_null)
{
MYSQL_BIND temp_bind;
memset(&temp_bind,0,sizeof(temp_bind));
temp_bind.buffer_type = type;
if(is_null != 0)
{
if(*is_null || param == 0)
temp_bind.is_null = (my_bool*) is_null;
else
{
temp_bind.length = &param_length;
temp_bind.buffer_length = param_length;
temp_bind.buffer = param;
}
}
else
{
temp_bind.length = &param_length;
temp_bind.buffer_length = param_length;
temp_bind.buffer = param;
}
bindme.push_back(temp_bind);
}
void MysqlCDR::set_mysql_time(switch_time_exp_t& param, MYSQL_TIME& destination)
{
destination.year = param.tm_year + 1900;
destination.month = param.tm_mon + 1;
destination.day = param.tm_mday;
destination.hour = param.tm_hour;
destination.minute = param.tm_min;
destination.second = param.tm_sec;
}
AUTO_REGISTER_BASECDR(MysqlCDR);
/* For Emacs:
* Local Variables:
* mode:c++
* indent-tabs-mode:t
* tab-width:4
* c-basic-offset:4
* End:
* For VIM:
* vim:set softtabstop=4 shiftwidth=4 tabstop=4 expandtab:
*/

View File

@ -1,117 +0,0 @@
/*
* 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 MysqlCDR class which handles formatting a CDR out to
* a MySQL 4.1.x or greater server using prepared statements.
*
* mysqlcdr.h
*
*/
#include "baseregistry.h"
#include <list>
#include <sstream>
#include <mysql.h>
#include <errmsg.h>
#ifndef MYSQLCDR
#define MYSQLCDR
class MysqlCDR:public BaseCDR {
public:
MysqlCDR();
MysqlCDR(switch_mod_cdr_newchannel_t * newchannel);
//MysqlCDR(const MysqlCDR& copyFrom);
virtual ~ MysqlCDR();
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 std::string tmp_sql_query; // Object must exist to bind the statement, this used for generating the sql
static char sql_query_chanvars[100];
static MYSQL *conn;
static MYSQL_STMT *stmt;
static MYSQL_STMT *stmt_chanvars;
static bool connectionstate;
static bool logchanvars;
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 hostname[255];
static char username[255];
static char dbname[255];
static char password[255];
static modcdr_time_convert_t convert_time;
static std::string display_name;
//static fstream tmpfile;
std::vector < MYSQL_BIND > bindme;
//MYSQL_BIND *bindme;
MYSQL_TIME my_callstartdate;
MYSQL_TIME my_callanswerdate;
MYSQL_TIME my_calltransferdate;
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.
long unsigned int clid_length;
long unsigned int dialplan_length;
long unsigned int myuuid_length;
long unsigned int destuuid_length;
long unsigned int src_length;
long unsigned int dst_length;
long unsigned int srcchannel_length;
long unsigned int dstchannel_length;
long unsigned int ani_length;
long unsigned int aniii_length;
long unsigned int lastapp_length;
long unsigned int lastdata_length;
long unsigned int network_addr_length;
// Now a couple internal methods
template < typename T > void add_parameter(T & param, enum_field_types type, bool * is_null = 0);
void add_string_parameter(char *param, long unsigned int &param_length, enum_field_types type, bool * is_null = 0);
void set_mysql_time(switch_time_exp_t & param, MYSQL_TIME & destination);
void connect_to_database();
};
#endif
/* For Emacs:
* Local Variables:
* mode:c++
* indent-tabs-mode:t
* tab-width:4
* c-basic-offset:4
* End:
* For VIM:
* vim:set softtabstop=4 shiftwidth=4 tabstop=4 expandtab:
*/

View File

@ -1,643 +0,0 @@
/*
* 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>
* Marcel Barbulescu <marcelbarbulescu@gmail.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_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "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_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "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,network_addr,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_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "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_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "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_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "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_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "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_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "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_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "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_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "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_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "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_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "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_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "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_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "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_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "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(network_addr), 0, network_addr, 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_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "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_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "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

@ -1,115 +0,0 @@
/*
* 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

@ -1,265 +0,0 @@
/*
* 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>
* Marcel Barbulescu <marcelbarbulescu@gmail.com>
*
* Description: This C++ source file describes the PddCDR class which handles formatting a CDR out to
* individual text files in a Perl Data Dumper format.
*
* pddcdr.cpp
*
*/
#include <switch.h>
#include "pddcdr.h"
PddCDR::PddCDR() : BaseCDR()
{
memset(formattedcallstartdate,0,100);
memset(formattedcallanswerdate,0,100);
memset(formattedcallenddate,0,100);
}
PddCDR::PddCDR(switch_mod_cdr_newchannel_t *newchannel) : BaseCDR(newchannel)
{
memset(formattedcallstartdate,0,100);
memset(formattedcallanswerdate,0,100);
memset(formattedcallenddate,0,100);
memset(formattedcalltransferdate,0,100);
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
size_t retsizecsd, retsizecad, retsizectd, retsizeced; //csd == callstartdate, cad == callanswerdate, ced == callenddate, ceff == callenddate_forfile
char format[] = "%Y-%m-%d %H:%M:%S";
switch_strftime(formattedcallstartdate,&retsizecsd,sizeof(formattedcallstartdate),format,&tempcallstart);
switch_strftime(formattedcallanswerdate,&retsizecad,sizeof(formattedcallanswerdate),format,&tempcallanswer);
switch_strftime(formattedcalltransferdate,&retsizectd,sizeof(formattedcalltransferdate),format,&tempcalltransfer);
switch_strftime(formattedcallenddate,&retsizeced,sizeof(formattedcallenddate),format,&tempcallend);
std::ostringstream ostring;
ostring << (callenddate/1000000);
std::string callenddate_forfile = ostring.str();
outputfile_name = outputfile_path;
outputfile_name.append(SWITCH_PATH_SEPARATOR);
outputfile_name.append(callenddate_forfile); // Make sorting a bit easier, kinda like Maildir does
outputfile_name.append(".");
outputfile_name.append(myuuid); // The goal is to have a resulting filename of "/path/to/myuuid"
outputfile_name.append(".pdd"); // .pdd - "perl data dumper"
bool repeat_fixed_in_supp = 1;
if(chanvars_supp_list.size() > 0)
process_channel_variables(chanvars_supp_list,chanvars_fixed_list,newchannel->channel,repeat_fixed_in_supp);
}
}
PddCDR::~PddCDR()
{
outputfile.close();
}
bool PddCDR::activated=0;
bool PddCDR::logchanvars=0;
bool PddCDR::connectionstate=0;
modcdr_time_convert_t PddCDR::convert_time = switch_time_exp_lt;
std::string PddCDR::outputfile_path;
std::list<std::string> PddCDR::chanvars_fixed_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)
{
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "PddCDR::connect() - Loading configuration file.\n");
activated = 0; // Set it as inactive initially
connectionstate = 0; // Initialize it to false to show that we aren't yet connected.
if ((settings = switch_xml_child(cfg, "pddcdr")))
{
int count_config_params = 0; // Need to make sure all params are set before we load
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, "path"))
{
if(val != 0)
outputfile_path = val;
count_config_params++;
}
else if (!strcmp(var, "chanvars"))
{
if(val != 0)
{
std::string unparsed;
unparsed = val;
if(unparsed.size() > 0)
{
bool fixed = 0;
parse_channel_variables_xconfig(unparsed,chanvars_supp_list,fixed);
logchanvars=1;
}
}
}
else if (!strcmp(var, "chanvars_fixed"))
{
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "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, "chanvars_supp"))
{
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "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_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "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)
activated = 1;
else
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "PddCDR::connect(): You did not specify the minimum parameters for using this module. You must specify at least a path to have the records logged to.\n");
}
}
bool PddCDR::process_record()
{
outputfile.open(outputfile_name.c_str());
bool retval = 0;
if(!outputfile)
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "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());
else
{
// Format the call record and proceed from here...
outputfile << "$VAR1 = {" << std::endl;
outputfile << "\t\'callstartdate\' => \'" << callstartdate << "\'," << std::endl;
outputfile << "\t\'formattedcallstartdate\' => \'" << formattedcallstartdate << "\'," << 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\'formattedcallenddate\' => \'" << formattedcallenddate << "\'," << std::endl;
outputfile << "\t\'hangupcause\' => \'" << hangupcause_text << "\'," << std::endl;
outputfile << "\t\'hangupcausecode\' => \'" << hangupcause << "\'," << std::endl;
outputfile << "\t\'clid\' => \'" << escape_chararray(clid) << "\'," << std::endl;
outputfile << "\t\'originated\' => \'" << originated << "\'," << std::endl;
outputfile << "\t\'dialplan\' => \'" << dialplan << "\'," << std::endl;
outputfile << "\t\'myuuid\' => \'" << myuuid << "\'," << std::endl;
outputfile << "\t\'destuuid\' => \'" << destuuid << "\'," << std::endl;
outputfile << "\t\'src\' => \'" << src << "\'," << std::endl;
outputfile << "\t\'dst\' => \'" << dst << "\'," << std::endl;
outputfile << "\t\'srcchannel\' => \'" << srcchannel << "\'," << std::endl;
outputfile << "\t\'dstchannel\' => \'" << dstchannel << "\'," << std::endl;
outputfile << "\t\'ani\' => \'" << ani << "\'," << std::endl;
outputfile << "\t\'aniii\' => \'" << aniii << "\'," << std::endl;
outputfile << "\t\'network_addr\' => \'" << network_addr << "\'," << std::endl;
outputfile << "\t\'lastapp\' => \'" << lastapp << "\'," << std::endl;
outputfile << "\t\'lastdata\' => \'" << lastdata << "\'," << std::endl;
outputfile << "\t\'billusec\' => \'" << billusec << "\'," << std::endl;
outputfile << "\t\'disposition\' => \'" << disposition << "\'," << std::endl;
outputfile << "\t\'amaflags\' => \'" << amaflags << "\'," << std::endl;
// Now to process chanvars
outputfile << "\t\'chanvars\' => {" << std::endl;
if(chanvars_supp.size() > 0 )
{
std::map< std::string,std::string >::iterator iItr,iEnd;
for(iItr = chanvars_supp.begin(), iEnd = chanvars_supp.end() ; iItr != iEnd; iItr++)
{
escape_string(iItr->second);
outputfile << "\t\t\'" << iItr->first;
outputfile << "\' => \'" << iItr->second << "\'," << std::endl;
}
}
outputfile << "\t}," << std::endl << "};" << std::endl << std::endl;
retval = 1;
}
return retval;
}
bool PddCDR::is_activated()
{
return activated;
}
void PddCDR::tempdump_record()
{
}
void PddCDR::reread_tempdumped_records()
{
}
std::string PddCDR::get_display_name()
{
return display_name;
}
void PddCDR::disconnect()
{
activated = 0;
connectionstate = 0;
logchanvars = 0;
outputfile_path.clear();
chanvars_supp_list.clear();
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Shutting down PddCDR... Done!");
}
AUTO_REGISTER_BASECDR(PddCDR);
/* For Emacs:
* Local Variables:
* mode:c++
* indent-tabs-mode:t
* tab-width:4
* c-basic-offset:4
* End:
* For VIM:
* vim:set softtabstop=4 shiftwidth=4 tabstop=4 expandtab:
*/

View File

@ -1,86 +0,0 @@
/*
* 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>
* Bret McDanel <trixter AT 0xdecafbad.com>
*
* Description: This C++ header file describes the PddCDR class which handles formatting a CDR out to
* individual text files in a Perl Data Dumper format.
*
* pddcdr.h
*
*/
#include "baseregistry.h"
#include <switch.h>
#include <iostream>
#include <sstream>
#include <fstream>
#include <list>
#ifndef PDDCDR
#define PDDMCDR
class PddCDR:public BaseCDR {
public:
PddCDR();
PddCDR(switch_mod_cdr_newchannel_t * newchannel);
virtual ~ PddCDR();
virtual bool process_record();
virtual void connect(switch_xml_t & cfg, switch_xml_t & xml, switch_xml_t & settings, switch_xml_t & param); // connect and disconnect need to be static because we're persisting connections until shutdown
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; // Is this module activated?
static bool connectionstate; // What is the status of the connection?
static bool logchanvars;
static modcdr_time_convert_t convert_time;
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_supp_list; // This will hold the list for all chanvars here
static std::string display_name;
char formattedcallstartdate[100];
char formattedcallanswerdate[100];
char formattedcalltransferdate[100];
char formattedcallenddate[100];
std::string outputfile_name;
std::ofstream outputfile;
};
#endif
/* For Emacs:
* Local Variables:
* mode:c++
* indent-tabs-mode:t
* tab-width:4
* c-basic-offset:4
* End:
* For VIM:
* vim:set softtabstop=4 shiftwidth=4 tabstop=4 expandtab:
*/

View File

@ -1,4 +0,0 @@
ALTER TABLE freeswitchcdr modify callid bigint unsigned auto_increment;
ALTER TABLE freeswitchcdr Engine=InnoDB;
ALTER TABLE chanvars Engine=InnoDB;

View File

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

View File

@ -1,36 +0,0 @@
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 */
callstartdate datetime NOT NULL,
callanswerdate datetime NOT NULL,
calltransferdate datetime NOT NULL,
callenddate datetime NOT NULL,
originated tinyint default 0,
clid varchar(80) default "Freeswitch - Unknown",
src varchar(80) NOT NULL,
dst varchar(80) NOT NULL,
ani varchar(80) default "",
aniii varchar(80) default "",
dialplan varchar(80) default "",
myuuid char(36) NOT NULL,
destuuid char(36) NOT NULL,
srcchannel varchar(80) NOT NULL,
dstchannel varchar(80) NOT NULL, /* Need to decide - this might be redundant as you can link the records via uuid */
network_addr varchar(40) default "",
lastapp varchar(80) default "",
lastdata varchar(255) default "",
billusec bigint default 0,
disposition tinyint default 0, /* 0 = Busy or Unanswered, 1 = Answered */
hangupcause int default 0,
amaflags tinyint default 0
);
create index myuuid_index on freeswitchcdr (myuuid);
create index destuuid_index on freeswitchcdr (destuuid);
create table chanvars (
callid bigint unsigned default 0,
varname varchar(80) NOT NULL,
varvalue varchar(255) default ""
);
create index callid_index on chanvars(callid,varname);

View File

@ -1,597 +0,0 @@
/*
* 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>
* Marcel Barbulescu <marcelbarbulescu@gmail.com>
*
* Description: his C++ header file describes the SqliteCDR class which handles formatting a CDR out to
* a SQLite database using prepared statements.
*
* sqlitecdr.cpp
*
*/
#include <switch.h>
#include <cstring>
#include <iostream>
#include "sqlitecdr.h"
SqliteCDR::SqliteCDR() : BaseCDR()
{
}
SqliteCDR::SqliteCDR(switch_mod_cdr_newchannel_t *newchannel) : BaseCDR(newchannel)
{
if(newchannel != 0)
{
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);
}
}
SqliteCDR::~SqliteCDR()
{
}
bool SqliteCDR::connectionstate = 0;
bool SqliteCDR::logchanvars = 0;
bool SqliteCDR::repeat_fixed_in_supp = 0;
std::list<std::string> SqliteCDR::chanvars_fixed_list;
std::list<std::string> SqliteCDR::chanvars_supp_list;
std::vector<switch_mod_cdr_sql_types_t> SqliteCDR::chanvars_fixed_types;
bool SqliteCDR::activated = 0;
char SqliteCDR::sql_query[1024] = "";
std::string SqliteCDR::tmp_sql_query;
char SqliteCDR::sql_query_chanvars[100] = "";
std::string SqliteCDR::db_filename;
switch_core_db_t* SqliteCDR::db = 0;
switch_core_db_stmt_t* SqliteCDR::stmt=0;
switch_core_db_stmt_t* SqliteCDR::stmt_chanvars=0;
switch_core_db_stmt_t* SqliteCDR::stmt_begin=0;
switch_core_db_stmt_t* SqliteCDR::stmt_commit=0;
bool SqliteCDR::use_utc_time = 0;
std::string SqliteCDR::display_name = "SqliteCDR - The sqlite3 cdr logging backend";
void SqliteCDR::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, "sqlitecdr")))
{
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, "path"))
{
if(val != 0)
{
db_filename = val;
db_filename.append(SWITCH_PATH_SEPARATOR);
db_filename.append("sqlitecdr.db");
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,"timezone"))
{
if(!strcmp(val,"utc"))
use_utc_time = 1;
else if(!strcmp(val,"local"))
use_utc_time = 0;
else
{
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid configuration parameter for timezone. Possible values are utc and local. You entered: %s\nDefaulting to local.\n", val);
use_utc_time = 0;
}
}
}
if (count_config_params==1)
activated = 1;
else
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "You did not specify the minimum parameters for using this module. You must specify an explicit (complete) path to the location of the database file in order to use SqliteCDR.\n");
if(activated)
{
tmp_sql_query = "INSERT INTO freeswitchcdr (callstartdate,callanswerdate,calltransferdate,callenddate,originated,clid,src,dst,ani,aniii,dialplan,myuuid,destuuid,srcchannel,dstchannel,network_addr,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(")");
char tempsql_query_chanvars[] = "INSERT INTO chanvars (callid,varname,varvalue) VALUES(?,?,?)";
memset(sql_query_chanvars,0,100);
strncpy(sql_query_chanvars,tempsql_query_chanvars,strlen(tempsql_query_chanvars));
strncpy(sql_query,tmp_sql_query.c_str(),tmp_sql_query.size());
int sql_rc = switch_core_db_open(db_filename.c_str(),&db);
if(sql_rc != SWITCH_CORE_DB_OK)
{
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "There was an error opening database filename %s. The error was: %s. SqliteCDR logging has been disabled until the problem is resolved and modcdr_reload is initiated.\n", db_filename.c_str(), switch_core_db_errmsg(db));
activated = 0;
switch_core_db_close(db);
}
else
{
char sql_query_check_tables[] = "SELECT name FROM sqlite_master WHERE type = \"table\"";
char **result;
int nrow = 0, ncol = 0;
char *errormessage;
sql_rc = switch_core_db_get_table(db,sql_query_check_tables,&result,&nrow,&ncol,&errormessage);
std::map<std::string,bool> temp_chanvars_map;
// Now to copy out all the chanvars from the list into
std::map<std::string,bool> temp_sql_tables;
temp_sql_tables["freeswitchcdr"] = 0;
temp_sql_tables["chanvars"] = 0;
if(sql_rc == SWITCH_CORE_DB_OK)
{
for(int i = 0; i < ((nrow+1)*ncol); i++)
{
std::string tablename = result[i];
if(tablename == "freeswitchcdr" || tablename == "chanvars")
{
temp_sql_tables[tablename] = 1;
}
}
}
else
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "There was an error in executing query %s: The error was %s.\n", sql_query_check_tables, errormessage);
switch_core_db_free_table(result);
if(!temp_sql_tables["freeswitchcdr"])
{
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Creating the freeswitchcdr table in the SQLite mod_cdr database file.\n");
// Must create the missing freeswitchcdr table.
char sql_query_create_freeswitchcdr[] = "CREATE TABLE freeswitchcdr (\n"
"callid INTEGER PRIMARY KEY AUTOINCREMENT,\n"
"callstartdate INTEGER NOT NULL,\n"
"callanswerdate INTEGER NOT NULL,\n"
"calltransferdate INTEGER NOT NULL,\n"
"callenddate INTEGER NOT NULL,\n"
"originated INTEGER default 0,\n"
"clid TEXT default \"Freeswitch - Unknown\",\n"
"src TEXT NOT NULL,\n"
"dst TEXT NOT NULL,\n"
"ani TEXT default \"\",\n"
"aniii TEXT default \"\",\n"
"dialplan TEXT default \"\",\n"
"myuuid TEXT NOT NULL,\n"
"destuuid TEXT NOT NULL,\n"
"srcchannel TEXT NOT NULL,\n"
"dstchannel TEXT NOT NULL,\n"
"network_addr TEXT,\n"
"lastapp TEXT default \"\",\n"
"lastdata TEXT default \"\",\n"
"billusec INTEGER default 0,\n"
"disposition INTEGER default 0,\n"
"hangupcause INTEGER default 0,\n"
"amaflags INTEGER default 0\n"
");\n";
switch_core_db_exec(db, sql_query_create_freeswitchcdr, NULL, NULL, NULL);
}
if(!temp_sql_tables["chanvars"])
{
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Creating the chanvars table in the SQLite mod_cdr database file.\n");
// Must create the missing chanvars table.
char sql_query_create_chanvars[] = "CREATE TABLE chanvars (\n"
"callid INTEGER default 0,\n"
"varname TEXT NOT NULL,\n"
"varvalue TEXT default \"\"\n"
");\n";
switch_core_db_exec(db, sql_query_create_chanvars, NULL, NULL, NULL);
}
if(chanvars_fixed_list.size() > 0)
{
// Now to check if the freeswitchcdr schema matches
std::map<std::string,std::string> freeswitchcdr_columns;
char sql_query_get_schema_of_freeswitchcdr[] = "SELECT sql FROM SQLITE_MASTER WHERE name=\"freeswitchcdr\"";
char **result2;
nrow = 0;
ncol = 0;
char *errormessage2;
sql_rc = switch_core_db_get_table(db,sql_query_get_schema_of_freeswitchcdr,&result2,&nrow,&ncol,&errormessage2);
if(sql_rc == SWITCH_CORE_DB_OK)
{
for(int k = 0; k < nrow; k++)
{
// Warning - this is slightly ugly for string parsing
std::string resultstring = result2[1];
std::string::size_type j = resultstring.find('(',0);
j = resultstring.find('\n',j);
std::string::size_type h = 0;
std::string tempstring1,tempstring2;
for(std::string::size_type i = j+1 ; j != std::string::npos; )
{
j = resultstring.find(' ',i);
if(j > 0)
{
if(j == i)
{
i++;
j = resultstring.find(' ',(i));
}
tempstring1 = resultstring.substr(i,(j-i));
i = j+1;
j =resultstring.find(',',i);
h = resultstring.find(' ',i);
if(j == std::string::npos)
tempstring2 = resultstring.substr(i,(resultstring.size() - i));
else if(j > h)
tempstring2 = resultstring.substr(i,(h-i));
else
tempstring2 = resultstring.substr(i,(j-i));
freeswitchcdr_columns[tempstring1] = tempstring2;
//switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "tempstring1 = %s, tempstring2 = %s\n", tempstring1.c_str(), tempstring2.c_str());
if(resultstring.find('\n',j+1) == (j+1))
j++;
i = j+1;
}
else
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "There has been a parsing problem with the freeswitchcdr schema.\n");
}
}
}
switch_core_db_free_table(result2);
// Now to actually compare what we have in the config against the db schema
std::map<std::string,std::string> freeswitchcdr_add_columns;
std::list<std::string>::iterator iItr, iEnd;
switch_size_t i = 0;
for(iItr = chanvars_fixed_list.begin(), iEnd = chanvars_fixed_list.end(); iItr != iEnd; iItr++, i++)
{
switch(chanvars_fixed_types[i])
{
case CDR_INTEGER:
case CDR_TINY:
if(freeswitchcdr_columns.find(*iItr) != freeswitchcdr_columns.end())
{
//switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "freeswitchcdr_columns[%s] == %s.\n", iItr->c_str(), freeswitchcdr_columns[*iItr].c_str());
if(freeswitchcdr_columns[*iItr].find("INTEGER",0) == std::string::npos)
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "SqliteCDR freeswitchcdr table column type mismatch: Column \"%s\" is not of an INTEGER type. This is not necessarily fatal, but may result in unexpected behavior.\n", iItr->c_str());
}
else
freeswitchcdr_add_columns[*iItr] = "INTEGER";
break;
case CDR_DOUBLE:
if(freeswitchcdr_columns.find(*iItr) != freeswitchcdr_columns.end())
{
if(freeswitchcdr_columns[*iItr].find("REAL",0) == std::string::npos)
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "SqliteCDR freeswitchcdr table column type mismatch: Column \"%s\" is not of a REAL type. This is not necessarily fatal, but may result in unexpected behavior.\n", iItr->c_str());
}
else
freeswitchcdr_add_columns[*iItr] = "REAL";
break;
case CDR_DECIMAL:
case CDR_STRING:
if(freeswitchcdr_columns.find(*iItr) != freeswitchcdr_columns.end())
{
if(freeswitchcdr_columns[*iItr].find("TEXT",0) == std::string::npos)
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "SqliteCDR freeswitchcdr table column type mismatch: Column \"%s\" is not of a TEXT type. This is not necessarily fatal, but may result in unexpected behavior.\n", iItr->c_str());
}
else
freeswitchcdr_add_columns[*iItr] = "TEXT";
break;
default:
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Oh bother, I should not have fallen into this hole in the switch/case statement. Please notify the author.\n");
}
}
if(freeswitchcdr_add_columns.size())
{
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Updating the freeswitchcdr table schema.\n");
std::string tempsql_freeswitchcdr_alter_table = "ALTER TABLE freeswitchcdr ADD ";
std::map<std::string, std::string>::iterator iItr, iEnd;
for(iItr = freeswitchcdr_add_columns.begin(), iEnd = freeswitchcdr_add_columns.end(); iItr != iEnd; iItr++)
{
std::string sql_query_freeswitchcdr_alter_table = tempsql_freeswitchcdr_alter_table;
sql_query_freeswitchcdr_alter_table.append(iItr->first);
sql_query_freeswitchcdr_alter_table.append(" ");
sql_query_freeswitchcdr_alter_table.append(iItr->second);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Updating the freeswitchcdr table with the following SQL command: %s.\n", sql_query_freeswitchcdr_alter_table.c_str());
switch_core_db_exec(db, sql_query_freeswitchcdr_alter_table.c_str(), NULL, NULL, NULL);
}
}
}
switch_core_db_prepare(db,"BEGIN TRANSACTION SqliteCDR",-1,&stmt_begin,0);
switch_core_db_prepare(db,"COMMIT TRANSACTION SqliteCDR",-1,&stmt_commit,0);
switch_core_db_prepare(db,tmp_sql_query.c_str(),-1,&stmt,0);
if(chanvars_supp_list.size())
switch_core_db_prepare(db,sql_query_chanvars,-1,&stmt_chanvars,0);
}
}
}
}
bool SqliteCDR::is_activated()
{
return activated;
}
void SqliteCDR::tempdump_record()
{
}
void SqliteCDR::reread_tempdumped_records()
{
}
bool SqliteCDR::process_record()
{
if(use_utc_time)
{
switch_time_exp_t tm1, tm2, tm3, tm4;
memset(&tm1,0,sizeof(tm1));
memset(&tm2,0,sizeof(tm2));
memset(&tm3,0,sizeof(tm3));
memset(&tm4,0,sizeof(tm4));
switch_time_exp_gmt(&tm1,callstartdate);
switch_time_exp_gmt(&tm2,callanswerdate);
switch_time_exp_gmt(&tm3,calltransferdate);
switch_time_exp_gmt(&tm4,calltransferdate);
switch_time_exp_gmt_get(&sqlite_callstartdate,&tm1);
switch_time_exp_gmt_get(&sqlite_callanswerdate,&tm2);
switch_time_exp_gmt_get(&sqlite_calltransferdate,&tm3);
switch_time_exp_gmt_get(&sqlite_callenddate,&tm4);
}
else
{
sqlite_callstartdate = callstartdate;
sqlite_callanswerdate = callanswerdate;
sqlite_calltransferdate = calltransferdate;
sqlite_callenddate = callenddate;
}
int column = 1;
switch_core_db_step(stmt_begin);
switch_core_db_reset(stmt_begin);
switch_core_db_bind_int64(stmt, column++, sqlite_callstartdate);
switch_core_db_bind_int64(stmt, column++, sqlite_callanswerdate);
switch_core_db_bind_int64(stmt, column++, sqlite_calltransferdate);
switch_core_db_bind_int64(stmt, column++, sqlite_callenddate);
switch_core_db_bind_int(stmt, column++, (int) originated);
switch_core_db_bind_text(stmt, column++, clid,-1,SWITCH_CORE_DB_STATIC);
switch_core_db_bind_text(stmt, column++, src,-1,SWITCH_CORE_DB_STATIC);
switch_core_db_bind_text(stmt, column++, dst,-1,SWITCH_CORE_DB_STATIC);
switch_core_db_bind_text(stmt, column++, ani,-1,SWITCH_CORE_DB_STATIC);
switch_core_db_bind_text(stmt, column++, aniii,-1,SWITCH_CORE_DB_STATIC);
switch_core_db_bind_text(stmt, column++, dialplan,-1,SWITCH_CORE_DB_STATIC);
switch_core_db_bind_text(stmt, column++, myuuid,36,SWITCH_CORE_DB_STATIC);
switch_core_db_bind_text(stmt, column++, destuuid,36,SWITCH_CORE_DB_STATIC);
switch_core_db_bind_text(stmt, column++, srcchannel,-1,SWITCH_CORE_DB_STATIC);
switch_core_db_bind_text(stmt, column++, dstchannel,-1,SWITCH_CORE_DB_STATIC);
switch_core_db_bind_text(stmt, column++, network_addr,-1,SWITCH_CORE_DB_STATIC);
switch_core_db_bind_text(stmt, column++, lastapp,-1,SWITCH_CORE_DB_STATIC);
switch_core_db_bind_text(stmt, column++, lastdata,-1,SWITCH_CORE_DB_STATIC);
switch_core_db_bind_int64(stmt, column++, billusec);
switch_core_db_bind_int(stmt, column++, disposition);
switch_core_db_bind_int(stmt, column++, (int) hangupcause);
switch_core_db_bind_int(stmt, column++, amaflags);
if(chanvars_fixed.size())
{
std::list< std::pair<std::string,std::string> >::iterator iItr, iEnd;
int count = 0;
for(iItr = chanvars_fixed.begin(), iEnd = chanvars_fixed.end(); iItr != iEnd; iItr++, count++)
{
switch(chanvars_fixed_types[count])
{
case CDR_INTEGER:
case CDR_TINY:
{
int x;
if(iItr->second.size() > 0)
{
std::istringstream istring(iItr->second);
istring >> x;
}
else
x = 0;
switch_core_db_bind_int(stmt,column++,x);
break;
}
case CDR_DOUBLE:
{
double x = 0;
if(iItr->second.size() > 0)
{
std::istringstream istring(iItr->second);
istring >> x;
}
switch_core_db_bind_double(stmt,column++,x);
break;
}
case CDR_DECIMAL:
case CDR_STRING:
{
switch_core_db_bind_text(stmt,column++,iItr->second.c_str(),-1,SWITCH_CORE_DB_STATIC);
break;
}
default:
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Oh bother, I should not have fallen into this hole in the switch/case statement. Please notify the author.\n");
}
}
}
int sql_rc = switch_core_db_step(stmt);
if(sql_rc != SWITCH_CORE_DB_DONE)
{
if(sql_rc == SWITCH_CORE_DB_BUSY)
sql_rc = switch_core_db_step(stmt);
else if (sql_rc == SWITCH_CORE_DB_ERROR || sql_rc == SWITCH_CORE_DB_MISUSE)
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "There was an error executing switch_core_db_step on SqliteCDR::stmt. The error was: %s\n", switch_core_db_errmsg(db));
}
sql_rc = switch_core_db_reset(stmt);
if(logchanvars && chanvars_supp.size())
{
int64_t rowid = switch_core_db_last_insert_rowid(db);
int column2 = 1;
std::map<std::string,std::string>::iterator iItr, iEnd;
for(iItr = chanvars_supp.begin(), iEnd = chanvars_supp.end(); iItr != iEnd; iItr++)
{
switch_core_db_bind_int64(stmt_chanvars, column2++, rowid);
switch_core_db_bind_text(stmt_chanvars, column2++, iItr->first.c_str(),-1,SWITCH_CORE_DB_STATIC);
switch_core_db_bind_text(stmt_chanvars, column2++, iItr->second.c_str(),-1,SWITCH_CORE_DB_STATIC);
int sql_rc = switch_core_db_step(stmt_chanvars);
if(sql_rc != SWITCH_CORE_DB_DONE)
{
if(sql_rc == SWITCH_CORE_DB_BUSY)
sql_rc = switch_core_db_step(stmt_chanvars);
else if (sql_rc == SWITCH_CORE_DB_ERROR || sql_rc == SWITCH_CORE_DB_MISUSE)
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "There was an error executing switch_core_db_step on SqliteCDR::stmt_chanvars. The error was: %s\n", switch_core_db_errmsg(db));
}
switch_core_db_reset(stmt_chanvars);
}
}
switch_core_db_step(stmt_commit);
switch_core_db_reset(stmt_commit);
return 1;
}
void SqliteCDR::disconnect()
{
switch_core_db_finalize(stmt_chanvars);
switch_core_db_finalize(stmt);
switch_core_db_finalize(stmt_begin);
switch_core_db_finalize(stmt_commit);
switch_core_db_close(db);
activated = 0;
logchanvars = 0;
chanvars_fixed_list.clear();
chanvars_supp_list.clear();
chanvars_fixed_types.clear();
connectionstate = 0;
tmp_sql_query.clear();
}
std::string SqliteCDR::get_display_name()
{
return display_name;
}
AUTO_REGISTER_BASECDR(SqliteCDR);
/* 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

@ -1,92 +0,0 @@
/*
* 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 SqliteCDR class which handles formatting a CDR out to
* a SQLite database using prepared statements.
*
* sqlitecdr.h
*
*/
#include "baseregistry.h"
#include <list>
#include <sstream>
#ifndef SQLITECDR
#define SQLITECDR
class SqliteCDR:public BaseCDR {
public:
SqliteCDR();
SqliteCDR(switch_mod_cdr_newchannel_t * newchannel);
//SqliteCDR(const SqliteCDR& copyFrom);
virtual ~ SqliteCDR();
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 std::string tmp_sql_query; // Object must exist to bind the statement, this used for generating the sql
static char sql_query_chanvars[100];
static std::string db_filename;
static bool use_utc_time;
switch_time_t sqlite_callstartdate;
switch_time_t sqlite_callanswerdate;
switch_time_t sqlite_calltransferdate;
switch_time_t sqlite_callenddate;
static switch_core_db_t *db;
static switch_core_db_stmt_t *stmt;
static switch_core_db_stmt_t *stmt_chanvars;
static switch_core_db_stmt_t *stmt_begin;
static switch_core_db_stmt_t *stmt_commit;
static bool connectionstate;
static bool logchanvars;
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 std::string display_name;
};
#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

@ -1,267 +0,0 @@
/*
* 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>
* Ken Rice of Asteria Solutions Group, INC <ken AT asteriasgi.com>
* Marcel Barbulescu <marcelbarbulescu@gmail.com>
*
* Description: This C++ source file describes the XmlCDR class which handles formatting a CDR out to
* individual text files in a XML format.
*
* xmlcdr.cpp
*
*/
#include <switch.h>
#include "xmlcdr.h"
XmlCDR::XmlCDR() : BaseCDR()
{
memset(formattedcallstartdate,0,100);
memset(formattedcallanswerdate,0,100);
memset(formattedcallenddate,0,100);
}
XmlCDR::XmlCDR(switch_mod_cdr_newchannel_t *newchannel) : BaseCDR(newchannel)
{
memset(formattedcallstartdate,0,100);
memset(formattedcallanswerdate,0,100);
memset(formattedcallenddate,0,100);
if(newchannel != 0)
{
switch_time_exp_t tempcallstart, tempcallanswer, tempcalltransfer, tempcallend;
memset(&tempcallstart,0,sizeof(tempcallstart));
memset(&tempcallanswer,0,sizeof(tempcallanswer));
memset(&tempcallend,0,sizeof(tempcallend));
memset(&tempcalltransfer,0,sizeof(tempcalltransfer));
convert_time(&tempcallstart, callstartdate);
convert_time(&tempcallanswer, callanswerdate);
convert_time(&tempcalltransfer, calltransferdate);
convert_time(&tempcallend, callenddate);
// Format the times
size_t retsizecsd, retsizecad, retsizectd, retsizeced; //csd == callstartdate, cad == callanswerdate, ced == callenddate, ceff == callenddate_forfile
char format[] = "%Y-%m-%d %H:%M:%S";
switch_strftime(formattedcallstartdate,&retsizecsd,sizeof(formattedcallstartdate),format,&tempcallstart);
switch_strftime(formattedcallanswerdate,&retsizecad,sizeof(formattedcallanswerdate),format,&tempcallanswer);
switch_strftime(formattedcalltransferdate,&retsizectd,sizeof(formattedcalltransferdate),format,&tempcalltransfer);
switch_strftime(formattedcallenddate,&retsizeced,sizeof(formattedcallenddate),format,&tempcallend);
std::ostringstream ostring;
ostring << (callenddate/1000000);
std::string callenddate_forfile = ostring.str();
outputfile_name = outputfile_path;
outputfile_name.append(SWITCH_PATH_SEPARATOR);
outputfile_name.append(callenddate_forfile); // Make sorting a bit easier, kinda like Maildir does
outputfile_name.append(".");
outputfile_name.append(myuuid); // The goal is to have a resulting filename of "/path/to/myuuid"
outputfile_name.append(".xml"); // .xml - "XML Data Dumper"
bool repeat_fixed_in_supp = 1;
if(chanvars_supp_list.size() > 0)
process_channel_variables(chanvars_supp_list,chanvars_fixed_list,newchannel->channel,repeat_fixed_in_supp);
}
}
XmlCDR::~XmlCDR()
{
outputfile.close();
}
bool XmlCDR::activated=0;
bool XmlCDR::logchanvars=0;
bool XmlCDR::connectionstate=0;
modcdr_time_convert_t XmlCDR::convert_time = switch_time_exp_lt;
std::string XmlCDR::outputfile_path;
std::list<std::string> XmlCDR::chanvars_fixed_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)
{
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "XmlCDR::connect() - Loading configuration file.\n");
activated = 0; // Set it as inactive initially
connectionstate = 0; // Initialize it to false to show that we aren't yet connected.
if ((settings = switch_xml_child(cfg, "xmlcdr")))
{
int count_config_params = 0; // Need to make sure all params are set before we load
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, "path"))
{
if(val != 0)
outputfile_path = val;
count_config_params++;
}
else if (!strcmp(var, "chanvars"))
{
if(val != 0)
{
std::string unparsed;
unparsed = val;
if(unparsed.size() > 0)
{
bool fixed = 0;
parse_channel_variables_xconfig(unparsed,chanvars_supp_list,fixed);
logchanvars=1;
}
}
}
else if (!strcmp(var, "chanvars_fixed"))
{
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "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, "chanvars_supp"))
{
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "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_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "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)
activated = 1;
else
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "XmlCDR::connect(): You did not specify the minimum parameters for using this module. You must specify at least a path to have the records logged to.\n");
}
}
bool XmlCDR::process_record()
{
bool retval = 0;
outputfile.open(outputfile_name.c_str());
if(!outputfile)
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "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
{
//switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "XmlCDR::process_record(): Preping the CDR to %s.\n", outputfile_name.c_str());
// Format the call record and proceed from here...
outputfile << "<?xml version=\"1.0\"?>" << std::endl;
outputfile << "<document type=\"freeswitch-cdr/xml\">" << 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<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<formattedcallenddate data=\"" << formattedcallenddate << "\" />" << std::endl;
outputfile << "\t<hangupcause data=\"" << hangupcause_text << "\" />" << std::endl;
outputfile << "\t<hangupcausecode data=\"" << hangupcause << "\" />" << std::endl;
outputfile << "\t<clid data=\"" << clid << "\" />" << std::endl;
outputfile << "\t<originated data=\"" << originated << "\" />" << std::endl;
outputfile << "\t<dialplan data=\"" << dialplan << "\" />" << std::endl;
outputfile << "\t<myuuid data=\"" << myuuid << "\" />" << std::endl;
outputfile << "\t<destuuid data=\"" << destuuid << "\" />" << std::endl;
outputfile << "\t<src data=\"" << src << "\" />" << std::endl;
outputfile << "\t<dst data=\"" << dst << "\" />" << std::endl;
outputfile << "\t<srcchannel data=\"" << srcchannel << "\" />" << std::endl;
outputfile << "\t<dstchannel data=\"" << dstchannel << "\" />" << std::endl;
outputfile << "\t<ani data=\"" << ani << "\" />" << std::endl;
outputfile << "\t<aniii data=\"" << aniii << "\" />" << std::endl;
outputfile << "\t<network_addr data=\"" << network_addr << "\" />" << std::endl;
outputfile << "\t<lastapp data=\"" << lastapp << "\" />" << std::endl;
outputfile << "\t<lastdata data=\"" << lastdata << "\" />" << std::endl;
outputfile << "\t<billusec data=\"" << billusec << "\" />" << std::endl;
outputfile << "\t<disposition data=\"" << disposition << "\" />" << std::endl;
outputfile << "\t<amaflags data=\"" << amaflags << "\" />" << std::endl;
// Now to process chanvars
outputfile << "\t<chanvars>" << std::endl;
if(chanvars_supp.size() > 0 )
{
std::map<std::string,std::string>::iterator iItr,iEnd;
for(iItr = chanvars_supp.begin(), iEnd = chanvars_supp.end() ; iItr != iEnd; iItr++)
outputfile << "\t\t<variable name=\"" << iItr->first << "\" data= \"" << iItr->second << "\" />" << std::endl;
}
outputfile << "\t</chanvars>" << std::endl << "</document>" << std::endl << std::endl;
//switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "XmlCDR::process_record(): Dumping the CDR to %s.\n", outputfile_name.c_str());
retval = 1;
}
return retval;
}
bool XmlCDR::is_activated()
{
return activated;
}
void XmlCDR::tempdump_record()
{
}
void XmlCDR::reread_tempdumped_records()
{
}
std::string XmlCDR::get_display_name()
{
return display_name;
}
void XmlCDR::disconnect()
{
activated = 0;
connectionstate = 0;
logchanvars = 0;
outputfile_path.clear();
chanvars_supp_list.clear();
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Shutting down XmlCDR... Done!");
}
AUTO_REGISTER_BASECDR(XmlCDR);
/* For Emacs:
* Local Variables:
* mode:c++
* indent-tabs-mode:t
* tab-width:4
* c-basic-offset:4
* End:
* For VIM:
* vim:set softtabstop=4 shiftwidth=4 tabstop=4 expandtab:
*/

View File

@ -1,87 +0,0 @@
/*
* 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>
* Ken Rice, Asteria Solutions Group, Inc. <ken AT asteriasgi.com>
*
* Description: This C++ header file describes the XmlCDR class which handles formatting a CDR out to
* individual text files in a XML format.
*
* xmlcdr.h
*
*/
#include "baseregistry.h"
#include <switch.h>
#include <iostream>
#include <sstream>
#include <fstream>
#include <list>
#ifndef XMLCDR
#define XMLCDR
class XmlCDR:public BaseCDR {
public:
XmlCDR();
XmlCDR(switch_mod_cdr_newchannel_t * newchannel);
virtual ~ XmlCDR();
virtual bool process_record();
virtual void connect(switch_xml_t & cfg, switch_xml_t & xml, switch_xml_t & settings, switch_xml_t & param); // connect and disconnect need to be static because we're persisting connections until shutdown
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; // Is this module activated?
static bool connectionstate; // What is the status of the connection?
static bool logchanvars;
static modcdr_time_convert_t convert_time;
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_supp_list; // This will hold the list for all chanvars here
static std::string display_name;
char formattedcallstartdate[100];
char formattedcallanswerdate[100];
char formattedcalltransferdate[100];
char formattedcallenddate[100];
std::string outputfile_name;
std::ofstream outputfile;
};
#endif
/* For Emacs:
* Local Variables:
* mode:c++
* indent-tabs-mode:t
* tab-width:4
* c-basic-offset:4
* End:
* For VIM:
* vim:set softtabstop=4 shiftwidth=4 tabstop=4 expandtab:
*/