[Freeswitch-svn] [commit] r3564 - freeswitch/trunk/src/mod/event_handlers/mod_cdr

Freeswitch SVN mikej at freeswitch.org
Wed Dec 6 23:15:39 EST 2006


Author: mikej
Date: Wed Dec  6 23:15:38 2006
New Revision: 3564

Added:
   freeswitch/trunk/src/mod/event_handlers/mod_cdr/odbccdr.cpp
   freeswitch/trunk/src/mod/event_handlers/mod_cdr/odbccdr.h
   freeswitch/trunk/src/mod/event_handlers/mod_cdr/schema-update.odbc
Modified:
   freeswitch/trunk/src/mod/event_handlers/mod_cdr/README
   freeswitch/trunk/src/mod/event_handlers/mod_cdr/basecdr.cpp
   freeswitch/trunk/src/mod/event_handlers/mod_cdr/basecdr.h
   freeswitch/trunk/src/mod/event_handlers/mod_cdr/baseregistry.cpp
   freeswitch/trunk/src/mod/event_handlers/mod_cdr/baseregistry.h
   freeswitch/trunk/src/mod/event_handlers/mod_cdr/cdrcontainer.cpp
   freeswitch/trunk/src/mod/event_handlers/mod_cdr/cdrcontainer.h
   freeswitch/trunk/src/mod/event_handlers/mod_cdr/csvcdr.cpp
   freeswitch/trunk/src/mod/event_handlers/mod_cdr/csvcdr.h
   freeswitch/trunk/src/mod/event_handlers/mod_cdr/mod_cdr.cpp
   freeswitch/trunk/src/mod/event_handlers/mod_cdr/mysqlcdr.cpp
   freeswitch/trunk/src/mod/event_handlers/mod_cdr/mysqlcdr.h
   freeswitch/trunk/src/mod/event_handlers/mod_cdr/pddcdr.cpp
   freeswitch/trunk/src/mod/event_handlers/mod_cdr/pddcdr.h
   freeswitch/trunk/src/mod/event_handlers/mod_cdr/schema.sql
   freeswitch/trunk/src/mod/event_handlers/mod_cdr/xmlcdr.cpp
   freeswitch/trunk/src/mod/event_handlers/mod_cdr/xmlcdr.h

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

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

Modified: freeswitch/trunk/src/mod/event_handlers/mod_cdr/basecdr.cpp
==============================================================================
--- freeswitch/trunk/src/mod/event_handlers/mod_cdr/basecdr.cpp	(original)
+++ freeswitch/trunk/src/mod/event_handlers/mod_cdr/basecdr.cpp	Wed Dec  6 23:15:38 2006
@@ -76,13 +76,18 @@
 		memset(aniii,0,80);
 		memset(lastapp,0,80);
 		memset(lastdata,0,255);
-
+		
+		// switch_channel_timetable_t *timetable = switch_channel_get_timetable(newchannel->channel->caller_profile);
+		
 		coresession = newchannel->session;
-		callstartdate= newchannel->timetable->created;
-		callanswerdate = newchannel->timetable->answered;
-		callenddate = newchannel->timetable->hungup;
 	
