diff options
Diffstat (limited to 'main/cdr.c')
-rw-r--r-- | main/cdr.c | 382 |
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) { |