static struct ast_multi_channel_blob *local_channel_optimization_blob(struct local_pvt *p, struct ast_json *json_object) { struct ast_multi_channel_blob *payload; RAII_VAR(struct ast_channel_snapshot *, local_one_snapshot, NULL, ao2_cleanup); RAII_VAR(struct ast_channel_snapshot *, local_two_snapshot, NULL, ao2_cleanup); local_one_snapshot = ast_channel_snapshot_get_latest(ast_channel_uniqueid(p->base.owner)); if (!local_one_snapshot) { return NULL; } local_two_snapshot = ast_channel_snapshot_get_latest(ast_channel_uniqueid(p->base.chan)); if (!local_two_snapshot) { return NULL; } payload = ast_multi_channel_blob_create(json_object); if (!payload) { return NULL; } ast_multi_channel_blob_add_channel(payload, "1", local_one_snapshot); ast_multi_channel_blob_add_channel(payload, "2", local_two_snapshot); return payload; }
/*! * \internal * \brief Post the \ref ast_local_bridge_type \ref stasis message * \since 12.0.0 * * \param p local_pvt to raise the local bridge message * * \return Nothing */ static void publish_local_bridge_message(struct local_pvt *p) { RAII_VAR(struct ast_multi_channel_blob *, multi_blob, NULL, ao2_cleanup); RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref); RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup); RAII_VAR(struct ast_channel_snapshot *, one_snapshot, NULL, ao2_cleanup); RAII_VAR(struct ast_channel_snapshot *, two_snapshot, NULL, ao2_cleanup); struct ast_channel *owner; struct ast_channel *chan; if (!ast_local_bridge_type()) { return; } ast_unreal_lock_all(&p->base, &chan, &owner); blob = ast_json_pack("{s: s, s: s, s: b}", "context", p->context, "exten", p->exten, "can_optimize", !ast_test_flag(&p->base, AST_UNREAL_NO_OPTIMIZATION)); if (!blob) { goto end; } multi_blob = ast_multi_channel_blob_create(blob); if (!multi_blob) { goto end; } one_snapshot = ast_channel_snapshot_get_latest(ast_channel_uniqueid(owner)); if (!one_snapshot) { goto end; } two_snapshot = ast_channel_snapshot_get_latest(ast_channel_uniqueid(chan)); if (!two_snapshot) { goto end; } ast_multi_channel_blob_add_channel(multi_blob, "1", one_snapshot); ast_multi_channel_blob_add_channel(multi_blob, "2", two_snapshot); msg = stasis_message_create(ast_local_bridge_type(), multi_blob); if (!msg) { goto end; } stasis_publish(ast_channel_topic(owner), msg); end: ast_channel_unlock(owner); ast_channel_unref(owner); ast_channel_unlock(chan); ast_channel_unref(chan); ao2_unlock(&p->base); }
/*! * \brief Finds the control object for a channel, filling the response with an * error, if appropriate. * \param[out] response Response to fill with an error if control is not found. * \param channel_id ID of the channel to lookup. * \return Channel control object. * \return \c NULL if control object does not exist. */ static struct stasis_app_control *find_channel_control( struct ast_ari_response *response, const char *channel_id) { RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup); ast_assert(response != NULL); control = stasis_app_control_find_by_channel_id(channel_id); if (control == NULL) { /* Distinguish between 400 and 422 errors */ RAII_VAR(struct ast_channel_snapshot *, snapshot, NULL, ao2_cleanup); snapshot = ast_channel_snapshot_get_latest(channel_id); if (snapshot == NULL) { ast_log(LOG_DEBUG, "Couldn't find '%s'\n", channel_id); ast_ari_response_error(response, 400, "Bad Request", "Channel not found"); return NULL; } ast_log(LOG_DEBUG, "Found non-stasis '%s'\n", channel_id); ast_ari_response_error(response, 422, "Unprocessable Entity", "Channel not in Stasis application"); return NULL; } ao2_ref(control, +1); return control; }
/*! * \internal * \brief Peek at channel before it is pushed into bridge * \since 13.2.0 * * \param self Bridge to operate upon. * \param bridge_channel Bridge channel to push. * \param swap Bridge channel to swap places with if not NULL. * * \note On entry, self is already locked. * * \retval 0 on success. * \retval -1 on failure. The channel should not be pushed. */ static int bridge_stasis_push_peek(struct ast_bridge *self, struct ast_bridge_channel *bridge_channel, struct ast_bridge_channel *swap) { struct stasis_app_control *swap_control; struct ast_channel_snapshot *to_be_replaced; if (!swap) { goto done; } swap_control = stasis_app_control_find_by_channel(swap->chan); if (!swap_control) { ast_log(LOG_ERROR,"Failed to find stasis app control for swapped channel %s\n", ast_channel_name(swap->chan)); return -1; } to_be_replaced = ast_channel_snapshot_get_latest(ast_channel_uniqueid(swap->chan)); ast_debug(3, "Copying stasis app name %s from %s to %s\n", app_name(control_app(swap_control)), ast_channel_name(swap->chan), ast_channel_name(bridge_channel->chan)); ast_channel_lock(bridge_channel->chan); /* copy the app name from the swap channel */ app_set_replace_channel_app(bridge_channel->chan, app_name(control_app(swap_control))); /* set the replace channel snapshot */ app_set_replace_channel_snapshot(bridge_channel->chan, to_be_replaced); ast_channel_unlock(bridge_channel->chan); ao2_ref(swap_control, -1); ao2_cleanup(to_be_replaced); done: return ast_bridge_base_v_table.push_peek(self, bridge_channel, swap); }
void ast_ari_channels_redirect(struct ast_variable *headers, struct ast_ari_channels_redirect_args *args, struct ast_ari_response *response) { RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup); RAII_VAR(struct ast_channel_snapshot *, chan_snapshot, NULL, ao2_cleanup); char *tech; char *resource; int tech_len; control = find_control(response, args->channel_id); if (!control) { return; } if (ast_strlen_zero(args->endpoint)) { ast_ari_response_error(response, 400, "Not Found", "Required parameter 'endpoint' not provided."); return; } tech = ast_strdupa(args->endpoint); if (!(resource = strchr(tech, '/')) || !(tech_len = resource - tech)) { ast_ari_response_error(response, 422, "Unprocessable Entity", "Endpoint parameter '%s' does not contain tech/resource", args->endpoint); return; } *resource++ = '\0'; if (ast_strlen_zero(resource)) { ast_ari_response_error(response, 422, "Unprocessable Entity", "No resource provided in endpoint parameter '%s'", args->endpoint); return; } chan_snapshot = ast_channel_snapshot_get_latest(args->channel_id); if (!chan_snapshot) { ast_ari_response_error(response, 500, "Internal Server Error", "Unable to find channel snapshot for '%s'", args->channel_id); return; } if (strncasecmp(chan_snapshot->type, tech, tech_len)) { ast_ari_response_error(response, 422, "Unprocessable Entity", "Endpoint technology '%s' does not match channel technology '%s'", tech, chan_snapshot->type); return; } if (stasis_app_control_redirect(control, resource)) { ast_ari_response_error(response, 500, "Internal Server Error", "Failed to redirect channel"); return; } ast_ari_response_no_content(response); }
/*! \internal * \brief Publish the chanspy message over Stasis-Core * \param snoop The snoop structure * \start start If non-zero, the spying is starting. Otherwise, the spyer is * finishing */ static void publish_chanspy_message(struct stasis_app_snoop *snoop, int start) { RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref); RAII_VAR(struct ast_multi_channel_blob *, payload, NULL, ao2_cleanup); RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup); RAII_VAR(struct ast_channel_snapshot *, snoop_snapshot, NULL, ao2_cleanup); RAII_VAR(struct ast_channel_snapshot *, spyee_snapshot, NULL, ao2_cleanup); struct stasis_message_type *type = start ? ast_channel_chanspy_start_type(): ast_channel_chanspy_stop_type(); blob = ast_json_null(); if (!blob || !type) { return; } payload = ast_multi_channel_blob_create(blob); if (!payload) { return; } snoop_snapshot = ast_channel_snapshot_get_latest(ast_channel_uniqueid(snoop->chan)); if (!snoop_snapshot) { return; } ast_multi_channel_blob_add_channel(payload, "spyer_channel", snoop_snapshot); spyee_snapshot = ast_channel_snapshot_get_latest(snoop->uniqueid); if (spyee_snapshot) { ast_multi_channel_blob_add_channel(payload, "spyee_channel", spyee_snapshot); } message = stasis_message_create(type, payload); if (!message) { return; } stasis_publish(ast_channel_topic(snoop->chan), message); }
/*! \brief Callback for \ref ast_unreal_pvt_callbacks \ref optimization_started_cb */ static void local_optimization_started_cb(struct ast_unreal_pvt *base, struct ast_channel *source, enum ast_unreal_channel_indicator dest, unsigned int id) { RAII_VAR(struct ast_json *, json_object, ast_json_null(), ast_json_unref); RAII_VAR(struct ast_multi_channel_blob *, payload, NULL, ao2_cleanup); RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup); struct local_pvt *p = (struct local_pvt *)base; if (!ast_local_optimization_begin_type()) { return; } json_object = ast_json_pack("{s: i, s: i}", "dest", dest, "id", id); if (!json_object) { return; } payload = local_channel_optimization_blob(p, json_object); if (!payload) { return; } if (source) { RAII_VAR(struct ast_channel_snapshot *, source_snapshot, NULL, ao2_cleanup); source_snapshot = ast_channel_snapshot_get_latest(ast_channel_uniqueid(source)); if (!source_snapshot) { return; } ast_multi_channel_blob_add_channel(payload, "source", source_snapshot); } msg = stasis_message_create(ast_local_optimization_begin_type(), payload); if (!msg) { return; } stasis_publish(ast_channel_topic(p->base.owner), msg); }
int ast_do_pickup(struct ast_channel *chan, struct ast_channel *target) { struct ast_party_connected_line connected_caller; struct ast_datastore *ds_pickup; const char *chan_name;/*!< A masquerade changes channel names. */ const char *target_name;/*!< A masquerade changes channel names. */ int res = -1; RAII_VAR(struct ast_channel_snapshot *, chan_snapshot, NULL, ao2_cleanup); RAII_VAR(struct ast_channel_snapshot *, target_snapshot, NULL, ao2_cleanup); target_name = ast_strdupa(ast_channel_name(target)); ast_debug(1, "Call pickup on '%s' by '%s'\n", target_name, ast_channel_name(chan)); /* Mark the target to block any call pickup race. */ ds_pickup = ast_datastore_alloc(&pickup_active, NULL); if (!ds_pickup) { ast_log(LOG_WARNING, "Unable to create channel datastore on '%s' for call pickup\n", target_name); return -1; } ast_channel_datastore_add(target, ds_pickup); ast_party_connected_line_init(&connected_caller); ast_party_connected_line_copy(&connected_caller, ast_channel_connected(target)); ast_channel_unlock(target);/* The pickup race is avoided so we do not need the lock anymore. */ /* Reset any earlier private connected id representation */ ast_party_id_reset(&connected_caller.priv); connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER; if (ast_channel_connected_line_sub(NULL, chan, &connected_caller, 0) && ast_channel_connected_line_macro(NULL, chan, &connected_caller, 0, 0)) { ast_channel_update_connected_line(chan, &connected_caller, NULL); } ast_party_connected_line_free(&connected_caller); ast_channel_lock(chan); chan_name = ast_strdupa(ast_channel_name(chan)); ast_connected_line_copy_from_caller(&connected_caller, ast_channel_caller(chan)); ast_channel_unlock(chan); connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER; if (ast_answer(chan)) { ast_log(LOG_WARNING, "Unable to answer '%s'\n", chan_name); goto pickup_failed; } if (ast_queue_control(chan, AST_CONTROL_ANSWER)) { ast_log(LOG_WARNING, "Unable to queue answer on '%s'\n", chan_name); goto pickup_failed; } ast_channel_queue_connected_line_update(chan, &connected_caller, NULL); /* setting the HANGUPCAUSE so the ringing channel knows this call was not a missed call */ ast_channel_hangupcause_set(chan, AST_CAUSE_ANSWERED_ELSEWHERE); ast_channel_lock(chan); chan_snapshot = ast_channel_snapshot_create(chan); ast_channel_unlock(chan); if (!chan_snapshot) { goto pickup_failed; } target_snapshot = ast_channel_snapshot_get_latest(ast_channel_uniqueid(target)); if (!target_snapshot) { goto pickup_failed; } if (ast_channel_move(target, chan)) { ast_log(LOG_WARNING, "Unable to complete call pickup of '%s' with '%s'\n", chan_name, target_name); goto pickup_failed; } /* target points to the channel that did the pickup at this point, so use that channel's topic instead of chan */ send_call_pickup_stasis_message(target, chan_snapshot, target_snapshot); res = 0; pickup_failed: ast_channel_lock(target); if (!ast_channel_datastore_remove(target, ds_pickup)) { ast_datastore_free(ds_pickup); } ast_party_connected_line_free(&connected_caller); return res; }