/* * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application Call Detail Recorder module * Copyright 2006, Author: Yossi Neiman of Cartis Solutions, Inc. * * 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 * Portions created by the Initial Developer are Copyright (C) * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Yossi Neiman * * Description: This C++ source file describes the CsvCDR class that handles processing CDRs to a CSV format. * This is the standard CSV module, and has a list of predefined variables to log out which can be * added to, but never have the default ones removed. If you want to use one that allows you to explicity * set all data variables to be logged and in what order, then this is not the class you want to use, and * one will be coming in the future to do just that. * * csvcdr.cpp * */ #include #include "csvcdr.h" #include CsvCDR::CsvCDR() : BaseCDR() { memset(formattedcallstartdate,0,100); memset(formattedcallanswerdate,0,100); memset(formattedcallenddate,0,100); } CsvCDR::CsvCDR(switch_mod_cdr_newchannel_t *newchannel) : BaseCDR(newchannel) { memset(formattedcallstartdate,0,100); memset(formattedcallanswerdate,0,100); memset(formattedcalltransferdate,0,100); memset(formattedcallenddate,0,100); if(newchannel != 0) { switch_time_exp_t tempcallstart, tempcallanswer, tempcalltransfer, tempcallend; memset(&tempcallstart,0,sizeof(tempcallstart)); memset(&tempcalltransfer,0,sizeof(tempcalltransfer)); memset(&tempcallanswer,0,sizeof(tempcallanswer)); memset(&tempcallend,0,sizeof(tempcallend)); convert_time(&tempcallstart, callstartdate); convert_time(&tempcallanswer, callanswerdate); convert_time(&tempcalltransfer, calltransferdate); convert_time(&tempcallend, callenddate); // Format the times apr_size_t retsizecsd, retsizecad, retsizectd, retsizeced; //csd == callstartdate, cad == callanswerdate, ced == callenddate, ceff == callenddate_forfile char format[] = "%Y-%m-%d %H:%M:%S"; switch_strftime(formattedcallstartdate,&retsizecsd,sizeof(formattedcallstartdate),format,&tempcallstart); switch_strftime(formattedcallanswerdate,&retsizecad,sizeof(formattedcallanswerdate),format,&tempcallanswer); switch_strftime(formattedcalltransferdate,&retsizectd,sizeof(formattedcalltransferdate),format,&tempcalltransfer); switch_strftime(formattedcallenddate,&retsizeced,sizeof(formattedcallenddate),format,&tempcallend); if(chanvars_fixed_list.size() > 0) process_channel_variables(chanvars_fixed_list,newchannel->channel); if(chanvars_supp_list.size() > 0) process_channel_variables(chanvars_supp_list,chanvars_fixed_list,newchannel->channel,repeat_fixed_in_supp); } } CsvCDR::~CsvCDR() { } bool CsvCDR::activated=0; bool CsvCDR::logchanvars=0; bool CsvCDR::connectionstate=0; bool CsvCDR::repeat_fixed_in_supp=0; modcdr_time_convert_t CsvCDR::convert_time = switch_time_exp_lt; std::string CsvCDR::outputfile_path; std::ofstream CsvCDR::outputfile; std::ofstream::pos_type CsvCDR::filesize_limit = (10 * 1024 * 1024); // Default file size is 10MB std::list CsvCDR::chanvars_fixed_list; std::list CsvCDR::chanvars_supp_list; std::string CsvCDR::display_name = "CsvCDR - The simple comma separated values CDR logger"; void CsvCDR::connect(switch_xml_t& cfg, switch_xml_t& xml, switch_xml_t& settings, switch_xml_t& param) { switch_console_printf(SWITCH_CHANNEL_LOG, "CsvCDR::connect() - Loading configuration file.\n"); activated = 0; // Set it as inactive initially connectionstate = 0; // Initialize it to false to show that we aren't yet connected. if ((settings = switch_xml_child(cfg, "csvcdr"))) { int count_config_params = 0; // Need to make sure all params are set before we load for (param = switch_xml_child(settings, "param"); param; param = param->next) { char *var = (char *) switch_xml_attr_soft(param, "name"); char *val = (char *) switch_xml_attr_soft(param, "value"); if (!strcmp(var, "path")) { if(val != 0) outputfile_path = val; count_config_params++; } else if (!strcmp(var, "chanvars_supp")) { if(val != 0) { std::string unparsed; unparsed = val; if(unparsed.size() > 0) { bool fixed = 0; parse_channel_variables_xconfig(unparsed,chanvars_supp_list,fixed); logchanvars=1; } } } else if (!strcmp(var, "chanvars_fixed")) { if(val != 0) { std::string unparsed; unparsed = val; if(unparsed.size() > 0) { bool fixed = 1; parse_channel_variables_xconfig(unparsed,chanvars_fixed_list,fixed); logchanvars=1; } } } else if (!strcmp(var, "repeat_fixed_in_supp")) { if(!strcmp(val,"1")) repeat_fixed_in_supp = 1; else if (!strcmp(val,"y") || !strcmp(val,"y")) repeat_fixed_in_supp = 0; } else if (!strcmp(var, "size_limit")) { if(val != 0) { filesize_limit = atoi(val) * 1024 * 1024; // Value is in MB std::cout << "File size limit from config file is " << filesize_limit << " byte(s)." << std::endl; } } else if(!strcmp(var,"timezone")) { if(!strcmp(val,"utc")) convert_time = switch_time_exp_gmt; else if(!strcmp(val,"local")) convert_time = switch_time_exp_lt; else { switch_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) { open_file(); if(outputfile.good()) { activated = 1; switch_console_printf(SWITCH_CHANNEL_LOG,"CsvCDR activated, log rotation will occur at or after %d MB\n",(int)(filesize_limit >> 20)); } } else switch_console_printf(SWITCH_CHANNEL_LOG,"CsvCDR::connect(): You did not specify the minimum parameters for using this module. You must specify at least a path to have the records logged to.\n"); } } void CsvCDR::check_file_size_and_open() { // Test if the file has been opened or not if(outputfile) { if(outputfile.tellp() > filesize_limit) outputfile.close(); } if (!outputfile) { open_file(); } } void CsvCDR::open_file() { switch_time_t now = switch_time_now(); switch_time_exp_t now_converted; memset(&now_converted,0,sizeof(now_converted)); switch_time_exp_lt(&now_converted,now); apr_size_t retsize; char format[] = "%Y-%m-%d-%H-%M-%S"; char formatteddate[100]; memset(formatteddate,0,100); switch_strftime(formatteddate,&retsize,100,format,&now_converted); std::string filename = outputfile_path; filename.append(SWITCH_PATH_SEPARATOR); filename.append(formatteddate); filename.append(".csv"); outputfile.clear(); outputfile.open(filename.c_str(),std::ios_base::app|std::ios_base::binary); if(outputfile.fail()) { switch_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::iterator iItr, iEnd; for(iItr = chanvars_fixed_list.begin(),iEnd = chanvars_fixed_list.end(); iItr != iEnd; iItr++) outputfile << "," << *iItr; } if(logchanvars) outputfile << ",chanvars_supp"; outputfile << std::endl; } } bool CsvCDR::process_record() { check_file_size_and_open(); bool retval = 0; // Format the call record and proceed from here... outputfile << "\"" << callstartdate << "\",\""; outputfile << formattedcallstartdate << "\",\""; outputfile << callanswerdate << "\",\""; outputfile << formattedcallanswerdate << "\",\""; outputfile << calltransferdate << "\",\""; outputfile << formattedcalltransferdate << "\",\""; outputfile << callenddate << "\",\""; outputfile << formattedcallenddate << "\",\""; outputfile << hangupcause_text << "\",\""; outputfile << hangupcause << "\",\""; outputfile << clid << "\",\""; outputfile << originated << "\",\""; outputfile << dialplan << "\",\""; outputfile << myuuid << "\",\""; outputfile << destuuid << "\",\""; outputfile << src << "\",\""; outputfile << dst << "\",\""; outputfile << srcchannel << "\",\""; outputfile << dstchannel << "\",\""; outputfile << ani << "\",\""; outputfile << aniii << "\",\""; outputfile << network_addr << "\",\""; outputfile << lastapp << "\",\""; outputfile << lastdata << "\",\""; outputfile << billusec << "\",\""; outputfile << disposition << "\",\""; outputfile << amaflags << "\""; // Now to process chanvars, fixed ones first if(chanvars_fixed.size() > 0 ) { std::list >::iterator iItr, iEnd; for(iItr = chanvars_fixed.begin(), iEnd = chanvars_fixed.end(); iItr != iEnd; iItr++) outputfile << ",\"" << iItr->second << "\""; } if(chanvars_supp.size() > 0 ) { std::map::iterator iItr,iEnd; for(iItr = chanvars_supp.begin(), iEnd = chanvars_supp.end() ; iItr != iEnd; iItr++) outputfile << ",\"" << iItr->first << "=" << iItr->second << "\""; } outputfile << std::endl; retval = 1; return retval; } bool CsvCDR::is_activated() { return activated; } void CsvCDR::tempdump_record() { } void CsvCDR::reread_tempdumped_records() { } std::string CsvCDR::get_display_name() { return display_name; } void CsvCDR::disconnect() { outputfile.close(); activated = 0; logchanvars = 0; repeat_fixed_in_supp = 0; outputfile_path.clear(); chanvars_fixed_list.clear(); chanvars_supp_list.clear(); connectionstate = 0; switch_console_printf(SWITCH_CHANNEL_LOG,"Shutting down CsvCDR... Done!\n"); } AUTO_REGISTER_BASECDR(CsvCDR); /* For Emacs: * Local Variables: * mode:c++ * indent-tabs-mode:t * tab-width:4 * c-basic-offset:4 * End: * For VIM: * vim:set softtabstop=4 shiftwidth=4 tabstop=4 expandtab: */