Update Asterisk's CDRs for the new bridging framework

This patch is the initial push to update Asterisk's CDR engine for the new
bridging framework. This patch guts the existing CDR engine and builds the new
on top of messages coming across Stasis. As changes in channel state and bridge
state are detected, CDRs are built and dispatched accordingly. This
fundamentally changes CDRs in a few ways.
(1) CDRs are now *very* reflective of the actual state of channels and bridges.
    This means CDRs track well with what an actual channel is doing - which
    is useful in transfer scenarios (which were previously difficult to pin
    down). It does, however, mean that CDRs cannot be 'fooled'. Previous
    behavior in Asterisk allowed for CDR applications, channels, and other
    properties to be spoofed in parts of the code - this no longer works.
(2) CDRs have defined behavior in multi-party scenarios. This behavior will not
    be what everyone wants, but it is a defined behavior and as such, it is
    predictable.
(3) The CDR manipulation functions and applications have been overhauled. Major
    changes have been made to ResetCDR and ForkCDR in particular. Many of the
    options for these two applications no longer made any sense with the new
    framework and the (slightly) more immutable nature of CDRs.

There are a plethora of other changes. For a full description of CDR behavior,
see the CDR specification on the Asterisk wiki.

(closes issue ASTERISK-21196)

Review: https://reviewboard.asterisk.org/r/2486/



git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@391947 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
Matthew Jordan 2013-06-17 03:00:38 +00:00
parent 67e35c7b47
commit 6258bbe7bd
68 changed files with 7207 additions and 2745 deletions

277
CHANGES
View File

@ -11,6 +11,94 @@
--- Functionality changes from Asterisk 11 to Asterisk 12 --------------------
------------------------------------------------------------------------------
Applications
------------------
AgentMonitorOutgoing
------------------
* The 'c' option has been removed. It is not possible to modify the name of a
channel involved in a CDR.
ForkCDR
------------------
* ForkCDR no longer automatically resets the forked CDR. See the 'r' option
for more information.
* Variables are no longer purged from the original CDR. See the 'v' option for
more information.
* The 'A' option has been removed. The Answer time on a CDR is never updated
once set.
* The 'd' option has been removed. The disposition on a CDR is a function of
the state of the channel and cannot be altered.
* The 'D' option has been removed. Who the Party B is on a CDR is a function
of the state of the respective channels, and cannot be altered.
* The 'r' option has been changed. Previously, ForkCDR always reset the CDR
such that the start time and, if applicable, the answer time was updated.
Now, by default, ForkCDR simply forks the CDR, maintaining any times. The
'r' option now triggers the Reset, setting the start time (and answer time
if applicable) to the current time.
* The 's' option has been removed. A variable can be set on the original CDR
if desired using the CDR function, and removed from a forked CDR using the
same function.
* The 'T' option has been removed. The concept of DONT_TOUCH and LOCKED no
longer applies in the CDR engine.
* The 'v' option now prevents the copy of the variables from the original CDR
to the forked CDR. Previously the variables were always copied but were
removed from the original. Removing variables from a CDR can have unintended
side effects - this option allows the user to prevent propagation of
variables from the original to the forked without modifying the original.
MeetMe
-------------------
* Added the 'n' option to MeetMe to prevent application of the DENOISE function
to a channel joining a conference. Some channel drivers that vary the number
of audio samples in a voice frame will experience significant quality problems
if a denoiser is attached to the channel; this option gives them the ability
to remove the denoiser without having to unload func_speex.
NoCDR
------------------
* The NoCDR application is deprecated. Please use the CDR_PROP function to
disable CDRs.
* While the NoCDR application will prevent CDRs for a channel from being
propagated to registered CDR backends, it will not prevent that data from
being collected. Hence, a subsequent call to ResetCDR or the CDR_PROP
function that enables CDRs on a channel will restore those records that have
not yet been finalized.
Queue
-------------------
* Add queue available hint. exten => 8501,hint,Queue:markq_avail
Note: the suffix '_avail' after the queuename.
Reports 'InUse' for no logged in agents or no free agents.
Reports 'Idle' when an agent is free.
ResetCDR
------------------
* The 'e' option has been deprecated. Use the CDR_PROP function to re-enable
CDRs when they were previously disabled on a channel.
* The 'w' and 'a' options have been removed. Dispatching CDRs to registered
backends occurs on an as-needed basis in order to preserve linkedid
propagation and other needed behavior.
SetAMAFlags
------------------
* This application is deprecated in favor of the CHANNEL function.
Core
------------------
* Redirecting reasons can now be set to arbitrary strings. This means
that the REDIRECTING dialplan function can be used to set the redirecting
reason to any string. It also allows for custom strings to be read as the
redirecting reason from SIP Diversion headers.
AMI (Asterisk Manager Interface)
------------------
@ -72,6 +160,9 @@ AMI (Asterisk Manager Interface)
event, the various ChanVariable fields will contain a suffix that specifies
which channel they correspond to.
* The NewPeerAccount AMI event is no longer raised. The NewAccountCode AMI
event always conveys the AMI event for a particular channel.
* All "Reload" events have been consolidated into a single event type. This
event will always contain a Module field specifying the name of the module
and a Status field denoting the result of the reload. All modules now issue
@ -118,33 +209,21 @@ AGI (Asterisk Gateway Interface)
* The manager event AsyncAGI has been split into AsyncAGIStart, AsyncAGIExec,
and AsyncAGIEnd.
Channel Drivers
CDR (Call Detail Records)
------------------
* When a channel driver is configured to enable jiterbuffers, they are now
applied unconditionally when a channel joins a bridge. If a jitterbuffer
is already set for that channel when it enters, such as by the JITTERBUFFER
function, then the existing jitterbuffer will be used and the one set by
the channel driver will not be applied.
* Significant changes have been made to the behavior of CDRs. For a full
definition of CDR behavior in Asterisk 12, please read the specification
on the Asterisk wiki (wiki.asterisk.org).
chan_local
------------------
* The /b option is removed.
* CDRs will now be created between all participants in a bridge. For each
pair of channels in a bridge, a CDR is created to represent the path of
communication between those two endpoints. This lets an end user choose who
to bill for what during multi-party bridges or bridge operations during
transfer scenarios.
* chan_local moved into the system core and is no longer a loadable module.
chan_mobile
------------------
* Added general support for busy detection.
* Added ECAM command support for Sony Ericsson phones.
chan_sip
------------------
* Added support for RFC 3327 "Path" headers. This can be enabled in sip.conf
using the 'supportpath' setting, either on a global basis or on a peer basis.
This setting enables Asterisk to route outgoing out-of-dialog requests via a
set of proxies by using a pre-loaded route-set defined by the Path headers in
the REGISTER request. See Realtime updates for more configuration information.
* When a CDR is dispatched, user defined CDR variables from both parties are
included in the resulting CDR. If both parties have the same variable, only
the Party A value is provided.
Features
-------------------
@ -163,12 +242,6 @@ Features
and FEATUREMAP() functions inherited to child channels by setting
FEATURE(inherit)=yes.
Functions
------------------
* JITTERBUFFER now accepts an argument of 'disabled' which can be used
to remove jitterbuffers previously set on a channel with JITTERBUFFER.
The value of this setting is ignored when disabled is used for the argument.
Logging
-------------------
* When performing queue pause/unpause on an interface without specifying an
@ -178,25 +251,12 @@ Logging
* Added the 'queue_log_realtime_use_gmt' option to have timestamps in GMT
for realtime queue log entries.
MeetMe
-------------------
* Added the 'n' option to MeetMe to prevent application of the DENOISE function
to a channel joining a conference. Some channel drivers that vary the number
of audio samples in a voice frame will experience significant quality problems
if a denoiser is attached to the channel; this option gives them the ability
to remove the denoiser without having to unload func_speex.
Parking
-------------------
* Parking is now implemented as a module instead of as core functionality.
The preferred way to configure parking is now through res_parking.conf while
configuration through features.conf is not currently supported.
* res_parking uses the configuration framework. If an invalid configuration is
supplied, res_parking will fail to load or fail to reload. Previously parking
lots that were misconfigured would generally be accepted with certain
configuration problems leading to individual disabled parking lots.
* Parked calls are now placed in bridges. This is a largely architectural change,
but it could have some implications in allowing for new parked call retrieval
methods and the contents of parking lots will be visible though certain bridge
@ -236,55 +296,6 @@ Parking
by default. Instead, it will follow the timeout rules of the parking lot. The
old behavior can be reproduced by using the 'c' option.
* Added a channel variable PARKER_FLAT which stores the name of the extension
that would be used to come back to if comebacktoorigin was set to use. This can
be useful when comebacktoorigin is off if you still want to use the extensions
in the park-dial context that are generated to redial the parker on timeout.
Queue
-------------------
* Add queue available hint. exten => 8501,hint,Queue:markq_avail
Note: the suffix '_avail' after the queuename.
Reports 'InUse' for no logged in agents or no free agents.
Reports 'Idle' when an agent is free.
* The configuration options eventwhencalled and eventmemberstatus have been
removed. As a result, the AMI events QueueMemberStatus, AgentCalled,
AgentConnect, AgentComplete, AgentDump, and AgentRingNoAnswer will always be
sent. The "Variable" fields will also no longer exist on the Agent* events.
Core
------------------
* Redirecting reasons can now be set to arbitrary strings. This means
that the REDIRECTING dialplan function can be used to set the redirecting
reason to any string. It also allows for custom strings to be read as the
redirecting reason from SIP Diversion headers.
* For DTMF blind and attended transfers, the channel variable TRANSFER_CONTEXT
must be on the channel initiating the transfer to have any effect.
* The channel variable ATTENDED_TRANSFER_COMPLETE_SOUND is no longer channel
driver specific. If the channel variable is set on the transferrer channel,
the sound will be played to the target of an attended transfer.
* The channel variable BRIDGEPEER becomes a comma separated list of peers in
a multi-party bridge. The BRIDGEPEER value can have a maximum of 10 peers
listed. Any more peers in the bridge will not be included in the list.
BRIDGEPEER is not valid in holding bridges like parking since those channels
do not talk to each other even though they are in a bridge.
* The channel variable BRIDGEPVTCALLID is only valid for two party bridges
and will contain a value if the BRIDGEPEER's channel driver supports it.
* The channel variable DYNAMIC_PEERNAME is redundant with BRIDGEPEER and is
removed. The more useful DYNAMIC_WHO_ACTIVATED gives the channel name that
activated the dynamic feature.
* The channel variables DYNAMIC_FEATURENAME and DYNAMIC_WHO_ACTIVATED are set
only on the channel executing the dynamic feature. Executing a dynamic
feature on the bridge peer in a multi-party bridge will execute it on all
peers of the activating channel.
Realtime
------------------
* Dynamic realtime tables for SIP Users can now include a 'path' field. This
@ -294,6 +305,88 @@ Realtime
* LDAP realtime configurations for SIP Users now have the AstAccountPathSupport
objectIdentifier. This maps to the supportpath option in sip.conf.
Sorcery
------------------
* All future modules which utilize Sorcery for object persistence must have a
column named "id" within their schema when using the Sorcery realtime module.
This column must be able to contain a string of up to 128 characters in length.
Channel Drivers
------------------
* When a channel driver is configured to enable jiterbuffers, they are now
applied unconditionally when a channel joins a bridge. If a jitterbuffer
is already set for that channel when it enters, such as by the JITTERBUFFER
function, then the existing jitterbuffer will be used and the one set by
the channel driver will not be applied.
chan_agent
------------------
* The updatecdr option has been removed. Altering the names of channels on a
CDR is not supported - the name of the channel is the name of the channel,
and pretending otherwise helps no one.
* The AGENTUPDATECDR channel variable has also been removed, for the same
reason as the updatecdr option.
chan_local
------------------
* The /b option is removed.
* chan_local moved into the system core and is no longer a loadable module.
chan_mobile
------------------
* Added general support for busy detection.
* Added ECAM command support for Sony Ericsson phones.
chan_sip
------------------
* Added support for RFC 3327 "Path" headers. This can be enabled in sip.conf
using the 'supportpath' setting, either on a global basis or on a peer basis.
This setting enables Asterisk to route outgoing out-of-dialog requests via a
set of proxies by using a pre-loaded route-set defined by the Path headers in
the REGISTER request. See Realtime updates for more configuration information.
Functions
------------------
JITTERBUFFER
------------------
* JITTERBUFFER now accepts an argument of 'disabled' which can be used
to remove jitterbuffers previously set on a channel with JITTERBUFFER.
The value of this setting is ignored when disabled is used for the argument.
CDR (function)
------------------
* The 'amaflags' and 'accountcode' attributes for the CDR function are
deprecated. Use the CHANNEL function instead to access these attributes.
* The 'l' option has been removed. When reading a CDR attribute, the most
recent record is always used. When writing a CDR attribute, all non-finalized
CDRs are updated.
* The 'r' option has been removed, for the same reason as the 'l' option.
* The 's' option has been removed, as LOCKED semantics no longer exist in the
CDR engine.
CDR_PROP
------------------
* A new function CDR_PROP has been added. This function lets you set properties
on a channel's active CDRs. This function is write-only. Properties accept
boolean values to set/clear them on the channel's CDRs. Valid properties
include:
* 'party_a' - make this channel the preferred Party A in any CDR between two
channels. If two channels have this property set, the creation time of the
channel is used to determine who is Party A. Note that dialed channels are
never Party A in a CDR.
* 'disable' - disable CDRs on this channel. This is analogous to the NoCDR
application when set to True, and analogous to the 'e' option in ResetCDR
when set to False.
Resources
------------------
RTP
------------------
* ICE/STUN/TURN support in res_rtp_asterisk has been made optional. To enable

View File

@ -21,6 +21,49 @@
===
===========================================================
AgentMonitorOutgoing
- The 'c' option has been removed. It is not possible to modify the name of a
channel involved in a CDR.
NoCDR:
- This application is deprecated. Please use the CDR_PROP function instead.
ResetCDR:
- The 'w' and 'a' options have been removed. Dispatching CDRs to registered
backends occurs on an as-needed basis in order to preserve linkedid
propagation and other needed behavior.
- The 'e' option is deprecated. Please use the CDR_PROP function to enable
CDRs on a channel that they were previously disabled on.
- The ResetCDR application is no longer a part of core Asterisk, and instead
is now delivered as part of app_cdr.
ForkCDR:
- ForkCDR no longer automatically resets the forked CDR. See the 'r' option
for more information.
- Variables are no longer purged from the original CDR. See the 'v' option for
more information.
- The 'A' option has been removed. The Answer time on a CDR is never updated
once set.
- The 'd' option has been removed. The disposition on a CDR is a function of
the state of the channel and cannot be altered.
- The 'D' option has been removed. Who the Party B is on a CDR is a function
of the state of the respective channels, and cannot be altered.
- The 'r' option has been changed. Previously, ForkCDR always reset the CDR
such that the start time and, if applicable, the answer time was updated.
Now, by default, ForkCDR simply forks the CDR, maintaining any times. The
'r' option now triggers the Reset, setting the start time (and answer time
if applicable) to the current time.
- The 's' option has been removed. A variable can be set on the original CDR
if desired using the CDR function, and removed from a forked CDR using the
same function.
- The 'T' option has been removed. The concept of DONT_TOUCH and LOCKED no
longer applies in the CDR engine.
- The 'v' option now prevents the copy of the variables from the original CDR
to the forked CDR. Previously the variables were always copied but were
removed from the original. Removing variables from a CDR can have unintended
side effects - this option allows the user to prevent propagation of
variables from the original to the forked without modifying the original.
AMI:
- The SIP SIPqualifypeer action now sends a response indicating it will qualify
a peer once a peer has been found to qualify. Once the qualify has been
@ -72,6 +115,16 @@ SendDTMF:
- Now recognizes 'W' to pause sending DTMF for one second in addition to
the previously existing 'w' that paused sending DTMF for half a second.
SetAMAFlags
- This application is deprecated in favor of the CHANNEL function.
chan_agent:
- The updatecdr option has been removed. Altering the names of channels on a
CDR is not supported - the name of the channel is the name of the channel,
and pretending otherwise helps no one.
- The AGENTUPDATECDR channel variable has also been removed, for the same
reason as the updatecdr option.
chan_dahdi:
- Analog port dialing and deferred DTMF dialing for PRI now distinguishes
between 'w' and 'W'. The 'w' pauses dialing for half a second. The 'W'
@ -79,7 +132,7 @@ chan_dahdi:
- The default for inband_on_proceeding has changed to no.
chan_local:
- The /b option is removed.
- The /b option has been removed.
Dialplan:
- All channel and global variable names are evaluated in a case-sensitive manner.

View File

