2005-05-05 05:39:33 +00:00
/*
2005-09-14 20:46:50 +00:00
* Asterisk -- An open source telephony toolkit.
*
2006-02-12 04:28:58 +00:00
* Copyright (C) 1999-2006, Digium, Inc.
2005-05-05 05:39:33 +00:00
*
2005-05-05 12:48:52 +00:00
* Portions Copyright (C) 2005, Anthony Minessale II
2005-05-05 05:39:33 +00:00
*
2005-09-14 20:46:50 +00:00
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
* the project provides a web site, mailing lists and IRC
* channels for your use.
*
2005-05-05 05:39:33 +00:00
* This program is free software, distributed under the terms of
2005-09-14 20:46:50 +00:00
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
2005-10-24 20:12:06 +00:00
/*! \file
2005-09-14 20:46:50 +00:00
*
2005-10-24 20:12:06 +00:00
* \brief Call Detail Record related dialplan functions
2005-12-30 21:18:06 +00:00
*
2013-08-28 21:38:39 +00:00
* \author Anthony Minessale II
2007-01-24 09:05:29 +00:00
*
* \ingroup functions
2005-05-05 05:39:33 +00:00
*/
2011-07-14 20:28:54 +00:00
/*** MODULEINFO
<support_level>core</support_level>
***/
2005-06-06 22:12:19 +00:00
# include "asterisk.h"
2006-02-11 03:14:05 +00:00
# include "asterisk/module.h"
2005-05-05 05:39:33 +00:00
# include "asterisk/channel.h"
# include "asterisk/pbx.h"
# include "asterisk/utils.h"
# include "asterisk/app.h"
# include "asterisk/cdr.h"
2013-12-19 00:50:01 +00:00
# include "asterisk/stasis.h"
2014-01-12 22:13:12 +00:00
# include "asterisk/stasis_message_router.h"
2005-05-05 05:39:33 +00:00
2008-11-01 21:10:07 +00:00
/*** DOCUMENTATION
<function name="CDR" language="en_US">
<synopsis>
Gets or sets a CDR variable.
2013-08-28 21:38:39 +00:00
</synopsis>
2008-11-01 21:10:07 +00:00
<syntax>
<parameter name="name" required="true">
<para>CDR field name:</para>
<enumlist>
<enum name="clid">
<para>Caller ID.</para>
</enum>
<enum name="lastdata">
<para>Last application arguments.</para>
</enum>
<enum name="disposition">
2013-06-17 03:00:38 +00:00
<para>The final state of the CDR.</para>
<enumlist>
<enum name="0">
<para><literal>NO ANSWER</literal></para>
</enum>
<enum name="1">
<para><literal>NO ANSWER</literal> (NULL record)</para>
</enum>
<enum name="2">
<para><literal>FAILED</literal></para>
</enum>
<enum name="4">
<para><literal>BUSY</literal></para>
</enum>
<enum name="8">
<para><literal>ANSWERED</literal></para>
</enum>
<enum name="16">
<para><literal>CONGESTION</literal></para>
</enum>
</enumlist>
2008-11-01 21:10:07 +00:00
</enum>
<enum name="src">
<para>Source.</para>
</enum>
<enum name="start">
<para>Time the call started.</para>
</enum>
<enum name="amaflags">
2013-06-17 03:00:38 +00:00
<para>R/W the Automatic Message Accounting (AMA) flags on the channel.
When read from a channel, the integer value will always be returned.
When written to a channel, both the string format or integer value
is accepted.</para>
<enumlist>
<enum name="1"><para><literal>OMIT</literal></para></enum>
<enum name="2"><para><literal>BILLING</literal></para></enum>
<enum name="3"><para><literal>DOCUMENTATION</literal></para></enum>
</enumlist>
<warning><para>Accessing this setting is deprecated in CDR. Please use the CHANNEL function instead.</para></warning>
2008-11-01 21:10:07 +00:00
</enum>
<enum name="dst">
<para>Destination.</para>
</enum>
<enum name="answer">
<para>Time the call was answered.</para>
</enum>
<enum name="accountcode">
<para>The channel's account code.</para>
2013-06-17 03:00:38 +00:00
<warning><para>Accessing this setting is deprecated in CDR. Please use the CHANNEL function instead.</para></warning>
2008-11-01 21:10:07 +00:00
</enum>
<enum name="dcontext">
<para>Destination context.</para>
</enum>
<enum name="end">
<para>Time the call ended.</para>
</enum>
<enum name="uniqueid">
<para>The channel's unique id.</para>
</enum>
<enum name="dstchannel">
<para>Destination channel.</para>
</enum>
<enum name="duration">
<para>Duration of the call.</para>
</enum>
<enum name="userfield">
<para>The channel's user specified field.</para>
</enum>
<enum name="lastapp">
<para>Last application.</para>
</enum>
<enum name="billsec">
<para>Duration of the call once it was answered.</para>
</enum>
<enum name="channel">
<para>Channel name.</para>
</enum>
2009-11-03 21:21:09 +00:00
<enum name="sequence">
2009-11-05 22:59:02 +00:00
<para>CDR sequence number.</para>
2009-11-03 21:21:09 +00:00
</enum>
2008-11-01 21:10:07 +00:00
</enumlist>
</parameter>
<parameter name="options" required="false">
<optionlist>
2010-06-08 23:48:17 +00:00
<option name="f">
<para>Returns billsec or duration fields as floating point values.</para>
</option>
2008-11-01 21:10:07 +00:00
<option name="u">
<para>Retrieves the raw, unprocessed value.</para>
<para>For example, 'start', 'answer', and 'end' will be retrieved as epoch
values, when the <literal>u</literal> option is passed, but formatted as YYYY-MM-DD HH:MM:SS
otherwise. Similarly, disposition and amaflags will return their raw
integral values.</para>
</option>
</optionlist>
</parameter>
</syntax>
<description>
<para>All of the CDR field names are read-only, except for <literal>accountcode</literal>,
<literal>userfield</literal>, and <literal>amaflags</literal>. You may, however, supply
a name not on the above list, and create your own variable, whose value can be changed
2013-06-17 03:00:38 +00:00
with this function, and this variable will be stored on the CDR.</para>
<note><para>CDRs can only be modified before the bridge between two channels is
torn down. For example, CDRs may not be modified after the <literal>Dial</literal>
application has returned.</para></note>
2008-11-01 21:10:07 +00:00
<para>Example: exten => 1,1,Set(CDR(userfield)=test)</para>
</description>
</function>
2013-06-17 03:00:38 +00:00
<function name="CDR_PROP" language="en_US">
<synopsis>
Set a property on a channel's CDR.
</synopsis>
<syntax>
<parameter name="name" required="true">
<para>The property to set on the CDR.</para>
<enumlist>
<enum name="party_a">
<para>Set this channel as the preferred Party A when
channels are associated together.</para>
<para>Write-Only</para>
</enum>
<enum name="disable">
2015-07-20 12:39:48 -05:00
<para>Setting to 1 will disable CDRs for this channel.
Setting to 0 will enable CDRs for this channel.</para>
2013-06-17 03:00:38 +00:00
<para>Write-Only</para>
</enum>
</enumlist>
</parameter>
</syntax>
<description>
<para>This function sets a property on a channel's CDR. Properties
alter the behavior of how the CDR operates for that channel.</para>
</description>
</function>
2008-11-01 21:10:07 +00:00
***/
2009-06-15 17:34:30 +00:00
enum cdr_option_flags {
2006-02-23 23:12:41 +00:00
OPT_UNPARSED = ( 1 < < 1 ) ,
2013-06-17 03:00:38 +00:00
OPT_FLOAT = ( 1 < < 2 ) ,
2009-06-15 17:34:30 +00:00
} ;
2006-01-11 19:52:29 +00:00
AST_APP_OPTIONS ( cdr_func_options , {
2010-06-08 23:48:17 +00:00
AST_APP_OPTION ( ' f ' , OPT_FLOAT ) ,
2006-02-23 23:12:41 +00:00
AST_APP_OPTION ( ' u ' , OPT_UNPARSED ) ,
2006-01-11 19:52:29 +00:00
} ) ;
2013-12-19 00:50:01 +00:00
struct cdr_func_payload {
struct ast_channel * chan ;
const char * cmd ;
const char * arguments ;
const char * value ;
2014-01-12 22:13:12 +00:00
void * data ;
2013-12-19 00:50:01 +00:00
} ;
struct cdr_func_data {
char * buf ;
size_t len ;
} ;
STASIS_MESSAGE_TYPE_DEFN_LOCAL ( cdr_read_message_type ) ;
STASIS_MESSAGE_TYPE_DEFN_LOCAL ( cdr_write_message_type ) ;
STASIS_MESSAGE_TYPE_DEFN_LOCAL ( cdr_prop_write_message_type ) ;
2016-01-18 17:16:24 -06:00
static struct timeval cdr_retrieve_time ( struct ast_channel * chan , const char * time_name )
{
2016-07-27 10:33:23 -05:00
struct timeval time = { 0 } ;
2016-01-18 17:16:24 -06:00
char * value = NULL ;
char tempbuf [ 128 ] ;
2016-07-27 10:33:23 -05:00
long int tv_sec ;
long int tv_usec ;
2016-01-18 17:16:24 -06:00
if ( ast_strlen_zero ( ast_channel_name ( chan ) ) ) {
/* Format request on a dummy channel */
ast_cdr_format_var ( ast_channel_cdr ( chan ) , time_name , & value , tempbuf , sizeof ( tempbuf ) , 1 ) ;
} else {
ast_cdr_getvar ( ast_channel_name ( chan ) , time_name , tempbuf , sizeof ( tempbuf ) ) ;
}
2016-07-27 10:33:23 -05:00
/* time.tv_usec is suseconds_t, which could be int or long */
if ( sscanf ( tempbuf , " %ld.%ld " , & tv_sec , & tv_usec ) = = 2 ) {
time . tv_sec = tv_sec ;
time . tv_usec = tv_usec ;
} else {
2016-01-18 17:16:24 -06:00
ast_log ( AST_LOG_WARNING , " Failed to fully extract '%s' from CDR \n " , time_name ) ;
}
return time ;
}
2013-12-19 00:50:01 +00:00
static void cdr_read_callback ( void * data , struct stasis_subscription * sub , struct stasis_message * message )
2005-05-05 05:39:33 +00:00
{
2013-12-19 00:50:01 +00:00
struct cdr_func_payload * payload = stasis_message_data ( message ) ;
2014-01-12 22:13:12 +00:00
struct cdr_func_data * output ;
2013-12-19 00:50:01 +00:00
char * info ;
2013-06-19 01:28:40 +00:00
char * value = NULL ;
2006-02-12 04:28:58 +00:00
struct ast_flags flags = { 0 } ;
2013-06-19 01:28:40 +00:00
char tempbuf [ 512 ] ;
2006-01-11 19:52:29 +00:00
AST_DECLARE_APP_ARGS ( args ,
2013-08-28 21:38:39 +00:00
AST_APP_ARG ( variable ) ;
AST_APP_ARG ( options ) ;
2006-01-11 19:52:29 +00:00
) ;
2005-05-05 05:39:33 +00:00
2013-12-19 00:50:01 +00:00
if ( cdr_read_message_type ( ) ! = stasis_message_type ( message ) ) {
return ;
}
2013-08-28 23:15:43 +00:00
2014-01-12 22:13:12 +00:00
ast_assert ( payload ! = NULL ) ;
output = payload - > data ;
ast_assert ( output ! = NULL ) ;
2005-05-05 05:39:33 +00:00
2013-12-19 00:50:01 +00:00
if ( ast_strlen_zero ( payload - > arguments ) ) {
ast_log ( AST_LOG_WARNING , " %s requires a variable (%s(variable[,option])) \n ) " ,
payload - > cmd , payload - > cmd ) ;
return ;
2011-12-16 21:10:19 +00:00
}
2013-12-19 00:50:01 +00:00
info = ast_strdupa ( payload - > arguments ) ;
2013-06-17 03:00:38 +00:00
AST_STANDARD_APP_ARGS ( args , info ) ;
2005-05-05 05:39:33 +00:00
2013-06-17 03:00:38 +00:00
if ( ! ast_strlen_zero ( args . options ) ) {
2006-01-11 19:52:29 +00:00
ast_app_parse_options ( cdr_func_options , & flags , NULL , args . options ) ;
2013-06-17 03:00:38 +00:00
}
2005-05-05 05:39:33 +00:00
2013-12-19 00:50:01 +00:00
if ( ast_strlen_zero ( ast_channel_name ( payload - > chan ) ) ) {
2013-06-19 01:28:40 +00:00
/* Format request on a dummy channel */
2017-09-05 14:31:50 +02:00
ast_cdr_format_var ( ast_channel_cdr ( payload - > chan ) , args . variable , & value , tempbuf , sizeof ( tempbuf ) , ast_test_flag ( & flags , OPT_UNPARSED ) ) ;
2013-06-19 01:28:40 +00:00
if ( ast_strlen_zero ( value ) ) {
2013-12-19 00:50:01 +00:00
return ;
2013-06-19 01:28:40 +00:00
}
ast_copy_string ( tempbuf , value , sizeof ( tempbuf ) ) ;
ast_set_flag ( & flags , OPT_UNPARSED ) ;
2013-12-19 00:50:01 +00:00
} else if ( ast_cdr_getvar ( ast_channel_name ( payload - > chan ) , args . variable , tempbuf , sizeof ( tempbuf ) ) ) {
return ;
2013-06-17 03:00:38 +00:00
}
2010-06-08 23:48:17 +00:00
2013-08-28 21:38:39 +00:00
if ( ast_test_flag ( & flags , OPT_FLOAT )
& & ( ! strcasecmp ( " billsec " , args . variable ) | | ! strcasecmp ( " duration " , args . variable ) ) ) {
2016-01-18 17:16:24 -06:00
struct timeval start = cdr_retrieve_time ( payload - > chan , ! strcasecmp ( " billsec " , args . variable ) ? " answer " : " start " ) ;
struct timeval finish = cdr_retrieve_time ( payload - > chan , " end " ) ;
double delta ;
2013-08-28 21:38:39 +00:00
2016-01-18 17:16:24 -06:00
if ( ast_tvzero ( finish ) ) {
finish = ast_tvnow ( ) ;
2010-06-08 23:48:17 +00:00
}
2016-01-18 17:16:24 -06:00
if ( ast_tvzero ( start ) ) {
delta = 0.0 ;
} else {
delta = ( double ) ( ast_tvdiff_us ( finish , start ) / 1000000.0 ) ;
}
snprintf ( tempbuf , sizeof ( tempbuf ) , " %lf " , delta ) ;
2013-06-17 03:00:38 +00:00
} else if ( ! ast_test_flag ( & flags , OPT_UNPARSED ) ) {
if ( ! strcasecmp ( " start " , args . variable )
2013-08-28 21:38:39 +00:00
| | ! strcasecmp ( " end " , args . variable )
| | ! strcasecmp ( " answer " , args . variable ) ) {
2013-06-17 03:00:38 +00:00
struct timeval fmt_time ;
struct ast_tm tm ;
2013-06-17 18:58:56 +00:00
/* tv_usec is suseconds_t, which could be int or long */
2015-03-23 00:05:48 +00:00
long int tv_sec ;
2013-06-17 18:58:56 +00:00
long int tv_usec ;
2013-08-28 21:38:39 +00:00
2015-03-23 00:05:48 +00:00
if ( sscanf ( tempbuf , " %ld.%ld " , & tv_sec , & tv_usec ) ! = 2 ) {
2013-06-17 03:00:38 +00:00
ast_log ( AST_LOG_WARNING , " Unable to parse %s (%s) from the CDR for channel %s \n " ,
2013-12-19 00:50:01 +00:00
args . variable , tempbuf , ast_channel_name ( payload - > chan ) ) ;
return ;
2013-06-17 03:00:38 +00:00
}
2015-03-23 00:05:48 +00:00
if ( tv_sec ) {
fmt_time . tv_sec = tv_sec ;
2014-02-07 19:40:23 +00:00
fmt_time . tv_usec = tv_usec ;
ast_localtime ( & fmt_time , & tm , NULL ) ;
ast_strftime ( tempbuf , sizeof ( tempbuf ) , " %Y-%m-%d %T " , & tm ) ;
} else {
tempbuf [ 0 ] = ' \0 ' ;
}
2013-06-17 03:00:38 +00:00
} else if ( ! strcasecmp ( " disposition " , args . variable ) ) {
int disposition ;
2013-08-28 21:38:39 +00:00
2013-06-17 03:00:38 +00:00
if ( sscanf ( tempbuf , " %8d " , & disposition ) ! = 1 ) {
ast_log ( AST_LOG_WARNING , " Unable to parse %s (%s) from the CDR for channel %s \n " ,
2013-12-19 00:50:01 +00:00
args . variable , tempbuf , ast_channel_name ( payload - > chan ) ) ;
return ;
2013-06-17 03:00:38 +00:00
}
2013-08-28 22:24:01 +00:00
snprintf ( tempbuf , sizeof ( tempbuf ) , " %s " , ast_cdr_disp2str ( disposition ) ) ;
2013-06-17 03:00:38 +00:00
} else if ( ! strcasecmp ( " amaflags " , args . variable ) ) {
int amaflags ;
2013-08-28 21:38:39 +00:00
2013-06-17 03:00:38 +00:00
if ( sscanf ( tempbuf , " %8d " , & amaflags ) ! = 1 ) {
ast_log ( AST_LOG_WARNING , " Unable to parse %s (%s) from the CDR for channel %s \n " ,
2013-12-19 00:50:01 +00:00
args . variable , tempbuf , ast_channel_name ( payload - > chan ) ) ;
return ;
2013-06-17 03:00:38 +00:00
}
2013-08-28 22:24:01 +00:00
snprintf ( tempbuf , sizeof ( tempbuf ) , " %s " , ast_channel_amaflags2string ( amaflags ) ) ;
2010-06-08 23:48:17 +00:00
}
}
2006-02-12 04:28:58 +00:00
2013-12-19 00:50:01 +00:00
ast_copy_string ( output - > buf , tempbuf , output - > len ) ;
2005-05-05 05:39:33 +00:00
}
2013-12-19 00:50:01 +00:00
static void cdr_write_callback ( void * data , struct stasis_subscription * sub , struct stasis_message * message )
2005-05-05 05:39:33 +00:00
{
2017-12-05 18:04:47 -06:00
struct cdr_func_payload * payload ;
2006-02-12 04:28:58 +00:00
struct ast_flags flags = { 0 } ;
AST_DECLARE_APP_ARGS ( args ,
2013-08-28 21:38:39 +00:00
AST_APP_ARG ( variable ) ;
AST_APP_ARG ( options ) ;
2006-02-12 04:28:58 +00:00
) ;
2013-12-19 00:50:01 +00:00
char * parse ;
2005-05-05 05:39:33 +00:00
2013-12-19 00:50:01 +00:00
if ( cdr_write_message_type ( ) ! = stasis_message_type ( message ) ) {
return ;
2013-08-28 21:38:39 +00:00
}
2017-12-05 18:04:47 -06:00
payload = stasis_message_data ( message ) ;
2013-12-19 00:50:01 +00:00
if ( ! payload ) {
return ;
}
2017-12-05 18:04:47 -06:00
if ( ast_strlen_zero ( payload - > arguments )
| | ! payload - > value ) {
/* Sanity check. cdr_write() could never send these bad messages */
ast_assert ( 0 ) ;
2013-12-19 00:50:01 +00:00
return ;
}
2017-12-05 18:04:47 -06:00
2013-12-19 00:50:01 +00:00
parse = ast_strdupa ( payload - > arguments ) ;
2006-01-11 19:52:29 +00:00
AST_STANDARD_APP_ARGS ( args , parse ) ;
2005-05-05 05:39:33 +00:00
2013-08-28 21:38:39 +00:00
if ( ! ast_strlen_zero ( args . options ) ) {
2006-01-11 19:52:29 +00:00
ast_app_parse_options ( cdr_func_options , & flags , NULL , args . options ) ;
2013-08-28 21:38:39 +00:00
}
2005-05-05 05:39:33 +00:00
2017-12-05 18:04:47 -06:00
/* These are already handled by cdr_write() */
ast_assert ( strcasecmp ( args . variable , " accountcode " )
& & strcasecmp ( args . variable , " peeraccount " )
& & strcasecmp ( args . variable , " amaflags " ) ) ;
if ( ! strcasecmp ( args . variable , " userfield " ) ) {
2013-12-19 00:50:01 +00:00
ast_cdr_setuserfield ( ast_channel_name ( payload - > chan ) , payload - > value ) ;
2013-06-17 03:00:38 +00:00
} else {
2013-12-19 00:50:01 +00:00
ast_cdr_setvar ( ast_channel_name ( payload - > chan ) , args . variable , payload - > value ) ;
2013-06-17 03:00:38 +00:00
}
}
2013-12-19 00:50:01 +00:00
static void cdr_prop_write_callback ( void * data , struct stasis_subscription * sub , struct stasis_message * message )
2013-06-17 03:00:38 +00:00
{
2013-12-19 00:50:01 +00:00
struct cdr_func_payload * payload = stasis_message_data ( message ) ;
2013-06-17 03:00:38 +00:00
enum ast_cdr_options option ;
2013-12-19 00:50:01 +00:00
char * parse ;
2013-09-30 19:58:19 +00:00
AST_DECLARE_APP_ARGS ( args ,
AST_APP_ARG ( variable ) ;
AST_APP_ARG ( options ) ;
) ;
2013-12-19 00:50:01 +00:00
if ( cdr_prop_write_message_type ( ) ! = stasis_message_type ( message ) ) {
return ;
2013-09-30 19:58:19 +00:00
}
2013-12-19 00:50:01 +00:00
if ( ! payload ) {
return ;
}
if ( ast_strlen_zero ( payload - > arguments ) ) {
ast_log ( AST_LOG_WARNING , " %s requires a variable (%s(variable)=value) \n ) " ,
payload - > cmd , payload - > cmd ) ;
return ;
}
if ( ast_strlen_zero ( payload - > value ) ) {
ast_log ( AST_LOG_WARNING , " %s requires a value (%s(variable)=value) \n ) " ,
payload - > cmd , payload - > cmd ) ;
return ;
}
parse = ast_strdupa ( payload - > arguments ) ;
2013-09-30 19:58:19 +00:00
AST_STANDARD_APP_ARGS ( args , parse ) ;
if ( ! strcasecmp ( " party_a " , args . variable ) ) {
2013-06-17 03:00:38 +00:00
option = AST_CDR_FLAG_PARTY_A ;
2013-09-30 19:58:19 +00:00
} else if ( ! strcasecmp ( " disable " , args . variable ) ) {
2013-06-17 03:00:38 +00:00
option = AST_CDR_FLAG_DISABLE_ALL ;
} else {
2013-12-19 00:50:01 +00:00
ast_log ( AST_LOG_WARNING , " Unknown option %s used with %s \n " , args . variable , payload - > cmd ) ;
return ;
2013-06-17 03:00:38 +00:00
}
2013-12-19 00:50:01 +00:00
if ( ast_true ( payload - > value ) ) {
ast_cdr_set_property ( ast_channel_name ( payload - > chan ) , option ) ;
2013-06-17 03:00:38 +00:00
} else {
2013-12-19 00:50:01 +00:00
ast_cdr_clear_property ( ast_channel_name ( payload - > chan ) , option ) ;
}
}
static int cdr_read ( struct ast_channel * chan , const char * cmd , char * parse ,
char * buf , size_t len )
{
RAII_VAR ( struct stasis_message * , message , NULL , ao2_cleanup ) ;
2014-08-06 12:55:28 +00:00
RAII_VAR ( struct cdr_func_payload * , payload , NULL , ao2_cleanup ) ;
2013-12-19 00:50:01 +00:00
struct cdr_func_data output = { 0 , } ;
2014-03-27 19:21:44 +00:00
if ( ! chan ) {
ast_log ( LOG_WARNING , " No channel was provided to %s function. \n " , cmd ) ;
return - 1 ;
}
2014-08-06 12:55:28 +00:00
if ( ! cdr_read_message_type ( ) ) {
ast_log ( AST_LOG_WARNING , " Failed to manipulate CDR for channel %s: message type not available \n " ,
ast_channel_name ( chan ) ) ;
return - 1 ;
}
payload = ao2_alloc ( sizeof ( * payload ) , NULL ) ;
2013-12-19 00:50:01 +00:00
if ( ! payload ) {
return - 1 ;
2013-06-17 03:00:38 +00:00
}
2013-12-19 00:50:01 +00:00
payload - > chan = chan ;
payload - > cmd = cmd ;
payload - > arguments = parse ;
2014-01-12 22:13:12 +00:00
payload - > data = & output ;
2013-12-19 00:50:01 +00:00
buf [ 0 ] = ' \0 ' ; /* Ensure the buffer is initialized. */
output . buf = buf ;
output . len = len ;
message = stasis_message_create ( cdr_read_message_type ( ) , payload ) ;
if ( ! message ) {
ast_log ( AST_LOG_WARNING , " Failed to manipulate CDR for channel %s: unable to create message \n " ,
ast_channel_name ( chan ) ) ;
return - 1 ;
}
/* If this is a request on a dummy channel, we're doing post-processing on an
* already dispatched CDR. Simply call the callback to calculate the value and
* return, instead of posting to Stasis as we would for a running channel.
*/
if ( ast_strlen_zero ( ast_channel_name ( chan ) ) ) {
cdr_read_callback ( NULL , NULL , message ) ;
} else {
2014-01-12 22:13:12 +00:00
RAII_VAR ( struct stasis_message_router * , router , ast_cdr_message_router ( ) , ao2_cleanup ) ;
2013-12-19 00:50:01 +00:00
2014-01-12 22:13:12 +00:00
if ( ! router ) {
ast_log ( AST_LOG_WARNING , " Failed to manipulate CDR for channel %s: no message router \n " ,
2013-12-19 00:50:01 +00:00
ast_channel_name ( chan ) ) ;
return - 1 ;
}
2014-01-12 22:13:12 +00:00
stasis_message_router_publish_sync ( router , message ) ;
2013-12-19 00:50:01 +00:00
}
return 0 ;
}
2017-12-05 18:04:47 -06:00
static int cdr_write ( struct ast_channel * chan , const char * cmd , char * arguments ,
const char * value )
2013-12-19 00:50:01 +00:00
{
2017-12-05 18:04:47 -06:00
struct stasis_message * message ;
struct cdr_func_payload * payload ;
struct stasis_message_router * router ;
AST_DECLARE_APP_ARGS ( args ,
AST_APP_ARG ( variable ) ;
AST_APP_ARG ( options ) ;
) ;
char * parse ;
2014-01-12 22:13:12 +00:00
2014-03-27 19:21:44 +00:00
if ( ! chan ) {
ast_log ( LOG_WARNING , " No channel was provided to %s function. \n " , cmd ) ;
return - 1 ;
}
2017-12-05 18:04:47 -06:00
if ( ast_strlen_zero ( arguments ) ) {
ast_log ( LOG_WARNING , " %s requires a variable (%s(variable)=value) \n ) " ,
cmd , cmd ) ;
return - 1 ;
}
if ( ! value ) {
ast_log ( LOG_WARNING , " %s requires a value (%s(variable)=value) \n ) " ,
cmd , cmd ) ;
2014-01-12 22:13:12 +00:00
return - 1 ;
}
2013-12-19 00:50:01 +00:00
2017-12-05 18:04:47 -06:00
parse = ast_strdupa ( arguments ) ;
AST_STANDARD_APP_ARGS ( args , parse ) ;
/* These CDR variables are no longer supported or set directly on the channel */
if ( ! strcasecmp ( args . variable , " accountcode " ) ) {
ast_log ( LOG_WARNING , " Using the %s function to set 'accountcode' is deprecated. Please use the CHANNEL function instead. \n " ,
cmd ) ;
ast_channel_lock ( chan ) ;
ast_channel_accountcode_set ( chan , value ) ;
ast_channel_unlock ( chan ) ;
return 0 ;
}
if ( ! strcasecmp ( args . variable , " amaflags " ) ) {
int amaflags ;
ast_log ( LOG_WARNING , " Using the %s function to set 'amaflags' is deprecated. Please use the CHANNEL function instead. \n " ,
cmd ) ;
if ( isdigit ( * value ) ) {
if ( sscanf ( value , " %30d " , & amaflags ) ! = 1 ) {
amaflags = AST_AMA_NONE ;
}
} else {
amaflags = ast_channel_string2amaflag ( value ) ;
}
ast_channel_lock ( chan ) ;
ast_channel_amaflags_set ( chan , amaflags ) ;
ast_channel_unlock ( chan ) ;
return 0 ;
}
if ( ! strcasecmp ( args . variable , " peeraccount " ) ) {
ast_log ( LOG_WARNING , " The 'peeraccount' setting is not supported. Please set the 'accountcode' on the appropriate channel using the CHANNEL function. \n " ) ;
return 0 ;
}
/* The remaining CDR variables are handled by CDR processing code */
2014-08-06 12:55:28 +00:00
if ( ! cdr_write_message_type ( ) ) {
2017-12-05 18:04:47 -06:00
ast_log ( LOG_WARNING , " Failed to manipulate CDR for channel %s: message type not available \n " ,
2014-08-06 12:55:28 +00:00
ast_channel_name ( chan ) ) ;
return - 1 ;
}
payload = ao2_alloc ( sizeof ( * payload ) , NULL ) ;
2013-12-19 00:50:01 +00:00
if ( ! payload ) {
return - 1 ;
}
payload - > chan = chan ;
payload - > cmd = cmd ;
2017-12-05 18:04:47 -06:00
payload - > arguments = arguments ;
2013-12-19 00:50:01 +00:00
payload - > value = value ;
message = stasis_message_create ( cdr_write_message_type ( ) , payload ) ;
2017-12-05 18:04:47 -06:00
ao2_ref ( payload , - 1 ) ;
2013-12-19 00:50:01 +00:00
if ( ! message ) {
2017-12-05 18:04:47 -06:00
ast_log ( LOG_WARNING , " Failed to manipulate CDR for channel %s: unable to create message \n " ,
2013-12-19 00:50:01 +00:00
ast_channel_name ( chan ) ) ;
return - 1 ;
}
2017-12-05 18:04:47 -06:00
router = ast_cdr_message_router ( ) ;
if ( ! router ) {
ast_log ( LOG_WARNING , " Failed to manipulate CDR for channel %s: no message router \n " ,
ast_channel_name ( chan ) ) ;
ao2_ref ( message , - 1 ) ;
return - 1 ;
}
2014-01-12 22:13:12 +00:00
stasis_message_router_publish_sync ( router , message ) ;
2017-12-05 18:04:47 -06:00
ao2_ref ( router , - 1 ) ;
ao2_ref ( message , - 1 ) ;
2013-12-19 00:50:01 +00:00
return 0 ;
}
static int cdr_prop_write ( struct ast_channel * chan , const char * cmd , char * parse ,
const char * value )
{
RAII_VAR ( struct stasis_message * , message , NULL , ao2_cleanup ) ;
2014-10-28 11:12:03 +00:00
RAII_VAR ( struct cdr_func_payload * , payload , NULL , ao2_cleanup ) ;
2014-01-12 22:13:12 +00:00
RAII_VAR ( struct stasis_message_router * , router , ast_cdr_message_router ( ) , ao2_cleanup ) ;
2014-03-27 19:21:44 +00:00
if ( ! chan ) {
ast_log ( LOG_WARNING , " No channel was provided to %s function. \n " , cmd ) ;
return - 1 ;
}
2014-01-12 22:13:12 +00:00
if ( ! router ) {
ast_log ( AST_LOG_WARNING , " Failed to manipulate CDR for channel %s: no message router \n " ,
ast_channel_name ( chan ) ) ;
return - 1 ;
}
2013-12-19 00:50:01 +00:00
2017-12-05 18:04:47 -06:00
if ( ! cdr_prop_write_message_type ( ) ) {
2014-08-06 12:55:28 +00:00
ast_log ( AST_LOG_WARNING , " Failed to manipulate CDR for channel %s: message type not available \n " ,
ast_channel_name ( chan ) ) ;
return - 1 ;
}
payload = ao2_alloc ( sizeof ( * payload ) , NULL ) ;
2013-12-19 00:50:01 +00:00
if ( ! payload ) {
return - 1 ;
}
payload - > chan = chan ;
payload - > cmd = cmd ;
payload - > arguments = parse ;
payload - > value = value ;
message = stasis_message_create ( cdr_prop_write_message_type ( ) , payload ) ;
if ( ! message ) {
ast_log ( AST_LOG_WARNING , " Failed to manipulate CDR for channel %s: unable to create message \n " ,
ast_channel_name ( chan ) ) ;
return - 1 ;
}
2014-01-12 22:13:12 +00:00
stasis_message_router_publish_sync ( router , message ) ;
2013-12-19 00:50:01 +00:00
2006-02-12 04:28:58 +00:00
return 0 ;
2005-05-05 05:39:33 +00:00
}
2006-02-11 03:14:05 +00:00
static struct ast_custom_function cdr_function = {
2005-05-05 05:39:33 +00:00
. name = " CDR " ,
2006-02-11 03:14:05 +00:00
. read = cdr_read ,
. write = cdr_write ,
2005-05-05 05:39:33 +00:00
} ;
2013-06-17 03:00:38 +00:00
static struct ast_custom_function cdr_prop_function = {
. name = " CDR_PROP " ,
. read = NULL ,
. write = cdr_prop_write ,
} ;
2006-08-21 02:11:39 +00:00
static int unload_module ( void )
2006-02-11 03:14:05 +00:00
{
2014-01-12 22:13:12 +00:00
RAII_VAR ( struct stasis_message_router * , router , ast_cdr_message_router ( ) , ao2_cleanup ) ;
2013-06-17 03:00:38 +00:00
int res = 0 ;
2014-01-12 22:13:12 +00:00
if ( router ) {
stasis_message_router_remove ( router , cdr_prop_write_message_type ( ) ) ;
stasis_message_router_remove ( router , cdr_write_message_type ( ) ) ;
stasis_message_router_remove ( router , cdr_read_message_type ( ) ) ;
}
2013-12-19 00:50:01 +00:00
STASIS_MESSAGE_TYPE_CLEANUP ( cdr_read_message_type ) ;
STASIS_MESSAGE_TYPE_CLEANUP ( cdr_write_message_type ) ;
STASIS_MESSAGE_TYPE_CLEANUP ( cdr_prop_write_message_type ) ;
2013-06-17 03:00:38 +00:00
res | = ast_custom_function_unregister ( & cdr_function ) ;
res | = ast_custom_function_unregister ( & cdr_prop_function ) ;
return res ;
2006-02-11 03:14:05 +00:00
}
2006-08-21 02:11:39 +00:00
static int load_module ( void )
2006-02-11 03:14:05 +00:00
{
2014-01-12 22:13:12 +00:00
RAII_VAR ( struct stasis_message_router * , router , ast_cdr_message_router ( ) , ao2_cleanup ) ;
2013-06-17 03:00:38 +00:00
int res = 0 ;
2014-01-12 22:13:12 +00:00
if ( ! router ) {
2017-04-11 10:07:39 -06:00
return AST_MODULE_LOAD_DECLINE ;
2014-01-12 22:13:12 +00:00
}
2013-12-19 00:50:01 +00:00
res | = STASIS_MESSAGE_TYPE_INIT ( cdr_read_message_type ) ;
res | = STASIS_MESSAGE_TYPE_INIT ( cdr_write_message_type ) ;
res | = STASIS_MESSAGE_TYPE_INIT ( cdr_prop_write_message_type ) ;
2013-06-17 03:00:38 +00:00
res | = ast_custom_function_register ( & cdr_function ) ;
res | = ast_custom_function_register ( & cdr_prop_function ) ;
2014-01-12 22:13:12 +00:00
res | = stasis_message_router_add ( router , cdr_prop_write_message_type ( ) ,
cdr_prop_write_callback , NULL ) ;
res | = stasis_message_router_add ( router , cdr_write_message_type ( ) ,
cdr_write_callback , NULL ) ;
res | = stasis_message_router_add ( router , cdr_read_message_type ( ) ,
cdr_read_callback , NULL ) ;
if ( res ) {
2017-04-11 10:07:39 -06:00
unload_module ( ) ;
return AST_MODULE_LOAD_DECLINE ;
2014-01-12 22:13:12 +00:00
}
return AST_MODULE_LOAD_SUCCESS ;
2006-02-11 03:14:05 +00:00
}
2018-02-16 22:11:42 -05:00
AST_MODULE_INFO ( ASTERISK_GPL_KEY , AST_MODFLAG_DEFAULT , " Call Detail Record (CDR) dialplan functions " ,
. support_level = AST_MODULE_SUPPORT_CORE ,
. load = load_module ,
. unload = unload_module ,
. requires = " cdr " ,
) ;