Merged changes in from my branch at r4020.

git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@4022 d0543943-73ff-0310-b7d9-9358b9ac24b2
This commit is contained in:
Yossi Neiman 2007-01-22 22:03:56 +00:00
parent d3e1f400e5
commit c5d86f3e6f
16 changed files with 1274 additions and 41 deletions

View File

@ -1,6 +1,8 @@
CFLAGS += $(shell mysql_config --include)
CFLAGS += -DSWITCH_QUEUE_ENHANCED
LDFLAGS += $(shell mysql_config --libs)
#LDFLAGS += -lcurl
CPPCC = g++
OBJS=cdrcontainer.o basecdr.o baseregistry.o mysqlcdr.o pddcdr.o csvcdr.o xmlcdr.o

View File

@ -82,6 +82,14 @@ Configuration: Section <odbccdr>
<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:

View File

@ -60,6 +60,7 @@ 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);
@ -132,7 +133,6 @@ BaseCDR::BaseCDR(switch_mod_cdr_newchannel_t *newchannel)
// Or were we maybe we were the caller?
if(newchannel->callerprofile->originatee_caller_profile)
{
originated = 1;
if (newchannel->callerprofile) {
if(newchannel->callerprofile->caller_id_number != 0)
strncpy(src,newchannel->callerprofile->caller_id_number,strlen(newchannel->callerprofile->caller_id_number));
@ -377,6 +377,35 @@ void BaseCDR::process_channel_variables(const std::list<std::string>& stringlist
}
}
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++

View File

@ -84,6 +84,8 @@ class BaseCDR {
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;

View File

@ -179,7 +179,7 @@ void CsvCDR::connect(switch_xml_t& cfg, switch_xml_t& xml, switch_xml_t& setting
if(outputfile.good())
{
activated = 1;
switch_console_printf(SWITCH_CHANNEL_LOG,"CsvCDR activated, log rotation will occur at or after %d MB",(filesize_limit/1024/1024));
switch_console_printf(SWITCH_CHANNEL_LOG,"CsvCDR activated, log rotation will occur at or after %d MB\n",(filesize_limit/1024/1024));
}
}
else
@ -228,7 +228,21 @@ void CsvCDR::open_file()
switch_console_printf(SWITCH_CHANNEL_LOG,"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()
@ -315,7 +329,7 @@ void CsvCDR::disconnect()
chanvars_fixed_list.clear();
chanvars_supp_list.clear();
connectionstate = 0;
switch_console_printf(SWITCH_CHANNEL_LOG,"Shutting down CsvCDR... Done!");
switch_console_printf(SWITCH_CHANNEL_LOG,"Shutting down CsvCDR... Done!\n");
}
AUTO_REGISTER_BASECDR(CsvCDR);

View File

@ -0,0 +1,384 @@
/*
* 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.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);
process_channel_variables(chanvars_fixed_list,newchannel->channel);
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_console_printf(SWITCH_CHANNEL_LOG, "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_console_printf(SWITCH_CHANNEL_LOG,"Checking to see if curlcdr is valid\n");
if ((settings = switch_xml_child(cfg, "curlcdr")))
{
switch_console_printf(SWITCH_CHANNEL_LOG,"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_console_printf(SWITCH_CHANNEL_LOG,"Invalid configuration parameter for timezone. Possible values are utc and local. You entered: %s\nDefaulting to local.\n",val);
convert_time = switch_time_exp_lt;
}
}
}
if(count_config_params > 0)
{
if(strlen(gateway_url))
{
activated = 1;
switch_console_printf(SWITCH_CHANNEL_LOG,"CurlCDR activated");
}
else
switch_console_printf(SWITCH_CHANNEL_LOG,"CurlCDR::connect(): You must specify a gateway_url to have the records logged to.\n");
}
else
switch_console_printf(SWITCH_CHANNEL_LOG,"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_console_printf(SWITCH_CHANNEL_LOG,"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

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

@ -62,6 +62,7 @@ MysqlCDR::MysqlCDR(switch_mod_cdr_newchannel_t *newchannel) : BaseCDR(newchannel
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);
@ -195,7 +196,7 @@ void MysqlCDR::connect(switch_xml_t& cfg, switch_xml_t& xml, switch_xml_t& setti
if(activated)
{
tmp_sql_query = "INSERT INTO freeswitchcdr (callstartdate,callanswerdate,calltransferdate,callenddate,originated,clid,src,dst,ani,aniii,dialplan,myuuid,destuuid,srcchannel,dstchannel,lastapp,lastdata,billusec,disposition,hangupcause,amaflags";
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;
@ -213,7 +214,7 @@ void MysqlCDR::connect(switch_xml_t& cfg, switch_xml_t& xml, switch_xml_t& setti
}
}
tmp_sql_query.append(") VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?");
tmp_sql_query.append(") VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?");
if(chanvars_fixed_list.size() > 0 )
{
@ -334,13 +335,13 @@ bool MysqlCDR::process_record()
set_mysql_time(tm1,my_callstartdate);
set_mysql_time(tm2,my_callanswerdate);
set_mysql_time(tm3,my_calltransferdate);
set_mysql_time(tm4,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_callenddate,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);
@ -353,6 +354,7 @@ bool MysqlCDR::process_record()
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);

View File

@ -95,6 +95,7 @@ class MysqlCDR : public BaseCDR {
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);

View File

@ -222,7 +222,7 @@ void OdbcCDR::connect(switch_xml_t& cfg, switch_xml_t& xml, switch_xml_t& settin
{
tmp_sql_query = "INSERT INTO ";
tmp_sql_query.append(tablename);
tmp_sql_query.append(" (callstartdate,callanswerdate,calltransferdate,callenddate,originated,clid,src,dst,ani,aniii,dialplan,myuuid,destuuid,srcchannel,dstchannel,lastapp,lastdata,billusec,disposition,hangupcause,amaflags");
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;
@ -240,7 +240,7 @@ void OdbcCDR::connect(switch_xml_t& cfg, switch_xml_t& xml, switch_xml_t& settin
}
}
tmp_sql_query.append(") VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?");
tmp_sql_query.append(") VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?");
if(chanvars_fixed_list.size() > 0 )
{
@ -422,6 +422,7 @@ bool OdbcCDR::process_record()
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);

View File

@ -172,41 +172,45 @@ bool PddCDR::process_record()
{
// 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\'formatcallenddate\' = \'" << formattedcallenddate << "\'," << std::endl;
outputfile << "\t\'hangupcause\' = \'" << hangupcause_text << "\'," << std::endl;
outputfile << "\t\'hangupcausecode\' = \'" << hangupcause << "\'," << std::endl;
outputfile << "\t\'clid\' = \'" << 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;
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;
std::map< std::string,std::string >::iterator iItr,iEnd;
for(iItr = chanvars_supp.begin(), iEnd = chanvars_supp.end() ; iItr != iEnd; iItr++)
outputfile << "\t\t\'" << iItr->first << "\' = \'" << iItr->second << "\'," << std::endl;
{
escape_string(iItr->second);
outputfile << "\t\t\'" << iItr->first;
outputfile << "\' => \'" << iItr->second << "\'," << std::endl;
}
}
outputfile << "\t}," << std::endl << "};" << std::endl << std::endl;
retval = 1;

View File

@ -24,6 +24,7 @@
* 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.
@ -54,7 +55,6 @@ class PddCDR : public BaseCDR {
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?

View File

@ -15,6 +15,7 @@ create table freeswitchcdr (
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,

View File

@ -0,0 +1,596 @@
/*
* 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: 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_console_printf(SWITCH_CHANNEL_LOG,"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_console_printf(SWITCH_CHANNEL_LOG,"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 != SQLITE_OK)
{
switch_console_printf(SWITCH_CHANNEL_LOG,"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 == SQLITE_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_console_printf(SWITCH_CHANNEL_LOG,"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_console_printf(SWITCH_CHANNEL_LOG,"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_console_printf(SWITCH_CHANNEL_LOG,"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 == SQLITE_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_console_printf(SWITCH_CHANNEL_LOG,"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_console_printf(SWITCH_CHANNEL_LOG,"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_console_printf(SWITCH_CHANNEL_LOG,"freeswitchcdr_columns[%s] == %s.\n",iItr->c_str(),freeswitchcdr_columns[*iItr].c_str());
if(freeswitchcdr_columns[*iItr].find("INTEGER",0) == std::string::npos)
switch_console_printf(SWITCH_CHANNEL_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_console_printf(SWITCH_CHANNEL_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_console_printf(SWITCH_CHANNEL_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_console_printf(SWITCH_CHANNEL_LOG,"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_console_printf(SWITCH_CHANNEL_LOG,"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_console_printf(SWITCH_CHANNEL_LOG,"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_int64) sqlite_callstartdate);
switch_core_db_bind_int64(stmt, column++, (sqlite_int64) sqlite_callanswerdate);
switch_core_db_bind_int64(stmt, column++, (sqlite_int64) sqlite_calltransferdate);
switch_core_db_bind_int64(stmt, column++, (sqlite_int64) sqlite_callenddate);
switch_core_db_bind_int(stmt, column++, (int) originated);
switch_core_db_bind_text(stmt, column++, clid,-1,SQLITE_STATIC);
switch_core_db_bind_text(stmt, column++, src,-1,SQLITE_STATIC);
switch_core_db_bind_text(stmt, column++, dst,-1,SQLITE_STATIC);
switch_core_db_bind_text(stmt, column++, ani,-1,SQLITE_STATIC);
switch_core_db_bind_text(stmt, column++, aniii,-1,SQLITE_STATIC);
switch_core_db_bind_text(stmt, column++, dialplan,-1,SQLITE_STATIC);
switch_core_db_bind_text(stmt, column++, myuuid,36,SQLITE_STATIC);
switch_core_db_bind_text(stmt, column++, destuuid,36,SQLITE_STATIC);
switch_core_db_bind_text(stmt, column++, srcchannel,-1,SQLITE_STATIC);
switch_core_db_bind_text(stmt, column++, dstchannel,-1,SQLITE_STATIC);
switch_core_db_bind_text(stmt, column++, network_addr,-1,SQLITE_STATIC);
switch_core_db_bind_text(stmt, column++, lastapp,-1,SQLITE_STATIC);
switch_core_db_bind_text(stmt, column++, lastdata,-1,SQLITE_STATIC);
switch_core_db_bind_int64(stmt, column++, (sqlite_int64) 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,SQLITE_STATIC);
break;
}
default:
switch_console_printf(SWITCH_CHANNEL_LOG,"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 != SQLITE_DONE)
{
if(sql_rc == SQLITE_BUSY)
sql_rc = switch_core_db_step(stmt);
else if (sql_rc == SQLITE_ERROR || sql_rc == SQLITE_MISUSE)
switch_console_printf(SWITCH_CHANNEL_LOG,"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())
{
sqlite_int64 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,SQLITE_STATIC);
switch_core_db_bind_text(stmt_chanvars, column2++, iItr->second.c_str(),-1,SQLITE_STATIC);
int sql_rc = switch_core_db_step(stmt_chanvars);
if(sql_rc != SQLITE_DONE)
{
if(sql_rc == SQLITE_BUSY)
sql_rc = switch_core_db_step(stmt_chanvars);
else if (sql_rc == SQLITE_ERROR || sql_rc == SQLITE_MISUSE)
switch_console_printf(SWITCH_CHANNEL_LOG,"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

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

@ -171,7 +171,7 @@ bool XmlCDR::process_record()
switch_console_printf(SWITCH_CHANNEL_LOG, "XmlCDR::process_record(): Unable to open file %s to commit the call record to. Invalid path name, invalid permissions, or no space available?\n",outputfile_name.c_str());
else
{
switch_console_printf(SWITCH_CHANNEL_LOG, "XmlCDR::process_record(): Preping the CDR to %s.\n",outputfile_name.c_str());
//switch_console_printf(SWITCH_CHANNEL_LOG, "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;
@ -213,7 +213,7 @@ bool XmlCDR::process_record()
}
outputfile << "\t</chanvars>" << std::endl << "</document>" << std::endl << std::endl;
switch_console_printf(SWITCH_CHANNEL_LOG, "XmlCDR::process_record(): Dumping the CDR to %s.\n",outputfile_name.c_str());
//switch_console_printf(SWITCH_CHANNEL_LOG, "XmlCDR::process_record(): Dumping the CDR to %s.\n",outputfile_name.c_str());
retval = 1;
}