static void bridge_after_cb(struct ast_channel *chan, void *data) { struct stasis_app_control *control = data; SCOPED_AO2LOCK(lock, control); struct ast_bridge_channel *bridge_channel; ast_debug(3, "%s, %s: Channel leaving bridge\n", ast_channel_uniqueid(chan), control->bridge->uniqueid); ast_assert(chan == control->channel); /* Restore the channel's PBX */ ast_channel_pbx_set(control->channel, control->pbx); control->pbx = NULL; /* No longer in the bridge */ control->bridge = NULL; /* Get the bridge channel so we don't depart from the wrong bridge */ ast_channel_lock(chan); bridge_channel = ast_channel_get_bridge_channel(chan); ast_channel_unlock(chan); /* Depart this channel from the bridge using the command queue if possible */ if (stasis_app_send_command_async(control, bridge_channel_depart, bridge_channel)) { ao2_cleanup(bridge_channel); } }
/*! * \brief Set a dial timeout interval hook on the channel. * * The absolute time that the timeout should occur is stored on * a datastore on the channel. This time is converted into a relative * number of milliseconds in the future. Then an interval hook is set * to trigger in that number of milliseconds. * * \pre chan is locked * * \param chan The channel on which to set the interval hook */ static void set_interval_hook(struct ast_channel *chan) { struct ast_datastore *datastore; struct timeval *hangup_time; int64_t ms; struct ast_bridge_channel *bridge_channel; datastore = ast_channel_datastore_find(chan, &timeout_datastore, NULL); if (!datastore) { return; } hangup_time = datastore->data; ms = ast_tvdiff_ms(*hangup_time, ast_tvnow()); bridge_channel = ast_channel_get_bridge_channel(chan); if (!bridge_channel) { return; } if (ast_bridge_interval_hook(bridge_channel->features, 0, ms > 0 ? ms : 1, bridge_timeout, NULL, NULL, 0)) { return; } ast_queue_frame(bridge_channel->chan, &ast_null_frame); }
static void internal_bridge_after_cb(struct ast_channel *chan, void *data, enum ast_bridge_after_cb_reason reason) { struct stasis_app_control *control = data; SCOPED_AO2LOCK(lock, control); struct ast_bridge_channel *bridge_channel; ast_debug(3, "%s, %s: %s\n", ast_channel_uniqueid(chan), control->bridge ? control->bridge->uniqueid : "unknown", ast_bridge_after_cb_reason_string(reason)); if (reason == AST_BRIDGE_AFTER_CB_REASON_IMPART_FAILED) { /* The impart actually failed so control->bridge isn't valid. */ control->bridge = NULL; } ast_assert(chan == control->channel); /* Restore the channel's PBX */ ast_channel_pbx_set(control->channel, control->pbx); control->pbx = NULL; if (control->bridge) { app_unsubscribe_bridge(control->app, control->bridge); /* No longer in the bridge */ control->bridge = NULL; /* Get the bridge channel so we don't depart from the wrong bridge */ ast_channel_lock(chan); bridge_channel = ast_channel_get_bridge_channel(chan); ast_channel_unlock(chan); /* Depart this channel from the bridge using the command queue if possible */ stasis_app_send_command_async(control, bridge_channel_depart, bridge_channel, __ao2_cleanup); } if (stasis_app_channel_is_stasis_end_published(chan)) { /* The channel has had a StasisEnd published on it, but until now had remained in * the bridging system. This means that the channel moved from a Stasis bridge to a * non-Stasis bridge and is now exiting the bridging system. Because of this, the * channel needs to exit the Stasis application and go to wherever the non-Stasis * bridge has directed it to go. If the non-Stasis bridge has not set up an after * bridge destination, then the channel should be hung up. */ int hangup_flag; hangup_flag = ast_bridge_setup_after_goto(chan) ? AST_SOFTHANGUP_DEV : AST_SOFTHANGUP_ASYNCGOTO; ast_channel_lock(chan); ast_softhangup_nolock(chan, hangup_flag); ast_channel_unlock(chan); } }
static void parker_parked_call_message_response(struct ast_parked_call_payload *message, struct parked_subscription_data *data, struct stasis_subscription *sub) { const char *parkee_to_act_on = data->parkee_uuid; char saynum_buf[16]; struct ast_channel_snapshot *parkee_snapshot = message->parkee; RAII_VAR(struct ast_channel *, parker, NULL, ast_channel_cleanup); RAII_VAR(struct ast_bridge_channel *, bridge_channel, NULL, ao2_cleanup); if (strcmp(parkee_to_act_on, parkee_snapshot->uniqueid)) { return; } if (message->event_type != PARKED_CALL && message->event_type != PARKED_CALL_FAILED) { /* We only care about these two event types */ return; } parker = ast_channel_get_by_name(data->parker_uuid); if (!parker) { return; } ast_channel_lock(parker); bridge_channel = ast_channel_get_bridge_channel(parker); ast_channel_unlock(parker); if (!bridge_channel) { return; } /* This subscription callback will block for the duration of the announcement if * parked_subscription_data is tracking a transfer_channel_data struct. */ if (message->event_type == PARKED_CALL) { /* queue the saynum on the bridge channel and hangup */ snprintf(saynum_buf, sizeof(saynum_buf), "%d %u", data->hangup_after, message->parkingspace); if (!data->transfer_data) { ast_bridge_channel_queue_playfile(bridge_channel, say_parking_space, saynum_buf, NULL); } else { ast_bridge_channel_queue_playfile_sync(bridge_channel, say_parking_space, saynum_buf, NULL); data->transfer_data->completed = 1; } wipe_subscription_datastore(parker); } else if (message->event_type == PARKED_CALL_FAILED) { if (!data->transfer_data) { ast_bridge_channel_queue_playfile(bridge_channel, NULL, "pbx-parkingfailed", NULL); } else { ast_bridge_channel_queue_playfile_sync(bridge_channel, NULL, "pbx-parkingfailed", NULL); data->transfer_data->completed = 1; } wipe_subscription_datastore(parker); } }
/*! * \brief after bridge callback for the dial bridge * * The only purpose of this callback is to ensure that the control structure's * bridge pointer is NULLed */ static void dial_bridge_after_cb(struct ast_channel *chan, void *data) { struct stasis_app_control *control = data; struct ast_bridge_channel *bridge_channel; ast_channel_lock(chan); bridge_channel = ast_channel_get_bridge_channel(chan); ast_channel_unlock(chan); ast_debug(3, "Channel: <%s> Reason: %d\n", ast_channel_name(control->channel), ast_channel_hangupcause(chan)); stasis_app_send_command_async(control, bridge_channel_depart, bridge_channel, __ao2_cleanup); control->bridge = NULL; }
static void manager_park_bridged(struct mansession *s, const struct message *m, struct ast_channel *chan, struct ast_channel *parker_chan, const char *parkinglot, int timeout_override) { struct ast_bridge_channel *bridge_channel; char *app_data; if (timeout_override != -1) { if (ast_asprintf(&app_data, "%s,t(%d)", parkinglot, timeout_override) == -1) { astman_send_error(s, m, "Park action failed\n"); return; } } else { if (ast_asprintf(&app_data, "%s", parkinglot) == -1) { astman_send_error(s, m, "Park action failed\n"); return; } } ast_channel_lock(parker_chan); bridge_channel = ast_channel_get_bridge_channel(parker_chan); ast_channel_unlock(parker_chan); if (!bridge_channel) { ast_free(app_data); astman_send_error(s, m, "Park action failed\n"); return; } /* Subscribe to park messages for the channel being parked */ if (create_parked_subscription(parker_chan, ast_channel_uniqueid(chan), 1)) { ast_free(app_data); astman_send_error(s, m, "Park action failed\n"); ao2_cleanup(bridge_channel); return; } ast_bridge_channel_write_park(bridge_channel, ast_channel_uniqueid(chan), ast_channel_uniqueid(parker_chan), app_data); ast_free(app_data); astman_send_ack(s, m, "Park successful\n"); ao2_cleanup(bridge_channel); }