mirror of
https://github.com/signalwire/freeswitch.git
synced 2025-04-13 07:45:26 +00:00
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:
parent
d3e1f400e5
commit
c5d86f3e6f
@ -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
|
||||
|
@ -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:
|
||||
|
||||
|
@ -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++
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
384
src/mod/event_handlers/mod_cdr/curlcdr.cpp
Normal file
384
src/mod/event_handlers/mod_cdr/curlcdr.cpp
Normal 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:
|
||||
*/
|
97
src/mod/event_handlers/mod_cdr/curlcdr.h
Normal file
97
src/mod/event_handlers/mod_cdr/curlcdr.h
Normal 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:
|
||||
*/
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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?
|
||||
|
@ -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,
|
||||
|
596
src/mod/event_handlers/mod_cdr/sqlitecdr.cpp
Normal file
596
src/mod/event_handlers/mod_cdr/sqlitecdr.cpp
Normal 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:
|
||||
*/
|
92
src/mod/event_handlers/mod_cdr/sqlitecdr.h
Normal file
92
src/mod/event_handlers/mod_cdr/sqlitecdr.h
Normal 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:
|
||||
*/
|
@ -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;
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user