/*! * \internal * \brief Helper function that creates an outgoing channel and returns it immediately. This function is nearly * identical to the dial_transfer function in bridge_basic.c, however it doesn't swap the * local channel and the channel that instigated the park. */ static struct ast_channel *park_local_transfer(struct ast_channel *parker, const char *context, const char *exten, struct transfer_channel_data *parked_channel_data) { char destination[AST_MAX_EXTENSION + AST_MAX_CONTEXT + 1]; struct ast_channel *parkee; struct ast_channel *parkee_side_2; int cause; /* Fill the variable with the extension and context we want to call */ snprintf(destination, sizeof(destination), "%s@%s", exten, context); /* Now we request that chan_local prepare to call the destination */ parkee = ast_request("Local", ast_channel_nativeformats(parker), NULL, parker, destination, &cause); if (!parkee) { return NULL; } /* Before we actually dial out let's inherit appropriate information. */ ast_channel_lock_both(parker, parkee); ast_channel_req_accountcodes(parkee, parker, AST_CHANNEL_REQUESTOR_REPLACEMENT); ast_connected_line_copy_from_caller(ast_channel_connected(parkee), ast_channel_caller(parker)); ast_channel_inherit_variables(parker, parkee); ast_channel_datastore_inherit(parker, parkee); ast_channel_unlock(parker); parkee_side_2 = ast_local_get_peer(parkee); ast_assert(parkee_side_2 != NULL); ast_channel_unlock(parkee); /* We need to have the parker subscribe to the new local channel before hand. */ if (create_parked_subscription_full(parker, ast_channel_uniqueid(parkee_side_2), 1, parked_channel_data)) { ast_channel_unref(parkee_side_2); ast_hangup(parkee); return NULL; } ast_bridge_set_transfer_variables(parkee_side_2, ast_channel_name(parker), 0); ast_channel_unref(parkee_side_2); /* Since the above worked fine now we actually call it and return the channel */ if (ast_call(parkee, destination, 0)) { ast_hangup(parkee); return NULL; } return parkee; }
static int manager_park(struct mansession *s, const struct message *m) { const char *channel = astman_get_header(m, "Channel"); const char *timeout_channel = S_OR(astman_get_header(m, "TimeoutChannel"), astman_get_header(m, "Channel2")); const char *announce_channel = astman_get_header(m, "AnnounceChannel"); const char *timeout = astman_get_header(m, "Timeout"); const char *parkinglot = astman_get_header(m, "Parkinglot"); char buf[BUFSIZ]; int timeout_override = -1; RAII_VAR(struct ast_channel *, parker_chan, NULL, ao2_cleanup); RAII_VAR(struct ast_channel *, chan, NULL, ao2_cleanup); if (ast_strlen_zero(channel)) { astman_send_error(s, m, "Channel not specified"); return 0; } if (!ast_strlen_zero(timeout)) { if (sscanf(timeout, "%30d", &timeout_override) != 1 || timeout < 0) { astman_send_error(s, m, "Invalid Timeout value."); return 0; } if (timeout_override > 0) { /* If greater than zero, convert to seconds for internal use. Must be >= 1 second. */ timeout_override = MAX(1, timeout_override / 1000); } } if (!(chan = ast_channel_get_by_name(channel))) { snprintf(buf, sizeof(buf), "Channel does not exist: %s", channel); astman_send_error(s, m, buf); return 0; } ast_channel_lock(chan); if (!ast_strlen_zero(timeout_channel)) { ast_bridge_set_transfer_variables(chan, timeout_channel, 0); } ast_channel_unlock(chan); parker_chan = ast_channel_bridge_peer(chan); if (!parker_chan || strcmp(ast_channel_name(parker_chan), timeout_channel)) { if (!ast_strlen_zero(announce_channel)) { struct ast_channel *announce_chan = ast_channel_get_by_name(announce_channel); if (!announce_channel) { astman_send_error(s, m, "AnnounceChannel does not exist"); return 0; } create_parked_subscription(announce_chan, ast_channel_uniqueid(chan), 0); ast_channel_cleanup(announce_chan); } manager_park_unbridged(s, m, chan, parkinglot, timeout_override); return 0; } if (!ast_strlen_zero(announce_channel) && strcmp(announce_channel, timeout_channel)) { /* When using an announce_channel in bridge mode, only add the announce channel if it isn't * the same as the timeout channel (which will play announcements anyway) */ struct ast_channel *announce_chan = ast_channel_get_by_name(announce_channel); if (!announce_channel) { astman_send_error(s, m, "AnnounceChannel does not exist"); return 0; } create_parked_subscription(announce_chan, ast_channel_uniqueid(chan), 0); ast_channel_cleanup(announce_chan); } manager_park_bridged(s, m, chan, parker_chan, parkinglot, timeout_override); return 0; }
/*! * \internal * \brief Determine if an extension is a parking extension */ static int parking_is_exten_park(const char *context, const char *exten) { struct ast_exten *exten_obj; struct pbx_find_info info = { .stacklen = 0 }; /* the rest is reset in pbx_find_extension */ const char *app_at_exten; ast_debug(4, "Checking if %s@%s is a parking exten\n", exten, context); exten_obj = pbx_find_extension(NULL, NULL, &info, context, exten, 1, NULL, NULL, E_MATCH); if (!exten_obj) { return 0; } app_at_exten = ast_get_extension_app(exten_obj); if (!app_at_exten || strcasecmp(PARK_APPLICATION, app_at_exten)) { return 0; } return 1; } /*! * \internal * \since 12.0.0 * \brief Perform a blind transfer to a parking lot * * In general, most parking features should work to call this function. This will safely * park either a channel in the bridge with \ref bridge_channel or will park the entire * bridge if more than one channel is in the bridge. It will create the correct data to * pass to the \ref AstBridging Bridging API to safely park the channel. * * \param bridge_channel The bridge_channel representing the channel performing the park * \param context The context to blind transfer to * \param exten The extension to blind transfer to * \param parked_channel_cb Optional callback executed prior to sending the parked channel into the bridge * \param parked_channel_data Data for the parked_channel_cb * * \retval 0 on success * \retval non-zero on error */ static int parking_blind_transfer_park(struct ast_bridge_channel *bridge_channel, const char *context, const char *exten, transfer_channel_cb parked_channel_cb, struct transfer_channel_data *parked_channel_data) { RAII_VAR(struct ast_bridge_channel *, other, NULL, ao2_cleanup); RAII_VAR(struct ast_channel *, other_chan, NULL, ast_channel_cleanup); struct ast_exten *e; struct pbx_find_info find_info = { .stacklen = 0 }; int peer_count; if (ast_strlen_zero(context) || ast_strlen_zero(exten)) { return -1; } if (!bridge_channel->in_bridge) { return -1; } if (!parking_is_exten_park(context, exten)) { return -1; } ast_bridge_channel_lock_bridge(bridge_channel); peer_count = bridge_channel->bridge->num_channels; if (peer_count == 2) { other = ast_bridge_channel_peer(bridge_channel); ao2_ref(other, +1); other_chan = other->chan; ast_channel_ref(other_chan); } ast_bridge_unlock(bridge_channel->bridge); if (peer_count < 2) { /* There is nothing to do if there is no one to park. */ return -1; } /* With a multiparty bridge, we need to do a regular blind transfer. We link the * existing bridge to the parking lot with a Local channel rather than * transferring others. */ if (peer_count > 2) { struct ast_channel *transfer_chan = NULL; transfer_chan = park_local_transfer(bridge_channel->chan, context, exten, parked_channel_data); if (!transfer_chan) { return -1; } ast_channel_ref(transfer_chan); if (parked_channel_cb) { parked_channel_cb(transfer_chan, parked_channel_data, AST_BRIDGE_TRANSFER_MULTI_PARTY); } if (ast_bridge_impart(bridge_channel->bridge, transfer_chan, NULL, NULL, AST_BRIDGE_IMPART_CHAN_INDEPENDENT)) { ast_hangup(transfer_chan); ast_channel_unref(transfer_chan); return -1; } ast_channel_unref(transfer_chan); return 0; } /* Subscribe to park messages with the other channel entering */ if (create_parked_subscription_full(bridge_channel->chan, ast_channel_uniqueid(other->chan), 1, parked_channel_data)) { return -1; } if (parked_channel_cb) { parked_channel_cb(other_chan, parked_channel_data, AST_BRIDGE_TRANSFER_SINGLE_PARTY); } e = pbx_find_extension(NULL, NULL, &find_info, context, exten, 1, NULL, NULL, E_MATCH); /* Write the park frame with the intended recipient and other data out to the bridge. */ ast_bridge_channel_write_park(bridge_channel, ast_channel_uniqueid(other_chan), ast_channel_uniqueid(bridge_channel->chan), e ? ast_get_extension_app_data(e) : NULL); return 0; } /*! * \internal * \since 12.0.0 * \brief Perform a direct park on a channel in a bridge * * \note This will be called from within the \ref AstBridging Bridging API * * \param bridge_channel The bridge_channel representing the channel to be parked * \param uuid_parkee The UUID of the channel being parked * \param uuid_parker The UUID of the channel performing the park * \param app_data Application parseable data to pass to the parking application */ static int parking_park_bridge_channel(struct ast_bridge_channel *bridge_channel, const char *uuid_parkee, const char *uuid_parker, const char *app_data) { RAII_VAR(struct ast_bridge *, parking_bridge, NULL, ao2_cleanup); RAII_VAR(struct ast_bridge *, original_bridge, NULL, ao2_cleanup); RAII_VAR(struct ast_channel *, parker, NULL, ao2_cleanup); if (strcmp(ast_channel_uniqueid(bridge_channel->chan), uuid_parkee)) { /* We aren't the parkee, so ignore this action. */ return -1; } parker = ast_channel_get_by_name(uuid_parker); if (!parker) { ast_log(LOG_NOTICE, "Channel with uuid %s left before we could start parking the call. Parking canceled.\n", uuid_parker); publish_parked_call_failure(bridge_channel->chan); return -1; } if (!(parking_bridge = park_application_setup(bridge_channel->chan, parker, app_data, NULL))) { publish_parked_call_failure(bridge_channel->chan); return -1; } ast_bridge_set_transfer_variables(bridge_channel->chan, ast_channel_name(parker), 0); /* bridge_channel must be locked so we can get a reference to the bridge it is currently on */ ao2_lock(bridge_channel); original_bridge = bridge_channel->bridge; if (!original_bridge) { ao2_unlock(bridge_channel); publish_parked_call_failure(bridge_channel->chan); return -1; } ao2_ref(original_bridge, +1); /* Cleaned by RAII_VAR */ ao2_unlock(bridge_channel); if (ast_bridge_move(parking_bridge, original_bridge, bridge_channel->chan, NULL, 1)) { ast_log(LOG_ERROR, "Failed to move %s into the parking bridge.\n", ast_channel_name(bridge_channel->chan)); return -1; } return 0; }