summaryrefslogtreecommitdiff
path: root/funcs/func_cdr.c
diff options
context:
space:
mode:
Diffstat (limited to 'funcs/func_cdr.c')
-rw-r--r--funcs/func_cdr.c317
1 files changed, 185 insertions, 132 deletions
diff --git a/funcs/func_cdr.c b/funcs/func_cdr.c
index adb42742b..0f900feda 100644
--- a/funcs/func_cdr.c
+++ b/funcs/func_cdr.c
@@ -56,7 +56,27 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<para>Last application arguments.</para>
</enum>
<enum name="disposition">
- <para>ANSWERED, NO ANSWER, BUSY, FAILED.</para>
+ <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>
</enum>
<enum name="src">
<para>Source.</para>
@@ -65,7 +85,16 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<para>Time the call started.</para>
</enum>
<enum name="amaflags">
- <para>DOCUMENTATION, BILL, IGNORE, etc.</para>
+ <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>
</enum>
<enum name="dst">
<para>Destination.</para>
@@ -75,6 +104,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
</enum>
<enum name="accountcode">
<para>The channel's account code.</para>
+ <warning><para>Accessing this setting is deprecated in CDR. Please use the CHANNEL function instead.</para></warning>
</enum>
<enum name="dcontext">
<para>Destination context.</para>
@@ -113,16 +143,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<option name="f">
<para>Returns billsec or duration fields as floating point values.</para>
</option>
- <option name="l">
- <para>Uses the most recent CDR on a channel with multiple records</para>
- </option>
- <option name="r">
- <para>Searches the entire stack of CDRs on the channel.</para>
- </option>
- <option name="s">
- <para>Skips any CDR's that are marked 'LOCKED' due to forkCDR() calls.
- (on setting/writing CDR vars only)</para>
- </option>
<option name="u">
<para>Retrieves the raw, unprocessed value.</para>
<para>For example, 'start', 'answer', and 'end' will be retrieved as epoch
@@ -137,138 +157,132 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<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
- with this function, and this variable will be stored on the cdr.</para>
- <note><para>For setting CDR values, the <literal>l</literal> flag does not apply to
- setting the <literal>accountcode</literal>, <literal>userfield</literal>, or
- <literal>amaflags</literal>.</para><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>
- <para>Raw values for <literal>disposition</literal>:</para>
- <enumlist>
- <enum name="0">
- <para>NO ANSWER</para>
- </enum>
- <enum name="1">
- <para>NO ANSWER (NULL record)</para>
- </enum>
- <enum name="2">
- <para>FAILED</para>
- </enum>
- <enum name="4">
- <para>BUSY</para>
- </enum>
- <enum name="8">
- <para>ANSWERED</para>
- </enum>
- </enumlist>
- <para>Raw values for <literal>amaflags</literal>:</para>
- <enumlist>
- <enum name="1">
- <para>OMIT</para>
- </enum>
- <enum name="2">
- <para>BILLING</para>
- </enum>
- <enum name="3">
- <para>DOCUMENTATION</para>
- </enum>
- </enumlist>
+ 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>
<para>Example: exten => 1,1,Set(CDR(userfield)=test)</para>
</description>
</function>
+ <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">
+ <para>Disable CDRs for this channel.</para>
+ <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>
***/
enum cdr_option_flags {
- OPT_RECURSIVE = (1 << 0),
OPT_UNPARSED = (1 << 1),
- OPT_LAST = (1 << 2),
- OPT_SKIPLOCKED = (1 << 3),
- OPT_FLOAT = (1 << 4),
+ OPT_FLOAT = (1 << 2),
};
AST_APP_OPTIONS(cdr_func_options, {
AST_APP_OPTION('f', OPT_FLOAT),
- AST_APP_OPTION('l', OPT_LAST),
- AST_APP_OPTION('r', OPT_RECURSIVE),
- AST_APP_OPTION('s', OPT_SKIPLOCKED),
AST_APP_OPTION('u', OPT_UNPARSED),
});
static int cdr_read(struct ast_channel *chan, const char *cmd, char *parse,
char *buf, size_t len)
{
- char *ret = NULL;
+ char format_buf[128];
struct ast_flags flags = { 0 };
- struct ast_cdr *cdr;
+ char tempbuf[128];
+ char *info;
AST_DECLARE_APP_ARGS(args,
AST_APP_ARG(variable);
AST_APP_ARG(options);
);
- if (ast_strlen_zero(parse) || !chan)
+ if (!chan) {
return -1;
+ }
- ast_channel_lock(chan);
- cdr = ast_channel_cdr(chan);
- if (!cdr) {
- ast_channel_unlock(chan);
+ if (ast_strlen_zero(parse)) {
+ ast_log(AST_LOG_WARNING, "FUNC_CDR requires a variable (FUNC_CDR(variable[,option]))\n)");
return -1;
}
+ info = ast_strdupa(parse);
+ AST_STANDARD_APP_ARGS(args, info);
- AST_STANDARD_APP_ARGS(args, parse);
-
- if (!ast_strlen_zero(args.options))
+ if (!ast_strlen_zero(args.options)) {
ast_app_parse_options(cdr_func_options, &flags, NULL, args.options);
+ }
- if (ast_test_flag(&flags, OPT_LAST))
- while (cdr->next)
- cdr = cdr->next;
-
- if (ast_test_flag(&flags, OPT_SKIPLOCKED))
- while (ast_test_flag(cdr, AST_CDR_FLAG_LOCKED) && cdr->next)
- cdr = cdr->next;
-
- if (!strcasecmp("billsec", args.variable) && ast_test_flag(&flags, OPT_FLOAT)) {
- if (!ast_tvzero(cdr->answer)) {
- double hrtime;
-
- if(!ast_tvzero(cdr->end))
- hrtime = (double)(ast_tvdiff_us(cdr->end, cdr->answer) / 1000000.0);
- else
- hrtime = (double)(ast_tvdiff_us(ast_tvnow(), cdr->answer) / 1000000.0);
+ if (ast_cdr_getvar(ast_channel_name(chan), args.variable, tempbuf, sizeof(tempbuf))) {
+ return 0;
+ }
- snprintf(buf, len, "%lf", hrtime);
- } else {
- snprintf(buf, len, "%lf", 0.0);
+ if (ast_test_flag(&flags, OPT_FLOAT) && (!strcasecmp("billsec", args.variable) || !strcasecmp("duration", args.variable))) {
+ long ms;
+ double dtime;
+ if (sscanf(tempbuf, "%30ld", &ms) != 1) {
+ ast_log(AST_LOG_WARNING, "Unable to parse %s (%s) from the CDR for channel %s\n",
+ args.variable, tempbuf, ast_channel_name(chan));
+ return 0;
}
- ret = buf;
- } else if (!strcasecmp("duration", args.variable) && ast_test_flag(&flags, OPT_FLOAT)) {
- double hrtime;
-
- if(!ast_tvzero(cdr->end))
- hrtime = (double)(ast_tvdiff_us(cdr->end, cdr->start) / 1000000.0);
- else
- hrtime = (double)(ast_tvdiff_us(ast_tvnow(), cdr->start) / 1000000.0);
-
- snprintf(buf, len, "%lf", hrtime);
-
- if (!ast_strlen_zero(buf)) {
- ret = buf;
+ dtime = (double)(ms / 1000.0);
+ sprintf(tempbuf, "%lf", dtime);
+ } else if (!ast_test_flag(&flags, OPT_UNPARSED)) {
+ if (!strcasecmp("start", args.variable)
+ || !strcasecmp("end", args.variable)
+ || !strcasecmp("answer", args.variable)) {
+ struct timeval fmt_time;
+ struct ast_tm tm;
+ if (sscanf(tempbuf, "%ld.%ld", &fmt_time.tv_sec, &fmt_time.tv_usec) != 2) {
+ ast_log(AST_LOG_WARNING, "Unable to parse %s (%s) from the CDR for channel %s\n",
+ args.variable, tempbuf, ast_channel_name(chan));
+ return 0;
+ }
+ ast_localtime(&fmt_time, &tm, NULL);
+ ast_strftime(tempbuf, sizeof(*tempbuf), "%Y-%m-%d %T", &tm);
+ } else if (!strcasecmp("disposition", args.variable)) {
+ int disposition;
+ if (sscanf(tempbuf, "%8d", &disposition) != 1) {
+ ast_log(AST_LOG_WARNING, "Unable to parse %s (%s) from the CDR for channel %s\n",
+ args.variable, tempbuf, ast_channel_name(chan));
+ return 0;
+ }
+ sprintf(format_buf, "%s", ast_cdr_disp2str(disposition));
+ strcpy(tempbuf, format_buf);
+ } else if (!strcasecmp("amaflags", args.variable)) {
+ int amaflags;
+ if (sscanf(tempbuf, "%8d", &amaflags) != 1) {
+ ast_log(AST_LOG_WARNING, "Unable to parse %s (%s) from the CDR for channel %s\n",
+ args.variable, tempbuf, ast_channel_name(chan));
+ return 0;
+ }
+ sprintf(format_buf, "%s", ast_channel_amaflags2string(amaflags));
+ strcpy(tempbuf, format_buf);
}
- } else {
- ast_cdr_getvar(cdr, args.variable, &ret, buf, len,
- ast_test_flag(&flags, OPT_RECURSIVE),
- ast_test_flag(&flags, OPT_UNPARSED));
}
- ast_channel_unlock(chan);
- return ret ? 0 : -1;
+ ast_copy_string(buf, tempbuf, len);
+ return 0;
}
static int cdr_write(struct ast_channel *chan, const char *cmd, char *parse,
const char *value)
{
- struct ast_cdr *cdr;
struct ast_flags flags = { 0 };
AST_DECLARE_APP_ARGS(args,
AST_APP_ARG(variable);
@@ -278,36 +292,59 @@ static int cdr_write(struct ast_channel *chan, const char *cmd, char *parse,
if (ast_strlen_zero(parse) || !value || !chan)
return -1;
- ast_channel_lock(chan);
- cdr = ast_channel_cdr(chan);
- if (!cdr) {
- ast_channel_unlock(chan);
- return -1;
- }
-
AST_STANDARD_APP_ARGS(args, parse);
if (!ast_strlen_zero(args.options))
ast_app_parse_options(cdr_func_options, &flags, NULL, args.options);
- if (ast_test_flag(&flags, OPT_LAST))
- while (cdr->next)
- cdr = cdr->next;
-
- if (!strcasecmp(args.variable, "accountcode")) /* the 'l' flag doesn't apply to setting the accountcode, userfield, or amaflags */
- ast_cdr_setaccount(chan, value);
- else if (!strcasecmp(args.variable, "peeraccount"))
- ast_cdr_setpeeraccount(chan, value);
- else if (!strcasecmp(args.variable, "userfield"))
- ast_cdr_setuserfield(chan, value);
- else if (!strcasecmp(args.variable, "amaflags"))
- ast_cdr_setamaflags(chan, value);
- else
- ast_cdr_setvar(cdr, args.variable, value, ast_test_flag(&flags, OPT_RECURSIVE));
- /* No need to worry about the u flag, as all fields for which setting
- * 'u' would do anything are marked as readonly. */
-
- ast_channel_unlock(chan);
+ if (!strcasecmp(args.variable, "accountcode")) {
+ ast_log(AST_LOG_WARNING, "Using the CDR function to set 'accountcode' is deprecated. Please use the CHANNEL function instead.\n");
+ ast_channel_lock(chan);
+ ast_channel_accountcode_set(chan, value);
+ ast_channel_unlock(chan);
+ } else if (!strcasecmp(args.variable, "peeraccount")) {
+ ast_log(AST_LOG_WARNING, "The 'peeraccount' setting is not supported. Please set the 'accountcode' on the appropriate channel using the CHANNEL function.\n");
+ } else if (!strcasecmp(args.variable, "userfield")) {
+ ast_cdr_setuserfield(ast_channel_name(chan), value);
+ } else if (!strcasecmp(args.variable, "amaflags")) {
+ ast_log(AST_LOG_WARNING, "Using the CDR function to set 'amaflags' is deprecated. Please use the CHANNEL function instead.\n");
+ if (isdigit(*value)) {
+ int amaflags;
+ sscanf(value, "%30d", &amaflags);
+ ast_channel_lock(chan);
+ ast_channel_amaflags_set(chan, amaflags);
+ ast_channel_unlock(chan);
+ } else {
+ ast_channel_lock(chan);
+ ast_channel_amaflags_set(chan, ast_channel_string2amaflag(value));
+ ast_channel_unlock(chan);
+ }
+ } else {
+ ast_cdr_setvar(ast_channel_name(chan), args.variable, value);
+ }
+
+ return 0;
+}
+
+static int cdr_prop_write(struct ast_channel *chan, const char *cmd, char *parse,
+ const char *value)
+{
+ enum ast_cdr_options option;
+
+ if (!strcasecmp("party_a", cmd)) {
+ option = AST_CDR_FLAG_PARTY_A;
+ } else if (!strcasecmp("disable", cmd)) {
+ option = AST_CDR_FLAG_DISABLE_ALL;
+ } else {
+ ast_log(AST_LOG_WARNING, "Unknown option %s used with CDR_PROP\n", cmd);
+ return 0;
+ }
+
+ if (ast_true(value)) {
+ ast_cdr_set_property(ast_channel_name(chan), option);
+ } else {
+ ast_cdr_clear_property(ast_channel_name(chan), option);
+ }
return 0;
}
@@ -317,14 +354,30 @@ static struct ast_custom_function cdr_function = {
.write = cdr_write,
};
+static struct ast_custom_function cdr_prop_function = {
+ .name = "CDR_PROP",
+ .read = NULL,
+ .write = cdr_prop_write,
+};
+
static int unload_module(void)
{
- return ast_custom_function_unregister(&cdr_function);
+ int res = 0;
+
+ res |= ast_custom_function_unregister(&cdr_function);
+ res |= ast_custom_function_unregister(&cdr_prop_function);
+
+ return res;
}
static int load_module(void)
{
- return ast_custom_function_register(&cdr_function);
+ int res = 0;
+
+ res |= ast_custom_function_register(&cdr_function);
+ res |= ast_custom_function_register(&cdr_prop_function);
+
+ return res;
}
-AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Call Detail Record (CDR) dialplan function");
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Call Detail Record (CDR) dialplan functions");