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);
}
Example #3
0
/*!
 * \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;
}
Example #4
0
/*!
 * \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);
}
Example #5
0
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);
}
Example #6
0
/*! \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);
}
Example #8
0
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;
}