-		if (newchannel->callerprofile) {
+		if (newchannel->callerprofile)
+		{
+			callstartdate= newchannel->callerprofile->times->created;
+			callanswerdate = newchannel->callerprofile->times->answered;
+			calltransferdate = newchannel->callerprofile->times->transferred;
+			callenddate = newchannel->callerprofile->times->hungup;
+
 			if(newchannel->callerprofile->caller_id_name != 0)
 			{
 				strncpy(clid,newchannel->callerprofile->caller_id_name,strlen(newchannel->callerprofile->caller_id_name));
@@ -104,34 +109,39 @@
 			if(newchannel->callerprofile->network_addr != 0)
 				strncpy(network_addr,newchannel->callerprofile->network_addr,strlen(newchannel->callerprofile->network_addr));
 		}
-
-		originated = newchannel->originate;
-	
-		if(newchannel->originateprofile && newchannel->originateprofile->uuid != 0)
-			strncpy(destuuid,newchannel->originateprofile->uuid,strlen(newchannel->originateprofile->uuid));
-	
-		// We still need to check if this is originated or not
-		if(originated == 0)
+		
+		//switch_caller_profile_t *originateprofile = switch_channel_get_originator_caller_profile(newchannel->channel->callerprofile);
+		
+		// Were we the receiver of the call?
+		if(newchannel->callerprofile->originator_caller_profile)
 		{
-			if (newchannel->callerprofile) {
-				if(newchannel->callerprofile->destination_number != 0)
+			originated = 0;
+			if(newchannel->callerprofile->originator_caller_profile->uuid != 0)
+				strncpy(destuuid,newchannel->callerprofile->originator_caller_profile->uuid,strlen(newchannel->callerprofile->originator_caller_profile->uuid));
+			if(newchannel->callerprofile)
+			{
+				if(newchannel->callerprofile->destination_number)
 					strncpy(src,newchannel->callerprofile->destination_number,strlen(newchannel->callerprofile->destination_number));
 				if(newchannel->callerprofile->caller_id_number != 0)
 					strncpy(dst,newchannel->callerprofile->caller_id_number,strlen(newchannel->callerprofile->caller_id_number));
 			}
-			if(newchannel->originateprofile && newchannel->originateprofile->chan_name != 0)
-				strncpy(dstchannel,newchannel->originateprofile->chan_name,strlen(newchannel->originateprofile->chan_name));
 		}
 		else
 		{
-			if (newchannel->callerprofile) {
-				if(newchannel->callerprofile->caller_id_number != 0)
-					strncpy(src,newchannel->callerprofile->caller_id_number,strlen(newchannel->callerprofile->caller_id_number));
-				if(newchannel->callerprofile->destination_number != 0)
-					strncpy(dst,newchannel->callerprofile->destination_number,strlen(newchannel->callerprofile->destination_number));
+			//originateprofile = switch_channel_get_originatee_profile(newchannel->channel->callerprofile);
+			// 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));
+					if(newchannel->callerprofile->destination_number != 0)
+						strncpy(dst,newchannel->callerprofile->destination_number,strlen(newchannel->callerprofile->destination_number));
+				}
+				if(newchannel->callerprofile->originatee_caller_profile->chan_name != 0)
+					strncpy(dstchannel,newchannel->callerprofile->originatee_caller_profile->chan_name,strlen(newchannel->callerprofile->originatee_caller_profile->chan_name));
 			}
-			if(newchannel->originateprofile && newchannel->originateprofile->chan_name != 0)
-				strncpy(dstchannel,newchannel->originateprofile->chan_name,strlen(newchannel->originateprofile->chan_name));
 		}
 		
 		strncpy(myuuid,newchannel->callerprofile->uuid,strlen(newchannel->callerprofile->uuid));
@@ -140,8 +150,16 @@
 		if(switch_channel_test_flag(newchannel->channel,CF_ANSWERED))
 		{
 			disposition=1;
-			billusec = newchannel->timetable->hungup - newchannel->timetable->answered;
+			if(callstartdate)
+				billusec = callenddate - callanswerdate;
+			else
+				billusec = callenddate - calltransferdate;
 		}
+		else if(switch_channel_test_flag(newchannel->channel,CF_TRANSFER))
+		{
+			disposition=1;
+			billusec = callenddate - calltransferdate;
+		}
 		else
 		{
 			disposition=0;
@@ -361,7 +379,7 @@
 
 /* For Emacs:
  * Local Variables:
- * mode:c
+ * mode:c++
  * indent-tabs-mode:nil
  * tab-width:4
  * c-basic-offset:4

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

Modified: freeswitch/trunk/src/mod/event_handlers/mod_cdr/baseregistry.cpp
==============================================================================
--- freeswitch/trunk/src/mod/event_handlers/mod_cdr/baseregistry.cpp	(original)
+++ freeswitch/trunk/src/mod/event_handlers/mod_cdr/baseregistry.cpp	Wed Dec  6 23:15:38 2006
@@ -82,7 +82,7 @@
 
 /* For Emacs:
  * Local Variables:
- * mode:c
+ * mode:c++
  * indent-tabs-mode:nil
  * tab-width:4
  * c-basic-offset:4

Modified: freeswitch/trunk/src/mod/event_handlers/mod_cdr/baseregistry.h
==============================================================================
--- freeswitch/trunk/src/mod/event_handlers/mod_cdr/baseregistry.h	(original)
+++ freeswitch/trunk/src/mod/event_handlers/mod_cdr/baseregistry.h	Wed Dec  6 23:15:38 2006
@@ -79,7 +79,7 @@
 
 /* For Emacs:
  * Local Variables:
- * mode:c
+ * mode:c++
  * indent-tabs-mode:nil
  * tab-width:4
  * c-basic-offset:4

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

Modified: freeswitch/trunk/src/mod/event_handlers/mod_cdr/cdrcontainer.h
==============================================================================
--- freeswitch/trunk/src/mod/event_handlers/mod_cdr/cdrcontainer.h	(original)
+++ freeswitch/trunk/src/mod/event_handlers/mod_cdr/cdrcontainer.h	Wed Dec  6 23:15:38 2006
@@ -58,10 +58,20 @@
 		~CDRContainer();
 		void add_cdr(switch_core_session_t *session);
 		void process_records();
+#ifdef SWITCH_QUEUE_ENHANCED
+		void reload(switch_stream_handle_t *stream);
+		void queue_pause(switch_stream_handle_t *stream);
+		void queue_resume(switch_stream_handle_t *stream);
+#endif
+		void active(switch_stream_handle_t *stream);
+		void available(switch_stream_handle_t *stream);
 	protected:
 	private:
+		switch_xml_t cfg, xml, settings, param;
 		switch_queue_t *cdrqueue;
 		std::string tempfilepath;
+		char configfile[13];
+		bool queue_paused;
 };
 
 #ifdef __cplusplus
@@ -72,7 +82,7 @@
 
 /* For Emacs:
  * Local Variables:
- * mode:c
+ * mode:c++
  * indent-tabs-mode:nil
  * tab-width:4
  * c-basic-offset:4

Modified: freeswitch/trunk/src/mod/event_handlers/mod_cdr/csvcdr.cpp
==============================================================================
--- freeswitch/trunk/src/mod/event_handlers/mod_cdr/csvcdr.cpp	(original)
+++ freeswitch/trunk/src/mod/event_handlers/mod_cdr/csvcdr.cpp	Wed Dec  6 23:15:38 2006
@@ -51,23 +51,27 @@
 {
 	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, tempcallend;
+		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));
-		switch_time_exp_lt(&tempcallstart, callstartdate);
-		switch_time_exp_lt(&tempcallanswer, callanswerdate);
-		switch_time_exp_lt(&tempcallend, callenddate);
+		convert_time(&tempcallstart, callstartdate);
+		convert_time(&tempcallanswer, callanswerdate);
+		convert_time(&tempcalltransfer, calltransferdate);
+		convert_time(&tempcallend, callenddate);
 		
 		// Format the times
-		apr_size_t retsizecsd, retsizecad, retsizeced;  //csd == callstartdate, cad == callanswerdate, ced == callenddate, ceff == callenddate_forfile
-		char format[] = "%Y-%m-%d-%H-%M-%S";
+		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);
@@ -84,11 +88,13 @@
 bool CsvCDR::logchanvars=0;
 bool CsvCDR::connectionstate=0;
 bool CsvCDR::repeat_fixed_in_supp=0;
+modcdr_time_convert_t CsvCDR::convert_time = switch_time_exp_lt;
 std::string CsvCDR::outputfile_path;
 std::ofstream CsvCDR::outputfile;
 std::ofstream::pos_type CsvCDR::filesize_limit = (10 * 1024 * 1024); // Default file size is 10MB
 std::list<std::string> CsvCDR::chanvars_fixed_list;
 std::list<std::string> CsvCDR::chanvars_supp_list;
+std::string CsvCDR::display_name = "CsvCDR - The simple comma separated values CDR logger";
 
 void CsvCDR::connect(switch_xml_t& cfg, switch_xml_t& xml, switch_xml_t& settings, switch_xml_t& param)
 {
@@ -153,6 +159,18 @@
 					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)
@@ -220,8 +238,13 @@
 	
 	// 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 << "\",\"";
@@ -277,9 +300,21 @@
 
 }
 
+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!");	
 }
 
@@ -287,7 +322,7 @@
 
 /* For Emacs:
  * Local Variables:
- * mode:c
+ * mode:c++
  * indent-tabs-mode:nil
  * tab-width:4
  * c-basic-offset:4

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

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

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

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

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

Added: freeswitch/trunk/src/mod/event_handlers/mod_cdr/odbccdr.h
==============================================================================
--- (empty file)
+++ freeswitch/trunk/src/mod/event_handlers/mod_cdr/odbccdr.h	Wed Dec  6 23:15:38 2006
@@ -0,0 +1,115 @@
+/*
+ * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application Call Detail Recorder module
+ * Copyright 2006, Author: Yossi Neiman of Cartis Solutions, Inc. <freeswitch AT cartissolutions.com>
+ *
+ * Version: MPL 1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application Call Detail Recorder module
+ *
+ * The Initial Developer of the Original Code is
+ * Yossi Neiman <freeswitch AT cartissolutions.com>
+ * Portions created by the Initial Developer are Copyright (C)
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * 
+ * Yossi Neiman <freeswitch AT cartissolutions.com>
+ *
+ * Description: This C++ header file describes the OdbcCDR class which handles formatting a CDR out to
+ * an ODBC backend using prepared statements.
+ *
+ * odbccdr.h
+ *
+ */
+
+#include "baseregistry.h"
+#include <switch.h>
+#include <iostream>
+#include <list>
+#include <sstream>
+
+#ifndef __CYGWIN__
+#include <sql.h>
+#include <sqlext.h>
+#include <sqltypes.h>
+#else
+#include <windows.h>
+#include <w32api/sql.h>
+#include <w32api/sqlext.h>
+#include <w32api/sqltypes.h>
+#endif
+
+#ifndef ODBCCDR
+#define ODBCCDR
+
+class OdbcCDR : public BaseCDR {
+	public:
+		OdbcCDR();
+		OdbcCDR(switch_mod_cdr_newchannel_t *newchannel);
+		//OdbcCDR(const MysqlCDR& copyFrom);
+		virtual ~OdbcCDR();
+		virtual bool process_record();
+		virtual void connect(switch_xml_t& cfg, switch_xml_t& xml, switch_xml_t& settings, switch_xml_t& param);
+		virtual void disconnect();
+		virtual bool is_activated();
+		virtual void tempdump_record();
+		virtual void reread_tempdumped_records();
+		virtual std::string get_display_name();
+
+	private:
+		static bool activated;
+		static char sql_query[1024];
+		static modcdr_time_convert_t convert_time;
+		static std::string display_name;
+		static std::string tmp_sql_query; // Object must exist to bind the statement, this used for generating the sql
+		static char sql_query_chanvars[355];
+		static char sql_query_ping[10];
+		static bool connectionstate;
+		static bool logchanvars;
+		static SQLHENV ODBC_env;     /* global ODBC Environment */
+		static SQLHDBC ODBC_con;     /* global ODBC Connection Handle */
+		static SQLHSTMT ODBC_stmt;
+		static SQLHSTMT ODBC_stmt_chanvars;
+		static SQLHSTMT ODBC_stmt_ping;
+		static std::list<std::string> chanvars_fixed_list;
+		static std::vector<switch_mod_cdr_sql_types_t> chanvars_fixed_types;
+		static std::list<std::string> chanvars_supp_list; // The supplemental list
+		static bool repeat_fixed_in_supp;
+		static char dsn[255];
+		static char hostname[255];
+		static char username[255];
+		static char dbname[255];
+		static char password[255];
+		static char tablename[255];
+		static char tablename_chanvars[255];
+		//static fstream tmpfile;
+		char odbc_callstartdate[128];
+		char odbc_callanswerdate[128];
+		char odbc_calltransferdate[128];
+		char odbc_callenddate[128];
+		void disconnect_stage_1();
+		void connect_to_database();
+};
+
+#endif
+
+/* For Emacs:
+ * Local Variables:
+ * mode:c++
+ * indent-tabs-mode:nil
+ * tab-width:4
+ * c-basic-offset:4
+ * End:
+ * For VIM:
+ * vim:set softtabstop=4 shiftwidth=4 tabstop=4 expandtab:
+ */

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

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

Added: freeswitch/trunk/src/mod/event_handlers/mod_cdr/schema-update.odbc
==============================================================================
--- (empty file)
+++ freeswitch/trunk/src/mod/event_handlers/mod_cdr/schema-update.odbc	Wed Dec  6 23:15:38 2006
@@ -0,0 +1,3 @@
+ALTER TABLE freeswitchcdr DROP callid;
+ALTER TABLE chanvars DROP callid;
+ALTER TABLE chanvars ADD myuuid char(36) NOT NULL FIRST;
\ No newline at end of file

Modified: freeswitch/trunk/src/mod/event_handlers/mod_cdr/schema.sql
==============================================================================
--- freeswitch/trunk/src/mod/event_handlers/mod_cdr/schema.sql	(original)
+++ freeswitch/trunk/src/mod/event_handlers/mod_cdr/schema.sql	Wed Dec  6 23:15:38 2006
@@ -2,6 +2,7 @@
 	callid bigint unsigned default 0 primary key, /* This will need to be handled specially for auto increment, as that might not be standard */
 	callstartdate datetime NOT NULL,
 	callanswerdate datetime NOT NULL,
+	calltransferdate datetime NOT NULL,
 	callenddate datetime NOT NULL,
 	originated tinyint default 0,
 	clid varchar(80) default "Freeswitch - Unknown",

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

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



More information about the Freeswitch-svn mailing list