summaryrefslogtreecommitdiff
path: root/main/cdr.c
diff options
context:
space:
mode:
authorMatthew Jordan <mjordan@digium.com>2013-06-28 15:50:56 +0000
committerMatthew Jordan <mjordan@digium.com>2013-06-28 15:50:56 +0000
commit6cc03db6426b70dce47471a20ab5d42f163dc419 (patch)
treef4c1c79573c63e00ef165e4784a6b806e1634593 /main/cdr.c
parentf6211b6b70414499878afd8101be6018dc08ec6a (diff)
Better handle parking in CDRs
Parking typically occurs when a channel is transferred to a parking extension. When this occurs, the channel never actually hits the dialplan if the extension it was transferred to was a "parking extension", that is, the extension in the first priority calls the Park application. Instead, the channel is immediately sent into the holding bridge acting as the parking bridge. This is problematic. Because we never go out to the dialplan, the CDRs won't transition properly and the application field will not be set to "Park". CDRs typically swallow holding bridges, so the CDR itself won't even be generated. This patch handles this by pulling out the holding bridge handling into its own CDR state. CDRs now have an explicit parking state that accounts for this specific subclass of the holding bridge. In addition, we handle the parking stasis message to set application specific data on the CDR such that the last known application for the CDR properly reflects "Park". This is a bit sad since we're working around the odd internal implementation of parking that exists in Asterisk (and that we had to maintain in order to continue to meet some odd use cases of parking), but at least the code to handle that is where it belongs: in CDRs as opposed to sprinkled liberally throughout the codebase. This patch also properly clears the OUTBOUND channel flag from a channel when it leaves a bridge, and tweaks up dialing handling to properly compare the correct CDR with the channel calling/being dialed. git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@393130 65c4cc65-6c06-0410-ace0-fbb531ad65f3
Diffstat (limited to 'main/cdr.c')
-rw-r--r--main/cdr.c382
1 files changed, 320 insertions, 62 deletions
diff --git a/main/cdr.c b/main/cdr.c
index 64a2bb490..dbce8e5c4 100644
--- a/main/cdr.c
+++ b/main/cdr.c
@@ -65,6 +65,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/data.h"
#include "asterisk/config_options.h"
#include "asterisk/json.h"
+#include "asterisk/parking.h"
#include "asterisk/stasis.h"
#include "asterisk/stasis_channels.h"
#include "asterisk/stasis_bridging.h"
@@ -329,6 +330,9 @@ static struct stasis_subscription *bridge_subscription;
/*! \brief Our subscription for channels */
static struct stasis_subscription *channel_subscription;
+/*! \brief Our subscription for parking */
+static struct stasis_subscription *parking_subscription;
+
/*! \brief The parent topic for all topics we want to aggregate for CDRs */
static struct stasis_topic *cdr_topic;
@@ -416,19 +420,38 @@ struct cdr_object_fn_table {
* of this channel into the bridge is handled by the higher level message
* handler.
*
+ * Note that this handler is for when a channel enters into a "normal"
+ * bridge, where people actually talk to each other. Parking is its own
+ * thing.
+ *
* \param cdr The \ref cdr_object
* \param bridge The bridge that the Party A just entered into
* \param channel The \ref ast_channel_snapshot for this CDR's Party A
*
* \retval 0 This CDR found a Party B for itself and updated it, or there
* was no Party B to find (we're all alone)
- * \retval 1 This CDR couldn't find a Party B, and there were options
+ * \retval 1 This CDR couldn't find a Party B and channels were in the bridge
*/
int (* const process_bridge_enter)(struct cdr_object *cdr,
struct ast_bridge_snapshot *bridge,
struct ast_channel_snapshot *channel);
/*!
+ * \brief Process entering into a parking bridge.
+ *
+ * \param cdr The \ref cdr_object
+ * \param bridge The parking bridge that Party A just entered into
+ * \param channel The \ref ast_channel_snapshot for this CDR's Party A
+ *
+ * \retval 0 This CDR successfully transitioned itself into the parked state
+ * \retval 1 This CDR couldn't handle the parking transition and we need a
+ * new CDR.
+ */
+ int (* const process_parking_bridge_enter)(struct cdr_object *cdr,
+ struct ast_bridge_snapshot *bridge,
+ struct ast_channel_snapshot *channel);
+
+ /*!
* \brief Process the leaving of a bridge by this CDR.
*
* \param cdr The \ref cdr_object
@@ -441,16 +464,30 @@ struct cdr_object_fn_table {
int (* const process_bridge_leave)(struct cdr_object *cdr,
struct ast_bridge_snapshot *bridge,
struct ast_channel_snapshot *channel);
+
+ /*!
+ * \brief Process an update informing us that the channel got itself parked
+ *
+ * \param cdr The \ref cdr_object
+ * \param channel The parking information for this CDR's party A
+ *
+ * \retval 0 This CDR successfully parked itself
+ * \retval 1 This CDR couldn't handle the park
+ */
+ int (* const process_parked_channel)(struct cdr_object *cdr,
+ struct ast_parked_call_payload *parking_info);
};
static int base_process_party_a(struct cdr_object *cdr, struct ast_channel_snapshot *snapshot);
static int base_process_bridge_leave(struct cdr_object *cdr, struct ast_bridge_snapshot *bridge, struct ast_channel_snapshot *channel);
static int base_process_dial_end(struct cdr_object *cdr, struct ast_channel_snapshot *caller, struct ast_channel_snapshot *peer, const char *dial_status);
+static int base_process_parked_channel(struct cdr_object *cdr, struct ast_parked_call_payload *parking_info);
static void single_state_init_function(struct cdr_object *cdr);
static void single_state_process_party_b(struct cdr_object *cdr, struct ast_channel_snapshot *snapshot);
static int single_state_process_dial_begin(struct cdr_object *cdr, struct ast_channel_snapshot *caller, struct ast_channel_snapshot *peer);
static int single_state_process_bridge_enter(struct cdr_object *cdr, struct ast_bridge_snapshot *bridge, struct ast_channel_snapshot *channel);
+static int single_state_process_parking_bridge_enter(struct cdr_object *cdr, struct ast_bridge_snapshot *bridge, struct ast_channel_snapshot *channel);
/*!
* \brief The virtual table for the Single state.
@@ -471,7 +508,9 @@ struct cdr_object_fn_table single_state_fn_table = {
.process_dial_begin = single_state_process_dial_begin,
.process_dial_end = base_process_dial_end,
.process_bridge_enter = single_state_process_bridge_enter,
+ .process_parking_bridge_enter = single_state_process_parking_bridge_enter,
.process_bridge_leave = base_process_bridge_leave,
+ .process_parked_channel = base_process_parked_channel,
};
static void dial_state_process_party_b(struct cdr_object *cdr, struct ast_channel_snapshot *snapshot);
@@ -505,6 +544,7 @@ struct cdr_object_fn_table dial_state_fn_table = {
static int dialed_pending_state_process_party_a(struct cdr_object *cdr, struct ast_channel_snapshot *snapshot);
static int dialed_pending_state_process_dial_begin(struct cdr_object *cdr, struct ast_channel_snapshot *caller, struct ast_channel_snapshot *peer);
static int dialed_pending_state_process_bridge_enter(struct cdr_object *cdr, struct ast_bridge_snapshot *bridge, struct ast_channel_snapshot *channel);
+static int dialed_pending_state_process_parking_bridge_enter(struct cdr_object *cdr, struct ast_bridge_snapshot *bridge, struct ast_channel_snapshot *channel);
/*!
* \brief The virtual table for the Dialed Pending state.
@@ -529,7 +569,9 @@ struct cdr_object_fn_table dialed_pending_state_fn_table = {
.process_party_a = dialed_pending_state_process_party_a,
.process_dial_begin = dialed_pending_state_process_dial_begin,
.process_bridge_enter = dialed_pending_state_process_bridge_enter,
+ .process_parking_bridge_enter = dialed_pending_state_process_parking_bridge_enter,
.process_bridge_leave = base_process_bridge_leave,
+ .process_parked_channel = base_process_parked_channel,
};
static void bridge_state_process_party_b(struct cdr_object *cdr, struct ast_channel_snapshot *snapshot);
@@ -550,12 +592,14 @@ struct cdr_object_fn_table bridge_state_fn_table = {
.process_party_a = base_process_party_a,
.process_party_b = bridge_state_process_party_b,
.process_bridge_leave = bridge_state_process_bridge_leave,
+ .process_parked_channel = base_process_parked_channel,
};
static void pending_state_init_function(struct cdr_object *cdr);
static int pending_state_process_party_a(struct cdr_object *cdr, struct ast_channel_snapshot *snapshot);
static int pending_state_process_dial_begin(struct cdr_object *cdr, struct ast_channel_snapshot *caller, struct ast_channel_snapshot *peer);
static int pending_state_process_bridge_enter(struct cdr_object *cdr, struct ast_bridge_snapshot *bridge, struct ast_channel_snapshot *channel);
+static int pending_state_process_parking_bridge_enter(struct cdr_object *cdr, struct ast_bridge_snapshot *bridge, struct ast_channel_snapshot *channel);
/*!
* \brief The virtual table for the Pending state
@@ -576,7 +620,27 @@ struct cdr_object_fn_table bridged_pending_state_fn_table = {
.process_dial_begin = pending_state_process_dial_begin,
.process_dial_end = base_process_dial_end,
.process_bridge_enter = pending_state_process_bridge_enter,
+ .process_parking_bridge_enter = pending_state_process_parking_bridge_enter,
.process_bridge_leave = base_process_bridge_leave,
+ .process_parked_channel = base_process_parked_channel,
+};
+
+static int parked_state_process_bridge_leave(struct cdr_object *cdr, struct ast_bridge_snapshot *bridge, struct ast_channel_snapshot *channel);
+
+/*!
+ * \brief The virtual table for the Parked state
+ *
+ * Parking is weird. Unlike typical bridges, it has to be treated somewhat
+ * uniquely - a channel in a parking bridge (which is a subclass of a holding
+ * bridge) has to be handled as if the channel went into an application.
+ * However, when the channel comes out, we need a new CDR - unlike the Single
+ * state.
+ */
+struct cdr_object_fn_table parked_state_fn_table = {
+ .name = "Parked",
+ .process_party_a = base_process_party_a,
+ .process_bridge_leave = parked_state_process_bridge_leave,
+ .process_parked_channel = base_process_parked_channel,
};
static void finalized_state_init_function(struct cdr_object *cdr);
@@ -1239,7 +1303,9 @@ static int base_process_party_a(struct cdr_object *cdr, struct ast_channel_snaps
* of "AppDialX". Prevent that, and any other application changes we might not want
* here.
*/
- if (!ast_strlen_zero(snapshot->appl) && (strncasecmp(snapshot->appl, "appdial", 7) || ast_strlen_zero(cdr->appl))) {
+ if (!ast_strlen_zero(snapshot->appl)
+ && (strncasecmp(snapshot->appl, "appdial", 7) || ast_strlen_zero(cdr->appl))
+ && !ast_test_flag(&cdr->flags, AST_CDR_LOCK_APP)) {
ast_string_field_set(cdr, appl, snapshot->appl);
ast_string_field_set(cdr, data, snapshot->data);
}
@@ -1265,6 +1331,26 @@ static int base_process_dial_end(struct cdr_object *cdr, struct ast_channel_snap
return 0;
}
+static int base_process_parked_channel(struct cdr_object *cdr, struct ast_parked_call_payload *parking_info)
+{
+ char park_info[128];
+
+ ast_assert(!strcmp(parking_info->parkee->name, cdr->party_a.snapshot->name));
+
+ /* Update Party A information regardless */
+ cdr->fn_table->process_party_a(cdr, parking_info->parkee);
+
+ /* Fake out where we're parked */
+ ast_string_field_set(cdr, appl, "Park");
+ snprintf(park_info, sizeof(park_info), "%s:%u", parking_info->parkinglot, parking_info->parkingspace);
+ ast_string_field_set(cdr, data, park_info);
+
+ /* Prevent any further changes to the App/Data fields for this record */
+ ast_set_flag(&cdr->flags, AST_CDR_LOCK_APP);
+
+ return 0;
+}
+
/* SINGLE STATE */
static void single_state_init_function(struct cdr_object *cdr) {
@@ -1395,6 +1481,13 @@ static int single_state_process_bridge_enter(struct cdr_object *cdr, struct ast_
return success;
}
+static int single_state_process_parking_bridge_enter(struct cdr_object *cdr, struct ast_bridge_snapshot *bridge, struct ast_channel_snapshot *channel)
+{
+ cdr_object_transition_state(cdr, &parked_state_fn_table);
+ return 0;
+}
+
+
/* DIAL STATE */
static void dial_state_process_party_b(struct cdr_object *cdr, struct ast_channel_snapshot *snapshot)
@@ -1567,14 +1660,18 @@ static int dialed_pending_state_process_bridge_enter(struct cdr_object *cdr, str
return cdr->fn_table->process_bridge_enter(cdr, bridge, channel);
}
-static int dialed_pending_state_process_dial_begin(struct cdr_object *cdr, struct ast_channel_snapshot *caller, struct ast_channel_snapshot *peer)
+static int dialed_pending_state_process_parking_bridge_enter(struct cdr_object *cdr, struct ast_bridge_snapshot *bridge, struct ast_channel_snapshot *channel)
{
- struct cdr_object *new_cdr;
+ /* We can't handle this as we have a Party B - ask for a new one */
+ return 1;
+}
+static int dialed_pending_state_process_dial_begin(struct cdr_object *cdr, struct ast_channel_snapshot *caller, struct ast_channel_snapshot *peer)
+{
cdr_object_transition_state(cdr, &finalized_state_fn_table);
- new_cdr = cdr_object_create_and_append(cdr);
- cdr_object_transition_state(cdr, &single_state_fn_table);
- return new_cdr->fn_table->process_dial_begin(cdr, caller, peer);
+
+ /* Ask for a new CDR */
+ return 1;
}
/* BRIDGE STATE */
@@ -1647,6 +1744,25 @@ static int pending_state_process_bridge_enter(struct cdr_object *cdr, struct ast
return cdr->fn_table->process_bridge_enter(cdr, bridge, channel);
}
+static int pending_state_process_parking_bridge_enter(struct cdr_object *cdr, struct ast_bridge_snapshot *bridge, struct ast_channel_snapshot *channel)
+{
+ cdr_object_transition_state(cdr, &single_state_fn_table);
+ ast_cdr_clear_property(cdr->name, AST_CDR_FLAG_DISABLE);
+ return cdr->fn_table->process_parking_bridge_enter(cdr, bridge, channel);
+}
+
+/* PARKED STATE */
+
+static int parked_state_process_bridge_leave(struct cdr_object *cdr, struct ast_bridge_snapshot *bridge, struct ast_channel_snapshot *channel)
+{
+ if (strcmp(cdr->party_a.snapshot->name, channel->name)) {
+ return 1;
+ }
+ cdr_object_transition_state(cdr, &finalized_state_fn_table);
+
+ return 0;
+}
+
/* FINALIZED STATE */
static void finalized_state_init_function(struct cdr_object *cdr)
@@ -1688,16 +1804,14 @@ static void handle_dial_message(void *data, struct stasis_subscription *sub, str
struct ast_multi_channel_blob *payload = stasis_message_data(message);
struct ast_channel_snapshot *caller;
struct ast_channel_snapshot *peer;
+ struct ast_channel_snapshot *party_a_snapshot;
+ struct ast_channel_snapshot *party_b_snapshot;
struct cdr_object_snapshot *party_a;
- struct cdr_object_snapshot *party_b;
struct cdr_object *it_cdr;
struct ast_json *dial_status_blob;
const char *dial_status = NULL;
int res = 1;
- CDR_DEBUG(mod_cfg, "Dial message: %u.%08u\n", (unsigned int)stasis_message_timestamp(message)->tv_sec, (unsigned int)stasis_message_timestamp(message)->tv_usec);
- ast_assert(payload != NULL);
-
caller = ast_multi_channel_blob_get_channel(payload, "caller");
peer = ast_multi_channel_blob_get_channel(payload, "peer");
if (!peer && !caller) {
@@ -1708,6 +1822,13 @@ static void handle_dial_message(void *data, struct stasis_subscription *sub, str
dial_status = ast_json_string_get(dial_status_blob);
}
+ CDR_DEBUG(mod_cfg, "Dial %s message for %s, %s: %u.%08u\n",
+ ast_strlen_zero(dial_status) ? "Begin" : "End",
+ caller ? caller->name : "(none)",
+ peer ? peer->name : "(none)",
+ (unsigned int)stasis_message_timestamp(message)->tv_sec,
+ (unsigned int)stasis_message_timestamp(message)->tv_usec);
+
/* Figure out who is running this show */
if (caller) {
cdr_caller = ao2_find(active_cdrs_by_channel, caller->name, OBJ_KEY);
@@ -1716,22 +1837,24 @@ static void handle_dial_message(void *data, struct stasis_subscription *sub, str
cdr_peer = ao2_find(active_cdrs_by_channel, peer->name, OBJ_KEY);
}
if (cdr_caller && cdr_peer) {
- party_a = cdr_object_pick_party_a(&cdr_caller->party_a, &cdr_peer->party_a);
- if (!strcmp(party_a->snapshot->name, cdr_caller->party_a.snapshot->name)) {
+ party_a = cdr_object_pick_party_a(&cdr_caller->last->party_a, &cdr_peer->last->party_a);
+ if (!strcmp(party_a->snapshot->name, cdr_caller->last->party_a.snapshot->name)) {
cdr = cdr_caller;
- party_b = &cdr_peer->party_a;
+ party_a_snapshot = caller;
+ party_b_snapshot = peer;
} else {
cdr = cdr_peer;
- party_b = &cdr_caller->party_a;
+ party_a_snapshot = peer;
+ party_b_snapshot = caller;
}
} else if (cdr_caller) {
cdr = cdr_caller;
- party_a = &cdr_caller->party_a;
- party_b = NULL;
+ party_a_snapshot = caller;
+ party_b_snapshot = NULL;
} else if (cdr_peer) {
cdr = cdr_peer;
- party_a = NULL;
- party_b = &cdr_peer->party_a;
+ party_a_snapshot = NULL;
+ party_b_snapshot = peer;
} else {
return;
}
@@ -1744,22 +1867,22 @@ static void handle_dial_message(void *data, struct stasis_subscription *sub, str
}
CDR_DEBUG(mod_cfg, "%p - Processing Dial Begin message for channel %s, peer %s\n",
cdr,
- party_a ? party_a->snapshot->name : "(none)",
- party_b ? party_b->snapshot->name : "(none)");
+ party_a_snapshot ? party_a_snapshot->name : "(none)",
+ party_b_snapshot ? party_b_snapshot->name : "(none)");
res &= it_cdr->fn_table->process_dial_begin(it_cdr,
- party_a ? party_a->snapshot : NULL,
- party_b ? party_b->snapshot : NULL);
+ party_a_snapshot,
+ party_b_snapshot);
} else {
if (!it_cdr->fn_table->process_dial_end) {
continue;
}
CDR_DEBUG(mod_cfg, "%p - Processing Dial End message for channel %s, peer %s\n",
cdr,
- party_a ? party_a->snapshot->name : "(none)",
- party_b ? party_b->snapshot->name : "(none)");
+ party_a_snapshot ? party_a_snapshot->name : "(none)",
+ party_b_snapshot ? party_b_snapshot->name : "(none)");
it_cdr->fn_table->process_dial_end(it_cdr,
- party_a ? party_a->snapshot : NULL,
- party_b ? party_b->snapshot : NULL,
+ party_a_snapshot,
+ party_b_snapshot,
dial_status);
}
}
@@ -1773,8 +1896,8 @@ static void handle_dial_message(void *data, struct stasis_subscription *sub, str
return;
}
new_cdr->fn_table->process_dial_begin(new_cdr,
- party_a ? party_a->snapshot : NULL,
- party_b ? party_b->snapshot : NULL);
+ party_a_snapshot,
+ party_b_snapshot);
}
ao2_unlock(cdr);
}
@@ -1932,7 +2055,9 @@ static void handle_channel_cache_message(void *data, struct stasis_subscription
/* We're not hung up and we have a new snapshot - we need a new CDR */
struct cdr_object *new_cdr;
new_cdr = cdr_object_create_and_append(cdr);
- new_cdr->fn_table->process_party_a(new_cdr, new_snapshot);
+ if (new_cdr) {
+ new_cdr->fn_table->process_party_a(new_cdr, new_snapshot);
+ }
}
} else {
CDR_DEBUG(mod_cfg, "%p - Beginning finalize/dispatch for %s\n", cdr, old_snapshot->name);
@@ -1998,7 +2123,7 @@ static int filter_bridge_messages(struct ast_bridge_snapshot *bridge)
/* Ignore holding bridge technology messages. We treat this simply as an application
* that a channel enters into.
*/
- if (!strcmp(bridge->technology, "holding_bridge")) {
+ if (!strcmp(bridge->technology, "holding_bridge") && strcmp(bridge->subclass, "parking")) {
return 1;
}
return 0;
@@ -2006,8 +2131,10 @@ static int filter_bridge_messages(struct ast_bridge_snapshot *bridge)
/*!
* \brief Handler for when a channel leaves a bridge
- * \param bridge The \ref ast_bridge_snapshot representing the bridge
- * \param channel The \ref ast_channel_snapshot representing the channel
+ * \param data Passed on
+ * \param sub The stasis subscription for this message callback
+ * \param topic The topic this message was published for
+ * \param message The message - hopefully a bridge one!
*/
static void handle_bridge_leave_message(void *data, struct stasis_subscription *sub,
struct stasis_topic *topic, struct stasis_message *message)
@@ -2032,7 +2159,10 @@ static void handle_bridge_leave_message(void *data, struct stasis_subscription *
return;
}
- CDR_DEBUG(mod_cfg, "Bridge Leave message: %u.%08u\n", (unsigned int)stasis_message_timestamp(message)->tv_sec, (unsigned int)stasis_message_timestamp(message)->tv_usec);
+ CDR_DEBUG(mod_cfg, "Bridge Leave message for %s: %u.%08u\n",
+ channel->name,
+ (unsigned int)stasis_message_timestamp(message)->tv_sec,
+ (unsigned int)stasis_message_timestamp(message)->tv_usec);
if (!cdr) {
ast_log(AST_LOG_WARNING, "No CDR for channel %s\n", channel->name);
@@ -2057,19 +2187,25 @@ static void handle_bridge_leave_message(void *data, struct stasis_subscription *
return;
}
- ao2_unlink(active_cdrs_by_bridge, cdr);
+ if (strcmp(bridge->subclass, "parking")) {
+ ao2_unlink(active_cdrs_by_bridge, cdr);
+ }
/* Create a new pending record. If the channel decides to do something else,
* the pending record will handle it - otherwise, if gets dropped.
*/
pending_cdr = cdr_object_create_and_append(cdr);
- cdr_object_transition_state(pending_cdr, &bridged_pending_state_fn_table);
+ if (pending_cdr) {
+ cdr_object_transition_state(pending_cdr, &bridged_pending_state_fn_table);
+ }
ao2_unlock(cdr);
- /* Party B */
- ao2_callback(active_cdrs_by_bridge, OBJ_NODATA,
- cdr_object_party_b_left_bridge_cb,
- &leave_data);
+ if (strcmp(bridge->subclass, "parking")) {
+ /* Party B */
+ ao2_callback(active_cdrs_by_bridge, OBJ_NODATA,
+ cdr_object_party_b_left_bridge_cb,
+ &leave_data);
+ }
}
struct bridge_candidate {
@@ -2239,6 +2375,9 @@ static void bridge_candidate_add_to_cdr(struct cdr_object *cdr,
struct cdr_object *new_cdr;
new_cdr = cdr_object_create_and_append(cdr);
+ if (!new_cdr) {
+ return;
+ }
cdr_object_snapshot_copy(&new_cdr->party_b, party_b);
cdr_object_check_party_a_answer(new_cdr);
ast_string_field_set(new_cdr, bridge, cdr->bridge);
@@ -2340,38 +2479,60 @@ static void handle_bridge_pairings(struct cdr_object *cdr, struct ast_bridge_sna
return;
}
-/*!
- * \brief Handler for Stasis-Core bridge enter messages
- * \param data Passed on
- * \param sub The stasis subscription for this message callback
- * \param topic The topic this message was published for
- * \param message The message - hopefully a bridge one!
+/*! \brief Handle entering into a parking bridge
+ * \param cdr The CDR to operate on
+ * \param bridge The bridge the channel just entered
+ * \param channel The channel snapshot
*/
-static void handle_bridge_enter_message(void *data, struct stasis_subscription *sub,
- struct stasis_topic *topic, struct stasis_message *message)
+static void handle_parking_bridge_enter_message(struct cdr_object *cdr,
+ struct ast_bridge_snapshot *bridge,
+ struct ast_channel_snapshot *channel)
{
- struct ast_bridge_blob *update = stasis_message_data(message);
- struct ast_bridge_snapshot *bridge = update->bridge;
- struct ast_channel_snapshot *channel = update->channel;
- RAII_VAR(struct cdr_object *, cdr,
- ao2_find(active_cdrs_by_channel, channel->name, OBJ_KEY),
- ao2_cleanup);
RAII_VAR(struct module_config *, mod_cfg,
ao2_global_obj_ref(module_configs), ao2_cleanup);
int res = 1;
struct cdr_object *it_cdr;
- struct cdr_object *handled_cdr = NULL;
+ struct cdr_object *new_cdr;
- if (filter_bridge_messages(bridge)) {
- return;
- }
+ ao2_lock(cdr);
- CDR_DEBUG(mod_cfg, "Bridge Enter message: %u.%08u\n", (unsigned int)stasis_message_timestamp(message)->tv_sec, (unsigned int)stasis_message_timestamp(message)->tv_usec);
+ for (it_cdr = cdr; it_cdr; it_cdr = it_cdr->next) {
+ if (it_cdr->fn_table->process_parking_bridge_enter) {
+ res &= it_cdr->fn_table->process_parking_bridge_enter(it_cdr, bridge, channel);
+ }
+ if (it_cdr->fn_table->process_party_a) {
+ CDR_DEBUG(mod_cfg, "%p - Updating Party A %s snapshot\n", it_cdr,
+ channel->name);
+ it_cdr->fn_table->process_party_a(it_cdr, channel);
+ }
+ }
- if (!cdr) {
- ast_log(AST_LOG_WARNING, "No CDR for channel %s\n", channel->name);
- return;
+ if (res) {
+ /* No one handled it - we need a new one! */
+ new_cdr = cdr_object_create_and_append(cdr);
+ if (new_cdr) {
+ /* Let the single state transition us to Parked */
+ cdr_object_transition_state(new_cdr, &single_state_fn_table);
+ new_cdr->fn_table->process_parking_bridge_enter(new_cdr, bridge, channel);
+ }
}
+ ao2_unlock(cdr);
+}
+
+/*! \brief Handle a bridge enter message for a 'normal' bridge
+ * \param cdr The CDR to operate on
+ * \param bridge The bridge the channel just entered
+ * \param channel The channel snapshot
+ */
+static void handle_standard_bridge_enter_message(struct cdr_object *cdr,
+ struct ast_bridge_snapshot *bridge,
+ struct ast_channel_snapshot *channel)
+{
+ RAII_VAR(struct module_config *, mod_cfg,
+ ao2_global_obj_ref(module_configs), ao2_cleanup);
+ int res = 1;
+ struct cdr_object *it_cdr;
+ struct cdr_object *handled_cdr = NULL;
ao2_lock(cdr);
@@ -2416,6 +2577,96 @@ static void handle_bridge_enter_message(void *data, struct stasis_subscription *
ao2_unlock(cdr);
}
+/*!
+ * \brief Handler for Stasis-Core bridge enter messages
+ * \param data Passed on
+ * \param sub The stasis subscription for this message callback
+ * \param topic The topic this message was published for
+ * \param message The message - hopefully a bridge one!
+ */
+static void handle_bridge_enter_message(void *data, struct stasis_subscription *sub,
+ struct stasis_topic *topic, struct stasis_message *message)
+{
+ struct ast_bridge_blob *update = stasis_message_data(message);
+ struct ast_bridge_snapshot *bridge = update->bridge;
+ struct ast_channel_snapshot *channel = update->channel;
+ RAII_VAR(struct cdr_object *, cdr,
+ ao2_find(active_cdrs_by_channel, channel->name, OBJ_KEY),
+ ao2_cleanup);
+ RAII_VAR(struct module_config *, mod_cfg,
+ ao2_global_obj_ref(module_configs), ao2_cleanup);
+
+ if (filter_bridge_messages(bridge)) {
+ return;
+ }
+
+ CDR_DEBUG(mod_cfg, "Bridge Enter message for channel %s: %u.%08u\n",
+ channel->name,
+ (unsigned int)stasis_message_timestamp(message)->tv_sec,
+ (unsigned int)stasis_message_timestamp(message)->tv_usec);
+
+ if (!cdr) {
+ ast_log(AST_LOG_WARNING, "No CDR for channel %s\n", channel->name);
+ return;
+ }
+
+ if (!strcmp(bridge->subclass, "parking")) {
+ handle_parking_bridge_enter_message(cdr, bridge, channel);
+ } else {
+ handle_standard_bridge_enter_message(cdr, bridge, channel);
+ }
+}
+
+/*!
+ * \brief Handler for when a channel is parked
+ * \param data Passed on
+ * \param sub The stasis subscription for this message callback
+ * \param topic The topic this message was published for
+ * \param message The message about who got parked
+ * */
+static void handle_parked_call_message(void *data, struct stasis_subscription *sub,
+ struct stasis_topic *topic, struct stasis_message *message)
+{
+ struct ast_parked_call_payload *payload = stasis_message_data(message);
+ struct ast_channel_snapshot *channel = payload->parkee;
+ RAII_VAR(struct cdr_object *, cdr, NULL, ao2_cleanup);
+ RAII_VAR(struct module_config *, mod_cfg,
+ ao2_global_obj_ref(module_configs), ao2_cleanup);
+ struct cdr_object *it_cdr;
+
+ /* Anything other than getting parked will be handled by other updates */
+ if (payload->event_type != PARKED_CALL) {
+ return;
+ }
+
+ /* No one got parked? */
+ if (!channel) {
+ return;
+ }
+
+ CDR_DEBUG(mod_cfg, "Parked Call message for channel %s: %u.%08u\n",
+ channel->name,
+ (unsigned int)stasis_message_timestamp(message)->tv_sec,
+ (unsigned int)stasis_message_timestamp(message)->tv_usec);
+
+ cdr = ao2_find(active_cdrs_by_channel, channel->name, OBJ_KEY);
+ if (!cdr) {
+ ast_log(AST_LOG_WARNING, "No CDR for channel %s\n", channel->name);
+ return;
+ }
+
+ ao2_lock(cdr);
+
+ for (it_cdr = cdr; it_cdr; it_cdr = it_cdr->next) {
+ if (it_cdr->fn_table->process_parked_channel) {
+ it_cdr->fn_table->process_parked_channel(it_cdr, payload);
+ }
+ }
+
+ ao2_unlock(cdr);
+
+}
+
struct ast_cdr_config *ast_cdr_get_config(void)
{
RAII_VAR(struct module_config *, mod_cfg, ao2_global_obj_ref(module_configs), ao2_cleanup);
@@ -2953,6 +3204,7 @@ static void post_cdr(struct ast_cdr *cdr)
if (!ast_test_flag(&mod_cfg->general->settings, CDR_UNANSWERED) &&
cdr->disposition < AST_CDR_ANSWERED &&
(ast_strlen_zero(cdr->channel) || ast_strlen_zero(cdr->dstchannel))) {
+ ast_log(AST_LOG_WARNING, "Skipping CDR since we weren't answered\n");
continue;
}
@@ -3584,6 +3836,11 @@ int ast_cdr_engine_init(void)
if (!bridge_subscription) {
return -1;
}
+ parking_subscription = stasis_forward_all(ast_parking_topic(), cdr_topic);
+ if (!parking_subscription) {
+ return -1;
+ }
+
stasis_router = stasis_message_router_create(cdr_topic);
if (!stasis_router) {
return -1;
@@ -3592,6 +3849,7 @@ int ast_cdr_engine_init(void)
stasis_message_router_add(stasis_router, ast_channel_dial_type(), handle_dial_message, NULL);
stasis_message_router_add(stasis_router, ast_channel_entered_bridge_type(), handle_bridge_enter_message, NULL);
stasis_message_router_add(stasis_router, ast_channel_left_bridge_type(), handle_bridge_leave_message, NULL);
+ stasis_message_router_add(stasis_router, ast_parked_call_type(), handle_parked_call_message, NULL);
sched = ast_sched_context_create();
if (!sched) {