diff options
Diffstat (limited to 'funcs/func_cdr.c')
-rw-r--r-- | funcs/func_cdr.c | 317 |
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"); |