@ -251,7 +251,7 @@ db_reconnect:
char timestr[128];
ast_localtime(&tv, &tm, ast_str_strlen(cdrzone) ? ast_str_buffer(cdrzone) : NULL);
ast_strftime(timestr, sizeof(timestr), "%Y-%m-%d %T", &tm);
ast_cdr_setvar(cdr, "calldate", timestr, 0);
ast_cdr_setvar(cdr, "calldate", timestr);
cdrname = "calldate";
} else {
cdrname = "start";
@ -277,9 +277,9 @@ db_reconnect:
strstr(entry->type, "real") ||
strstr(entry->type, "numeric") ||
strstr(entry->type, "fixed"))) {
ast_cdr_getvar(cdr, cdrname, &value, workspace, sizeof(workspace), 0, 1);
ast_cdr_format_var(cdr, cdrname, &value, workspace, sizeof(workspace), 1);
} else {
ast_cdr_getvar(cdr, cdrname, &value, workspace, sizeof(workspace), 0, 0);
ast_cdr_format_var(cdr, cdrname, &value, workspace, sizeof(workspace), 0);
}
if (value) {

View File

@ -2376,7 +2376,7 @@ static struct ooh323_user *build_user(const char *name, struct ast_variable *v)
ast_parse_allow_disallow(&user->prefs,
user->cap, tcodecs, 1);
} else if (!strcasecmp(v->name, "amaflags")) {
user->amaflags = ast_cdr_amaflags2int(v->value);
user->amaflags = ast_channel_string2amaflag(v->value);
} else if (!strcasecmp(v->name, "ip") || !strcasecmp(v->name, "host")) {
struct ast_sockaddr p;
if (!ast_parse_arg(v->value, PARSE_ADDR, &p)) {
@ -2560,7 +2560,7 @@ static struct ooh323_peer *build_peer(const char *name, struct ast_variable *v,
ast_parse_allow_disallow(&peer->prefs, peer->cap,
tcodecs, 1);
} else if (!strcasecmp(v->name, "amaflags")) {
peer->amaflags = ast_cdr_amaflags2int(v->value);
peer->amaflags = ast_channel_string2amaflag(v->value);
} else if (!strcasecmp(v->name, "roundtrip")) {
sscanf(v->value, "%d,%d", &peer->rtdrcount, &peer->rtdrinterval);
} else if (!strcasecmp(v->name, "dtmfmode")) {
@ -2917,7 +2917,7 @@ int reload_config(int reload)
"'lowdelay', 'throughput', 'reliability', "
"'mincost', or 'none'\n", v->lineno);
} else if (!strcasecmp(v->name, "amaflags")) {
gAMAFLAGS = ast_cdr_amaflags2int(v->value);
gAMAFLAGS = ast_channel_string2amaflag(v->value);
} else if (!strcasecmp(v->name, "accountcode")) {
ast_copy_string(gAccountcode, v->value, sizeof(gAccountcode));
} else if (!strcasecmp(v->name, "disallow")) {
@ -3117,7 +3117,7 @@ static char *handle_cli_ooh323_show_peer(struct ast_cli_entry *e, int cmd, struc
}
ast_cli(a->fd, "%-15.15s%s\n", "AccountCode: ", peer->accountcode);
ast_cli(a->fd, "%-15.15s%s\n", "AMA flags: ", ast_cdr_flags2str(peer->amaflags));
ast_cli(a->fd, "%-15.15s%s\n", "AMA flags: ", ast_channel_amaflags2string(peer->amaflags));
ast_cli(a->fd, "%-15.15s%s\n", "IP:Port: ", ip_port);
ast_cli(a->fd, "%-15.15s%d\n", "OutgoingLimit: ", peer->outgoinglimit);
ast_cli(a->fd, "%-15.15s%d\n", "rtptimeout: ", peer->rtptimeout);
@ -3276,7 +3276,7 @@ static char *handle_cli_ooh323_show_user(struct ast_cli_entry *e, int cmd, struc
}
ast_cli(a->fd, "%-15.15s%s\n", "AccountCode: ", user->accountcode);
ast_cli(a->fd, "%-15.15s%s\n", "AMA flags: ", ast_cdr_flags2str(user->amaflags));
ast_cli(a->fd, "%-15.15s%s\n", "AMA flags: ", ast_channel_amaflags2string(user->amaflags));
ast_cli(a->fd, "%-15.15s%s\n", "Context: ", user->context);
ast_cli(a->fd, "%-15.15s%d\n", "IncomingLimit: ", user->incominglimit);
ast_cli(a->fd, "%-15.15s%d\n", "InUse: ", user->inUse);
@ -3524,7 +3524,7 @@ static char *handle_cli_ooh323_show_config(struct ast_cli_entry *e, int cmd, str
ast_cli(a->fd, "%-20s%ld\n", "Call counter: ", callnumber);
ast_cli(a->fd, "%-20s%s\n", "AccountCode: ", gAccountcode);
ast_cli(a->fd, "%-20s%s\n", "AMA flags: ", ast_cdr_flags2str(gAMAFLAGS));
ast_cli(a->fd, "%-20s%s\n", "AMA flags: ", ast_channel_amaflags2string(gAMAFLAGS));
pAlias = gAliasList;
if(pAlias) {

View File

@ -213,9 +213,9 @@ static int auth_exec(struct ast_channel *chan, const char *data)
continue;
ast_md5_hash(md5passwd, passwd);
if (!strcmp(md5passwd, md5secret)) {
if (ast_test_flag(&flags,OPT_ACCOUNT)) {
if (ast_test_flag(&flags, OPT_ACCOUNT)) {
ast_channel_lock(chan);
ast_cdr_setaccount(chan, buf);
ast_channel_accountcode_set(chan, buf);
ast_channel_unlock(chan);
}
break;
@ -224,7 +224,7 @@ static int auth_exec(struct ast_channel *chan, const char *data)
if (!strcmp(passwd, buf)) {
if (ast_test_flag(&flags, OPT_ACCOUNT)) {
ast_channel_lock(chan);
ast_cdr_setaccount(chan, buf);
ast_channel_accountcode_set(chan, buf);
ast_channel_unlock(chan);
}
break;
@ -250,7 +250,7 @@ static int auth_exec(struct ast_channel *chan, const char *data)
if ((retries < 3) && !res) {
if (ast_test_flag(&flags,OPT_ACCOUNT) && !ast_test_flag(&flags,OPT_MULTIPLE)) {
ast_channel_lock(chan);
ast_cdr_setaccount(chan, passwd);
ast_channel_accountcode_set(chan, passwd);
ast_channel_unlock(chan);
}
if (!(res = ast_streamfile(chan, "auth-thankyou", ast_channel_language(chan))))

View File

@ -35,25 +35,114 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/channel.h"
#include "asterisk/module.h"
#include "asterisk/app.h"
/*** DOCUMENTATION
<application name="NoCDR" language="en_US">
<synopsis>
Tell Asterisk to not maintain a CDR for the current call
Tell Asterisk to not maintain a CDR for this channel.
</synopsis>
<syntax />
<description>
<para>This application will tell Asterisk not to maintain a CDR for the current call.</para>
<para>This application will tell Asterisk not to maintain a CDR for
the current channel. This does <emphasis>NOT</emphasis> mean that
information is not tracked; rather, if the channel is hung up no
CDRs will be created for that channel.</para>
<para>If a subsequent call to ResetCDR occurs, all non-finalized
CDRs created for the channel will be enabled.</para>
<note><para>This application is deprecated. Please use the CDR_PROP
function to disable CDRs on a channel.</para></note>
</description>
<see-also>
<ref type="application">ResetCDR</ref>
<ref type="function">CDR_PROP</ref>
</see-also>
</application>
<application name="ResetCDR" language="en_US">
<synopsis>
Resets the Call Data Record.
</synopsis>
<syntax>
<parameter name="options">
<optionlist>
<option name="v">
<para>Save the CDR variables during the reset.</para>
</option>
<option name="e">
<para>Enable the CDRs for this channel only (negate
effects of NoCDR).</para>
</option>
</optionlist>
</parameter>
</syntax>
<description>
<para>This application causes the Call Data Record to be reset.
Depending on the flags passed in, this can have several effects.
With no options, a reset does the following:</para>
<para>1. The <literal>start</literal> time is set to the current time.</para>
<para>2. If the channel is answered, the <literal>answer</literal> time is set to the
current time.</para>
<para>3. All variables are wiped from the CDR. Note that this step
can be prevented with the <literal>v</literal> option.</para>
<para>On the other hand, if the <literal>e</literal> option is
specified, the effects of the NoCDR application will be lifted. CDRs
will be re-enabled for this channel.</para>
<note><para>The <literal>e</literal> option is deprecated. Please
use the CDR_PROP function instead.</para></note>
</description>
<see-also>
<ref type="application">ForkCDR</ref>
<ref type="application">NoCDR</ref>
<ref type="function">CDR_PROP</ref>
</see-also>
</application>
***/
static const char nocdr_app[] = "NoCDR";
static const char resetcdr_app[] = "ResetCDR";
enum reset_cdr_options {
OPT_DISABLE_DISPATCH = (1 << 0),
OPT_KEEP_VARS = (1 << 1),
OPT_ENABLE = (1 << 2),
};
AST_APP_OPTIONS(resetcdr_opts, {
AST_APP_OPTION('v', AST_CDR_FLAG_KEEP_VARS),
AST_APP_OPTION('e', AST_CDR_FLAG_DISABLE_ALL),
});
static int resetcdr_exec(struct ast_channel *chan, const char *data)
{
char *args;
struct ast_flags flags = { 0 };
int res = 0;
if (!ast_strlen_zero(data)) {
args = ast_strdupa(data);
ast_app_parse_options(resetcdr_opts, &flags, NULL, args);
}
if (ast_test_flag(&flags, AST_CDR_FLAG_DISABLE_ALL)) {
if (ast_cdr_clear_property(ast_channel_name(chan), AST_CDR_FLAG_DISABLE_ALL)) {
res = 1;
}
}
if (ast_cdr_reset(ast_channel_name(chan), &flags)) {
res = 1;
}
if (res) {
ast_log(AST_LOG_WARNING, "Failed to reset CDR for channel %s\n", ast_channel_name(chan));
}
return res;
}
static int nocdr_exec(struct ast_channel *chan, const char *data)
{
if (ast_channel_cdr(chan))
ast_set_flag(ast_channel_cdr(chan), AST_CDR_FLAG_POST_DISABLED);
if (ast_cdr_set_property(ast_channel_name(chan), AST_CDR_FLAG_DISABLE_ALL)) {
ast_log(AST_LOG_WARNING, "Failed to disable CDR for channel %s\n", ast_channel_name(chan));
}
return 0;
}
@ -65,8 +154,14 @@ static int unload_module(void)
static int load_module(void)
{
if (ast_register_application_xml(nocdr_app, nocdr_exec))
int res = 0;
res |= ast_register_application_xml(nocdr_app, nocdr_exec);
res |= ast_register_application_xml(resetcdr_app, resetcdr_exec);
if (res) {
return AST_MODULE_LOAD_FAILURE;
}
return AST_MODULE_LOAD_SUCCESS;
}

View File

@ -55,7 +55,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/app.h"
#include "asterisk/causes.h"
#include "asterisk/rtp_engine.h"
#include "asterisk/cdr.h"
#include "asterisk/manager.h"
#include "asterisk/privacy.h"
#include "asterisk/stringfields.h"
@ -753,36 +752,20 @@ struct cause_args {
static void handle_cause(int cause, struct cause_args *num)
{
struct ast_cdr *cdr = ast_channel_cdr(num->chan);
switch(cause) {
case AST_CAUSE_BUSY:
if (cdr)
ast_cdr_busy(cdr);
num->busy++;
break;
case AST_CAUSE_CONGESTION:
if (cdr)
ast_cdr_failed(cdr);
num->congestion++;
break;
case AST_CAUSE_NO_ROUTE_DESTINATION:
case AST_CAUSE_UNREGISTERED:
if (cdr)
ast_cdr_failed(cdr);
num->nochan++;
break;
case AST_CAUSE_NO_ANSWER:
if (cdr) {
ast_cdr_noanswer(cdr);
}
break;
case AST_CAUSE_NORMAL_CLEARING:
break;
default:
num->nochan++;
break;
@ -974,7 +957,7 @@ static void do_forward(struct chanlist *o, struct cause_args *num,
ast_channel_appl_set(c, "AppDial");
ast_channel_data_set(c, "(Outgoing Line)");
ast_publish_channel_state(c);
ast_channel_publish_snapshot(c);
ast_channel_unlock(in);
if (single && !ast_test_flag64(o, OPT_IGNORE_CONNECTEDLINE)) {
@ -1096,7 +1079,6 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
*/
*to = -1;
strcpy(pa->status, "CONGESTION");
ast_cdr_failed(ast_channel_cdr(in));
ast_channel_publish_dial(in, outgoing->chan, NULL, pa->status);
return NULL;
}
@ -1301,10 +1283,6 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
peer = c;
ast_channel_publish_dial(in, peer, NULL, "ANSWER");
publish_dial_end_event(in, out_chans, peer, "CANCEL");
if (ast_channel_cdr(peer)) {
ast_channel_cdr(peer)->answer = ast_tvnow();
ast_channel_cdr(peer)->disposition = AST_CDR_ANSWERED;
}
ast_copy_flags64(peerflags, o,
OPT_CALLEE_TRANSFER | OPT_CALLER_TRANSFER |
OPT_CALLEE_HANGUP | OPT_CALLER_HANGUP |
@ -1326,7 +1304,7 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
case AST_CONTROL_BUSY:
ast_verb(3, "%s is busy\n", ast_channel_name(c));
ast_channel_hangupcause_set(in, ast_channel_hangupcause(c));
ast_channel_publish_dial(in, c, NULL, ast_hangup_cause_to_dial_status(ast_channel_hangupcause(c)));
ast_channel_publish_dial(in, c, NULL, "BUSY");
ast_hangup(c);
c = o->chan = NULL;
ast_clear_flag64(o, DIAL_STILLGOING);
@ -1335,7 +1313,7 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
case AST_CONTROL_CONGESTION:
ast_verb(3, "%s is circuit-busy\n", ast_channel_name(c));
ast_channel_hangupcause_set(in, ast_channel_hangupcause(c));
ast_channel_publish_dial(in, c, NULL, ast_hangup_cause_to_dial_status(ast_channel_hangupcause(c)));
ast_channel_publish_dial(in, c, NULL, "CONGESTION");
ast_hangup(c);
c = o->chan = NULL;
ast_clear_flag64(o, DIAL_STILLGOING);
@ -1542,7 +1520,6 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
/* Got hung up */
*to = -1;
strcpy(pa->status, "CANCEL");
ast_cdr_noanswer(ast_channel_cdr(in));
publish_dial_end_event(in, out_chans, NULL, pa->status);
if (f) {
if (f->data.uint32) {
@ -1565,7 +1542,6 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
if (onedigit_goto(in, context, (char) f->subclass.integer, 1)) {
ast_verb(3, "User hit %c to disconnect call.\n", f->subclass.integer);
*to = 0;
ast_cdr_noanswer(ast_channel_cdr(in));
*result = f->subclass.integer;
strcpy(pa->status, "CANCEL");
publish_dial_end_event(in, out_chans, NULL, pa->status);
@ -1584,7 +1560,6 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
ast_verb(3, "User requested call disconnect.\n");
*to = 0;
strcpy(pa->status, "CANCEL");
ast_cdr_noanswer(ast_channel_cdr(in));
publish_dial_end_event(in, out_chans, NULL, pa->status);
ast_frfree(f);
if (is_cc_recall) {
@ -1679,13 +1654,10 @@ skip_frame:;
}
}
if (!*to) {
if (!*to || ast_check_hangup(in)) {
ast_verb(3, "Nobody picked up in %d ms\n", orig);
publish_dial_end_event(in, out_chans, NULL, "NOANSWER");
}
if (!*to || ast_check_hangup(in)) {
ast_cdr_noanswer(ast_channel_cdr(in));
}
#ifdef HAVE_EPOLL
AST_LIST_TRAVERSE(out_chans, epollo, node) {
@ -1985,22 +1957,13 @@ static void end_bridge_callback(void *data)
time_t end;
struct ast_channel *chan = data;
if (!ast_channel_cdr(chan)) {
return;
}
time(&end);
ast_channel_lock(chan);
if (ast_channel_cdr(chan)->answer.tv_sec) {
snprintf(buf, sizeof(buf), "%ld", (long) end - ast_channel_cdr(chan)->answer.tv_sec);
pbx_builtin_setvar_helper(chan, "ANSWEREDTIME", buf);
}
if (ast_channel_cdr(chan)->start.tv_sec) {
snprintf(buf, sizeof(buf), "%ld", (long) end - ast_channel_cdr(chan)->start.tv_sec);
pbx_builtin_setvar_helper(chan, "DIALEDTIME", buf);
}
snprintf(buf, sizeof(buf), "%d", ast_channel_get_up_time(chan));
pbx_builtin_setvar_helper(chan, "ANSWEREDTIME", buf);
snprintf(buf, sizeof(buf), "%d", ast_channel_get_duration(chan));
pbx_builtin_setvar_helper(chan, "DIALEDTIME", buf);
ast_channel_unlock(chan);
}
@ -2294,8 +2257,9 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
ast_channel_unlock(chan);
}
if (ast_test_flag64(&opts, OPT_RESETCDR) && ast_channel_cdr(chan))
ast_cdr_reset(ast_channel_cdr(chan), NULL);
if (ast_test_flag64(&opts, OPT_RESETCDR)) {
ast_cdr_reset(ast_channel_name(chan), 0);
}
if (ast_test_flag64(&opts, OPT_PRIVACY) && ast_strlen_zero(opt_args[OPT_ARG_PRIVACY]))
opt_args[OPT_ARG_PRIVACY] = ast_strdupa(ast_channel_exten(chan));
@ -2489,7 +2453,7 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
ast_channel_appl_set(tc, "AppDial");
ast_channel_data_set(tc, "(Outgoing Line)");
ast_publish_channel_state(tc);
ast_channel_publish_snapshot(tc);
memset(ast_channel_whentohangup(tc), 0, sizeof(*ast_channel_whentohangup(tc)));
@ -2620,10 +2584,6 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
res = ast_call(tmp->chan, tmp->number, 0); /* Place the call, but don't wait on the answer */
ast_channel_lock(chan);
/* Save the info in cdr's that we called them */
if (ast_channel_cdr(chan))
ast_cdr_setdestchan(ast_channel_cdr(chan), ast_channel_name(tmp->chan));
/* check the results of ast_call */
if (res) {
/* Again, keep going even if there's an error */
@ -2738,10 +2698,6 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
conversation. */
hanguptree(&out_chans, peer, 1);
/* If appropriate, log that we have a destination channel and set the answer time */
if (ast_channel_cdr(chan)) {
ast_cdr_setdestchan(ast_channel_cdr(chan), ast_channel_name(peer));
ast_cdr_setanswer(ast_channel_cdr(chan), ast_channel_cdr(peer)->answer);
}
if (ast_channel_name(peer))
pbx_builtin_setvar_helper(chan, "DIALEDPEERNAME", ast_channel_name(peer));
@ -2836,10 +2792,10 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
}
if (chan && peer && ast_test_flag64(&opts, OPT_GOTO) && !ast_strlen_zero(opt_args[OPT_ARG_GOTO])) {
/* chan and peer are going into the PBX, they both
* should probably get CDR records. */
ast_clear_flag(ast_channel_cdr(chan), AST_CDR_FLAG_DIALED);
ast_clear_flag(ast_channel_cdr(peer), AST_CDR_FLAG_DIALED);
/* chan and peer are going into the PBX; as such neither are considered
* outgoing channels any longer */
ast_clear_flag(ast_channel_flags(chan), AST_FLAG_OUTGOING);
ast_clear_flag(ast_channel_flags(peer), AST_FLAG_OUTGOING);
ast_replace_subargument_delimiter(opt_args[OPT_ARG_GOTO]);
ast_parseable_goto(chan, opt_args[OPT_ARG_GOTO]);

View File

@ -362,7 +362,7 @@ static int disa_exec(struct ast_channel *chan, const char *data)
if (k == 3) {
int recheck = 0;
struct ast_flags cdr_flags = { AST_CDR_FLAG_POSTED };
struct ast_flags cdr_flags = { AST_CDR_FLAG_DISABLE, };
if (!ast_exists_extension(chan, args.context, exten, 1,
S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
@ -384,8 +384,10 @@ static int disa_exec(struct ast_channel *chan, const char *data)
if (!ast_strlen_zero(acctcode))
ast_channel_accountcode_set(chan, acctcode);
if (special_noanswer) cdr_flags.flags = 0;
ast_cdr_reset(ast_channel_cdr(chan), &cdr_flags);
if (special_noanswer) {
ast_clear_flag(&cdr_flags, AST_CDR_FLAG_DISABLE);
}
ast_cdr_reset(ast_channel_name(chan), &cdr_flags);
ast_explicit_goto(chan, args.context, exten, 1);
return 0;
}

View File

@ -70,7 +70,6 @@ static const char app[] = "DumpChan";
static int serialize_showchan(struct ast_channel *c, char *buf, size_t size)
{
struct timeval now;
long elapsed_seconds = 0;
int hour = 0, min = 0, sec = 0;
char nf[256];
@ -80,21 +79,19 @@ static int serialize_showchan(struct ast_channel *c, char *buf, size_t size)
struct ast_str *read_transpath = ast_str_alloca(256);
struct ast_bridge *bridge;
now = ast_tvnow();
memset(buf, 0, size);
if (!c)
return 0;
if (ast_channel_cdr(c)) {
elapsed_seconds = now.tv_sec - ast_channel_cdr(c)->start.tv_sec;
hour = elapsed_seconds / 3600;
min = (elapsed_seconds % 3600) / 60;
sec = elapsed_seconds % 60;
}
elapsed_seconds = ast_channel_get_duration(c);
hour = elapsed_seconds / 3600;
min = (elapsed_seconds % 3600) / 60;
sec = elapsed_seconds % 60;
ast_channel_lock(c);
bridge = ast_channel_get_bridge(c);
ast_channel_unlock(c);
snprintf(buf,size,
"Name= %s\n"
"Type= %s\n"

View File

@ -578,29 +578,6 @@ static void clear_caller(struct findme_user *tmpuser)
}
outbound = tmpuser->ochan;
ast_channel_lock(outbound);
if (!ast_channel_cdr(outbound)) {
ast_channel_cdr_set(outbound, ast_cdr_alloc());
if (ast_channel_cdr(outbound)) {
ast_cdr_init(ast_channel_cdr(outbound), outbound);
}
}
if (ast_channel_cdr(outbound)) {
char tmp[256];
snprintf(tmp, sizeof(tmp), "Local/%s", tmpuser->dialarg);
ast_cdr_setapp(ast_channel_cdr(outbound), "FollowMe", tmp);
ast_cdr_update(outbound);
ast_cdr_start(ast_channel_cdr(outbound));
ast_cdr_end(ast_channel_cdr(outbound));
/* If the cause wasn't handled properly */
if (ast_cdr_disposition(ast_channel_cdr(outbound), ast_channel_hangupcause(outbound))) {
ast_cdr_failed(ast_channel_cdr(outbound));
}
} else {
ast_log(LOG_WARNING, "Unable to create Call Detail Record\n");
}
ast_channel_unlock(outbound);
ast_hangup(outbound);
tmpuser->ochan = NULL;
}
@ -1127,11 +1104,6 @@ static struct ast_channel *findmeexec(struct fm_args *tpargs, struct ast_channel
* Destoy all new outgoing calls.
*/
while ((tmpuser = AST_LIST_REMOVE_HEAD(&new_user_list, entry))) {
ast_channel_lock(tmpuser->ochan);
if (ast_channel_cdr(tmpuser->ochan)) {
ast_cdr_init(ast_channel_cdr(tmpuser->ochan), tmpuser->ochan);
}
ast_channel_unlock(tmpuser->ochan);
destroy_calling_node(tmpuser);
}
@ -1153,11 +1125,6 @@ static struct ast_channel *findmeexec(struct fm_args *tpargs, struct ast_channel
AST_LIST_REMOVE_CURRENT(entry);
/* Destroy this failed new outgoing call. */
ast_channel_lock(tmpuser->ochan);
if (ast_channel_cdr(tmpuser->ochan)) {
ast_cdr_init(ast_channel_cdr(tmpuser->ochan), tmpuser->ochan);
}
ast_channel_unlock(tmpuser->ochan);
destroy_calling_node(tmpuser);
continue;
}
@ -1310,15 +1277,10 @@ static void end_bridge_callback(void *data)
time(&end);
ast_channel_lock(chan);
if (ast_channel_cdr(chan)->answer.tv_sec) {
snprintf(buf, sizeof(buf), "%ld", (long) end - ast_channel_cdr(chan)->answer.tv_sec);
pbx_builtin_setvar_helper(chan, "ANSWEREDTIME", buf);
}
if (ast_channel_cdr(chan)->start.tv_sec) {
snprintf(buf, sizeof(buf), "%ld", (long) end - ast_channel_cdr(chan)->start.tv_sec);
pbx_builtin_setvar_helper(chan, "DIALEDTIME", buf);
}
snprintf(buf, sizeof(buf), "%d", ast_channel_get_up_time(chan));
pbx_builtin_setvar_helper(chan, "ANSWEREDTIME", buf);
snprintf(buf, sizeof(buf), "%d", ast_channel_get_duration(chan));
pbx_builtin_setvar_helper(chan, "DIALEDTIME", buf);
ast_channel_unlock(chan);
}

View File

@ -44,98 +44,46 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
/*** DOCUMENTATION
<application name="ForkCDR" language="en_US">
<synopsis>
Forks the Call Data Record.
Forks the current Call Data Record for this channel.
</synopsis>
<syntax>
<parameter name="options">
<optionlist>
<option name="a">
<para>Update the answer time on the NEW CDR just after it's been inited.
The new CDR may have been answered already. The reset that forkcdr does
will erase the answer time. This will bring it back, but the answer time
will be a copy of the fork/start time. It will only do this if the initial
cdr was indeed already answered.</para>
</option>
<option name="A">
<para>Lock the original CDR against the answer time being updated. This
will allow the disposition on the original CDR to remain the same.</para>
</option>
<option name="d">
<para>Copy the disposition forward from the old cdr, after the init.</para>
</option>
<option name="D">
<para>Clear the <literal>dstchannel</literal> on the new CDR after
reset.</para>
<para>If the channel is answered, set the answer time on
the forked CDR to the current time. If this option is
not used, the answer time on the forked CDR will be the
answer time on the original CDR. If the channel is not
answered, this option has no effect.</para>
<para>Note that this option is implicitly assumed if the
<literal>r</literal> option is used.</para>
</option>
<option name="e">
<para>End the original CDR. Do this after all the necessary data is copied
from the original CDR to the new forked CDR.</para>
<para>End (finalize) the original CDR.</para>
</option>
<option name="r">
<para>Do <emphasis>NOT</emphasis> reset the new cdr.</para>
</option>
<option name="s(name=val)">
<para>Set the CDR var <replaceable>name</replaceable> in the original CDR,
with value <replaceable>val</replaceable>.</para>
</option>
<option name="T">
<para>Mark the original CDR with a DONT_TOUCH flag. setvar, answer, and end
cdr funcs will obey this flag; normally they don't honor the LOCKED flag
set on the original CDR record.</para>
<note><para>Using this flag may cause CDR's not to have their end times
updated! It is suggested that if you specify this flag, you might wish
to use the <literal>e</literal> flag as well!.</para></note>
<para>Reset the start and answer times on the forked CDR.
This will set the start and answer times (if the channel
is answered) to be set to the current time.</para>
<para>Note that this option implicitly assumes the
<literal>a</literal> option.</para>
</option>
<option name="v">
<para>When the new CDR is forked, it gets a copy of the vars attached to
the current CDR. The vars attached to the original CDR are removed unless
this option is specified.</para>
<para>Do not copy CDR variables and attributes from the
original CDR to the forked CDR.</para>
<warning><para>This option has changed. Previously, the
variables were removed from the original CDR. This no
longer occurs - this option now controls whether or not
a forked CDR inherits the variables from the original
CDR.</para></warning>
</option>
</optionlist>
</parameter>
</syntax>
<description>
<para> Causes the Call Data Record to fork an additional cdr record starting from the time
of the fork call. This new cdr record will be linked to end of the list of cdr records attached
to the channel. The original CDR has a LOCKED flag set, which forces most cdr operations to skip
it, except for the functions that set the answer and end times, which ignore the LOCKED flag. This
allows all the cdr records in the channel to be 'ended' together when the channel is closed.</para>
<para>The CDR() func (when setting CDR values) normally ignores the LOCKED flag also, but has options
to vary its behavior. The 'T' option (described below), can override this behavior, but beware
the risks.</para>
<para>First, this app finds the last cdr record in the list, and makes a copy of it. This new copy
will be the newly forked cdr record. Next, this new record is linked to the end of the cdr record list.
Next, The new cdr record is RESET (unless you use an option to prevent this)</para>
<para>This means that:</para>
<para> 1. All flags are unset on the cdr record</para>
<para> 2. the start, end, and answer times are all set to zero.</para>
<para> 3. the billsec and duration fields are set to zero.</para>
<para> 4. the start time is set to the current time.</para>
<para> 5. the disposition is set to NULL.</para>
<para>Next, unless you specified the <literal>v</literal> option, all variables will be removed from
the original cdr record. Thus, the <literal>v</literal> option allows any CDR variables to be replicated
to all new forked cdr records. Without the <literal>v</literal> option, the variables on the original
are effectively moved to the new forked cdr record.</para>
<para>Next, if the <literal>s</literal> option is set, the provided variable and value are set on the
original cdr record.</para>
<para>Next, if the <literal>a</literal> option is given, and the original cdr record has an answer time
set, then the new forked cdr record will have its answer time set to its start time. If the old answer
time were carried forward, the answer time would be earlier than the start time, giving strange
duration and billsec times.</para>
<para>If the <literal>d</literal> option was specified, the disposition is copied from
the original cdr record to the new forked cdr. If the <literal>D</literal> option was specified,
the destination channel field in the new forked CDR is erased. If the <literal>e</literal> option
was specified, the 'end' time for the original cdr record is set to the current time. Future hang-up or
ending events will not override this time stamp. If the <literal>A</literal> option is specified,
the original cdr record will have it ANS_LOCKED flag set, which prevent future answer events from updating
the original cdr record's disposition. Normally, an <literal>ANSWERED</literal> event would mark all cdr
records in the chain as <literal>ANSWERED</literal>. If the <literal>T</literal> option is specified,
the original cdr record will have its <literal>DONT_TOUCH</literal> flag set, which will force the
cdr_answer, cdr_end, and cdr_setvar functions to leave that cdr record alone.</para>
<para>And, last but not least, the original cdr record has its LOCKED flag set. Almost all internal
CDR functions (except for the funcs that set the end, and answer times, and set a variable) will honor
this flag and leave a LOCKED cdr record alone. This means that the newly created forked cdr record
will be affected by events transpiring within Asterisk, with the previously noted exceptions.</para>
<para>Causes the Call Data Record engine to fork a new CDR starting
from the time the application is executed. The forked CDR will be
linked to the end of the CDRs associated with the channel.</para>
</description>
<see-also>
<ref type="function">CDR</ref>
@ -147,126 +95,34 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
static char *app = "ForkCDR";
enum {
OPT_SETANS = (1 << 0),
OPT_SETDISP = (1 << 1),
OPT_RESETDEST = (1 << 2),
OPT_ENDCDR = (1 << 3),
OPT_NORESET = (1 << 4),
OPT_KEEPVARS = (1 << 5),
OPT_VARSET = (1 << 6),
OPT_ANSLOCK = (1 << 7),
OPT_DONTOUCH = (1 << 8),
};
enum {
OPT_ARG_VARSET = 0,
/* note: this entry _MUST_ be the last one in the enum */
OPT_ARG_ARRAY_SIZE,
};
AST_APP_OPTIONS(forkcdr_exec_options, {
AST_APP_OPTION('a', OPT_SETANS),
AST_APP_OPTION('A', OPT_ANSLOCK),
AST_APP_OPTION('d', OPT_SETDISP),
AST_APP_OPTION('D', OPT_RESETDEST),
AST_APP_OPTION('e', OPT_ENDCDR),
AST_APP_OPTION('R', OPT_NORESET),
AST_APP_OPTION_ARG('s', OPT_VARSET, OPT_ARG_VARSET),
AST_APP_OPTION('T', OPT_DONTOUCH),
AST_APP_OPTION('v', OPT_KEEPVARS),
AST_APP_OPTION('a', AST_CDR_FLAG_SET_ANSWER),
AST_APP_OPTION('e', AST_CDR_FLAG_FINALIZE),
AST_APP_OPTION('r', AST_CDR_FLAG_RESET),
AST_APP_OPTION('v', AST_CDR_FLAG_KEEP_VARS),
});
static void ast_cdr_fork(struct ast_channel *chan, struct ast_flags optflags, char *set)
{
struct ast_cdr *cdr;
struct ast_cdr *newcdr;
struct ast_flags flags = { AST_CDR_FLAG_KEEP_VARS };
cdr = ast_channel_cdr(chan);
while (cdr->next)
cdr = cdr->next;
if (!(newcdr = ast_cdr_dup_unique(cdr)))
return;
/*
* End the original CDR if requested BEFORE appending the new CDR
* otherwise we incorrectly end the new CDR also.
*/
if (ast_test_flag(&optflags, OPT_ENDCDR)) {
ast_cdr_end(cdr);
}
ast_cdr_append(cdr, newcdr);
if (!ast_test_flag(&optflags, OPT_NORESET))
ast_cdr_reset(newcdr, &flags);
if (!ast_test_flag(cdr, AST_CDR_FLAG_KEEP_VARS))
ast_cdr_free_vars(cdr, 0);
if (!ast_strlen_zero(set)) {
char *varname = ast_strdupa(set), *varval;
varval = strchr(varname,'=');
if (varval) {
*varval = 0;
varval++;
ast_cdr_setvar(cdr, varname, varval, 0);
}
}
if (ast_test_flag(&optflags, OPT_SETANS) && !ast_tvzero(cdr->answer))
newcdr->answer = newcdr->start;
if (ast_test_flag(&optflags, OPT_SETDISP))
newcdr->disposition = cdr->disposition;
if (ast_test_flag(&optflags, OPT_RESETDEST))
newcdr->dstchannel[0] = 0;
if (ast_test_flag(&optflags, OPT_ANSLOCK))
ast_set_flag(cdr, AST_CDR_FLAG_ANSLOCKED);
if (ast_test_flag(&optflags, OPT_DONTOUCH))
ast_set_flag(cdr, AST_CDR_FLAG_DONT_TOUCH);
ast_set_flag(cdr, AST_CDR_FLAG_CHILD | AST_CDR_FLAG_LOCKED);
}
static int forkcdr_exec(struct ast_channel *chan, const char *data)
{
int res = 0;
char *argcopy = NULL;
struct ast_flags flags = {0};
char *opts[OPT_ARG_ARRAY_SIZE];
AST_DECLARE_APP_ARGS(arglist,
char *parse;
struct ast_flags flags = { 0, };
AST_DECLARE_APP_ARGS(args,
AST_APP_ARG(options);
);
if (!ast_channel_cdr(chan)) {
ast_log(LOG_WARNING, "Channel does not have a CDR\n");
return 0;
parse = ast_strdupa(data);
AST_STANDARD_APP_ARGS(args, parse);
if (!ast_strlen_zero(args.options)) {
ast_app_parse_options(forkcdr_exec_options, &flags, NULL, args.options);
}
argcopy = ast_strdupa(data);
AST_STANDARD_APP_ARGS(arglist, argcopy);
opts[OPT_ARG_VARSET] = 0;
if (!ast_strlen_zero(arglist.options))
ast_app_parse_options(forkcdr_exec_options, &flags, opts, arglist.options);
if (!ast_strlen_zero(data)) {
int keepvars = ast_test_flag(&flags, OPT_KEEPVARS) ? 1 : 0;
ast_set2_flag(ast_channel_cdr(chan), keepvars, AST_CDR_FLAG_KEEP_VARS);
if (ast_cdr_fork(ast_channel_name(chan), &flags)) {
ast_log(AST_LOG_WARNING, "Failed to fork CDR for channel %s\n", ast_channel_name(chan));
}
ast_cdr_fork(chan, flags, opts[OPT_ARG_VARSET]);
return res;
return 0;
}
static int unload_module(void)

View File

@ -2814,7 +2814,7 @@ static int ospfinished_exec(
int inhandle = OSP_INVALID_HANDLE;
int outhandle = OSP_INVALID_HANDLE;
int recorded = 0;
time_t start, connect, end;
time_t start = 0, connect = 0, end = 0;
unsigned int release;
char buffer[OSP_SIZE_INTSTR];
char inqos[OSP_SIZE_QOSSTR] = { 0 };
@ -2866,19 +2866,18 @@ static int ospfinished_exec(
}
ast_debug(1, "OSPFinish: cause '%d'\n", cause);
if (ast_channel_cdr(chan)) {
start = ast_channel_cdr(chan)->start.tv_sec;
connect = ast_channel_cdr(chan)->answer.tv_sec;
if (connect) {
end = time(NULL);
} else {
end = connect;
}
} else {
start = 0;
connect = 0;
end = 0;
if (!ast_tvzero(ast_channel_creationtime(chan))) {
start = ast_channel_creationtime(chan).tv_sec;
}
if (!ast_tvzero(ast_channel_answertime(chan))) {
connect = ast_channel_answertime(chan).tv_sec;
}
if (connect) {
end = time(NULL);
} else {
end = connect;
}
ast_debug(1, "OSPFinish: start '%ld'\n", start);
ast_debug(1, "OSPFinish: connect '%ld'\n", connect);
ast_debug(1, "OSPFinish: end '%ld'\n", end);

View File

@ -3666,10 +3666,10 @@ static void publish_dial_end_event(struct ast_channel *in, struct callattempt *o
struct callattempt *cur;
for (cur = outgoing; cur; cur = cur->q_next) {
if (cur->chan && cur->chan != exception) {
if (cur->chan && cur->chan != exception) {
ast_channel_publish_dial(in, cur->chan, NULL, status);
}
}
}
}
}
/*! \brief Hang up a list of outgoing calls */
@ -3931,9 +3931,7 @@ static int ring_entry(struct queue_ent *qe, struct callattempt *tmp, int *busies
member_call_pending_clear(tmp->member);
if (ast_channel_cdr(qe->chan)) {
ast_cdr_busy(ast_channel_cdr(qe->chan));
}
/* BUGBUG: Raise a BUSY dial end message here */
tmp->stillgoing = 0;
++*busies;
return 0;
@ -3987,21 +3985,6 @@ static int ring_entry(struct queue_ent *qe, struct callattempt *tmp, int *busies
} else {
ast_channel_exten_set(tmp->chan, ast_channel_exten(qe->chan));
}
if (ast_cdr_isset_unanswered()) {
/* they want to see the unanswered dial attempts! */
/* set up the CDR fields on all the CDRs to give sensical information */
ast_cdr_setdestchan(ast_channel_cdr(tmp->chan), ast_channel_name(tmp->chan));
strcpy(ast_channel_cdr(tmp->chan)->clid, ast_channel_cdr(qe->chan)->clid);
strcpy(ast_channel_cdr(tmp->chan)->channel, ast_channel_cdr(qe->chan)->channel);
strcpy(ast_channel_cdr(tmp->chan)->src, ast_channel_cdr(qe->chan)->src);
strcpy(ast_channel_cdr(tmp->chan)->dst, ast_channel_exten(qe->chan));
strcpy(ast_channel_cdr(tmp->chan)->dcontext, ast_channel_context(qe->chan));
strcpy(ast_channel_cdr(tmp->chan)->lastapp, ast_channel_cdr(qe->chan)->lastapp);
strcpy(ast_channel_cdr(tmp->chan)->lastdata, ast_channel_cdr(qe->chan)->lastdata);
ast_channel_cdr(tmp->chan)->amaflags = ast_channel_cdr(qe->chan)->amaflags;
strcpy(ast_channel_cdr(tmp->chan)->accountcode, ast_channel_cdr(qe->chan)->accountcode);
strcpy(ast_channel_cdr(tmp->chan)->userfield, ast_channel_cdr(qe->chan)->userfield);
}
ast_channel_unlock(tmp->chan);
ast_channel_unlock(qe->chan);
@ -4371,14 +4354,14 @@ static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callatte
if (pos == 1 /* not found */) {
if (numlines == (numbusies + numnochan)) {
ast_debug(1, "Everyone is busy at this time\n");
if (ast_channel_cdr(in) && ast_channel_state(in) != AST_STATE_UP) {
ast_cdr_busy(ast_channel_cdr(in));
}
/* BUGBUG: We shouldn't have to set anything here, as each
* individual dial attempt should have set that CDR to busy
*/
} else {
ast_debug(3, "No one is answering queue '%s' (%d numlines / %d busies / %d failed channels)\n", queue, numlines, numbusies, numnochan);
if (ast_channel_cdr(in) && ast_channel_state(in) != AST_STATE_UP) {
ast_cdr_failed(ast_channel_cdr(in));
}
/* BUGBUG: We shouldn't have to set anything here, as each
* individual dial attempt should have set that CDR to busy
*/
}
*to = 0;
return NULL;
@ -4609,9 +4592,6 @@ static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callatte
break;
case AST_CONTROL_BUSY:
ast_verb(3, "%s is busy\n", ochan_name);
if (ast_channel_cdr(in)) {
ast_cdr_busy(ast_channel_cdr(in));
}
ast_channel_publish_dial(qe->chan, o->chan, on, "BUSY");
do_hang(o);
endtime = (long) time(NULL);
@ -4631,9 +4611,6 @@ static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callatte
break;
case AST_CONTROL_CONGESTION:
ast_verb(3, "%s is circuit-busy\n", ochan_name);
if (ast_channel_cdr(in)) {
ast_cdr_failed(ast_channel_cdr(in));
}
ast_channel_publish_dial(qe->chan, o->chan, on, "CONGESTION");
endtime = (long) time(NULL);
endtime -= starttime;
@ -4769,9 +4746,6 @@ static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callatte
*to = 0;
publish_dial_end_event(in, outgoing, NULL, "CANCEL");
ast_frfree(f);
if (ast_channel_cdr(in) && ast_channel_state(in) != AST_STATE_UP) {
ast_cdr_noanswer(ast_channel_cdr(in));
}
return NULL;
}
if ((f->frametype == AST_FRAME_DTMF) && valid_exit(qe, f->subclass.integer)) {
@ -4780,9 +4754,6 @@ static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callatte
publish_dial_end_event(in, outgoing, NULL, "CANCEL");
*digit = f->subclass.integer;
ast_frfree(f);
if (ast_channel_cdr(in) && ast_channel_state(in) != AST_STATE_UP) {
ast_cdr_noanswer(ast_channel_cdr(in));
}
return NULL;
}
@ -4839,11 +4810,6 @@ skip_frame:;
}
publish_dial_end_event(qe->chan, outgoing, NULL, "NOANSWER");
if (ast_channel_cdr(in)
&& ast_channel_state(in) != AST_STATE_UP
&& (!*to || ast_check_hangup(in))) {
ast_cdr_noanswer(ast_channel_cdr(in));
}
}
#ifdef HAVE_EPOLL
@ -5678,20 +5644,6 @@ static int try_calling(struct queue_ent *qe, struct ast_flags opts, char **opt_a
if (res == -1) {
ast_debug(1, "%s: Nobody answered.\n", ast_channel_name(qe->chan));
}
if (ast_cdr_isset_unanswered()) {
/* channel contains the name of one of the outgoing channels
in its CDR; zero out this CDR to avoid a dual-posting */
struct callattempt *o;
for (o = outgoing; o; o = o->q_next) {
if (!o->chan) {
continue;
}
if (strcmp(ast_channel_cdr(o->chan)->dstchannel, ast_channel_cdr(qe->chan)->dstchannel) == 0) {
ast_set_flag(ast_channel_cdr(o->chan), AST_CDR_FLAG_POST_DISABLED);
break;
}
}
}
} else { /* peer is valid */
RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
/* Ah ha! Someone answered within the desired timeframe. Of course after this
@ -5785,17 +5737,14 @@ static int try_calling(struct queue_ent *qe, struct ast_flags opts, char **opt_a
} else {
ast_moh_stop(qe->chan);
}
/* If appropriate, log that we have a destination channel */
if (ast_channel_cdr(qe->chan)) {
ast_cdr_setdestchan(ast_channel_cdr(qe->chan), ast_channel_name(peer));
}
/* Make sure channels are compatible */
res = ast_channel_make_compatible(qe->chan, peer);
if (res < 0) {
ast_queue_log(queuename, ast_channel_uniqueid(qe->chan), member->membername, "SYSCOMPAT", "%s", "");
ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", ast_channel_name(qe->chan), ast_channel_name(peer));
record_abandoned(qe);
ast_cdr_failed(ast_channel_cdr(qe->chan));
ast_channel_publish_dial(qe->chan, peer, member->interface, ast_hangup_cause_to_dial_status(ast_channel_hangupcause(peer)));
ast_autoservice_chan_hangup_peer(qe->chan, peer);
ao2_ref(member, -1);
return -1;
@ -5855,10 +5804,10 @@ static int try_calling(struct queue_ent *qe, struct ast_flags opts, char **opt_a
ast_channel_unlock(qe->chan);
if (monitorfilename) {
ast_monitor_start(which, qe->parent->monfmt, monitorfilename, 1, X_REC_IN | X_REC_OUT);
} else if (ast_channel_cdr(qe->chan)) {
ast_monitor_start(which, qe->parent->monfmt, ast_channel_cdr(qe->chan)->uniqueid, 1, X_REC_IN | X_REC_OUT);
} else if (qe->chan) {
ast_monitor_start(which, qe->parent->monfmt, ast_channel_uniqueid(qe->chan), 1, X_REC_IN | X_REC_OUT);
} else {
/* Last ditch effort -- no CDR, make up something */
/* Last ditch effort -- no channel, make up something */
snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random());
ast_monitor_start(which, qe->parent->monfmt, tmpid, 1, X_REC_IN | X_REC_OUT);
}
@ -5871,8 +5820,8 @@ static int try_calling(struct queue_ent *qe, struct ast_flags opts, char **opt_a
if (mixmonapp) {
ast_debug(1, "Starting MixMonitor as requested.\n");
if (!monitorfilename) {
if (ast_channel_cdr(qe->chan)) {
ast_copy_string(tmpid, ast_channel_cdr(qe->chan)->uniqueid, sizeof(tmpid));
if (qe->chan) {
ast_copy_string(tmpid, ast_channel_uniqueid(qe->chan), sizeof(tmpid));
} else {
snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random());
}
@ -5944,14 +5893,15 @@ static int try_calling(struct queue_ent *qe, struct ast_flags opts, char **opt_a
}
ast_debug(1, "Arguments being passed to MixMonitor: %s\n", mixmonargs);
/* We purposely lock the CDR so that pbx_exec does not update the application data */
if (ast_channel_cdr(qe->chan)) {
ast_set_flag(ast_channel_cdr(qe->chan), AST_CDR_FLAG_LOCKED);
}
/* BUGBUG
* This needs to be done differently. We need to start a MixMonitor on
* the actual queue bridge itself, not drop some channel out and pull it
* back. Once the media channel work is done, start a media channel on
* the bridge.
*
* Alternatively, don't use pbx_exec to put an audio hook on a channel.
*/
pbx_exec(qe->chan, mixmonapp, mixmonargs);
if (ast_channel_cdr(qe->chan)) {
ast_clear_flag(ast_channel_cdr(qe->chan), AST_CDR_FLAG_LOCKED);
}
} else {
ast_log(LOG_WARNING, "Asked to run MixMonitor on this call, but cannot find the MixMonitor app!\n");
}
@ -6039,33 +5989,6 @@ static int try_calling(struct queue_ent *qe, struct ast_flags opts, char **opt_a
ast_queue_log(queuename, ast_channel_uniqueid(qe->chan), member->membername, "CONNECT", "%ld|%s|%ld", (long) time(NULL) - qe->start, ast_channel_uniqueid(peer),
(long)(orig - to > 0 ? (orig - to) / 1000 : 0));
if (ast_channel_cdr(qe->chan)) {
struct ast_cdr *cdr;
struct ast_cdr *newcdr;
/* Only work with the last CDR in the stack*/
cdr = ast_channel_cdr(qe->chan);
while (cdr->next) {
cdr = cdr->next;
}
/* If this CDR is not related to us add new one*/
if ((strcasecmp(cdr->uniqueid, ast_channel_uniqueid(qe->chan))) &&
(strcasecmp(cdr->linkedid, ast_channel_uniqueid(qe->chan))) &&
(newcdr = ast_cdr_dup(cdr))) {
ast_channel_lock(qe->chan);
ast_cdr_init(newcdr, qe->chan);
ast_cdr_reset(newcdr, 0);
cdr = ast_cdr_append(cdr, newcdr);
cdr = cdr->next;
ast_channel_unlock(qe->chan);
}
if (update_cdr) {
ast_copy_string(cdr->dstchannel, member->membername, sizeof(cdr->dstchannel));
}
}
blob = ast_json_pack("{s: s, s: s, s: s, s: i, s: i}",
"Queue", queuename,
"Interface", member->interface,

View File

@ -433,7 +433,7 @@ static int odbc_log(struct ast_cdr *cdr)
ast_strftime(colbuf, sizeof(colbuf), "%Y-%m-%d %H:%M:%S", &tm);
colptr = colbuf;
} else {
ast_cdr_getvar(cdr, entry->cdrname, &colptr, colbuf, sizeof(colbuf), 0, datefield ? 0 : 1);
ast_cdr_format_var(cdr, entry->cdrname, &colptr, colbuf, sizeof(colbuf), datefield ? 0 : 1);
}
if (colptr) {
@ -472,9 +472,9 @@ static int odbc_log(struct ast_cdr *cdr)
* form (but only when we're dealing with a character-based field).
*/
if (strcasecmp(entry->name, "disposition") == 0) {
ast_cdr_getvar(cdr, entry->name, &colptr, colbuf, sizeof(colbuf), 0, 0);
ast_cdr_format_var(cdr, entry->name, &colptr, colbuf, sizeof(colbuf), 0);
} else if (strcasecmp(entry->name, "amaflags") == 0) {
ast_cdr_getvar(cdr, entry->name, &colptr, colbuf, sizeof(colbuf), 0, 0);
ast_cdr_format_var(cdr, entry->name, &colptr, colbuf, sizeof(colbuf), 0);
}
/* Truncate too-long fields */

View File

@ -234,7 +234,7 @@ static int build_csv_record(char *buf, size_t bufsize, struct ast_cdr *cdr)
/* Disposition */
append_string(buf, ast_cdr_disp2str(cdr->disposition), bufsize);
/* AMA Flags */
append_string(buf, ast_cdr_flags2str(cdr->amaflags), bufsize);
append_string(buf, ast_channel_amaflags2string(cdr->amaflags), bufsize);
/* Unique ID */
if (loguniqueid)
append_string(buf, cdr->uniqueid, bufsize);
@ -285,9 +285,6 @@ static int csv_log(struct ast_cdr *cdr)
char buf[1024];
char csvmaster[PATH_MAX];
snprintf(csvmaster, sizeof(csvmaster),"%s/%s/%s", ast_config_AST_LOG_DIR, CSV_LOG_DIR, CSV_MASTER);
#if 0
printf("[CDR] %s ('%s' -> '%s') Dur: %ds Bill: %ds Disp: %s Flags: %s Account: [%s]\n", cdr->channel, cdr->src, cdr->dst, cdr->duration, cdr->billsec, ast_cdr_disp2str(cdr->disposition), ast_cdr_flags2str(cdr->amaflags), cdr->accountcode);
#endif
if (build_csv_record(buf, sizeof(buf), cdr)) {
ast_log(LOG_WARNING, "Unable to create CSV record in %d bytes. CDR not recorded!\n", (int)sizeof(buf));
return 0;

View File

@ -67,20 +67,20 @@ AST_THREADSTORAGE(custom_buf);
static const char name[] = "cdr-custom";
struct cdr_config {
struct cdr_custom_config {
AST_DECLARE_STRING_FIELDS(
AST_STRING_FIELD(filename);
AST_STRING_FIELD(format);
);
ast_mutex_t lock;
AST_RWLIST_ENTRY(cdr_config) list;
AST_RWLIST_ENTRY(cdr_custom_config) list;
};
static AST_RWLIST_HEAD_STATIC(sinks, cdr_config);
static AST_RWLIST_HEAD_STATIC(sinks, cdr_custom_config);
static void free_config(void)
{
struct cdr_config *sink;
struct cdr_custom_config *sink;
while ((sink = AST_RWLIST_REMOVE_HEAD(&sinks, list))) {
ast_mutex_destroy(&sink->lock);
ast_free(sink);
@ -103,7 +103,7 @@ static int load_config(void)
var = ast_variable_browse(cfg, "mappings");
while (var) {
if (!ast_strlen_zero(var->name) && !ast_strlen_zero(var->value)) {
struct cdr_config *sink = ast_calloc_with_stringfields(1, struct cdr_config, 1024);
struct cdr_custom_config *sink = ast_calloc_with_stringfields(1, struct cdr_custom_config, 1024);
if (!sink) {
ast_log(LOG_ERROR, "Unable to allocate memory for configuration settings.\n");
@ -130,7 +130,7 @@ static int custom_log(struct ast_cdr *cdr)
{
struct ast_channel *dummy;
struct ast_str *str;
struct cdr_config *config;
struct cdr_custom_config *config;
/* Batching saves memory management here. Otherwise, it's the same as doing an allocation and free each time. */
if (!(str = ast_str_thread_get(&custom_buf, 16))) {

View File

@ -203,7 +203,7 @@ static int manager_log(struct ast_cdr *cdr)
cdr->accountcode, cdr->src, cdr->dst, cdr->dcontext, cdr->clid, cdr->channel,
cdr->dstchannel, cdr->lastapp, cdr->lastdata, strStartTime, strAnswerTime, strEndTime,
cdr->duration, cdr->billsec, ast_cdr_disp2str(cdr->disposition),
ast_cdr_flags2str(cdr->amaflags), cdr->uniqueid, cdr->userfield,buf);
ast_channel_amaflags2string(cdr->amaflags), cdr->uniqueid, cdr->userfield,buf);
return 0;
}

View File

@ -124,10 +124,13 @@ static SQLHSTMT execute_cb(struct odbc_obj *obj, void *data)
SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, 0, 0, &cdr->billsec, 0, NULL);
}
if (ast_test_flag(&config, CONFIG_DISPOSITIONSTRING))
SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(ast_cdr_disp2str(cdr->disposition)) + 1, 0, ast_cdr_disp2str(cdr->disposition), 0, NULL);
else
if (ast_test_flag(&config, CONFIG_DISPOSITIONSTRING)) {
char *disposition;
disposition = ast_strdupa(ast_cdr_disp2str(cdr->disposition));
SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(disposition) + 1, 0, disposition, 0, NULL);
} else {
SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, 0, 0, &cdr->disposition, 0, NULL);
}
SQLBindParameter(stmt, 12, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, 0, 0, &cdr->amaflags, 0, NULL);
SQLBindParameter(stmt, 13, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(cdr->accountcode), 0, cdr->accountcode, 0, NULL);

View File

@ -222,9 +222,9 @@ static int pgsql_log(struct ast_cdr *cdr)
AST_RWLIST_RDLOCK(&psql_columns);
AST_RWLIST_TRAVERSE(&psql_columns, cur, list) {
/* For fields not set, simply skip them */
ast_cdr_getvar(cdr, cur->name, &value, buf, sizeof(buf), 0, 0);
ast_cdr_format_var(cdr, cur->name, &value, buf, sizeof(buf), 0);
if (strcmp(cur->name, "calldate") == 0 && !value) {
ast_cdr_getvar(cdr, "start", &value, buf, sizeof(buf), 0, 0);
ast_cdr_format_var(cdr, "start", &value, buf, sizeof(buf), 0);
}
if (!value) {
if (cur->notnull && !cur->hasdefault) {
@ -286,7 +286,7 @@ static int pgsql_log(struct ast_cdr *cdr)
} else if (strcmp(cur->name, "duration") == 0 || strcmp(cur->name, "billsec") == 0) {
if (cur->type[0] == 'i') {
/* Get integer, no need to escape anything */
ast_cdr_getvar(cdr, cur->name, &value, buf, sizeof(buf), 0, 0);
ast_cdr_format_var(cdr, cur->name, &value, buf, sizeof(buf), 0);
LENGTHEN_BUF2(13);
ast_str_append(&sql2, 0, "%s%s", first ? "" : ",", value);
} else if (strncmp(cur->type, "float", 5) == 0) {
@ -302,18 +302,18 @@ static int pgsql_log(struct ast_cdr *cdr)
} else if (strcmp(cur->name, "disposition") == 0 || strcmp(cur->name, "amaflags") == 0) {
if (strncmp(cur->type, "int", 3) == 0) {
/* Integer, no need to escape anything */
ast_cdr_getvar(cdr, cur->name, &value, buf, sizeof(buf), 0, 1);
ast_cdr_format_var(cdr, cur->name, &value, buf, sizeof(buf), 1);
LENGTHEN_BUF2(13);
ast_str_append(&sql2, 0, "%s%s", first ? "" : ",", value);
} else {
/* Although this is a char field, there are no special characters in the values for these fields */
ast_cdr_getvar(cdr, cur->name, &value, buf, sizeof(buf), 0, 0);
ast_cdr_format_var(cdr, cur->name, &value, buf, sizeof(buf), 0);
LENGTHEN_BUF2(31);
ast_str_append(&sql2, 0, "%s'%s'", first ? "" : ",", value);
}
} else {
/* Arbitrary field, could be anything */
ast_cdr_getvar(cdr, cur->name, &value, buf, sizeof(buf), 0, 0);
ast_cdr_format_var(cdr, cur->name, &value, buf, sizeof(buf), 0);
if (strncmp(cur->type, "int", 3) == 0) {
long long whatever;
if (value && sscanf(value, "%30lld", &whatever) == 1) {

View File

@ -170,12 +170,12 @@ static int build_radius_record(VALUE_PAIR **tosend, struct ast_cdr *cdr)
return -1;
/* Disposition */
tmp = ast_cdr_disp2str(cdr->disposition);
tmp = ast_strdupa(ast_cdr_disp2str(cdr->disposition));
if (!rc_avpair_add(rh, tosend, PW_AST_DISPOSITION, tmp, strlen(tmp), VENDOR_CODE))
return -1;
/* AMA Flags */
tmp = ast_cdr_flags2str(cdr->amaflags);
tmp = ast_strdupa(ast_channel_amaflags2string(cdr->amaflags));
if (!rc_avpair_add(rh, tosend, PW_AST_AMA_FLAGS, tmp, strlen(tmp), VENDOR_CODE))
return -1;

View File

@ -60,7 +60,7 @@ AST_THREADSTORAGE(syslog_buf);
static const char name[] = "cdr-syslog";
struct cdr_config {
struct cdr_syslog_config {
AST_DECLARE_STRING_FIELDS(
AST_STRING_FIELD(ident);
AST_STRING_FIELD(format);
@ -68,14 +68,14 @@ struct cdr_config {
int facility;
int priority;
ast_mutex_t lock;
AST_LIST_ENTRY(cdr_config) list;
AST_LIST_ENTRY(cdr_syslog_config) list;
};
static AST_RWLIST_HEAD_STATIC(sinks, cdr_config);
static AST_RWLIST_HEAD_STATIC(sinks, cdr_syslog_config);
static void free_config(void)
{
struct cdr_config *sink;
struct cdr_syslog_config *sink;
while ((sink = AST_RWLIST_REMOVE_HEAD(&sinks, list))) {
ast_mutex_destroy(&sink->lock);
ast_free(sink);
@ -86,7 +86,7 @@ static int syslog_log(struct ast_cdr *cdr)
{
struct ast_channel *dummy;
struct ast_str *str;
struct cdr_config *sink;
struct cdr_syslog_config *sink;
/* Batching saves memory management here. Otherwise, it's the same as doing an
allocation and free each time. */
@ -174,7 +174,7 @@ static int load_config(int reload)
}
while ((catg = ast_category_browse(cfg, catg))) {
struct cdr_config *sink;
struct cdr_syslog_config *sink;
if (!strcasecmp(catg, "general")) {
continue;
@ -186,7 +186,7 @@ static int load_config(int reload)
continue;
}
sink = ast_calloc_with_stringfields(1, struct cdr_config, 1024);
sink = ast_calloc_with_stringfields(1, struct cdr_syslog_config, 1024);
if (!sink) {
ast_log(AST_LOG_ERROR,

View File

@ -176,7 +176,7 @@ retry:
settings->table,
accountcode, src, dst, dcontext, clid, channel,
dstchannel, lastapp, lastdata, start, answer, end, hrduration,
hrbillsec, ast_cdr_disp2str(cdr->disposition), ast_cdr_flags2str(cdr->amaflags), uniqueid,
hrbillsec, ast_cdr_disp2str(cdr->disposition), ast_channel_amaflags2string(cdr->amaflags), uniqueid,
userfield
);
} else {
@ -196,7 +196,7 @@ retry:
settings->table,
accountcode, src, dst, dcontext, clid, channel,
dstchannel, lastapp, lastdata, start, answer, end, cdr->duration,
cdr->billsec, ast_cdr_disp2str(cdr->disposition), ast_cdr_flags2str(cdr->amaflags), uniqueid,
cdr->billsec, ast_cdr_disp2str(cdr->disposition), ast_channel_amaflags2string(cdr->amaflags), uniqueid,
userfield
);
}
@ -226,7 +226,7 @@ retry:
settings->table,
accountcode, src, dst, dcontext, clid, channel,
dstchannel, lastapp, lastdata, start, answer, end, hrduration,
hrbillsec, ast_cdr_disp2str(cdr->disposition), ast_cdr_flags2str(cdr->amaflags), uniqueid
hrbillsec, ast_cdr_disp2str(cdr->disposition), ast_channel_amaflags2string(cdr->amaflags), uniqueid
);
} else {
erc = dbfcmd(settings->dbproc,
@ -245,7 +245,7 @@ retry:
settings->table,
accountcode, src, dst, dcontext, clid, channel,
dstchannel, lastapp, lastdata, start, answer, end, cdr->duration,
cdr->billsec, ast_cdr_disp2str(cdr->disposition), ast_cdr_flags2str(cdr->amaflags), uniqueid
cdr->billsec, ast_cdr_disp2str(cdr->disposition), ast_channel_amaflags2string(cdr->amaflags), uniqueid
);
}
}

View File

@ -129,7 +129,7 @@ static void manager_log(const struct ast_event *event, void *userdata)
record.application_name,
record.application_data,
start_time,
ast_cel_get_ama_flag_name(record.amaflag),
ast_channel_amaflags2string(record.amaflag),
record.unique_id,
record.linked_id,
record.user_field,

View File

@ -150,7 +150,7 @@ static int build_radius_record(VALUE_PAIR **send, struct ast_cel_event_record *r
return -1;
}
/* AMA Flags */
amaflags = ast_strdupa(ast_cel_get_ama_flag_name(record->amaflag));
amaflags = ast_strdupa(ast_channel_amaflags2string(record->amaflag));
if (!rc_avpair_add(rh, send, PW_AST_AMA_FLAGS, amaflags, strlen(amaflags), VENDOR_CODE)) {
return -1;
}

View File

@ -206,7 +206,7 @@ retry:
ciddnid_ai, exten_ai, context_ai, channel_ai, app_ai, appdata_ai, start,
(record.event_type == AST_CEL_USER_DEFINED)
? record.user_defined_name : record.event_name,
ast_cel_get_ama_flag_name(record.amaflag), uniqueid_ai, linkedid_ai,
ast_channel_amaflags2string(record.amaflag), uniqueid_ai, linkedid_ai,
userfield_ai, peer_ai);
if (erc == FAIL) {

View File

@ -112,10 +112,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<option name="d">
<para>make the app return <literal>-1</literal> if there is an error condition.</para>
</option>
<option name="c">
<para>change the CDR so that the source of the call is
<literal>Agent/agent_id</literal></para>
</option>
<option name="n">
<para>don't generate the warnings when there is no callerid or the
agentid is not known. It's handy if you want to have one context
@ -234,7 +230,6 @@ static char recordformat[AST_MAX_BUF] = "";
static char recordformatext[AST_MAX_BUF] = "";
static char urlprefix[AST_MAX_BUF] = "";
static char savecallsin[AST_MAX_BUF] = "";
static int updatecdr = 0;
static char beep[AST_MAX_BUF] = "beep";
#define GETAGENTBYCALLERID "AGENTBYCALLERID"
@ -573,7 +568,7 @@ static int agent_answer(struct ast_channel *ast)
static int __agent_start_monitoring(struct ast_channel *ast, struct agent_pvt *p, int needlock)
{
char tmp[AST_MAX_BUF],tmp2[AST_MAX_BUF], *pointer;
char tmp[AST_MAX_BUF], tmp2[AST_MAX_BUF], *pointer;
char filename[AST_MAX_BUF];
int res = -1;
if (!p)
@ -590,9 +585,7 @@ static int __agent_start_monitoring(struct ast_channel *ast, struct agent_pvt *p
#if 0
ast_verbose("name is %s, link is %s\n",tmp, tmp2);
#endif
if (!ast_channel_cdr(ast))
ast_channel_cdr_set(ast, ast_cdr_alloc());
ast_cdr_setuserfield(ast, tmp2);
ast_cdr_setuserfield(ast_channel_name(ast), tmp2);
res = 0;
} else
ast_log(LOG_ERROR, "Recording already started on that call.\n");
@ -1199,11 +1192,6 @@ static int read_agent_config(int reload)
strcpy(agentgoodbye,v->value);
} else if (!strcasecmp(v->name, "musiconhold")) {
ast_copy_string(moh, v->value, sizeof(moh));
} else if (!strcasecmp(v->name, "updatecdr")) {
if (ast_true(v->value))
updatecdr = 1;
else
updatecdr = 0;
} else if (!strcasecmp(v->name, "autologoffunavail")) {
if (ast_true(v->value))
autologoffunavail = 1;
@ -1898,7 +1886,6 @@ static int login_exec(struct ast_channel *chan, const char *data)
const char *tmpoptions = NULL;
int play_announcement = 1;
char agent_goodbye[AST_MAX_FILENAME_LEN];
int update_cdr = updatecdr;
user[0] = '\0';
xpass[0] = '\0';
@ -1918,14 +1905,6 @@ static int login_exec(struct ast_channel *chan, const char *data)
tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTMAXLOGINTRIES");
ast_verb(3, "Saw variable AGENTMAXLOGINTRIES=%s, setting max_login_tries to: %d on Channel '%s'.\n",tmpoptions,max_login_tries,ast_channel_name(chan));
}
if (!ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENTUPDATECDR"))) {
if (ast_true(pbx_builtin_getvar_helper(chan, "AGENTUPDATECDR")))
update_cdr = 1;
else
update_cdr = 0;
tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTUPDATECDR");
ast_verb(3, "Saw variable AGENTUPDATECDR=%s, setting update_cdr to: %d on Channel '%s'.\n",tmpoptions,update_cdr,ast_channel_name(chan));
}
if (!ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENTGOODBYE"))) {
strcpy(agent_goodbye, pbx_builtin_getvar_helper(chan, "AGENTGOODBYE"));
tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTGOODBYE");
@ -2093,8 +2072,6 @@ static int login_exec(struct ast_channel *chan, const char *data)
"Channel: %s\r\n"
"Uniqueid: %s\r\n",
p->agent, ast_channel_name(chan), ast_channel_uniqueid(chan));
if (update_cdr && ast_channel_cdr(chan))
snprintf(ast_channel_cdr(chan)->channel, sizeof(ast_channel_cdr(chan)->channel), "%s", agent);
ast_queue_log("NONE", ast_channel_uniqueid(chan), agent, "AGENTLOGIN", "%s", ast_channel_name(chan));
ast_verb(2, "Agent '%s' logged in (format %s/%s)\n", p->agent,
ast_getformatname(ast_channel_readformat(chan)), ast_getformatname(ast_channel_writeformat(chan)));
@ -2242,17 +2219,16 @@ static int agentmonitoroutgoing_exec(struct ast_channel *chan, const char *data)
{
int exitifnoagentid = 0;
int nowarnings = 0;
int changeoutgoing = 0;
int res = 0;
char agent[AST_MAX_AGENT];
if (data) {
if (strchr(data, 'd'))
if (strchr(data, 'd')) {
exitifnoagentid = 1;
if (strchr(data, 'n'))
}
if (strchr(data, 'n')) {
nowarnings = 1;
if (strchr(data, 'c'))
changeoutgoing = 1;
}
}
if (ast_channel_caller(chan)->id.number.valid
&& !ast_strlen_zero(ast_channel_caller(chan)->id.number.str)) {
@ -2266,7 +2242,6 @@ static int agentmonitoroutgoing_exec(struct ast_channel *chan, const char *data)
AST_LIST_LOCK(&agents);
AST_LIST_TRAVERSE(&agents, p, list) {
if (!strcasecmp(p->agent, tmp)) {
if (changeoutgoing) snprintf(ast_channel_cdr(chan)->channel, sizeof(ast_channel_cdr(chan)->channel), "Agent/%s", p->agent);
__agent_start_monitoring(chan, p, 1);
break;
}

View File

@ -107,7 +107,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/callerid.h"
#include "asterisk/adsi.h"
#include "asterisk/cli.h"
#include "asterisk/cdr.h"
#include "asterisk/cel.h"
#include "asterisk/features.h"
#include "asterisk/musiconhold.h"
@ -17726,7 +17725,7 @@ static int process_dahdi(struct dahdi_chan_conf *confp, const char *cat, struct
} else if (!strcasecmp(v->name, "accountcode")) {
ast_copy_string(confp->chan.accountcode, v->value, sizeof(confp->chan.accountcode));
} else if (!strcasecmp(v->name, "amaflags")) {
y = ast_cdr_amaflags2int(v->value);
y = ast_channel_string2amaflag(v->value);
if (y < 0)
ast_log(LOG_WARNING, "Invalid AMA flags: %s at line %d.\n", v->value, v->lineno);
else

View File

@ -1482,7 +1482,7 @@ static struct oh323_user *build_user(const char *name, struct ast_variable *v, s
/* Let us know we need to use ip authentication */
user->host = 1;
} else if (!strcasecmp(v->name, "amaflags")) {
format = ast_cdr_amaflags2int(v->value);
format = ast_channel_string2amaflag(v->value);
if (format < 0) {
ast_log(LOG_WARNING, "Invalid AMA Flags: %s at line %d\n", v->value, v->lineno);
} else {

View File

@ -77,7 +77,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/cli.h"
#include "asterisk/translate.h"
#include "asterisk/md5.h"
#include "asterisk/cdr.h"
#include "asterisk/crypto.h"
#include "asterisk/acl.h"
#include "asterisk/manager.h"
@ -12802,7 +12801,7 @@ static struct iax2_user *build_user(const char *name, struct ast_variable *v, st
} else if (!strcasecmp(v->name, "language")) {
ast_string_field_set(user, language, v->value);
} else if (!strcasecmp(v->name, "amaflags")) {
format = ast_cdr_amaflags2int(v->value);
format = ast_channel_string2amaflag(v->value);
if (format < 0) {
ast_log(LOG_WARNING, "Invalid AMA Flags: %s at line %d\n", v->value, v->lineno);
} else {
@ -13273,7 +13272,7 @@ static int set_config(const char *config_file, int reload, int forced)
} else if (!strcasecmp(v->name, "mohsuggest")) {
ast_copy_string(mohsuggest, v->value, sizeof(mohsuggest));
} else if (!strcasecmp(v->name, "amaflags")) {
format = ast_cdr_amaflags2int(v->value);
format = ast_channel_string2amaflag(v->value);
if (format < 0) {
ast_log(LOG_WARNING, "Invalid AMA Flags: %s at line %d\n", v->value, v->lineno);
} else {
@ -14513,7 +14512,7 @@ static int users_data_provider_get(const struct ast_data_search *search,
continue;
}
ast_data_add_int(data_enum_node, "value", user->amaflags);
ast_data_add_str(data_enum_node, "text", ast_cdr_flags2str(user->amaflags));
ast_data_add_str(data_enum_node, "text", ast_channel_amaflags2string(user->amaflags));
ast_data_add_bool(data_user, "access-control", ast_acl_list_is_empty(user->acl) ? 0 : 1);

View File

@ -67,7 +67,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/callerid.h"
#include "asterisk/cli.h"
#include "asterisk/say.h"
#include "asterisk/cdr.h"
#include "asterisk/astdb.h"
#include "asterisk/features.h"
#include "asterisk/app.h"
@ -4087,7 +4086,7 @@ static struct mgcp_gateway *build_gateway(char *cat, struct ast_variable *v)
} else if (!strcasecmp(v->name, "accountcode")) {
ast_copy_string(accountcode, v->value, sizeof(accountcode));
} else if (!strcasecmp(v->name, "amaflags")) {
y = ast_cdr_amaflags2int(v->value);
y = ast_channel_string2amaflag(v->value);
if (y < 0) {
ast_log(LOG_WARNING, "Invalid AMA flags: %s at line %d\n", v->value, v->lineno);
} else {

View File

@ -20224,7 +20224,7 @@ static char *_sip_show_peer(int type, int fd, struct mansession *s, const struct
ast_cli(fd, " Tonezone : %s\n", peer->zone[0] != '\0' ? peer->zone : "<Not set>");
if (!ast_strlen_zero(peer->accountcode))
ast_cli(fd, " Accountcode : %s\n", peer->accountcode);
ast_cli(fd, " AMA flags : %s\n", ast_cdr_flags2str(peer->amaflags));
ast_cli(fd, " AMA flags : %s\n", ast_channel_amaflags2string(peer->amaflags));
ast_cli(fd, " Transfer mode: %s\n", transfermode2str(peer->allowtransfer));
ast_cli(fd, " CallingPres : %s\n", ast_describe_caller_presentation(peer->callingpres));
if (!ast_strlen_zero(peer->fromuser))
@ -20362,7 +20362,7 @@ static char *_sip_show_peer(int type, int fd, struct mansession *s, const struct
astman_append(s, "ToneZone: %s\r\n", peer->zone[0] != '\0' ? peer->zone : "<Not set>");
if (!ast_strlen_zero(peer->accountcode))
astman_append(s, "Accountcode: %s\r\n", peer->accountcode);
astman_append(s, "AMAflags: %s\r\n", ast_cdr_flags2str(peer->amaflags));
astman_append(s, "AMAflags: %s\r\n", ast_channel_amaflags2string(peer->amaflags));
astman_append(s, "CID-CallingPres: %s\r\n", ast_describe_caller_presentation(peer->callingpres));
if (!ast_strlen_zero(peer->fromuser))
astman_append(s, "SIP-FromUser: %s\r\n", peer->fromuser);
@ -20537,7 +20537,7 @@ static char *sip_show_user(struct ast_cli_entry *e, int cmd, struct ast_cli_args
ast_cli(a->fd, " Language : %s\n", user->language);
if (!ast_strlen_zero(user->accountcode))
ast_cli(a->fd, " Accountcode : %s\n", user->accountcode);
ast_cli(a->fd, " AMA flags : %s\n", ast_cdr_flags2str(user->amaflags));
ast_cli(a->fd, " AMA flags : %s\n", ast_channel_amaflags2string(user->amaflags));
ast_cli(a->fd, " Tonezone : %s\n", user->zone[0] != '\0' ? user->zone : "<Not set>");
ast_cli(a->fd, " Transfer mode: %s\n", transfermode2str(user->allowtransfer));
ast_cli(a->fd, " MaxCallBR : %d kbps\n", user->maxcallbitrate);
@ -20724,8 +20724,6 @@ static int show_chanstats_cb(void *__cur, void *__arg, int flags)
struct sip_pvt *cur = __cur;
struct ast_rtp_instance_stats stats;
char durbuf[10];
int duration;
int durh, durm, durs;
struct ast_channel *c;
struct __show_chan_arg *arg = __arg;
int fd = arg->fd;
@ -20756,12 +20754,8 @@ static int show_chanstats_cb(void *__cur, void *__arg, int flags)
return 0;
}
if (c && ast_channel_cdr(c) && !ast_tvzero(ast_channel_cdr(c)->start)) {
duration = (int)(ast_tvdiff_ms(ast_tvnow(), ast_channel_cdr(c)->start) / 1000);
durh = duration / 3600;
durm = (duration % 3600) / 60;
durs = duration % 60;
snprintf(durbuf, sizeof(durbuf), "%02d:%02d:%02d", durh, durm, durs);
if (c) {
ast_format_duration_hh_mm_ss(ast_channel_get_duration(c), durbuf, sizeof(durbuf));
} else {
durbuf[0] = '\0';
}
@ -21694,11 +21688,8 @@ static void handle_request_info(struct sip_pvt *p, struct sip_request *req)
} else if (!ast_strlen_zero(c = sip_get_header(req, "X-ClientCode"))) {
/* Client code (from SNOM phone) */
if (ast_test_flag(&p->flags[0], SIP_USECLIENTCODE)) {
if (p->owner && ast_channel_cdr(p->owner)) {
ast_cdr_setuserfield(p->owner, c);
}
if (p->owner && ast_bridged_channel(p->owner) && ast_channel_cdr(ast_bridged_channel(p->owner))) {
ast_cdr_setuserfield(ast_bridged_channel(p->owner), c);
if (p->owner) {
ast_cdr_setuserfield(ast_channel_name(p->owner), c);
}
transmit_response(p, "200 OK", req);
} else {
@ -24831,7 +24822,7 @@ static int handle_invite_replaces(struct sip_pvt *p, struct sip_request *req,
ast_channel_unlock(c);
sip_pvt_unlock(p);
ast_raw_answer(c, 1);
ast_raw_answer(c);
ast_channel_lock(replaces_chan);
bridge = ast_channel_get_bridge(replaces_chan);
@ -30458,7 +30449,7 @@ static struct sip_peer *build_peer(const char *name, struct ast_variable *v, str
} else if (!strcasecmp(v->name, "callbackextension")) {
ast_string_field_set(peer, callback, v->value);
} else if (!strcasecmp(v->name, "amaflags")) {
format = ast_cdr_amaflags2int(v->value);
format = ast_channel_string2amaflag(v->value);
if (format < 0) {
ast_log(LOG_WARNING, "Invalid AMA Flags for peer: %s at line %d\n", v->value, v->lineno);
} else {
@ -34023,7 +34014,7 @@ static int peers_data_provider_get(const struct ast_data_search *search,
continue;
}
ast_data_add_int(enum_node, "value", peer->amaflags);
ast_data_add_str(enum_node, "text", ast_cdr_flags2str(peer->amaflags));
ast_data_add_str(enum_node, "text", ast_channel_amaflags2string(peer->amaflags));
/* sip options */
data_sip_options = ast_data_add_node(data_peer, "sipoptions");

View File

@ -67,7 +67,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/cli.h"
#include "asterisk/manager.h"
#include "asterisk/say.h"
#include "asterisk/cdr.h"
#include "asterisk/astdb.h"
#include "asterisk/features.h"
#include "asterisk/app.h"
@ -4477,7 +4476,7 @@ static char *_skinny_show_line(int type, int fd, struct mansession *s, const str
ast_str_reset(tmp_str);
ast_cli(fd, "Language: %s\n", S_OR(l->language, "<not set>"));
ast_cli(fd, "Accountcode: %s\n", S_OR(l->accountcode, "<not set>"));
ast_cli(fd, "AmaFlag: %s\n", ast_cdr_flags2str(l->amaflags));
ast_cli(fd, "AmaFlag: %s\n", ast_channel_amaflags2string(l->amaflags));
ast_cli(fd, "CallerId Number: %s\n", S_OR(l->cid_num, "<not set>"));
ast_cli(fd, "CallerId Name: %s\n", S_OR(l->cid_name, "<not set>"));
ast_cli(fd, "Hide CallerId: %s\n", (l->hidecallerid ? "Yes" : "No"));
@ -4535,7 +4534,7 @@ static char *_skinny_show_line(int type, int fd, struct mansession *s, const str
ast_str_reset(tmp_str);
astman_append(s, "Language: %s\r\n", S_OR(l->language, "<not set>"));
astman_append(s, "Accountcode: %s\r\n", S_OR(l->accountcode, "<not set>"));
astman_append(s, "AMAflags: %s\r\n", ast_cdr_flags2str(l->amaflags));
astman_append(s, "AMAflags: %s\r\n", ast_channel_amaflags2string(l->amaflags));
astman_append(s, "Callerid: %s\r\n", ast_callerid_merge(cbuf, sizeof(cbuf), l->cid_name, l->cid_num, ""));
astman_append(s, "HideCallerId: %s\r\n", (l->hidecallerid ? "Yes" : "No"));
astman_append(s, "CFwdAll: %s\r\n", S_COR((l->cfwdtype & SKINNY_CFWD_ALL), l->call_forward_all, "<not set>"));
@ -7809,7 +7808,7 @@ static void config_parse_variables(int type, void *item, struct ast_variable *vp
}
} else if (!strcasecmp(v->name, "amaflags")) {
if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
int tempamaflags = ast_cdr_amaflags2int(v->value);
int tempamaflags = ast_channel_string2amaflag(v->value);
if (tempamaflags < 0) {
ast_log(LOG_WARNING, "Invalid AMA flags: %s at line %d\n", v->value, v->lineno);
} else {

View File

@ -6392,7 +6392,7 @@ static struct unistim_device *build_device(const char *cat, const struct ast_var
ast_copy_string(lt->accountcode, v->value, sizeof(lt->accountcode));
} else if (!strcasecmp(v->name, "amaflags")) {
int y;
y = ast_cdr_amaflags2int(v->value);
y = ast_channel_string2amaflag(v->value);
if (y < 0) {
ast_log(LOG_WARNING, "Invalid AMA flags: %s at line %d\n", v->value,
v->lineno);

View File

@ -1142,9 +1142,6 @@ static int callerid_write(struct ast_channel *chan, const char *cmd, char *data,
ast_channel_redirecting(chan)->from.number.valid = 1;
ast_free(ast_channel_redirecting(chan)->from.number.str);
ast_channel_redirecting(chan)->from.number.str = ast_strdup(value);
if (ast_channel_cdr(chan)) {
ast_cdr_setcid(ast_channel_cdr(chan), chan);
}
} else if (!strcasecmp("dnid", member.argv[0])) {
ast_party_dialed_set_init(&dialed, ast_channel_dialed(chan));
if (member.argc == 1) {
@ -1162,9 +1159,6 @@ static int callerid_write(struct ast_channel *chan, const char *cmd, char *data,
dialed.number.str = ast_strdup(value);
ast_trim_blanks(dialed.number.str);
ast_party_dialed_set(ast_channel_dialed(chan), &dialed);
if (ast_channel_cdr(chan)) {
ast_cdr_setcid(ast_channel_cdr(chan), chan);
}
} else if (member.argc == 3 && !strcasecmp("plan", member.argv[2])) {
/* dnid-num-plan */
val = ast_strdupa(value);
@ -1172,9 +1166,6 @@ static int callerid_write(struct ast_channel *chan, const char *cmd, char *data,
if (('0' <= val[0]) && (val[0] <= '9')) {
ast_channel_dialed(chan)->number.plan = atoi(val);
if (ast_channel_cdr(chan)) {
ast_cdr_setcid(ast_channel_cdr(chan), chan);
}
} else {
ast_log(LOG_ERROR,
"Unknown type-of-number/numbering-plan '%s', value unchanged\n", val);
@ -1192,9 +1183,6 @@ static int callerid_write(struct ast_channel *chan, const char *cmd, char *data,
switch (status) {
case ID_FIELD_VALID:
ast_party_dialed_set(ast_channel_dialed(chan), &dialed);
if (ast_channel_cdr(chan)) {
ast_cdr_setcid(ast_channel_cdr(chan), chan);
}
break;
case ID_FIELD_INVALID:
break;
@ -1212,9 +1200,6 @@ static int callerid_write(struct ast_channel *chan, const char *cmd, char *data,
if (('0' <= val[0]) && (val[0] <= '9')) {
ast_channel_caller(chan)->ani2 = atoi(val);
if (ast_channel_cdr(chan)) {
ast_cdr_setcid(ast_channel_cdr(chan), chan);
}
} else {
ast_log(LOG_ERROR, "Unknown callerid ani2 '%s', value unchanged\n", val);
}
@ -1229,9 +1214,6 @@ static int callerid_write(struct ast_channel *chan, const char *cmd, char *data,
switch (status) {
case ID_FIELD_VALID:
ast_party_caller_set(ast_channel_caller(chan), &caller, NULL);
if (ast_channel_cdr(chan)) {
ast_cdr_setcid(ast_channel_cdr(chan), chan);
}
break;
case ID_FIELD_INVALID:
break;
@ -1246,9 +1228,6 @@ static int callerid_write(struct ast_channel *chan, const char *cmd, char *data,
switch (status) {
case ID_FIELD_VALID:
ast_party_caller_set(ast_channel_caller(chan), &caller, NULL);
if (ast_channel_cdr(chan)) {
ast_cdr_setcid(ast_channel_cdr(chan), chan);
}
break;
case ID_FIELD_INVALID:
break;
@ -1263,9 +1242,6 @@ static int callerid_write(struct ast_channel *chan, const char *cmd, char *data,
switch (status) {
case ID_FIELD_VALID:
ast_channel_set_caller_event(chan, &caller, NULL);
if (ast_channel_cdr(chan)) {
ast_cdr_setcid(ast_channel_cdr(chan), chan);
}
break;
case ID_FIELD_INVALID:
break;

View File

@ -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)
return -1;
ast_channel_lock(chan);
cdr = ast_channel_cdr(chan);
if (!cdr) {
ast_channel_unlock(chan);
if (!chan) {
return -1;
}
AST_STANDARD_APP_ARGS(args, parse);
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);
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);
snprintf(buf, len, "%lf", hrtime);
} else {
snprintf(buf, len, "%lf", 0.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;
}
} 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;
if (ast_cdr_getvar(ast_channel_name(chan), args.variable, tempbuf, sizeof(tempbuf))) {
return 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;
}
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);
}
}
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")) {
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);
}
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. */
return 0;
}
ast_channel_unlock(chan);
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");

View File

@ -343,13 +343,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
</function>
***/
/*
* BUGBUG add CHANNEL(after_bridge_goto)=<parseable-goto> Sets an after bridge goto datastore property on the channel.
* CHANNEL(after_bridge_goto)=<empty> Deletes any after bridge goto datastore property on the channel.
*
* BUGBUG add CHANNEL(dtmf_features)=tkhwx sets channel dtmf features to specified. (transfer, park, hangup, monitor, mixmonitor)
*/
#define locked_copy_string(chan, dest, source, len) \
do { \
ast_channel_lock(chan); \
@ -450,7 +443,7 @@ static int func_channel_read(struct ast_channel *chan, const char *function,
ast_channel_lock(chan);
p = ast_bridged_channel(chan);
if (p || ast_channel_tech(chan) || ast_channel_cdr(chan)) /* dummy channel? if so, we hid the peer name in the language */
if (p || ast_channel_tech(chan)) /* dummy channel? if so, we hid the peer name in the language */
ast_copy_string(buf, (p ? ast_channel_name(p) : ""), len);
else {
/* a dummy channel can still pass along bridged peer info via
@ -525,7 +518,7 @@ static int func_channel_write_real(struct ast_channel *chan, const char *functio
locked_string_field_set(chan, userfield, value);
else if (!strcasecmp(data, "amaflags")) {
ast_channel_lock(chan);
if(isdigit(*value)) {
if (isdigit(*value)) {
int amaflags;
sscanf(value, "%30d", &amaflags);
ast_channel_amaflags_set(chan, amaflags);

View File

@ -1031,6 +1031,37 @@ void ast_bridge_change_state(struct ast_bridge_channel *bridge_channel, enum ast
*/
int ast_bridge_queue_action(struct ast_bridge *bridge, struct ast_frame *action);
/*!
* \brief Update the linkedid for all channels in a bridge
* \since 12.0.0
*
* \param bridge The bridge to update the linkedids on
* \param bridge_channel The channel joining the bridge
* \param swap The channel being swapped out of the bridge. May be NULL.
*
* \note The bridge must be locked prior to calling this function.
* \note This API call is meant for internal bridging operations.
*/
void ast_bridge_update_linkedids(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, struct ast_bridge_channel *swap);
/*!
* \brief Update the accountcodes for a channel entering a bridge
* \since 12.0.0
*
* This function updates the accountcode and peeraccount on channels in two-party
* bridges. In multi-party bridges, peeraccount is not set - it doesn't make much sense -
* however accountcode propagation will still occur if the channel joining has an
* accountcode.
*
* \param bridge The bridge to update the accountcodes in
* \param bridge_channel The channel joining the bridge
* \param swap The channel being swapped out of the bridge. May be NULL.
*
* \note The bridge must be locked prior to calling this function.
* \note This API call is meant for internal bridging operations.
*/
void ast_bridge_update_accountcodes(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, struct ast_bridge_channel *swap);
/*!
* \brief Write a frame to the specified bridge_channel.
* \since 12.0.0

View File

@ -26,33 +26,216 @@
#ifndef _ASTERISK_CDR_H
#define _ASTERISK_CDR_H
#include <sys/time.h>
#include "asterisk/channel.h"
#include "asterisk/data.h"
/*! \file
*
* \since 12
*
* \brief Call Detail Record Engine.
*
* \page CDR Call Detail Record Engine
*
* \par Intro
*
* The Call Detail Record (CDR) engine uses the \ref stasis Stasis Message Bus
* to build records for the channels in Asterisk. As the state of a channel and
* the bridges it participates in changes, notifications are sent over the
* Stasis Message Bus. The CDR engine consumes these notifications and builds
* records that reflect that state. Over the lifetime of a channel, many CDRs
* may be generated for that channel or that involve that channel.
*
* CDRs have a lifecycle that is a subset of the channel that they reflect. A
* single CDR for a channel represents a path of communication between the
* endpoint behind a channel and Asterisk, or between two endpoints. When a
* channel establishes a new path of communication, a new CDR is created for the
* channel. Likewise, when a path of communication is terminated, a CDR is
* finalized. Finally, when a channel is no longer present in Asterisk, all CDRs
* for that channel are dispatched for recording.
*
* Dispatching of CDRs occurs to registered CDR backends. CDR backends register
* through \ref ast_cdr_register and are responsible for taking the produced
* CDRs and storing them in permanent storage.
*
* \par CDR attributes
*
* While a CDR can have many attributes, all CDRs have two parties: a Party A
* and a Party B. The Party A is \em always the channel that owns the CDR. A CDR
* may or may not have a Party B, depending on its state.
*
* For the most part, attributes on a CDR are reflective of those same
* attributes on the channel at the time when the CDR was finalized. Specific
* CDR attributes include:
* \li \c start The time when the CDR was created
* \li \c answer The time when the Party A was answered, or when the path of
* communication between Party A and Party B was established
* \li \c end The time when the CDR was finalized
* \li \c duration \c end - \c start. If \c end is not known, the current time
* is used
* \li \c billsec \c end - \c answer. If \c end is not known, the current time
* is used
* \li \c userfield User set data on some party in the CDR
*
* Note that \c accountcode and \c amaflags are actually properties of a
* channel, not the CDR.
*
* \par CDR States
*
* CDRs go through various states during their lifetime. State transitions occur
* due to messages received over the \ref stasis Stasis Message Bus. The
* following describes the possible states a CDR can be in, and how it
* transitions through the states.
*
* \par Single
*
* When a CDR is created, it is put into the Single state. The Single state
* represents a CDR for a channel that has no Party B. CDRs can be unanswered
* or answered while in the Single state.
*
* The following transitions can occur while in the Single state:
* \li If a \ref ast_channel_dial_type indicating a Dial Begin is received, the
* state transitions to Dial
* \li If a \ref ast_channel_snapshot is received indicating that the channel
* has hung up, the state transitions to Finalized
* \li If a \ref ast_bridge_blob_type is received indicating a Bridge Enter, the
* state transitions to Bridge
*
* \par Dial
*
* This state represents a dial that is occurring within Asterisk. The Party A
* can either be the caller for a two party dial, or it can be the dialed party
* if the calling party is Asterisk (that is, an Originated channel). In the
* first case, the Party B is \em always the dialed channel; in the second case,
* the channel is not considered to be a "dialed" channel as it is alone in the
* dialed operation.
*
* While in the Dial state, multiple CDRs can be created for the Party A if a
* parallel dial occurs. Each dialed party receives its own CDR with Party A.
*
* The following transitions can occur while in the Dial state:
* \li If a \ref ast_channel_dial_type indicating a Dial End is received where
* the \ref dial_status is not ANSWER, the state transitions to Finalized
* \li If a \ref ast_channel_snapshot is received indicating that the channel
* has hung up, the state transitions to Finalized
* \li If a \ref ast_channel_dial_type indicating a Dial End is received where
* the \ref dial_status is ANSWER, the state transitions to DialedPending
* \li If a \ref ast_bridge_blob_type is received indicating a Bridge Enter, the
* state transitions to Bridge
*
* \par DialedPending
*
* Technically, after being dialed, a CDR does not have to transition to the
* Bridge state. If the channel being dialed was originated, the channel may
* being executing dialplan. Strangely enough, it is also valid to have both
* Party A and Party B - after a dial - to not be bridged and instead execute
* dialplan. DialedPending handles the state where we figure out if the CDR
* showing the dial needs to move to the Bridge state; if the CDR should show
* that we started executing dialplan; of if we need a new CDR.
*
* The following transition can occur while in the DialedPending state:
* \li If a \ref ast_channel_snapshot is received that indicates that the
* channel has begun executing dialplan, we transition to the Finalized state
* if we have a Party B. Otherwise, we transition to the Single state.
* \li If a \ref ast_bridge_blob_type is received indicating a Bridge Enter, the
* state transitions to Bridge (through the Dial state)
*
* \par Bridge
*
* The Bridge state represents a path of communication between Party A and one
* or more other parties. When a CDR enters into the Bridge state, the following
* occurs:
* \li The CDR attempts to find a Party B. If the CDR has a Party B, it looks
* for that channel in the bridge and updates itself accordingly. If the CDR
* does not yet have a Party B, it attempts to find a channel that can be its
* Party B. If it finds one, it updates itself; otherwise, the CDR is
* temporarily finalized.
* \li Once the CDR has a Party B or it is determined that it cannot have a
* Party B, new CDRs are created for each pairing of channels with the CDR's
* Party A.
*
* As an example, consider the following:
* \li A Dials B - both answer
* \li B joins a bridge. Since no one is in the bridge and it was a dialed
* channel, it cannot have a Party B.
* \li A joins the bridge. Since A's Party B is B, A updates itself with B.
* \li Now say an Originated channel, C, joins the bridge. The bridge becomes
* a multi-party bridge.
* \li C attempts to get a Party B. A cannot be C's Party B, as it was created
* before it. B is a dialed channel and can thus be C's Party B, so C's CDR
* updates its Party B to B.
* \li New CDRs are now generated. A gets a new CDR for A -> C. B is dialed, and
* hence cannot get any CDR.
* \li Now say another Originated channel, D, joins the bridge. Say D has the
* \ref party_a flag set on it, such that it is always the preferred Party A.
* As such, it takes A as its Party B.
* \li New CDRs are generated. D gets new CDRs for D -> B and D -> C.
*
* The following transitions can occur while in the Bridge state:
* \li If a \ref ast_bridge_blob_type message indicating a leave is received,
* the state transitions to the Pending state
*
* \par Pending
*
* After a channel leaves a bridge, we often don't know what's going to happen
* to it. It can enter another bridge; it can be hung up; it can continue on
* in the dialplan. It can even enter into limbo! Pending holds the state of the
* CDR until we get a subsequent Stasis message telling us what should happen.
*
* The following transitions can occur while in the Pending state:
* \li If a \ref ast_bridge_blob_type message is received, a new CDR is created
* and it is transitioned to the Bridge state
* \li If a \ref ast_channel_dial_type indicating a Dial Begin is received, a
* new CDR is created and it is transitioned to the Dial state
* \li If a \ref ast_channel_cache_update is received indicating a change in
* Context/Extension/Priority, a new CDR is created and transitioned to the
* Single state. If the update indicates that the party has been hung up, the
* CDR is transitioned to the Finalized state.
*
* \par Finalized
*
* Once a CDR enters the finalized state, it is finished. No further updates
* can be made to the party information, and the CDR cannot be changed.
*
* One exception to this occurs during linkedid propagation, in which the CDRs
* linkedids are updated based on who the channel is bridged with. In general,
* however, a finalized CDR is waiting for dispatch to the CDR backends.
*/
/*! \brief CDR engine settings */
enum ast_cdr_settings {
CDR_ENABLED = 1 << 0, /*< Enable CDRs */
CDR_BATCHMODE = 1 << 1, /*< Whether or not we should dispatch CDRs in batches */
CDR_UNANSWERED = 1 << 2, /*< Log unanswered CDRs */
CDR_CONGESTION = 1 << 3, /*< Treat congestion as if it were a failed call */
CDR_END_BEFORE_H_EXTEN = 1 << 4, /*< End the CDR before the 'h' extension runs */
CDR_INITIATED_SECONDS = 1 << 5, /*< Include microseconds into the billing time */
CDR_DEBUG = 1 << 6, /*< Enables extra debug statements */
};
/*! \brief CDR Batch Mode settings */
enum ast_cdr_batch_mode_settings {
BATCH_MODE_SCHEDULER_ONLY = 1 << 0, /*< Don't spawn a thread to handle the batches - do it on the scheduler */
BATCH_MODE_SAFE_SHUTDOWN = 1 << 1, /*< During safe shutdown, submit the batched CDRs */
};
/*!
* \brief CDR Flags
* \brief CDR manipulation options. Certain function calls will manipulate the
* state of a CDR object based on these flags.
*/
enum {
AST_CDR_FLAG_KEEP_VARS = (1 << 0),
AST_CDR_FLAG_POSTED = (1 << 1),
AST_CDR_FLAG_LOCKED = (1 << 2),
AST_CDR_FLAG_CHILD = (1 << 3),
AST_CDR_FLAG_POST_DISABLED = (1 << 4),
AST_CDR_FLAG_BRIDGED = (1 << 5),
AST_CDR_FLAG_MAIN = (1 << 6),
AST_CDR_FLAG_ENABLE = (1 << 7),
AST_CDR_FLAG_ANSLOCKED = (1 << 8),
AST_CDR_FLAG_DONT_TOUCH = (1 << 9),
AST_CDR_FLAG_POST_ENABLE = (1 << 10),
AST_CDR_FLAG_DIALED = (1 << 11),
AST_CDR_FLAG_ORIGINATED = (1 << 12),
enum ast_cdr_options {
AST_CDR_FLAG_KEEP_VARS = (1 << 0), /*< Copy variables during the operation */
AST_CDR_FLAG_DISABLE = (1 << 1), /*< Disable the current CDR */
AST_CDR_FLAG_DISABLE_ALL = (3 << 1), /*< Disable the CDR and all future CDRs */
AST_CDR_FLAG_PARTY_A = (1 << 3), /*< Set the channel as party A */
AST_CDR_FLAG_FINALIZE = (1 << 4), /*< Finalize the current CDRs */
AST_CDR_FLAG_SET_ANSWER = (1 << 5), /*< If the channel is answered, set the answer time to now */
AST_CDR_FLAG_RESET = (1 << 6), /*< If set, set the start and answer time to now */
};
/*!
* \brief CDR Flags - Disposition
*/
enum {
enum ast_cdr_disposition {
AST_CDR_NOANSWER = 0,
AST_CDR_NULL = (1 << 0),
AST_CDR_FAILED = (1 << 1),
@ -61,22 +244,17 @@ enum {
AST_CDR_CONGESTION = (1 << 4),
};
/*!
* \brief CDR AMA Flags
*/
enum {
AST_CDR_OMIT = 1,
AST_CDR_BILLING = 2,
AST_CDR_DOCUMENTATION = 3,
/*! \brief The global options available for CDRs */
struct ast_cdr_config {
struct ast_flags settings; /*< CDR settings */
struct batch_settings {
unsigned int time; /*< Time between batches */
unsigned int size; /*< Size to trigger a batch */
struct ast_flags settings; /*< Settings for batches */
} batch_settings;
};
#define AST_MAX_USER_FIELD 256
#define AST_MAX_ACCOUNT_CODE 20
/* Include channel.h after relevant declarations it will need */
#include "asterisk/channel.h"
#include "asterisk/utils.h"
/*!
* \brief Responsible for call detail data
*/
@ -133,13 +311,133 @@ struct ast_cdr {
struct ast_cdr *next;
};
int ast_cdr_isset_unanswered(void);
int ast_cdr_isset_congestion(void);
void ast_cdr_getvar(struct ast_cdr *cdr, const char *name, char **ret, char *workspace, int workspacelen, int recur, int raw);
int ast_cdr_setvar(struct ast_cdr *cdr, const char *name, const char *value, int recur);
int ast_cdr_serialize_variables(struct ast_cdr *cdr, struct ast_str **buf, char delim, char sep, int recur);
void ast_cdr_free_vars(struct ast_cdr *cdr, int recur);
int ast_cdr_copy_vars(struct ast_cdr *to_cdr, struct ast_cdr *from_cdr);
/*!
* \since 12
* \brief Obtain the current CDR configuration
*
* The configuration is a ref counted object. The caller of this function must
* decrement the ref count when finished with the configuration.
*
* \retval NULL on error
* \retval The current CDR configuration
*/
struct ast_cdr_config *ast_cdr_get_config(void);
/*!
* \since 12
* \brief Set the current CDR configuration
*
* \param config The new CDR configuration
*/
void ast_cdr_set_config(struct ast_cdr_config *config);
/*!
* \since 12
* \brief Format a CDR variable from an already posted CDR
*
* \param cdr The dispatched CDR to process
* \param name The name of the variable
* \param ret Pointer to the formatted buffer
* \param workspace A pointer to the buffer to use to format the variable
* \param workspacelen The size of \ref workspace
* \param raw If non-zero and a date/time is extraced, provide epoch seconds. Otherwise format as a date/time stamp
*/
void ast_cdr_format_var(struct ast_cdr *cdr, const char *name, char **ret, char *workspace, int workspacelen, int raw);
/*!
* \since 12
* \brief Retrieve a CDR variable from a channel's current CDR
*
* \param channel_name The name of the party A channel that the CDR is associated with
* \param name The name of the variable to retrieve
* \param value Buffer to hold the value
* \param length The size of the buffer
*
* \retval 0 on success
* \retval non-zero on failure
*/
int ast_cdr_getvar(const char *channel_name, const char *name, char *value, size_t length);
/*!
* \since 12
* \brief Set a variable on a CDR
*
* \param channel_name The channel to set the variable on
* \param name The name of the variable to set
* \param value The value of the variable to set
*
* \retval 0 on success
* \retval non-zero on failure
*/
int ast_cdr_setvar(const char *channel_name, const char *name, const char *value);
/*!
* \since 12
* \brief Fork a CDR
*
* \param channel_name The name of the channel whose CDR should be forked
* \param options Options to control how the fork occurs.
*
* \retval 0 on success
* \retval -1 on failure
*/
int ast_cdr_fork(const char *channel_name, struct ast_flags *options);
/*!
* \since 12
* \brief Set a property on a CDR for a channel
*
* This function sets specific administrative properties on a CDR for a channel.
* This includes properties like preventing a CDR from being dispatched, to
* setting the channel as the preferred Party A in future CDRs. See
* \ref enum ast_cdr_options for more information.
*
* \param channel_name The CDR's channel
* \param option Option to apply to the CDR
*
* \retval 0 on success
* \retval 1 on error
*/
int ast_cdr_set_property(const char *channel_name, enum ast_cdr_options option);
/*!
* \since 12
* \brief Clear a property on a CDR for a channel
*
* Clears a flag previously set by \ref ast_cdr_set_property
*
* \param channel_name The CDR's channel
* \param option Option to clear from the CDR
*
* \retval 0 on success
* \retval 1 on error
*/
int ast_cdr_clear_property(const char *channel_name, enum ast_cdr_options option);
/*!
* \brief Reset the detail record
* \param channel_name The channel that the CDR is associated with
* \param options Options that control what the reset operation does.
*
* Valid options are:
* \ref AST_CDR_FLAG_KEEP_VARS - keep the variables during the reset
* \ref AST_CDR_FLAG_DISABLE_ALL - when used with \ref ast_cdr_reset, re-enables
* the CDR
*
* \retval 0 on success
* \retval -1 on failure
*/
int ast_cdr_reset(const char *channel_name, struct ast_flags *options);
/*!
* \brief Serializes all the data and variables for a current CDR record
* \param channel_name The channel to get the CDR for
* \param buf A buffer to use for formatting the data
* \param delim A delimeter to use to separate variable keys/values
* \param sep A separator to use between nestings
* \retval the total number of serialized variables
*/
int ast_cdr_serialize_variables(const char *channel_name, struct ast_str **buf, char delim, char sep);
/*!
* \brief CDR backend callback
@ -150,7 +448,7 @@ int ast_cdr_copy_vars(struct ast_cdr *to_cdr, struct ast_cdr *from_cdr);
typedef int (*ast_cdrbe)(struct ast_cdr *cdr);
/*! \brief Return TRUE if CDR subsystem is enabled */
int check_cdr_enabled(void);
int ast_cdr_is_enabled(void);
/*!
* \brief Allocate a CDR record
@ -159,38 +457,13 @@ int check_cdr_enabled(void);
*/
struct ast_cdr *ast_cdr_alloc(void);
/*!
* \brief Duplicate a record and increment the sequence number.
* \param cdr the record to duplicate
* \retval a malloc'd ast_cdr structure,
* \retval NULL on error (malloc failure)
* \see ast_cdr_dup()
* \see ast_cdr_dup_unique_swap()
*/
struct ast_cdr *ast_cdr_dup_unique(struct ast_cdr *cdr);
/*!
* \brief Duplicate a record and increment the sequence number of the old
* record.
* \brief Duplicate a public CDR
* \param cdr the record to duplicate
* \retval a malloc'd ast_cdr structure,
* \retval NULL on error (malloc failure)
* \note This version increments the original CDR's sequence number rather than
* the duplicate's sequence number. The effect is as if the original CDR's
* sequence number was swapped with the duplicate's sequence number.
*
* \see ast_cdr_dup()
* \see ast_cdr_dup_unique()
*/
struct ast_cdr *ast_cdr_dup_unique_swap(struct ast_cdr *cdr);
/*!
* \brief Duplicate a record
* \param cdr the record to duplicate
* \retval a malloc'd ast_cdr structure,
* \retval NULL on error (malloc failure)
* \see ast_cdr_dup_unique()
* \see ast_cdr_dup_unique_swap()
*/
struct ast_cdr *ast_cdr_dup(struct ast_cdr *cdr);
@ -201,33 +474,6 @@ struct ast_cdr *ast_cdr_dup(struct ast_cdr *cdr);
*/
void ast_cdr_free(struct ast_cdr *cdr);
/*!
* \brief Discard and free a CDR record
* \param cdr ast_cdr structure to free
* Returns nothing -- same as free, but no checks or complaints
*/
void ast_cdr_discard(struct ast_cdr *cdr);
/*!
* \brief Initialize based on a channel
* \param cdr Call Detail Record to use for channel
* \param chan Channel to bind CDR with
* Initializes a CDR and associates it with a particular channel
* \note The channel should be locked before calling.
* \return 0 by default
*/
int ast_cdr_init(struct ast_cdr *cdr, struct ast_channel *chan);
/*!
* \brief Initialize based on a channel
* \param cdr Call Detail Record to use for channel
* \param chan Channel to bind CDR with
* Initializes a CDR and associates it with a particular channel
* \note The channel should be locked before calling.
* \return 0 by default
*/
int ast_cdr_setcid(struct ast_cdr *cdr, struct ast_channel *chan);
/*!
* \brief Register a CDR handling engine
* \param name name associated with the particular CDR handler
@ -246,218 +492,21 @@ int ast_cdr_register(const char *name, const char *desc, ast_cdrbe be);
*/
void ast_cdr_unregister(const char *name);
/*!
* \brief Start a call
* \param cdr the cdr you wish to associate with the call
* Starts all CDR stuff necessary for monitoring a call
* Returns nothing
*/
void ast_cdr_start(struct ast_cdr *cdr);
/*! \brief Answer a call
* \param cdr the cdr you wish to associate with the call
* Starts all CDR stuff necessary for doing CDR when answering a call
* \note NULL argument is just fine.
*/
void ast_cdr_answer(struct ast_cdr *cdr);
/*!
* \brief A call wasn't answered
* \param cdr the cdr you wish to associate with the call
* Marks the channel disposition as "NO ANSWER"
* Will skip CDR's in chain with ANS_LOCK bit set. (see
* forkCDR() application.
*/
extern void ast_cdr_noanswer(struct ast_cdr *cdr);
/*!
* \brief A call was set to congestion
* \param cdr the cdr you wish to associate with the call
* Markst he channel disposition as "CONGESTION"
* Will skip CDR's in chain with ANS_LOCK bit set. (see
* forkCDR() application
*/
extern void ast_cdr_congestion(struct ast_cdr *cdr);
/*!
* \brief Busy a call
* \param cdr the cdr you wish to associate with the call
* Marks the channel disposition as "BUSY"
* Will skip CDR's in chain with ANS_LOCK bit set. (see
* forkCDR() application.
* Returns nothing
*/
void ast_cdr_busy(struct ast_cdr *cdr);
/*!
* \brief Fail a call
* \param cdr the cdr you wish to associate with the call
* Marks the channel disposition as "FAILED"
* Will skip CDR's in chain with ANS_LOCK bit set. (see
* forkCDR() application.
* Returns nothing
*/
void ast_cdr_failed(struct ast_cdr *cdr);
/*!
* \brief Save the result of the call based on the AST_CAUSE_*
* \param cdr the cdr you wish to associate with the call
* \param cause the AST_CAUSE_*
* Returns nothing
*/
int ast_cdr_disposition(struct ast_cdr *cdr, int cause);
/*!
* \brief End a call
* \param cdr the cdr you have associated the call with
* Registers the end of call time in the cdr structure.
* Returns nothing
*/
void ast_cdr_end(struct ast_cdr *cdr);
/*!
* \brief Detaches the detail record for posting (and freeing) either now or at a
* later time in bulk with other records during batch mode operation.
* \param cdr Which CDR to detach from the channel thread
* Prevents the channel thread from blocking on the CDR handling
* Returns nothing
*/
void ast_cdr_detach(struct ast_cdr *cdr);
/*!
* \brief Spawns (possibly) a new thread to submit a batch of CDRs to the backend engines
* \param shutdown Whether or not we are shutting down
* Blocks the asterisk shutdown procedures until the CDR data is submitted.
* Returns nothing
*/
void ast_cdr_submit_batch(int shutdown);
/*!
* \brief Set the destination channel, if there was one
* \param cdr Which cdr it's applied to
* \param chan Channel to which dest will be
* Sets the destination channel the CDR is applied to
* Returns nothing
*/
void ast_cdr_setdestchan(struct ast_cdr *cdr, const char *chan);
/*!
* \brief Set the last executed application
* \param cdr which cdr to act upon
* \param app the name of the app you wish to change it to
* \param data the data you want in the data field of app you set it to
* Changes the value of the last executed app
* Returns nothing
*/
void ast_cdr_setapp(struct ast_cdr *cdr, const char *app, const char *data);
/*!
* \brief Set the answer time for a call
* \param cdr the cdr you wish to associate with the call
* \param t the answer time
* Starts all CDR stuff necessary for doing CDR when answering a call
* NULL argument is just fine.
*/
void ast_cdr_setanswer(struct ast_cdr *cdr, struct timeval t);
/*!
* \brief Set the disposition for a call
* \param cdr the cdr you wish to associate with the call
* \param disposition the new disposition
* Set the disposition on a call.
* NULL argument is just fine.
*/
void ast_cdr_setdisposition(struct ast_cdr *cdr, long int disposition);
/*!
* \brief Convert a string to a detail record AMA flag
* \param flag string form of flag
* Converts the string form of the flag to the binary form.
* \return the binary form of the flag
*/
int ast_cdr_amaflags2int(const char *flag);
/*!
* \brief Disposition to a string
* \param disposition input binary form
* Converts the binary form of a disposition to string form.
* \return a pointer to the string form
*/
char *ast_cdr_disp2str(int disposition);
/*!
* \brief Reset the detail record, optionally posting it first
* \param cdr which cdr to act upon
* \param flags |AST_CDR_FLAG_POSTED whether or not to post the cdr first before resetting it
* |AST_CDR_FLAG_LOCKED whether or not to reset locked CDR's
*/
void ast_cdr_reset(struct ast_cdr *cdr, struct ast_flags *flags);
/*! Reset the detail record times, flags */
/*!
* \param cdr which cdr to act upon
* \param flags |AST_CDR_FLAG_POSTED whether or not to post the cdr first before resetting it
* |AST_CDR_FLAG_LOCKED whether or not to reset locked CDR's
*/
void ast_cdr_specialized_reset(struct ast_cdr *cdr, struct ast_flags *flags);
/*! Flags to a string */
/*!
* \param flags binary flag
* Converts binary flags to string flags
* Returns string with flag name
*/
char *ast_cdr_flags2str(int flags);
/*!
* \brief Move the non-null data from the "from" cdr to the "to" cdr
* \param to the cdr to get the goodies
* \param from the cdr to give the goodies
*/
void ast_cdr_merge(struct ast_cdr *to, struct ast_cdr *from);
/*!
* \brief Set account code, will generate AMI event
* \note The channel should be locked before calling.
*/
int ast_cdr_setaccount(struct ast_channel *chan, const char *account);
/*!
* \brief Set the peer account
* \note The channel should be locked before calling.
*/
int ast_cdr_setpeeraccount(struct ast_channel *chan, const char *account);
/*!
* \brief Set AMA flags for channel
* \note The channel should be locked before calling.
*/
int ast_cdr_setamaflags(struct ast_channel *chan, const char *amaflags);
const char *ast_cdr_disp2str(int disposition);
/*!
* \brief Set CDR user field for channel (stored in CDR)
* \note The channel should be locked before calling.
*
* \param channel_name The name of the channel that owns the CDR
* \param userfield The user field to set
*/
int ast_cdr_setuserfield(struct ast_channel *chan, const char *userfield);
/*!
* \brief Append to CDR user field for channel (stored in CDR)
* \note The channel should be locked before calling.
*/
int ast_cdr_appenduserfield(struct ast_channel *chan, const char *userfield);
/*!
* \brief Update CDR on a channel
* \note The channel should be locked before calling.
*/
int ast_cdr_update(struct ast_channel *chan);
extern int ast_default_amaflags;
extern char ast_default_accountcode[AST_MAX_ACCOUNT_CODE];
struct ast_cdr *ast_cdr_append(struct ast_cdr *cdr, struct ast_cdr *newcdr);
void ast_cdr_setuserfield(const char *channel_name, const char *userfield);
/*! \brief Reload the configuration file cdr.conf and start/stop CDR scheduling thread */
int ast_cdr_engine_reload(void);
@ -468,14 +517,4 @@ int ast_cdr_engine_init(void);
/*! Submit any remaining CDRs and prepare for shutdown */
void ast_cdr_engine_term(void);
/*!
* \brief
* \param[in] tree Where to insert the cdr.
* \param[in] cdr The cdr structure to insert in 'tree'.
* \param[in] recur Go throw all the cdr levels.
* \retval <0 on error.
* \retval 0 on success.
*/
int ast_cdr_data_add_structure(struct ast_data *tree, struct ast_cdr *cdr, int recur);
#endif /* _ASTERISK_CDR_H */

View File

@ -35,20 +35,6 @@ extern "C" {
#include "asterisk/event.h"
/*!
* \brief AMA Flags
*
* \note This must much up with the AST_CDR_* defines for AMA flags.
*/
enum ast_cel_ama_flag {
AST_CEL_AMA_FLAG_NONE,
AST_CEL_AMA_FLAG_OMIT,
AST_CEL_AMA_FLAG_BILLING,
AST_CEL_AMA_FLAG_DOCUMENTATION,
/*! \brief Must be final entry */
AST_CEL_AMA_FLAG_TOTAL,
};
/*!
* \brief CEL event types
*/
@ -162,17 +148,6 @@ const char *ast_cel_get_type_name(enum ast_cel_event_type type);
*/
enum ast_cel_event_type ast_cel_str_to_event_type(const char *name);
/*!
* \brief Convert AMA flag to printable string
*
* \param[in] flag the flag to convert to a string
*
* \since 1.8
*
* \return the string representation of the flag
*/
const char *ast_cel_get_ama_flag_name(enum ast_cel_ama_flag flag);
/*!
* \brief Check and potentially retire a Linked ID
*

View File

@ -131,11 +131,13 @@ References:
extern "C" {
#endif
#define AST_MAX_EXTENSION 80 /*!< Max length of an extension */
#define AST_MAX_CONTEXT 80 /*!< Max length of a context */
#define AST_CHANNEL_NAME 80 /*!< Max length of an ast_channel name */
#define MAX_LANGUAGE 40 /*!< Max length of the language setting */
#define MAX_MUSICCLASS 80 /*!< Max length of the music class setting */
#define AST_MAX_EXTENSION 80 /*!< Max length of an extension */
#define AST_MAX_CONTEXT 80 /*!< Max length of a context */
#define AST_MAX_ACCOUNT_CODE 20 /*!< Max length of an account code */
#define AST_CHANNEL_NAME 80 /*!< Max length of an ast_channel name */
#define MAX_LANGUAGE 40 /*!< Max length of the language setting */
#define MAX_MUSICCLASS 80 /*!< Max length of the music class setting */
#define AST_MAX_USER_FIELD 256 /*!< Max length of the channel user field */
#include "asterisk/frame.h"
#include "asterisk/chanvars.h"
@ -915,6 +917,10 @@ enum {
* to continue.
*/
AST_FLAG_BRIDGE_DUAL_REDIRECT_WAIT = (1 << 22),
/*!
* This flag indicates that the channel was originated.
*/
AST_FLAG_ORIGINATED = (1 << 23),
};
/*! \brief ast_bridge_config flags */
@ -1027,6 +1033,16 @@ enum channelreloadreason {
CHANNEL_ACL_RELOAD,
};
/*!
* \brief Channel AMA Flags
*/
enum ama_flags {
AST_AMA_NONE = 0,
AST_AMA_OMIT,
AST_AMA_BILLING,
AST_AMA_DOCUMENTATION,
};
/*!
* \note None of the datastore API calls lock the ast_channel they are using.
* So, the channel should be locked before calling the functions that
@ -1100,7 +1116,7 @@ struct ast_channel * attribute_malloc __attribute__((format(printf, 13, 14)))
__ast_channel_alloc(int needqueue, int state, const char *cid_num,
const char *cid_name, const char *acctcode,
const char *exten, const char *context,
const char *linkedid, const int amaflag,
const char *linkedid, enum ama_flags amaflag,
const char *file, int line, const char *function,
const char *name_fmt, ...);
@ -1591,7 +1607,6 @@ int ast_answer(struct ast_channel *chan);
* \brief Answer a channel
*
* \param chan channel to answer
* \param cdr_answer flag to control whether any associated CDR should be marked as 'answered'
*
* This function answers a channel and handles all necessary call
* setup functions.
@ -1607,14 +1622,13 @@ int ast_answer(struct ast_channel *chan);
* \retval 0 on success
* \retval non-zero on failure
*/
int ast_raw_answer(struct ast_channel *chan, int cdr_answer);
int ast_raw_answer(struct ast_channel *chan);
/*!
* \brief Answer a channel, with a selectable delay before returning
*
* \param chan channel to answer
* \param delay maximum amount of time to wait for incoming media
* \param cdr_answer flag to control whether any associated CDR should be marked as 'answered'
*
* This function answers a channel and handles all necessary call
* setup functions.
@ -1630,7 +1644,7 @@ int ast_raw_answer(struct ast_channel *chan, int cdr_answer);
* \retval 0 on success
* \retval non-zero on failure
*/
int __ast_answer(struct ast_channel *chan, unsigned int delay, int cdr_answer);
int __ast_answer(struct ast_channel *chan, unsigned int delay);
/*!
* \brief Execute a Gosub call on the channel before a call is placed.
@ -2196,6 +2210,28 @@ int ast_activate_generator(struct ast_channel *chan, struct ast_generator *gen,
/*! Deactivate an active generator */
void ast_deactivate_generator(struct ast_channel *chan);
/*!
* \since 12
* \brief Obtain how long the channel since the channel was created
*
* \param chan The channel object
*
* \retval 0 if the time value cannot be computed (or you called this really fast)
* \retval The number of seconds the channel has been up
*/
int ast_channel_get_duration(struct ast_channel *chan);
/*!
* \since 12
* \brief Obtain how long it has been since the channel was answered
*
* \param chan The channel object
*
* \retval 0 if the channel isn't answered (or you called this really fast)
* \retval The number of seconds the channel has been up
*/
int ast_channel_get_up_time(struct ast_channel *chan);
/*!
* \brief Set caller ID number, name and ANI and generate AMI event.
*
@ -2727,12 +2763,6 @@ struct ast_channel *ast_channel_get_by_exten(const char *exten, const char *cont
/*! @} End channel search functions. */
/*!
\brief propagate the linked id between chan and peer
*/
void ast_channel_set_linkgroup(struct ast_channel *chan, struct ast_channel *peer);
/*!
* \brief Initialize the given name structure.
* \since 1.8
@ -3806,6 +3836,26 @@ void ast_channel_unlink(struct ast_channel *chan);
*/
void ast_channel_hangupcause_hash_set(struct ast_channel *chan, const struct ast_control_pvt_cause_code *cause_code, int datalen);
/*!
* \since 12
* \brief Convert a string to a detail record AMA flag
*
* \param flag string form of flag
*
* \retval the enum (integer) form of the flag
*/
enum ama_flags ast_channel_string2amaflag(const char *flag);
/*!
* \since 12
* \brief Convert the enum representation of an AMA flag to a string representation
*
* \param flags integer flag
*
* \retval A string representation of the flag
*/
const char *ast_channel_amaflags2string(enum ama_flags flags);
/* ACCESSOR FUNTIONS */
/*! \brief Set the channel name */
void ast_channel_name_set(struct ast_channel *chan, const char *name);
@ -3863,8 +3913,8 @@ char ast_channel_sending_dtmf_digit(const struct ast_channel *chan);
void ast_channel_sending_dtmf_digit_set(struct ast_channel *chan, char value);
struct timeval ast_channel_sending_dtmf_tv(const struct ast_channel *chan);
void ast_channel_sending_dtmf_tv_set(struct ast_channel *chan, struct timeval value);
int ast_channel_amaflags(const struct ast_channel *chan);
void ast_channel_amaflags_set(struct ast_channel *chan, int value);
enum ama_flags ast_channel_amaflags(const struct ast_channel *chan);
void ast_channel_amaflags_set(struct ast_channel *chan, enum ama_flags value);
int ast_channel_epfd(const struct ast_channel *chan);
void ast_channel_epfd_set(struct ast_channel *chan, int value);
int ast_channel_fdno(const struct ast_channel *chan);
@ -3988,6 +4038,8 @@ void ast_channel_whentohangup_set(struct ast_channel *chan, struct timeval *valu
void ast_channel_varshead_set(struct ast_channel *chan, struct varshead *value);
struct timeval ast_channel_creationtime(struct ast_channel *chan);
void ast_channel_creationtime_set(struct ast_channel *chan, struct timeval *value);
struct timeval ast_channel_answertime(struct ast_channel *chan);
void ast_channel_answertime_set(struct ast_channel *chan, struct timeval *value);
/* List getters */
struct ast_hangup_handler_list *ast_channel_hangup_handlers(struct ast_channel *chan);
@ -4278,4 +4330,27 @@ int ast_channel_move(struct ast_channel *dest, struct ast_channel *source);
*/
int ast_channel_forward_endpoint(struct ast_channel *chan, struct ast_endpoint *endpoint);
/*!
* \brief Return the oldest linkedid between two channels.
*
* A channel linkedid is derived from the channel uniqueid which is formed like this:
* [systemname-]ctime.seq
*
* The systemname, and the dash are optional, followed by the epoch time followed by an
* integer sequence. Note that this is not a decimal number, since 1.2 is less than 1.11
* in uniqueid land.
*
* To compare two uniqueids, we parse out the integer values of the time and the sequence
* numbers and compare them, with time trumping sequence.
*
* \param a The linkedid value of the first channel to compare
* \param b The linkedid value of the second channel to compare
*
* \retval NULL on failure
* \retval The oldest linkedid value
* \since 12.0.0
*/
const char *ast_channel_oldest_linkedid(const char *a, const char *b);
#endif /* _ASTERISK_CHANNEL_H */

View File

@ -18,8 +18,8 @@
* \brief Internal channel functions for channel.c to use
*/
#define ast_channel_internal_alloc(destructor) __ast_channel_internal_alloc(destructor, __FILE__, __LINE__, __PRETTY_FUNCTION__)
struct ast_channel *__ast_channel_internal_alloc(void (*destructor)(void *obj), const char *file, int line, const char *function);
#define ast_channel_internal_alloc(destructor, linkedid) __ast_channel_internal_alloc(destructor, linkedid, __FILE__, __LINE__, __PRETTY_FUNCTION__)
struct ast_channel *__ast_channel_internal_alloc(void (*destructor)(void *obj), const char *linkedid, const char *file, int line, const char *function);
void ast_channel_internal_finalize(struct ast_channel *chan);
int ast_channel_internal_is_finalized(struct ast_channel *chan);
void ast_channel_internal_cleanup(struct ast_channel *chan);

View File

@ -38,36 +38,38 @@
*/
struct ast_channel_snapshot {
AST_DECLARE_STRING_FIELDS(
AST_STRING_FIELD(name); /*!< ASCII unique channel name */
AST_STRING_FIELD(accountcode); /*!< Account code for billing */
AST_STRING_FIELD(peeraccount); /*!< Peer account code for billing */
AST_STRING_FIELD(userfield); /*!< Userfield for CEL billing */
AST_STRING_FIELD(uniqueid); /*!< Unique Channel Identifier */
AST_STRING_FIELD(linkedid); /*!< Linked Channel Identifier -- gets propagated by linkage */
AST_STRING_FIELD(parkinglot); /*!< Default parking lot, if empty, default parking lot */
AST_STRING_FIELD(hangupsource); /*!< Who is responsible for hanging up this channel */
AST_STRING_FIELD(appl); /*!< Current application */
AST_STRING_FIELD(data); /*!< Data passed to current application */
AST_STRING_FIELD(context); /*!< Dialplan: Current extension context */
AST_STRING_FIELD(exten); /*!< Dialplan: Current extension number */
AST_STRING_FIELD(caller_name); /*!< Caller ID Name */
AST_STRING_FIELD(caller_number); /*!< Caller ID Number */
AST_STRING_FIELD(caller_ani); /*!< Caller ID ANI Number */
AST_STRING_FIELD(caller_rdnis); /*!< Caller ID RDNIS Number */
AST_STRING_FIELD(caller_dnid); /*!< Caller ID DNID Number */
AST_STRING_FIELD(connected_name); /*!< Connected Line Name */
AST_STRING_FIELD(connected_number); /*!< Connected Line Number */
AST_STRING_FIELD(language); /*!< The default spoken language for the channel */
AST_STRING_FIELD(name); /*!< ASCII unique channel name */
AST_STRING_FIELD(accountcode); /*!< Account code for billing */
AST_STRING_FIELD(peeraccount); /*!< Peer account code for billing */
AST_STRING_FIELD(userfield); /*!< Userfield for CEL billing */
AST_STRING_FIELD(uniqueid); /*!< Unique Channel Identifier */
AST_STRING_FIELD(linkedid); /*!< Linked Channel Identifier -- gets propagated by linkage */
AST_STRING_FIELD(parkinglot); /*!< Default parking lot, if empty, default parking lot */
AST_STRING_FIELD(hangupsource); /*!< Who is responsible for hanging up this channel */
AST_STRING_FIELD(appl); /*!< Current application */
AST_STRING_FIELD(data); /*!< Data passed to current application */
AST_STRING_FIELD(context); /*!< Dialplan: Current extension context */
AST_STRING_FIELD(exten); /*!< Dialplan: Current extension number */
AST_STRING_FIELD(caller_name); /*!< Caller ID Name */
AST_STRING_FIELD(caller_number); /*!< Caller ID Number */
AST_STRING_FIELD(caller_dnid); /*!< Dialed ID Number */
AST_STRING_FIELD(caller_ani); /*< Caller ID ANI Number */
AST_STRING_FIELD(caller_rdnis); /*!< Caller ID RDNIS Number */
AST_STRING_FIELD(caller_subaddr); /*!< Caller subaddress */
AST_STRING_FIELD(dialed_subaddr); /*!< Dialed subaddress */
AST_STRING_FIELD(connected_name); /*!< Connected Line Name */
AST_STRING_FIELD(connected_number); /*!< Connected Line Number */
AST_STRING_FIELD(language); /*!< The default spoken language for the channel */
);
struct timeval creationtime; /*!< The time of channel creation */
enum ast_channel_state state; /*!< State of line */
int priority; /*!< Dialplan: Current extension priority */
int amaflags; /*!< AMA flags for billing */
int hangupcause; /*!< Why is the channel hanged up. See causes.h */
int caller_pres; /*!< Caller ID presentation. */
struct ast_flags flags; /*!< channel flags of AST_FLAG_ type */
struct varshead *manager_vars; /*!< Variables to be appended to manager events */
struct timeval creationtime; /*!< The time of channel creation */
enum ast_channel_state state; /*!< State of line */
int priority; /*!< Dialplan: Current extension priority */
int amaflags; /*!< AMA flags for billing */
int hangupcause; /*!< Why is the channel hanged up. See causes.h */
int caller_pres; /*!< Caller ID presentation. */
struct ast_flags flags; /*!< channel flags of AST_FLAG_ type */
struct varshead *manager_vars; /*!< Variables to be appended to manager events */
};
/*!
@ -300,7 +302,7 @@ void ast_channel_publish_snapshot(struct ast_channel *chan);
* \since 12
* \brief Publish a \ref ast_channel_varset for a channel.
*
* \param chan Channel to pulish the event for, or \c NULL for 'none'.
* \param chan Channel to publish the event for, or \c NULL for 'none'.
* \param variable Name of the variable being set
* \param value Value.
*/

View File

@ -0,0 +1,69 @@
/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 2013, Digium, Inc.
*
* Matt Jordan <mjordan@digium.com>
*
* 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.
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
#ifndef STASIS_INTERNAL_H_
#define STASIS_INTERNAL_H_
/*! \file
*
* \brief Internal Stasis APIs.
*
* This header file is used to define functions that are shared between files that make
* up \ref stasis. Functions declared here should not be used by any module outside of
* Stasis.
*
* If you find yourself needing to call one of these functions directly, something has
* probably gone horribly wrong.
*
* \author Matt Jordan <mjordan@digium.com>
*/
struct stasis_topic;
struct stasis_subscription;
struct stasis_message;
/*!
* \brief Create a subscription.
*
* In addition to being AO2 managed memory (requiring an ao2_cleanup() to free
* up this reference), the subscription must be explicitly unsubscribed from its
* topic using stasis_unsubscribe().
*
* The invocations of the callback are serialized, but may not always occur on
* the same thread. The invocation order of different subscriptions is
* unspecified.
*
* Note: modules outside of Stasis should use \ref stasis_subscribe.
*
* \param topic Topic to subscribe to.
* \param callback Callback function for subscription messages.
* \param data Data to be passed to the callback, in addition to the message.
* \param needs_mailbox Determines whether or not the subscription requires a mailbox.
* Subscriptions with mailboxes will be delivered on a thread in the Stasis threadpool;
* subscriptions without mailboxes will be delivered on the publisher thread.
* \return New \ref stasis_subscription object.
* \return \c NULL on error.
* \since 12
*/
struct stasis_subscription *internal_stasis_subscribe(
struct stasis_topic *topic,
void (*stasis_subscription_cb)(void *data, struct stasis_subscription *sub, struct stasis_topic *topic, struct stasis_message *message),
void *data,
int needs_mailbox);
#endif /* STASIS_INTERNAL_H_ */

View File

@ -238,7 +238,9 @@ struct ast_test_info {
/*!
* \brief Generic test callback function
*
* \param error buffer string for failure results
* \param info The test info object
* \param cmd What to perform in the test
* \param test The actual test object being manipulated
*
* \retval AST_TEST_PASS for pass
* \retval AST_TEST_FAIL for failure
@ -246,6 +248,30 @@ struct ast_test_info {
typedef enum ast_test_result_state (ast_test_cb_t)(struct ast_test_info *info,
enum ast_test_command cmd, struct ast_test *test);
/*!
* \since 12
* \brief A test initialization callback function
*
* \param info The test info object
* \param test The actual test object that will be manipulated
*
* \retval 0 success
* \retval other failure. This will fail the test.
*/
typedef int (ast_test_init_cb_t)(struct ast_test_info *info, struct ast_test *test);
/*!
* \since 12
* \brief A test cleanup callback function
*
* \param info The test info object
* \param test The actual test object that was executed
*
* \retval 0 success
* \retval other failure. This will fail the test.
*/
typedef int (ast_test_cleanup_cb_t)(struct ast_test_info *info, struct ast_test *test);
/*!
* \brief unregisters a test with the test framework
*
@ -266,6 +292,40 @@ int ast_test_unregister(ast_test_cb_t *cb);
*/
int ast_test_register(ast_test_cb_t *cb);
/*!
* \since 12
* \brief Register an initialization function to be run before each test
* executes
*
* This function lets a registered test have an initialization function that
* will be run prior to test execution. Each category may have a single init
* function.
*
* If the initialization function returns a non-zero value, the test will not
* be executed and the result will be set to \ref AST_TEST_FAIL.
*
* \retval 0 success
* \retval other failure
*/
int ast_test_register_init(const char *category, ast_test_init_cb_t *cb);
/*!
* \since 12
* \brief Register a cleanup function to be run after each test executes
*
* This function lets a registered test have a cleanup function that will be
* run immediately after test execution. Each category may have a single
* cleanup function.
*
* If the cleanup function returns a non-zero value, the test result will be
* set to \ref AST_TEST_FAIL.
*
* \retval 0 success
* \retval other failure
*/
int ast_test_register_cleanup(const char *category, ast_test_cleanup_cb_t *cb);
/*!
* \brief Unit test debug output.
* \since 12.0.0
@ -277,6 +337,17 @@ int ast_test_register(ast_test_cb_t *cb);
*/
void ast_test_debug(struct ast_test *test, const char *fmt, ...) __attribute__((format(printf, 2, 3)));
/*!
* \brief Set the result of a test.
*
* If the caller of this function sets the result to AST_TEST_FAIL, returning
* AST_TEST_PASS from the test will not pass the test. This lets a test writer
* end and fail a test and continue on with logic, catching multiple failure
* conditions within a single test.
*/
void ast_test_set_result(struct ast_test *test, enum ast_test_result_state state);
/*!
* \brief update test's status during testing.
*

View File

@ -151,6 +151,17 @@ struct timeval ast_tvadd(struct timeval a, struct timeval b);
*/
struct timeval ast_tvsub(struct timeval a, struct timeval b);
/*!
* \since 12
* \brief Formats a duration into HH:MM:SS
*
* \param duration The time (in seconds) to format
* \param buf A buffer to hold the formatted string'
* \param length The size of the buffer
*/
void ast_format_duration_hh_mm_ss(int duration, char *buf, size_t length);
/*!
* \brief Calculate remaining milliseconds given a starting timestamp
* and upper bound

View File

@ -643,7 +643,7 @@ static char *handle_show_settings(struct ast_cli_entry *e, int cmd, struct ast_c
ast_cli(a->fd, " -------------\n");
ast_cli(a->fd, " Manager (AMI): %s\n", check_manager_enabled() ? "Enabled" : "Disabled");
ast_cli(a->fd, " Web Manager (AMI/HTTP): %s\n", check_webmanager_enabled() ? "Enabled" : "Disabled");
ast_cli(a->fd, " Call data records: %s\n", check_cdr_enabled() ? "Enabled" : "Disabled");
ast_cli(a->fd, " Call data records: %s\n", ast_cdr_is_enabled() ? "Enabled" : "Disabled");
ast_cli(a->fd, " Realtime Architecture (ARA): %s\n", ast_realtime_enabled() ? "Enabled" : "Disabled");
/*! \todo we could check musiconhold, voicemail, smdi, adsi, queues */
@ -4318,6 +4318,21 @@ int main(int argc, char *argv[])
ast_http_init(); /* Start the HTTP server, if needed */
if (ast_indications_init()) {
printf("%s", term_quit());
exit(1);
}
if (ast_features_init()) {
printf("%s", term_quit());
exit(1);
}
if (ast_bridging_init()) {
printf("%s", term_quit());
exit(1);
}
if (ast_cdr_engine_init()) {
printf("%s", term_quit());
exit(1);
@ -4351,21 +4366,6 @@ int main(int argc, char *argv[])
exit(1);
}
if (ast_indications_init()) {
printf("%s", term_quit());
exit(1);
}
if (ast_features_init()) {
printf("%s", term_quit());
exit(1);
}
if (ast_bridging_init()) {
printf("%s", term_quit());
exit(1);
}
if (ast_parking_stasis_init()) {
printf("%s", term_quit());
exit(1);

View File

@ -292,6 +292,75 @@ int ast_bridge_queue_action(struct ast_bridge *bridge, struct ast_frame *action)
return 0;
}
void ast_bridge_update_accountcodes(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, struct ast_bridge_channel *swap)
{
struct ast_bridge_channel *other = NULL;
AST_LIST_TRAVERSE(&bridge->channels, other, entry) {
if (other == swap) {
continue;
}
if (!ast_strlen_zero(ast_channel_accountcode(bridge_channel->chan)) && ast_strlen_zero(ast_channel_peeraccount(other->chan))) {
ast_debug(1, "Setting peeraccount to %s for %s from data on channel %s\n",
ast_channel_accountcode(bridge_channel->chan), ast_channel_name(other->chan), ast_channel_name(bridge_channel->chan));
ast_channel_peeraccount_set(other->chan, ast_channel_accountcode(bridge_channel->chan));
}
if (!ast_strlen_zero(ast_channel_accountcode(other->chan)) && ast_strlen_zero(ast_channel_peeraccount(bridge_channel->chan))) {
ast_debug(1, "Setting peeraccount to %s for %s from data on channel %s\n",
ast_channel_accountcode(other->chan), ast_channel_name(bridge_channel->chan), ast_channel_name(other->chan));
ast_channel_peeraccount_set(bridge_channel->chan, ast_channel_accountcode(other->chan));
}
if (!ast_strlen_zero(ast_channel_peeraccount(bridge_channel->chan)) && ast_strlen_zero(ast_channel_accountcode(other->chan))) {
ast_debug(1, "Setting accountcode to %s for %s from data on channel %s\n",
ast_channel_peeraccount(bridge_channel->chan), ast_channel_name(other->chan), ast_channel_name(bridge_channel->chan));
ast_channel_accountcode_set(other->chan, ast_channel_peeraccount(bridge_channel->chan));
}
if (!ast_strlen_zero(ast_channel_peeraccount(other->chan)) && ast_strlen_zero(ast_channel_accountcode(bridge_channel->chan))) {
ast_debug(1, "Setting accountcode to %s for %s from data on channel %s\n",
ast_channel_peeraccount(other->chan), ast_channel_name(bridge_channel->chan), ast_channel_name(other->chan));
ast_channel_accountcode_set(bridge_channel->chan, ast_channel_peeraccount(other->chan));
}
if (bridge->num_channels == 2) {
if (strcmp(ast_channel_accountcode(bridge_channel->chan), ast_channel_peeraccount(other->chan))) {
ast_debug(1, "Changing peeraccount from %s to %s on %s to match channel %s\n",
ast_channel_peeraccount(other->chan), ast_channel_peeraccount(bridge_channel->chan), ast_channel_name(other->chan), ast_channel_name(bridge_channel->chan));
ast_channel_peeraccount_set(other->chan, ast_channel_accountcode(bridge_channel->chan));
}
if (strcmp(ast_channel_accountcode(other->chan), ast_channel_peeraccount(bridge_channel->chan))) {
ast_debug(1, "Changing peeraccount from %s to %s on %s to match channel %s\n",
ast_channel_peeraccount(bridge_channel->chan), ast_channel_peeraccount(other->chan), ast_channel_name(bridge_channel->chan), ast_channel_name(other->chan));
ast_channel_peeraccount_set(bridge_channel->chan, ast_channel_accountcode(other->chan));
}
}
}
}
void ast_bridge_update_linkedids(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, struct ast_bridge_channel *swap)
{
struct ast_bridge_channel *other = NULL;
const char *oldest_linkedid = ast_channel_linkedid(bridge_channel->chan);
AST_LIST_TRAVERSE(&bridge->channels, other, entry) {
if (other == swap) {
continue;
}
oldest_linkedid = ast_channel_oldest_linkedid(oldest_linkedid, ast_channel_linkedid(other->chan));
}
if (ast_strlen_zero(oldest_linkedid)) {
return;
}
ast_channel_linkedid_set(bridge_channel->chan, oldest_linkedid);
AST_LIST_TRAVERSE(&bridge->channels, other, entry) {
if (other == swap) {
continue;
}
ast_channel_linkedid_set(other->chan, oldest_linkedid);
}
}
int ast_bridge_channel_queue_frame(struct ast_bridge_channel *bridge_channel, struct ast_frame *fr)
{
struct ast_frame *dup;
@ -529,6 +598,14 @@ static void bridge_channel_pull(struct ast_bridge_channel *bridge_channel)
ast_bridge_channel_clear_roles(bridge_channel);
/* If we are not going to be hung up after leaving a bridge, and we were an
* outgoing channel, clear the outgoing flag.
*/
if (ast_test_flag(ast_channel_flags(bridge_channel->chan), AST_FLAG_OUTGOING)
&& (ast_channel_softhangup_internal_flag(bridge_channel->chan) & (AST_SOFTHANGUP_ASYNCGOTO | AST_SOFTHANGUP_UNBRIDGE))) {
ast_clear_flag(ast_channel_flags(bridge_channel->chan), AST_FLAG_OUTGOING);
}
bridge_dissolve_check(bridge_channel);
bridge->reconfigured = 1;

View File

@ -131,6 +131,9 @@ static int bridge_basic_push(struct ast_bridge *self, struct ast_bridge_channel
return -1;
}
ast_bridge_update_accountcodes(self, bridge_channel, swap);
ast_bridge_update_linkedids(self, bridge_channel, swap);
return ast_bridge_base_v_table.push(self, bridge_channel, swap);
}

4112
main/cdr.c

File diff suppressed because it is too large Load Diff

View File

@ -433,16 +433,6 @@ static int dialstatus_cmp(void *obj, void *arg, int flags)
return !strcmp(blob1_id, blob2_id) ? CMP_MATCH | CMP_STOP : 0;
}
/*!
* \brief Map of ast_cel_ama_flags to strings
*/
static const char * const cel_ama_flags[AST_CEL_AMA_FLAG_TOTAL] = {
[AST_CEL_AMA_FLAG_NONE] = "NONE",
[AST_CEL_AMA_FLAG_OMIT] = "OMIT",
[AST_CEL_AMA_FLAG_BILLING] = "BILLING",
[AST_CEL_AMA_FLAG_DOCUMENTATION] = "DOCUMENTATION",
};
unsigned int ast_cel_check_enabled(void)
{
RAII_VAR(struct cel_config *, cfg, ao2_global_obj_ref(cel_configs), ao2_cleanup);
@ -625,16 +615,6 @@ const char *ast_cel_get_type_name(enum ast_cel_event_type type)
return S_OR(cel_event_types[type], "Unknown");
}
const char *ast_cel_get_ama_flag_name(enum ast_cel_ama_flag flag)
{
if (flag < 0 || flag >= ARRAY_LEN(cel_ama_flags)) {
ast_log(LOG_WARNING, "Invalid AMA flag: %d\n", flag);
return "Unknown";
}
return S_OR(cel_ama_flags[flag], "Unknown");
}
static int cel_track_app(const char *const_app)
{
RAII_VAR(struct cel_config *, cfg, ao2_global_obj_ref(cel_configs), ao2_cleanup);

View File

@ -104,7 +104,6 @@ struct ast_epoll_data {
/*! \brief Prevent new channel allocation if shutting down. */
static int shutting_down;
static int uniqueint;
static int chancount;
unsigned long global_fin, global_fout;
@ -116,6 +115,8 @@ AST_THREADSTORAGE(state2str_threadbuf);
* 100ms */
#define AST_DEFAULT_EMULATE_DTMF_DURATION 100
#define DEFAULT_AMA_FLAGS AST_AMA_DOCUMENTATION
/*! Minimum amount of time between the end of the last digit and the beginning
* of a new one - 45ms */
#define AST_MIN_DTMF_GAP 45
@ -984,7 +985,7 @@ static void ast_dummy_channel_destructor(void *obj);
static struct ast_channel * attribute_malloc __attribute__((format(printf, 13, 0)))
__ast_channel_alloc_ap(int needqueue, int state, const char *cid_num, const char *cid_name,
const char *acctcode, const char *exten, const char *context,
const char *linkedid, const int amaflag, const char *file, int line,
const char *linkedid, enum ama_flags amaflag, const char *file, int line,
const char *function, const char *name_fmt, va_list ap)
{
struct ast_channel *tmp;
@ -1001,7 +1002,7 @@ __ast_channel_alloc_ap(int needqueue, int state, const char *cid_num, const char
return NULL;
}
if (!(tmp = ast_channel_internal_alloc(ast_channel_destructor))) {
if (!(tmp = ast_channel_internal_alloc(ast_channel_destructor, linkedid))) {
/* Channel structure allocation failure. */
return NULL;
}
@ -1078,20 +1079,6 @@ __ast_channel_alloc_ap(int needqueue, int state, const char *cid_num, const char
now = ast_tvnow();
ast_channel_creationtime_set(tmp, &now);
if (ast_strlen_zero(ast_config_AST_SYSTEM_NAME)) {
ast_channel_uniqueid_build(tmp, "%li.%d", (long) time(NULL),
ast_atomic_fetchadd_int(&uniqueint, 1));
} else {
ast_channel_uniqueid_build(tmp, "%s-%li.%d", ast_config_AST_SYSTEM_NAME,
(long) time(NULL), ast_atomic_fetchadd_int(&uniqueint, 1));
}
if (!ast_strlen_zero(linkedid)) {
ast_channel_linkedid_set(tmp, linkedid);
} else {
ast_channel_linkedid_set(tmp, ast_channel_uniqueid(tmp));
}
ast_channel_internal_setup_topics(tmp);
if (!ast_strlen_zero(name_fmt)) {
@ -1123,25 +1110,20 @@ __ast_channel_alloc_ap(int needqueue, int state, const char *cid_num, const char
/* Reminder for the future: under what conditions do we NOT want to track cdrs on channels? */
/* These 4 variables need to be set up for the cdr_init() to work right */
if (amaflag) {
if (amaflag != AST_AMA_NONE) {
ast_channel_amaflags_set(tmp, amaflag);
} else {
ast_channel_amaflags_set(tmp, ast_default_amaflags);
ast_channel_amaflags_set(tmp, DEFAULT_AMA_FLAGS);
}
if (!ast_strlen_zero(acctcode))
if (!ast_strlen_zero(acctcode)) {
ast_channel_accountcode_set(tmp, acctcode);
else
ast_channel_accountcode_set(tmp, ast_default_accountcode);
}
ast_channel_context_set(tmp, S_OR(context, "default"));
ast_channel_exten_set(tmp, S_OR(exten, "s"));
ast_channel_priority_set(tmp, 1);
ast_channel_cdr_set(tmp, ast_cdr_alloc());
ast_cdr_init(ast_channel_cdr(tmp), tmp);
ast_cdr_start(ast_channel_cdr(tmp));
ast_atomic_fetchadd_int(&chancount, +1);
headp = ast_channel_varshead(tmp);
@ -1165,7 +1147,7 @@ __ast_channel_alloc_ap(int needqueue, int state, const char *cid_num, const char
* a lot of data into this func to do it here!
*/
if (ast_get_channel_tech(tech) || (tech2 && ast_get_channel_tech(tech2))) {
ast_publish_channel_state(tmp);
ast_channel_publish_snapshot(tmp);
}
ast_channel_internal_finalize(tmp);
@ -1176,7 +1158,7 @@ __ast_channel_alloc_ap(int needqueue, int state, const char *cid_num, const char
struct ast_channel *__ast_channel_alloc(int needqueue, int state, const char *cid_num,
const char *cid_name, const char *acctcode,
const char *exten, const char *context,
const char *linkedid, const int amaflag,
const char *linkedid, const enum ama_flags amaflag,
const char *file, int line, const char *function,
const char *name_fmt, ...)
{
@ -1202,7 +1184,7 @@ struct ast_channel *ast_dummy_channel_alloc(void)
struct ast_channel *tmp;
struct varshead *headp;
if (!(tmp = ast_channel_internal_alloc(ast_dummy_channel_destructor))) {
if (!(tmp = ast_channel_internal_alloc(ast_dummy_channel_destructor, NULL))) {
/* Dummy channel structure allocation failure. */
return NULL;
}
@ -2470,7 +2452,7 @@ static void ast_channel_destructor(void *obj)
ast_jb_destroy(chan);
if (ast_channel_cdr(chan)) {
ast_cdr_discard(ast_channel_cdr(chan));
ast_cdr_free(ast_channel_cdr(chan));
ast_channel_cdr_set(chan, NULL);
}
@ -2530,7 +2512,7 @@ static void ast_dummy_channel_destructor(void *obj)
ast_var_delete(vardata);
if (ast_channel_cdr(chan)) {
ast_cdr_discard(ast_channel_cdr(chan));
ast_cdr_free(ast_channel_cdr(chan));
ast_channel_cdr_set(chan, NULL);
}
@ -2884,26 +2866,17 @@ int ast_hangup(struct ast_channel *chan)
ast_cc_offer(chan);
ast_publish_channel_state(chan);
if (ast_channel_cdr(chan) && !ast_test_flag(ast_channel_cdr(chan), AST_CDR_FLAG_BRIDGED) &&
!ast_test_flag(ast_channel_cdr(chan), AST_CDR_FLAG_POST_DISABLED) &&
(ast_channel_cdr(chan)->disposition != AST_CDR_NULL || ast_test_flag(ast_channel_cdr(chan), AST_CDR_FLAG_DIALED))) {
ast_channel_lock(chan);
ast_cdr_end(ast_channel_cdr(chan));
ast_cdr_detach(ast_channel_cdr(chan));
ast_channel_cdr_set(chan, NULL);
ast_channel_unlock(chan);
}
ast_channel_publish_snapshot(chan);
ast_channel_unref(chan);
return 0;
}
int ast_raw_answer(struct ast_channel *chan, int cdr_answer)
int ast_raw_answer(struct ast_channel *chan)
{
int res = 0;
struct timeval answertime;
ast_channel_lock(chan);
@ -2919,6 +2892,9 @@ int ast_raw_answer(struct ast_channel *chan, int cdr_answer)
return -1;
}
answertime = ast_tvnow();
ast_channel_answertime_set(chan, &answertime);
ast_channel_unlock(chan);
switch (ast_channel_state(chan)) {
@ -2929,18 +2905,9 @@ int ast_raw_answer(struct ast_channel *chan, int cdr_answer)
res = ast_channel_tech(chan)->answer(chan);
}
ast_setstate(chan, AST_STATE_UP);
if (cdr_answer) {
ast_cdr_answer(ast_channel_cdr(chan));
}
ast_channel_unlock(chan);
break;
case AST_STATE_UP:
/* Calling ast_cdr_answer when it it has previously been called
* is essentially a no-op, so it is safe.
*/
if (cdr_answer) {
ast_cdr_answer(ast_channel_cdr(chan));
}
break;
default:
break;
@ -2951,13 +2918,13 @@ int ast_raw_answer(struct ast_channel *chan, int cdr_answer)
return res;
}
int __ast_answer(struct ast_channel *chan, unsigned int delay, int cdr_answer)
int __ast_answer(struct ast_channel *chan, unsigned int delay)
{
int res = 0;
enum ast_channel_state old_state;
old_state = ast_channel_state(chan);
if ((res = ast_raw_answer(chan, cdr_answer))) {
if ((res = ast_raw_answer(chan))) {
return res;
}
@ -3059,7 +3026,27 @@ int __ast_answer(struct ast_channel *chan, unsigned int delay, int cdr_answer)
int ast_answer(struct ast_channel *chan)
{
return __ast_answer(chan, 0, 1);
return __ast_answer(chan, 0);
}
int ast_channel_get_duration(struct ast_channel *chan)
{
ast_assert(NULL != chan);
if (ast_tvzero(ast_channel_creationtime(chan))) {
return 0;
}
return (ast_tvdiff_ms(ast_tvnow(), ast_channel_creationtime(chan)) / 1000);
}
int ast_channel_get_up_time(struct ast_channel *chan)
{
ast_assert(NULL != chan);
if (ast_tvzero(ast_channel_answertime(chan))) {
return 0;
}
return (ast_tvdiff_ms(ast_tvnow(), ast_channel_answertime(chan)) / 1000);
}
void ast_deactivate_generator(struct ast_channel *chan)
@ -4472,6 +4459,33 @@ void ast_channel_hangupcause_hash_set(struct ast_channel *chan, const struct ast
}
}
enum ama_flags ast_channel_string2amaflag(const char *flag)
{
if (!strcasecmp(flag, "default"))
return DEFAULT_AMA_FLAGS;
if (!strcasecmp(flag, "omit"))
return AST_AMA_OMIT;
if (!strcasecmp(flag, "billing"))
return AST_AMA_BILLING;
if (!strcasecmp(flag, "documentation"))
return AST_AMA_DOCUMENTATION;
return AST_AMA_NONE;
}
const char *ast_channel_amaflags2string(enum ama_flags flag)
{
switch (flag) {
case AST_AMA_OMIT:
return "OMIT";
case AST_AMA_BILLING:
return "BILLING";
case AST_AMA_DOCUMENTATION:
return "DOCUMENTATION";
default:
return "Unknown";
}
}
int ast_indicate_data(struct ast_channel *chan, int _condition,
const void *data, size_t datalen)
{
@ -5625,15 +5639,15 @@ struct ast_channel *ast_call_forward(struct ast_channel *caller, struct ast_chan
}
if (oh->account) {
ast_channel_lock(new_chan);
ast_cdr_setaccount(new_chan, oh->account);
ast_channel_accountcode_set(new_chan, oh->account);
ast_channel_unlock(new_chan);
}
} else if (caller) { /* no outgoing helper so use caller if avaliable */
call_forward_inherit(new_chan, caller, orig);
}
ast_set_flag(ast_channel_flags(new_chan), AST_FLAG_ORIGINATED);
ast_channel_lock_both(orig, new_chan);
ast_copy_flags(ast_channel_cdr(new_chan), ast_channel_cdr(orig), AST_CDR_FLAG_ORIGINATED);
ast_channel_accountcode_set(new_chan, ast_channel_accountcode(orig));
ast_party_connected_line_copy(ast_channel_connected(new_chan), ast_channel_connected(orig));
ast_party_redirecting_copy(ast_channel_redirecting(new_chan), ast_channel_redirecting(orig));
@ -5699,7 +5713,7 @@ struct ast_channel *__ast_request_and_dial(const char *type, struct ast_format_c
}
if (oh->account) {
ast_channel_lock(chan);
ast_cdr_setaccount(chan, oh->account);
ast_channel_accountcode_set(chan, oh->account);
ast_channel_unlock(chan);
}
}
@ -5714,7 +5728,7 @@ struct ast_channel *__ast_request_and_dial(const char *type, struct ast_format_c
*/
ast_set_callerid(chan, cid_num, cid_name, cid_num);
ast_set_flag(ast_channel_cdr(chan), AST_CDR_FLAG_ORIGINATED);
ast_set_flag(ast_channel_flags(chan), AST_FLAG_ORIGINATED);
ast_party_connected_line_set_init(&connected, ast_channel_connected(chan));
if (cid_num) {
connected.id.number.valid = 1;
@ -5764,25 +5778,21 @@ struct ast_channel *__ast_request_and_dial(const char *type, struct ast_format_c
break;
case AST_CONTROL_BUSY:
ast_cdr_busy(ast_channel_cdr(chan));
*outstate = f->subclass.integer;
timeout = 0;
break;
case AST_CONTROL_INCOMPLETE:
ast_cdr_failed(ast_channel_cdr(chan));
*outstate = AST_CONTROL_CONGESTION;
timeout = 0;
break;
case AST_CONTROL_CONGESTION:
ast_cdr_failed(ast_channel_cdr(chan));
*outstate = f->subclass.integer;
timeout = 0;
break;
case AST_CONTROL_ANSWER:
ast_cdr_answer(ast_channel_cdr(chan));
*outstate = f->subclass.integer;
timeout = 0; /* trick to force exit from the while() */
break;
@ -5833,28 +5843,10 @@ struct ast_channel *__ast_request_and_dial(const char *type, struct ast_format_c
*outstate = AST_CONTROL_ANSWER;
if (res <= 0) {
struct ast_cdr *chancdr;
ast_channel_lock(chan);
if (AST_CONTROL_RINGING == last_subclass) {
ast_channel_hangupcause_set(chan, AST_CAUSE_NO_ANSWER);
}
if (!ast_channel_cdr(chan) && (chancdr = ast_cdr_alloc())) {
ast_channel_cdr_set(chan, chancdr);
ast_cdr_init(ast_channel_cdr(chan), chan);
}
if (ast_channel_cdr(chan)) {
char tmp[256];
snprintf(tmp, sizeof(tmp), "%s/%s", type, addr);
ast_cdr_setapp(ast_channel_cdr(chan), "Dial", tmp);
ast_cdr_update(chan);
ast_cdr_start(ast_channel_cdr(chan));
ast_cdr_end(ast_channel_cdr(chan));
/* If the cause wasn't handled properly */
if (ast_cdr_disposition(ast_channel_cdr(chan), ast_channel_hangupcause(chan))) {
ast_cdr_failed(ast_channel_cdr(chan));
}
}
ast_channel_unlock(chan);
ast_hangup(chan);
chan = NULL;
@ -6024,9 +6016,6 @@ int ast_call(struct ast_channel *chan, const char *addr, int timeout)
/* Stop if we're a zombie or need a soft hangup */
ast_channel_lock(chan);
if (!ast_test_flag(ast_channel_flags(chan), AST_FLAG_ZOMBIE) && !ast_check_hangup(chan)) {
if (ast_channel_cdr(chan)) {
ast_set_flag(ast_channel_cdr(chan), AST_CDR_FLAG_DIALED);
}
if (ast_channel_tech(chan)->call)
res = ast_channel_tech(chan)->call(chan, addr, timeout);
ast_set_flag(ast_channel_flags(chan), AST_FLAG_OUTGOING);
@ -6517,32 +6506,20 @@ static void clone_variables(struct ast_channel *original, struct ast_channel *cl
}
}
/* return the oldest of two linkedids. linkedid is derived from
uniqueid which is formed like this: [systemname-]ctime.seq
The systemname, and the dash are optional, followed by the epoch
time followed by an integer sequence. Note that this is not a
decimal number, since 1.2 is less than 1.11 in uniqueid land.
To compare two uniqueids, we parse out the integer values of the
time and the sequence numbers and compare them, with time trumping
sequence.
*/
static const char *oldest_linkedid(const char *a, const char *b)
const char *ast_channel_oldest_linkedid(const char *a, const char *b)
{
const char *satime, *saseq;
const char *sbtime, *sbseq;
const char *dash;
unsigned int atime, aseq, btime, bseq;
if (ast_strlen_zero(a))
if (ast_strlen_zero(a)) {
return b;
}
if (ast_strlen_zero(b))
if (ast_strlen_zero(b)) {
return a;
}
satime = a;
sbtime = b;
@ -6558,8 +6535,9 @@ static const char *oldest_linkedid(const char *a, const char *b)
/* the sequence comes after the '.' */
saseq = strchr(satime, '.');
sbseq = strchr(sbtime, '.');
if (!saseq || !sbseq)
if (!saseq || !sbseq) {
return NULL;
}
saseq++;
sbseq++;
@ -6578,122 +6556,6 @@ static const char *oldest_linkedid(const char *a, const char *b)
}
}
/*! Set the channel's linkedid to the given string, and also check to
* see if the channel's old linkedid is now being retired */
static void ast_channel_change_linkedid(struct ast_channel *chan, const char *linkedid)
{
ast_assert(linkedid != NULL);
/* if the linkedid for this channel is being changed from something, check... */
if (ast_channel_linkedid(chan) && !strcmp(ast_channel_linkedid(chan), linkedid)) {
return;
}
ast_channel_linkedid_set(chan, linkedid);
ast_cel_linkedid_ref(linkedid);
}
/*! \brief Propagate the oldest linkedid between associated channels */
void ast_channel_set_linkgroup(struct ast_channel *chan, struct ast_channel *peer)
{
const char* linkedid=NULL;
struct ast_channel *bridged;
/*
* BUGBUG this needs to be updated to not use ast_channel_internal_bridged_channel().
* BUGBUG this needs to be updated to not use ast_bridged_channel().
*
* We may be better off making this a function of the bridging
* framework. Essentially, as each channel joins a bridge, the
* oldest linkedid should be propagated between all pairs of
* channels. This should be handled by bridging (unless you're
* in an infinite wait bridge...) just like the BRIDGEPEER
* channel variable.
*
* This is currently called in two places:
*
* (1) In channel masquerade. To some extent this shouldn't
* really be done any longer - we don't really want a channel to
* have its linkedid change, even if it replaces a channel that
* had an older linkedid. The two channels aren't really
* 'related', they're simply swapping with each other.
*
* (2) In features.c as two channels are bridged.
*/
linkedid = oldest_linkedid(ast_channel_linkedid(chan), ast_channel_linkedid(peer));
linkedid = oldest_linkedid(linkedid, ast_channel_uniqueid(chan));
linkedid = oldest_linkedid(linkedid, ast_channel_uniqueid(peer));
if (ast_channel_internal_bridged_channel(chan)) {
bridged = ast_bridged_channel(chan);
if (bridged && bridged != peer) {
linkedid = oldest_linkedid(linkedid, ast_channel_linkedid(bridged));
linkedid = oldest_linkedid(linkedid, ast_channel_uniqueid(bridged));
}
}
if (ast_channel_internal_bridged_channel(peer)) {
bridged = ast_bridged_channel(peer);
if (bridged && bridged != chan) {
linkedid = oldest_linkedid(linkedid, ast_channel_linkedid(bridged));
linkedid = oldest_linkedid(linkedid, ast_channel_uniqueid(bridged));
}
}
/* just in case setting a stringfield to itself causes problems */
linkedid = ast_strdupa(linkedid);
ast_channel_change_linkedid(chan, linkedid);
ast_channel_change_linkedid(peer, linkedid);
if (ast_channel_internal_bridged_channel(chan)) {
bridged = ast_bridged_channel(chan);
if (bridged && bridged != peer) {
ast_channel_change_linkedid(bridged, linkedid);
}
}
if (ast_channel_internal_bridged_channel(peer)) {
bridged = ast_bridged_channel(peer);
if (bridged && bridged != chan) {
ast_channel_change_linkedid(bridged, linkedid);
}
}
}
#if 0 //BUGBUG setting up peeraccount needs to be removed.
/* copy accountcode and peeraccount across during a link */
static void ast_set_owners_and_peers(struct ast_channel *chan1,
struct ast_channel *chan2)
{
if (!ast_strlen_zero(ast_channel_accountcode(chan1)) && ast_strlen_zero(ast_channel_peeraccount(chan2))) {
ast_debug(1, "setting peeraccount to %s for %s from data on channel %s\n",
ast_channel_accountcode(chan1), ast_channel_name(chan2), ast_channel_name(chan1));
ast_channel_peeraccount_set(chan2, ast_channel_accountcode(chan1));
}
if (!ast_strlen_zero(ast_channel_accountcode(chan2)) && ast_strlen_zero(ast_channel_peeraccount(chan1))) {
ast_debug(1, "setting peeraccount to %s for %s from data on channel %s\n",
ast_channel_accountcode(chan2), ast_channel_name(chan1), ast_channel_name(chan2));
ast_channel_peeraccount_set(chan1, ast_channel_accountcode(chan2));
}
if (!ast_strlen_zero(ast_channel_peeraccount(chan1)) && ast_strlen_zero(ast_channel_accountcode(chan2))) {
ast_debug(1, "setting accountcode to %s for %s from data on channel %s\n",
ast_channel_peeraccount(chan1), ast_channel_name(chan2), ast_channel_name(chan1));
ast_channel_accountcode_set(chan2, ast_channel_peeraccount(chan1));
}
if (!ast_strlen_zero(ast_channel_peeraccount(chan2)) && ast_strlen_zero(ast_channel_accountcode(chan1))) {
ast_debug(1, "setting accountcode to %s for %s from data on channel %s\n",
ast_channel_peeraccount(chan2), ast_channel_name(chan1), ast_channel_name(chan2));
ast_channel_accountcode_set(chan1, ast_channel_peeraccount(chan2));
}
if (0 != strcmp(ast_channel_accountcode(chan1), ast_channel_peeraccount(chan2))) {
ast_debug(1, "changing peeraccount from %s to %s on %s to match channel %s\n",
ast_channel_peeraccount(chan2), ast_channel_peeraccount(chan1), ast_channel_name(chan2), ast_channel_name(chan1));
ast_channel_peeraccount_set(chan2, ast_channel_accountcode(chan1));
}
if (0 != strcmp(ast_channel_accountcode(chan2), ast_channel_peeraccount(chan1))) {
ast_debug(1, "changing peeraccount from %s to %s on %s to match channel %s\n",
ast_channel_peeraccount(chan1), ast_channel_peeraccount(chan2), ast_channel_name(chan1), ast_channel_name(chan2));
ast_channel_peeraccount_set(chan1, ast_channel_accountcode(chan2));
}
}
#endif //BUGBUG
/*!
* \internal
* \brief Transfer COLP between target and transferee channels.
@ -6775,7 +6637,6 @@ void ast_do_masquerade(struct ast_channel *original)
} exchange;
struct ast_channel *clonechan, *chans[2];
struct ast_channel *bridged;
struct ast_cdr *cdr;
struct ast_datastore *xfer_ds;
struct xfer_masquerade_ds *xfer_colp;
struct ast_format rformat;
@ -6943,9 +6804,6 @@ void ast_do_masquerade(struct ast_channel *original)
snprintf(tmp_name, sizeof(tmp_name), "%s<ZOMBIE>", ast_channel_name(clonechan)); /* quick, hide the brains! */
__ast_change_name_nolink(clonechan, tmp_name);
/* share linked id's */
ast_channel_set_linkgroup(original, clonechan);
/* Swap the technologies */
t = ast_channel_tech(original);
ast_channel_tech_set(original, ast_channel_tech(clonechan));
@ -6955,11 +6813,6 @@ void ast_do_masquerade(struct ast_channel *original)
ast_channel_tech_pvt_set(original, ast_channel_tech_pvt(clonechan));
ast_channel_tech_pvt_set(clonechan, t_pvt);
/* Swap the cdrs */
cdr = ast_channel_cdr(original);
ast_channel_cdr_set(original, ast_channel_cdr(clonechan));
ast_channel_cdr_set(clonechan, cdr);
/* Swap the alertpipes */
ast_channel_internal_alertpipe_swap(original, clonechan);
@ -7098,7 +6951,7 @@ void ast_do_masquerade(struct ast_channel *original)
ast_channel_redirecting_set(original, ast_channel_redirecting(clonechan));
ast_channel_redirecting_set(clonechan, &exchange.redirecting);
ast_publish_channel_state(original);
ast_channel_publish_snapshot(original);
/* Restore original timing file descriptor */
ast_channel_set_fd(original, AST_TIMING_FD, ast_channel_timingfd(original));
@ -7257,11 +7110,8 @@ void ast_set_callerid(struct ast_channel *chan, const char *cid_num, const char
ast_free(ast_channel_caller(chan)->ani.number.str);
ast_channel_caller(chan)->ani.number.str = ast_strdup(cid_ani);
}
if (ast_channel_cdr(chan)) {
ast_cdr_setcid(ast_channel_cdr(chan), chan);
}
ast_publish_channel_state(chan);
ast_channel_publish_snapshot(chan);
ast_channel_unlock(chan);
}
@ -7287,10 +7137,7 @@ void ast_channel_set_caller_event(struct ast_channel *chan, const struct ast_par
ast_channel_lock(chan);
ast_party_caller_set(ast_channel_caller(chan), caller, update);
ast_publish_channel_state(chan);
if (ast_channel_cdr(chan)) {
ast_cdr_setcid(ast_channel_cdr(chan), chan);
}
ast_channel_publish_snapshot(chan);
ast_channel_unlock(chan);
}

View File

@ -38,6 +38,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include <unistd.h>
#include <fcntl.h>
#include "asterisk/paths.h"
#include "asterisk/channel.h"
#include "asterisk/channel_internal.h"
#include "asterisk/data.h"
@ -143,6 +144,7 @@ struct ast_channel {
struct ast_namedgroups *named_callgroups; /*!< Named call group for call pickups */
struct ast_namedgroups *named_pickupgroups; /*!< Named pickup group - which call groups can be picked up? */
struct timeval creationtime; /*!< The time of channel creation */
struct timeval answertime; /*!< The time the channel was answered */
struct ast_readq_list readq;
struct ast_jb jb; /*!< The jitterbuffer state */
struct timeval dtmf_tv; /*!< The time that an in process digit began, or the last digit ended */
@ -206,6 +208,9 @@ struct ast_channel {
struct stasis_subscription *endpoint_forward; /*!< Subscription for event forwarding to endpoint's topic */
};
/*! \brief The monotonically increasing integer counter for channel uniqueids */
static int uniqueint;
/* AST_DATA definitions, which will probably have to be re-thought since the channel will be opaque */
#if 0 /* XXX AstData: ast_callerid no longer exists. (Equivalent code not readily apparent.) */
@ -333,7 +338,7 @@ int ast_channel_data_add_structure(struct ast_data *tree,
if (!enum_node) {
return -1;
}
ast_data_add_str(enum_node, "text", ast_cdr_flags2str(ast_channel_amaflags(chan)));
ast_data_add_str(enum_node, "text", ast_channel_amaflags2string(ast_channel_amaflags(chan)));
ast_data_add_int(enum_node, "value", ast_channel_amaflags(chan));
/* transfercapability */
@ -400,8 +405,6 @@ int ast_channel_data_add_structure(struct ast_data *tree,
return -1;
}
ast_cdr_data_add_structure(data_cdr, ast_channel_cdr(chan), 1);
return 0;
}
@ -413,9 +416,11 @@ int ast_channel_data_cmp_structure(const struct ast_data_search *tree,
/* ACCESSORS */
#define DEFINE_STRINGFIELD_SETTERS_FOR(field, publish) \
#define DEFINE_STRINGFIELD_SETTERS_FOR(field, publish, assert_on_null) \
void ast_channel_##field##_set(struct ast_channel *chan, const char *value) \
{ \
if ((assert_on_null)) ast_assert(!ast_strlen_zero(value)); \
if (!strcmp(value, chan->field)) return; \
ast_string_field_set(chan, field, value); \
if (publish) ast_channel_publish_snapshot(chan); \
} \
@ -431,20 +436,20 @@ void ast_channel_##field##_build(struct ast_channel *chan, const char *fmt, ...)
va_start(ap, fmt); \
ast_channel_##field##_build_va(chan, fmt, ap); \
va_end(ap); \
if (publish) ast_channel_publish_snapshot(chan); \
}
DEFINE_STRINGFIELD_SETTERS_FOR(name, 0);
DEFINE_STRINGFIELD_SETTERS_FOR(language, 1);
DEFINE_STRINGFIELD_SETTERS_FOR(musicclass, 0);
DEFINE_STRINGFIELD_SETTERS_FOR(accountcode, 0);
DEFINE_STRINGFIELD_SETTERS_FOR(peeraccount, 0);
DEFINE_STRINGFIELD_SETTERS_FOR(userfield, 0);
DEFINE_STRINGFIELD_SETTERS_FOR(call_forward, 0);
DEFINE_STRINGFIELD_SETTERS_FOR(uniqueid, 0);
DEFINE_STRINGFIELD_SETTERS_FOR(parkinglot, 0);
DEFINE_STRINGFIELD_SETTERS_FOR(hangupsource, 0);
DEFINE_STRINGFIELD_SETTERS_FOR(dialcontext, 0);
DEFINE_STRINGFIELD_SETTERS_FOR(name, 0, 1);
DEFINE_STRINGFIELD_SETTERS_FOR(language, 1, 0);
DEFINE_STRINGFIELD_SETTERS_FOR(musicclass, 0, 0);
DEFINE_STRINGFIELD_SETTERS_FOR(accountcode, 1, 0);
DEFINE_STRINGFIELD_SETTERS_FOR(peeraccount, 1, 0);
DEFINE_STRINGFIELD_SETTERS_FOR(userfield, 0, 0);
DEFINE_STRINGFIELD_SETTERS_FOR(call_forward, 0, 0);
DEFINE_STRINGFIELD_SETTERS_FOR(uniqueid, 0, 1);
DEFINE_STRINGFIELD_SETTERS_FOR(linkedid, 1, 1);
DEFINE_STRINGFIELD_SETTERS_FOR(parkinglot, 0, 0);
DEFINE_STRINGFIELD_SETTERS_FOR(hangupsource, 0, 0);
DEFINE_STRINGFIELD_SETTERS_FOR(dialcontext, 0, 0);
#define DEFINE_STRINGFIELD_GETTER_FOR(field) const char *ast_channel_##field(const struct ast_channel *chan) \
{ \
@ -464,12 +469,6 @@ DEFINE_STRINGFIELD_GETTER_FOR(parkinglot);
DEFINE_STRINGFIELD_GETTER_FOR(hangupsource);
DEFINE_STRINGFIELD_GETTER_FOR(dialcontext);
void ast_channel_linkedid_set(struct ast_channel *chan, const char *value)
{
ast_assert(!ast_strlen_zero(value));
ast_string_field_set(chan, linkedid, value);
}
const char *ast_channel_appl(const struct ast_channel *chan)
{
return chan->appl;
@ -555,14 +554,20 @@ void ast_channel_sending_dtmf_tv_set(struct ast_channel *chan, struct timeval va
chan->sending_dtmf_tv = value;
}
int ast_channel_amaflags(const struct ast_channel *chan)
enum ama_flags ast_channel_amaflags(const struct ast_channel *chan)
{
return chan->amaflags;
}
void ast_channel_amaflags_set(struct ast_channel *chan, int value)
void ast_channel_amaflags_set(struct ast_channel *chan, enum ama_flags value)
{
if (chan->amaflags == value) {
return;
}
chan->amaflags = value;
ast_channel_publish_snapshot(chan);
}
#ifdef HAVE_EPOLL
int ast_channel_epfd(const struct ast_channel *chan)
{
@ -1043,6 +1048,16 @@ void ast_channel_creationtime_set(struct ast_channel *chan, struct timeval *valu
chan->creationtime = *value;
}
struct timeval ast_channel_answertime(struct ast_channel *chan)
{
return chan->answertime;
}
void ast_channel_answertime_set(struct ast_channel *chan, struct timeval *value)
{
chan->answertime = *value;
}
/* Evil softhangup accessors */
int ast_channel_softhangup_internal_flag(struct ast_channel *chan)
{
@ -1350,7 +1365,7 @@ static int pvt_cause_cmp_fn(void *obj, void *vstr, int flags)
#define DIALED_CAUSES_BUCKETS 37
struct ast_channel *__ast_channel_internal_alloc(void (*destructor)(void *obj), const char *file, int line, const char *function)
struct ast_channel *__ast_channel_internal_alloc(void (*destructor)(void *obj), const char *linkedid, const char *file, int line, const char *function)
{
struct ast_channel *tmp;
#if defined(REF_DEBUG)
@ -1368,7 +1383,21 @@ struct ast_channel *__ast_channel_internal_alloc(void (*destructor)(void *obj),
}
if (!(tmp->dialed_causes = ao2_container_alloc(DIALED_CAUSES_BUCKETS, pvt_cause_hash_fn, pvt_cause_cmp_fn))) {
return ast_channel_unref(tmp);
return ast_channel_unref(tmp);
}
if (ast_strlen_zero(ast_config_AST_SYSTEM_NAME)) {
ast_channel_uniqueid_build(tmp, "%li.%d", (long)time(NULL),
ast_atomic_fetchadd_int(&uniqueint, 1));
} else {
ast_channel_uniqueid_build(tmp, "%s-%li.%d", ast_config_AST_SYSTEM_NAME,
(long)time(NULL), ast_atomic_fetchadd_int(&uniqueint, 1));
}
if (!ast_strlen_zero(linkedid)) {
ast_string_field_set(tmp, linkedid, linkedid);
} else {
ast_string_field_set(tmp, linkedid, tmp->uniqueid);
}
return tmp;

View File

@ -932,8 +932,8 @@ static char *handle_chanlist(struct ast_cli_entry *e, int cmd, struct ast_cli_ar
bridge = ast_channel_get_bridge(c);
if (!count) {
if ((concise || verbose) && ast_channel_cdr(c) && !ast_tvzero(ast_channel_cdr(c)->start)) {
int duration = (int)(ast_tvdiff_ms(ast_tvnow(), ast_channel_cdr(c)->start) / 1000);
if ((concise || verbose) && !ast_tvzero(ast_channel_creationtime(c))) {
int duration = (int)(ast_tvdiff_ms(ast_tvnow(), ast_channel_creationtime(c)) / 1000);
if (verbose) {
int durh = duration / 3600;
int durm = (duration % 3600) / 60;
@ -1465,8 +1465,8 @@ static char *handle_showchan(struct ast_cli_entry *e, int cmd, struct ast_cli_ar
ast_channel_lock(c);
if (ast_channel_cdr(c)) {
elapsed_seconds = now.tv_sec - ast_channel_cdr(c)->start.tv_sec;
if (!ast_tvzero(ast_channel_creationtime(c))) {
elapsed_seconds = now.tv_sec - ast_channel_creationtime(c).tv_sec;
hour = elapsed_seconds / 3600;
min = (elapsed_seconds % 3600) / 60;
sec = elapsed_seconds % 60;
@ -1565,7 +1565,7 @@ static char *handle_showchan(struct ast_cli_entry *e, int cmd, struct ast_cli_ar
ast_str_append(&output, 0, " Variables:\n%s\n", ast_str_buffer(obuf));
}
if (ast_channel_cdr(c) && ast_cdr_serialize_variables(ast_channel_cdr(c), &obuf, '=', '\n', 1)) {
if (ast_cdr_serialize_variables(ast_channel_name(c), &obuf, '=', '\n')) {
ast_str_append(&output, 0, " CDR Variables:\n%s\n", ast_str_buffer(obuf));
}

View File

@ -332,7 +332,7 @@ int ast_dial_prerun(struct ast_dial *dial, struct ast_channel *chan, struct ast_
}
/*! \brief Helper function that does the beginning dialing per-appended channel */
static int begin_dial_channel(struct ast_dial_channel *channel, struct ast_channel *chan)
static int begin_dial_channel(struct ast_dial_channel *channel, struct ast_channel *chan, int async)
{
char numsubst[AST_MAX_EXTENSION];
int res = 1;
@ -351,9 +351,10 @@ static int begin_dial_channel(struct ast_dial_channel *channel, struct ast_chann
ast_hangup(channel->owner);
channel->owner = NULL;
} else {
if (chan)
if (chan) {
ast_poll_channel_add(chan, channel->owner);
ast_channel_publish_dial(chan, channel->owner, channel->device, NULL);
}
ast_channel_publish_dial(async ? NULL : chan, channel->owner, channel->device, NULL);
res = 1;
ast_verb(3, "Called %s\n", numsubst);
}
@ -362,7 +363,7 @@ static int begin_dial_channel(struct ast_dial_channel *channel, struct ast_chann
}
/*! \brief Helper function that does the beginning dialing per dial structure */
static int begin_dial(struct ast_dial *dial, struct ast_channel *chan)
static int begin_dial(struct ast_dial *dial, struct ast_channel *chan, int async)
{
struct ast_dial_channel *channel = NULL;
int success = 0;
@ -370,7 +371,7 @@ static int begin_dial(struct ast_dial *dial, struct ast_channel *chan)
/* Iterate through channel list, requesting and calling each one */
AST_LIST_LOCK(&dial->channels);
AST_LIST_TRAVERSE(&dial->channels, channel, list) {
success += begin_dial_channel(channel, chan);
success += begin_dial_channel(channel, chan, async);
}
AST_LIST_UNLOCK(&dial->channels);
@ -409,7 +410,7 @@ static int handle_call_forward(struct ast_dial *dial, struct ast_dial_channel *c
AST_LIST_UNLOCK(&dial->channels);
/* Finally give it a go... send it out into the world */
begin_dial_channel(channel, chan);
begin_dial_channel(channel, chan, chan ? 0 : 1);
/* Drop the original channel */
ast_hangup(original);
@ -819,7 +820,7 @@ enum ast_dial_result ast_dial_run(struct ast_dial *dial, struct ast_channel *cha
}
/* Dial each requested channel */
if (!begin_dial(dial, chan))
if (!begin_dial(dial, chan, async))
return AST_DIAL_RESULT_FAILED;
/* If we are running async spawn a thread and send it away... otherwise block here */

View File

@ -3618,7 +3618,7 @@ static int pre_bridge_setup(struct ast_channel *chan, struct ast_channel *peer,
/* Answer if need be */
if (ast_channel_state(chan) != AST_STATE_UP) {
if (ast_raw_answer(chan, 1)) {
if (ast_raw_answer(chan)) {
return -1;
}
}
@ -3628,8 +3628,6 @@ static int pre_bridge_setup(struct ast_channel *chan, struct ast_channel *peer,
ast_channel_log("Pre-bridge CHAN Channel info", chan);
ast_channel_log("Pre-bridge PEER Channel info", peer);
#endif
/* two channels are being marked as linked here */
ast_channel_set_linkgroup(chan, peer);
/*
* If we are bridging a call, stop worrying about forwarding

View File

@ -3770,8 +3770,8 @@ static int action_status(struct mansession *s, const struct message *m)
bridge_text[0] = '\0';
}
if (ast_channel_pbx(c)) {
if (ast_channel_cdr(c)) {
elapsed_seconds = now.tv_sec - ast_channel_cdr(c)->start.tv_sec;
if (!ast_tvzero(ast_channel_creationtime(c))) {
elapsed_seconds = now.tv_sec - ast_channel_creationtime(c).tv_sec;
}
astman_append(s,
"Event: Status\r\n"
@ -5120,7 +5120,7 @@ static int action_coresettings(struct mansession *s, const struct message *m)
ast_config_AST_RUN_GROUP,
option_maxfiles,
AST_CLI_YESNO(ast_realtime_enabled()),
AST_CLI_YESNO(check_cdr_enabled()),
AST_CLI_YESNO(ast_cdr_is_enabled()),
AST_CLI_YESNO(check_webmanager_enabled())
);
return 0;
@ -5228,8 +5228,8 @@ static int action_coreshowchannels(struct mansession *s, const struct message *m
ast_channel_lock(c);
bc = ast_bridged_channel(c);
if (ast_channel_cdr(c) && !ast_tvzero(ast_channel_cdr(c)->start)) {
duration = (int)(ast_tvdiff_ms(ast_tvnow(), ast_channel_cdr(c)->start) / 1000);
if (!ast_tvzero(ast_channel_creationtime(c))) {
duration = (int)(ast_tvdiff_ms(ast_tvnow(), ast_channel_creationtime(c)) / 1000);
durh = duration / 3600;
durm = (duration % 3600) / 60;
durs = duration % 60;

View File

@ -153,6 +153,17 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
</syntax>
</managerEventInstance>
</managerEvent>
<managerEvent language="en_US" name="NewAccountCode">
<managerEventInstance class="EVENT_FLAG_CALL">
<synopsis>Raised when a Channel's AccountCode is changed.</synopsis>
<syntax>
<xi:include xpointer="xpointer(/docs/managerEvent[@name='Newchannel']/managerEventInstance/syntax/parameter)" />
<parameter name="OldAccountCode">
<para>The channel's previous account code</para>
</parameter>
</syntax>
</managerEventInstance>
</managerEvent>
<managerEvent language="en_US" name="DialBegin">
<managerEventInstance class="EVENT_FLAG_CALL">
<synopsis>Raised when a dial action has started.</synopsis>
@ -627,7 +638,8 @@ static struct ast_manager_event_blob *channel_newexten(
return NULL;
}
if (old_snapshot && ast_channel_snapshot_cep_equal(old_snapshot, new_snapshot)) {
if (old_snapshot && ast_channel_snapshot_cep_equal(old_snapshot, new_snapshot)
&& !strcmp(old_snapshot->appl, new_snapshot->appl)) {
return NULL;
}
@ -662,10 +674,28 @@ static struct ast_manager_event_blob *channel_new_callerid(
ast_describe_caller_presentation(new_snapshot->caller_pres));
}
static struct ast_manager_event_blob *channel_new_accountcode(
struct ast_channel_snapshot *old_snapshot,
struct ast_channel_snapshot *new_snapshot)
{
if (!old_snapshot || !new_snapshot) {
return NULL;
}
if (!strcmp(old_snapshot->accountcode, new_snapshot->accountcode)) {
return NULL;
}
return ast_manager_event_blob_create(
EVENT_FLAG_CALL, "NewAccountCode",
"OldAccountCode: %s\r\n", old_snapshot->accountcode);
}
channel_snapshot_monitor channel_monitors[] = {
channel_state_change,
channel_newexten,
channel_new_callerid
channel_new_callerid,
channel_new_accountcode
};
static void channel_snapshot_update(void *data, struct stasis_subscription *sub,

View File

@ -470,36 +470,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<ref type="function">Exception</ref>
</see-also>
</application>
<application name="ResetCDR" language="en_US">
<synopsis>
Resets the Call Data Record.
</synopsis>
<syntax>
<parameter name="options">
<optionlist>
<option name="w">
<para>Store the current CDR record before resetting it.</para>
</option>
<option name="a">
<para>Store any stacked records.</para>
</option>
<option name="v">
<para>Save CDR variables.</para>
</option>
<option name="e">
<para>Enable CDR only (negate effects of NoCDR).</para>
</option>
</optionlist>
</parameter>
</syntax>
<description>
<para>This application causes the Call Data Record to be reset.</para>
</description>
<see-also>
<ref type="application">ForkCDR</ref>
<ref type="application">NoCDR</ref>
</see-also>
</application>
<application name="Ringing" language="en_US">
<synopsis>
Indicate ringing tone.
@ -657,9 +627,11 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
</syntax>
<description>
<para>This application will set the channel's AMA Flags for billing purposes.</para>
<warning><para>This application is deprecated. Please use the CHANNEL function instead.</para></warning>
</description>
<see-also>
<ref type="function">CDR</ref>
<ref type="function">CHANNEL</ref>
</see-also>
</application>
<application name="Wait" language="en_US">
@ -1139,7 +1111,6 @@ static int pbx_builtin_background(struct ast_channel *, const char *);
static int pbx_builtin_wait(struct ast_channel *, const char *);
static int pbx_builtin_waitexten(struct ast_channel *, const char *);
static int pbx_builtin_incomplete(struct ast_channel *, const char *);
static int pbx_builtin_resetcdr(struct ast_channel *, const char *);
static int pbx_builtin_setamaflags(struct ast_channel *, const char *);
static int pbx_builtin_ringing(struct ast_channel *, const char *);
static int pbx_builtin_proceeding(struct ast_channel *, const char *);
@ -1326,7 +1297,6 @@ static struct pbx_builtin {
{ "Proceeding", pbx_builtin_proceeding },
{ "Progress", pbx_builtin_progress },
{ "RaiseException", pbx_builtin_raise_exception },
{ "ResetCDR", pbx_builtin_resetcdr },
{ "Ringing", pbx_builtin_ringing },
{ "SayAlpha", pbx_builtin_saycharacters },
{ "SayDigits", pbx_builtin_saydigits },
@ -1565,15 +1535,13 @@ int pbx_exec(struct ast_channel *c, /*!< Channel */
const char *saved_c_appl;
const char *saved_c_data;
if (ast_channel_cdr(c) && !ast_check_hangup(c))
ast_cdr_setapp(ast_channel_cdr(c), app->name, data);
/* save channel values */
saved_c_appl= ast_channel_appl(c);
saved_c_data= ast_channel_data(c);
ast_channel_appl_set(c, app->name);
ast_channel_data_set(c, data);
ast_channel_publish_snapshot(c);
if (app->module)
u = __ast_module_user_add(app->module, c);
@ -5713,10 +5681,6 @@ void ast_pbx_h_exten_run(struct ast_channel *chan, const char *context)
ast_channel_lock(chan);
if (ast_channel_cdr(chan) && ast_opt_end_cdr_before_h_exten) {
ast_cdr_end(ast_channel_cdr(chan));
}
/* Set h exten location */
if (context != ast_channel_context(chan)) {
ast_channel_context_set(chan, context);
@ -5797,10 +5761,6 @@ int ast_pbx_hangup_handler_run(struct ast_channel *chan)
return 0;
}
if (ast_channel_cdr(chan) && ast_opt_end_cdr_before_h_exten) {
ast_cdr_end(ast_channel_cdr(chan));
}
/*
* Make sure that the channel is marked as hungup since we are
* going to run the hangup handlers on it.
@ -6114,12 +6074,6 @@ static enum ast_pbx_result __ast_pbx_run(struct ast_channel *c,
set_ext_pri(c, "s", 1);
}
ast_channel_lock(c);
if (ast_channel_cdr(c)) {
/* allow CDR variables that have been collected after channel was created to be visible during call */
ast_cdr_update(c);
}
ast_channel_unlock(c);
for (;;) {
char dst_exten[256]; /* buffer to accumulate digits */
int pos = 0; /* XXX should check bounds */
@ -6229,11 +6183,6 @@ static enum ast_pbx_result __ast_pbx_run(struct ast_channel *c,
}
/* Call timed out with no special extension to jump to. */
}
ast_channel_lock(c);
if (ast_channel_cdr(c)) {
ast_cdr_update(c);
}
ast_channel_unlock(c);
error = 1;
break;
}
@ -6339,12 +6288,6 @@ static enum ast_pbx_result __ast_pbx_run(struct ast_channel *c,
}
}
}
ast_channel_lock(c);
if (ast_channel_cdr(c)) {
ast_verb(2, "CDR updated on %s\n",ast_channel_name(c));
ast_cdr_update(c);
}
ast_channel_unlock(c);
}
}
@ -9991,13 +9934,16 @@ static int pbx_outgoing_attempt(const char *type, struct ast_format_cap *cap, co
}
dialed = ast_dial_get_channel(outgoing->dial, 0);
if (!dialed) {
return -1;
}
ast_set_variables(dialed, vars);
if (account) {
ast_cdr_setaccount(dialed, account);
ast_channel_accountcode_set(dialed, account);
}
ast_set_flag(ast_channel_cdr(dialed), AST_CDR_FLAG_ORIGINATED);
ast_set_flag(ast_channel_flags(dialed), AST_FLAG_ORIGINATED);
if (!ast_strlen_zero(cid_num) && !ast_strlen_zero(cid_name)) {
struct ast_party_connected_line connected;
@ -10043,12 +9989,13 @@ static int pbx_outgoing_attempt(const char *type, struct ast_format_cap *cap, co
/* Wait for dialing to complete */
if (channel || synchronous) {
if (channel) {
ast_channel_ref(*channel);
ast_channel_unlock(*channel);
}
while (!outgoing->dialed) {
ast_cond_wait(&outgoing->cond, &outgoing->lock);
}
if (channel) {
if (channel && *channel) {
ast_channel_lock(*channel);
}
}
@ -10078,7 +10025,7 @@ static int pbx_outgoing_attempt(const char *type, struct ast_format_cap *cap, co
}
if (account) {
ast_cdr_setaccount(failed, account);
ast_channel_accountcode_set(failed, account);
}
set_ext_pri(failed, "failed", 1);
@ -10387,8 +10334,8 @@ static int pbx_builtin_busy(struct ast_channel *chan, const char *data)
/* Don't change state of an UP channel, just indicate
busy in audio */
if (ast_channel_state(chan) != AST_STATE_UP) {
ast_channel_hangupcause_set(chan, AST_CAUSE_BUSY);
ast_setstate(chan, AST_STATE_BUSY);
ast_cdr_busy(ast_channel_cdr(chan));
}
wait_for_hangup(chan, data);
return -1;
@ -10403,8 +10350,8 @@ static int pbx_builtin_congestion(struct ast_channel *chan, const char *data)
/* Don't change state of an UP channel, just indicate
congestion in audio */
if (ast_channel_state(chan) != AST_STATE_UP) {
ast_channel_hangupcause_set(chan, AST_CAUSE_CONGESTION);
ast_setstate(chan, AST_STATE_BUSY);
ast_cdr_congestion(ast_channel_cdr(chan));
}
wait_for_hangup(chan, data);
return -1;
@ -10416,7 +10363,6 @@ static int pbx_builtin_congestion(struct ast_channel *chan, const char *data)
static int pbx_builtin_answer(struct ast_channel *chan, const char *data)
{
int delay = 0;
int answer_cdr = 1;
char *parse;
AST_DECLARE_APP_ARGS(args,
AST_APP_ARG(delay);
@ -10424,7 +10370,7 @@ static int pbx_builtin_answer(struct ast_channel *chan, const char *data)
);
if (ast_strlen_zero(data)) {
return __ast_answer(chan, 0, 1);
return __ast_answer(chan, 0);
}
parse = ast_strdupa(data);
@ -10439,10 +10385,12 @@ static int pbx_builtin_answer(struct ast_channel *chan, const char *data)
}
if (!ast_strlen_zero(args.answer_cdr) && !strcasecmp(args.answer_cdr, "nocdr")) {
answer_cdr = 0;
if (ast_cdr_set_property(ast_channel_name(chan), AST_CDR_FLAG_DISABLE_ALL)) {
ast_log(AST_LOG_WARNING, "Failed to disable CDR on %s\n", ast_channel_name(chan));
}
}
return __ast_answer(chan, delay, answer_cdr);
return __ast_answer(chan, delay);
}
static int pbx_builtin_incomplete(struct ast_channel *chan, const char *data)
@ -10459,7 +10407,7 @@ static int pbx_builtin_incomplete(struct ast_channel *chan, const char *data)
if (ast_check_hangup(chan)) {
return -1;
} else if (ast_channel_state(chan) != AST_STATE_UP && answer) {
__ast_answer(chan, 0, 1);
__ast_answer(chan, 0);
}
ast_indicate(chan, AST_CONTROL_INCOMPLETE);
@ -10467,39 +10415,30 @@ static int pbx_builtin_incomplete(struct ast_channel *chan, const char *data)
return AST_PBX_INCOMPLETE;
}
AST_APP_OPTIONS(resetcdr_opts, {
AST_APP_OPTION('w', AST_CDR_FLAG_POSTED),
AST_APP_OPTION('a', AST_CDR_FLAG_LOCKED),
AST_APP_OPTION('v', AST_CDR_FLAG_KEEP_VARS),
AST_APP_OPTION('e', AST_CDR_FLAG_POST_ENABLE),
});
/*!
* \ingroup applications
*/
static int pbx_builtin_resetcdr(struct ast_channel *chan, const char *data)
{
char *args;
struct ast_flags flags = { 0 };
if (!ast_strlen_zero(data)) {
args = ast_strdupa(data);
ast_app_parse_options(resetcdr_opts, &flags, NULL, args);
}
ast_cdr_reset(ast_channel_cdr(chan), &flags);
return 0;
}
/*!
* \ingroup applications
*/
static int pbx_builtin_setamaflags(struct ast_channel *chan, const char *data)
{
ast_log(AST_LOG_WARNING, "The SetAMAFlags application is deprecated. Please use the CHANNEL function instead.\n");
if (ast_strlen_zero(data)) {
ast_log(AST_LOG_WARNING, "No parameter passed to SetAMAFlags\n");
return 0;
}
/* Copy the AMA Flags as specified */
ast_channel_lock(chan);
ast_cdr_setamaflags(chan, data ? data : "");
if (isdigit(data[0])) {
int amaflags;
if (sscanf(data, "%30d", &amaflags) != 1) {
ast_log(AST_LOG_WARNING, "Unable to set AMA flags on channel %s\n", ast_channel_name(chan));
ast_channel_unlock(chan);
return 0;
}
ast_channel_amaflags_set(chan, amaflags);
} else {
ast_channel_amaflags_set(chan, ast_channel_string2amaflag(data));
}
ast_channel_unlock(chan);
return 0;
}

View File

@ -32,6 +32,7 @@
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/astobj2.h"
#include "asterisk/stasis_internal.h"
#include "asterisk/stasis.h"
#include "asterisk/threadpool.h"
#include "asterisk/taskprocessor.h"
@ -170,7 +171,7 @@ static void subscription_invoke(struct stasis_subscription *sub,
static void send_subscription_change_message(struct stasis_topic *topic, char *uniqueid, char *description);
static struct stasis_subscription *__stasis_subscribe(
struct stasis_subscription *internal_stasis_subscribe(
struct stasis_topic *topic,
stasis_subscription_cb callback,
void *data,
@ -213,7 +214,7 @@ struct stasis_subscription *stasis_subscribe(
stasis_subscription_cb callback,
void *data)
{
return __stasis_subscribe(topic, callback, data, 1);
return internal_stasis_subscribe(topic, callback, data, 1);
}
struct stasis_subscription *stasis_unsubscribe(struct stasis_subscription *sub)
@ -476,7 +477,7 @@ struct stasis_subscription *stasis_forward_all(struct stasis_topic *from_topic,
* mailbox. Otherwise, messages forwarded to the same topic from
* different topics may get reordered. Which is bad.
*/
sub = __stasis_subscribe(from_topic, stasis_forward_cb, to_topic, 0);
sub = internal_stasis_subscribe(from_topic, stasis_forward_cb, to_topic, 0);
if (sub) {
/* hold a ref to to_topic for this forwarding subscription */
ao2_ref(to_topic, +1);

View File

@ -33,6 +33,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/astobj2.h"
#include "asterisk/hashtab.h"
#include "asterisk/stasis_internal.h"
#include "asterisk/stasis.h"
#include "asterisk/utils.h"
@ -486,7 +487,7 @@ struct stasis_caching_topic *stasis_caching_topic_create(struct stasis_topic *or
caching_topic->id_fn = id_fn;
sub = stasis_subscribe(original_topic, caching_topic_exec, caching_topic);
sub = internal_stasis_subscribe(original_topic, caching_topic_exec, caching_topic, 0);
if (sub == NULL) {
return NULL;
}

View File

@ -156,10 +156,17 @@ struct ast_channel_snapshot *ast_channel_snapshot_create(struct ast_channel *cha
ast_string_field_set(snapshot, exten, ast_channel_exten(chan));
ast_string_field_set(snapshot, caller_name,
S_COR(ast_channel_caller(chan)->id.name.valid, ast_channel_caller(chan)->id.name.str, ""));
S_COR(ast_channel_caller(chan)->ani.name.valid, ast_channel_caller(chan)->ani.name.str,
S_COR(ast_channel_caller(chan)->id.name.valid, ast_channel_caller(chan)->id.name.str, "")));
ast_string_field_set(snapshot, caller_number,
S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, ""));
S_COR(ast_channel_caller(chan)->ani.number.valid, ast_channel_caller(chan)->ani.number.str,
S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, "")));
ast_string_field_set(snapshot, caller_dnid, S_OR(ast_channel_dialed(chan)->number.str, ""));
ast_string_field_set(snapshot, caller_subaddr,
S_COR(ast_channel_caller(chan)->ani.subaddress.valid, ast_channel_caller(chan)->ani.subaddress.str,
S_COR(ast_channel_caller(chan)->id.subaddress.valid, ast_channel_caller(chan)->id.subaddress.str, "")));
ast_string_field_set(snapshot, dialed_subaddr,
S_COR(ast_channel_dialed(chan)->subaddress.valid, ast_channel_dialed(chan)->subaddress.str, ""));
ast_string_field_set(snapshot, caller_ani,
S_COR(ast_channel_caller(chan)->ani.number.valid, ast_channel_caller(chan)->ani.number.str, ""));
ast_string_field_set(snapshot, caller_rdnis,
@ -493,20 +500,6 @@ struct ast_json *ast_multi_channel_blob_get_json(struct ast_multi_channel_blob *
return obj->blob;
}
void ast_channel_publish_blob(struct ast_channel *chan, struct stasis_message_type *type, struct ast_json *blob)
{
RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup);
if (!blob) {
blob = ast_json_null();
}
message = ast_channel_blob_create(chan, type, blob);
if (message) {
stasis_publish(ast_channel_topic(chan), message);
}
}
void ast_channel_publish_snapshot(struct ast_channel *chan)
{
RAII_VAR(struct ast_channel_snapshot *, snapshot, NULL, ao2_cleanup);
@ -526,6 +519,20 @@ void ast_channel_publish_snapshot(struct ast_channel *chan)
stasis_publish(ast_channel_topic(chan), message);
}
void ast_channel_publish_blob(struct ast_channel *chan, struct stasis_message_type *type, struct ast_json *blob)
{
RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup);
if (!blob) {
blob = ast_json_null();
}
message = ast_channel_blob_create(chan, type, blob);
if (message) {
stasis_publish(ast_channel_topic(chan), message);
}
}
void ast_channel_publish_varset(struct ast_channel *chan, const char *name, const char *value)
{
RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);

View File

@ -48,6 +48,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$");
#include "asterisk/stasis.h"
#include "asterisk/json.h"
#include "asterisk/astobj2.h"
#include "asterisk/stasis.h"
#include "asterisk/json.h"
/*! \since 12
* \brief The topic for test suite messages
@ -80,9 +82,11 @@ struct ast_test {
* CLI in addition to being saved off in status_str.
*/
struct ast_cli_args *cli;
enum ast_test_result_state state; /*!< current test state */
unsigned int time; /*!< time in ms test took */
ast_test_cb_t *cb; /*!< test callback function */
enum ast_test_result_state state; /*!< current test state */
unsigned int time; /*!< time in ms test took */
ast_test_cb_t *cb; /*!< test callback function */
ast_test_init_cb_t *init_cb; /*!< test init function */
ast_test_cleanup_cb_t *cleanup_cb; /*!< test cleanup function */
AST_LIST_ENTRY(ast_test) entry;
};
@ -159,6 +163,40 @@ int __ast_test_status_update(const char *file, const char *func, int line, struc
return 0;
}
int ast_test_register_init(const char *category, ast_test_init_cb_t *cb)
{
struct ast_test *test;
int registered = 1;
AST_LIST_LOCK(&tests);
AST_LIST_TRAVERSE(&tests, test, entry) {
if (!(test_cat_cmp(test->info.category, category))) {
test->init_cb = cb;
registered = 0;
}
}
AST_LIST_UNLOCK(&tests);
return registered;
}
int ast_test_register_cleanup(const char *category, ast_test_cleanup_cb_t *cb)
{
struct ast_test *test;
int registered = 1;
AST_LIST_LOCK(&tests);
AST_LIST_TRAVERSE(&tests, test, entry) {
if (!(test_cat_cmp(test->info.category, category))) {
test->cleanup_cb = cb;
registered = 0;
}
}
AST_LIST_UNLOCK(&tests);
return registered;
}
int ast_test_register(ast_test_cb_t *cb)
{
struct ast_test *test;
@ -203,14 +241,34 @@ int ast_test_unregister(ast_test_cb_t *cb)
static void test_execute(struct ast_test *test)
{
struct timeval begin;
enum ast_test_result_state result;
ast_str_reset(test->status_str);
begin = ast_tvnow();
test->state = test->cb(&test->info, TEST_EXECUTE, test);
if (test->init_cb && test->init_cb(&test->info, test)) {
test->state = AST_TEST_FAIL;
goto exit;
}
result = test->cb(&test->info, TEST_EXECUTE, test);
if (test->state != AST_TEST_FAIL) {
test->state = result;
}
if (test->cleanup_cb && test->cleanup_cb(&test->info, test)) {
test->state = AST_TEST_FAIL;
}
exit:
test->time = ast_tvdiff_ms(ast_tvnow(), begin);
}
void ast_test_set_result(struct ast_test *test, enum ast_test_result_state state)
{
if (test->state == AST_TEST_FAIL || state == AST_TEST_NOT_RUN) {
return;
}
test->state = state;
}
static void test_xml_entry(struct ast_test *test, FILE *f)
{
if (!f || !test || test->state == AST_TEST_NOT_RUN) {

View File

@ -1546,6 +1546,15 @@ int ast_remaining_ms(struct timeval start, int max_ms)
return ms;
}
void ast_format_duration_hh_mm_ss(int duration, char *buf, size_t length)
{
int durh, durm, durs;
durh = duration / 3600;
durm = (duration % 3600) / 60;
durs = duration % 60;
snprintf(buf, length, "%02d:%02d:%02d", durh, durm, durs);
}
#undef ONE_MILLION
#ifndef linux

View File

@ -3625,11 +3625,6 @@ static enum agi_result agi_handle_command(struct ast_channel *chan, AGI *agi, ch
the module we are using */
if (c->mod != ast_module_info->self)
ast_module_ref(c->mod);
/* If the AGI command being executed is an actual application (using agi exec)
the app field will be updated in pbx_exec via handle_exec */
if (ast_channel_cdr(chan) && !ast_check_hangup(chan) && strcasecmp(argv[0], "EXEC"))
ast_cdr_setapp(ast_channel_cdr(chan), "AGI", buf);
res = c->handler(chan, agi, argc, argv);
if (c->mod != ast_module_info->self)
ast_module_unref(c->mod);

View File

@ -791,7 +791,7 @@ static int cdr_handler(struct ast_cdr *cdr)
AST_RWLIST_TRAVERSE(&(tbl->columns), col, list) {
if (col->isint) {
ast_cdr_getvar(cdr, col->name, &tmp, workspace, sizeof(workspace), 0, 1);
ast_cdr_format_var(cdr, col->name, &tmp, workspace, sizeof(workspace), 1);
if (!tmp) {
continue;
}
@ -800,7 +800,7 @@ static int cdr_handler(struct ast_cdr *cdr)
ast_str_append(&sql2, 0, "%s%d", first ? "" : ",", scannum);
}
} else {
ast_cdr_getvar(cdr, col->name, &tmp, workspace, sizeof(workspace), 0, 0);
ast_cdr_format_var(cdr, col->name, &tmp, workspace, sizeof(workspace), 0);
if (!tmp) {
continue;
}

View File

@ -692,18 +692,10 @@ static int start_monitor_exec(struct ast_channel *chan, const char *data)
}
if (!ast_strlen_zero(urlprefix) && !ast_strlen_zero(args.fname_base)) {
struct ast_cdr *chan_cdr;
snprintf(tmp, sizeof(tmp), "%s/%s.%s", urlprefix, args.fname_base,
((strcmp(args.format, "gsm")) ? "wav" : "gsm"));
ast_channel_lock(chan);
if (!ast_channel_cdr(chan)) {
if (!(chan_cdr = ast_cdr_alloc())) {
ast_channel_unlock(chan);
return -1;
}
ast_channel_cdr_set(chan, chan_cdr);
}
ast_cdr_setuserfield(chan, tmp);
ast_cdr_setuserfield(ast_channel_name(chan), tmp);
ast_channel_unlock(chan);
}
if (waitforbridge) {

View File

@ -42,10 +42,9 @@ static void *app_control_answer(struct stasis_app_control *control,
struct ast_channel *chan, void *data)
{
const int delay = 0;
const int cdr_answer = 1;
ast_debug(3, "%s: Answering",
stasis_app_control_get_channel_id(control));
return __ast_answer(chan, delay, cdr_answer) == 0 ? &OK : &FAIL;
return __ast_answer(chan, delay) == 0 ? &OK : &FAIL;
}
int stasis_app_control_answer(struct stasis_app_control *control)

2413
tests/test_cdr.c Normal file

File diff suppressed because it is too large Load Diff