Beispiel #1
0
static int bridgewait_timeout_callback(struct ast_bridge_channel *bridge_channel, void *hook_pvt)
{
    ast_verb(3, "Channel %s timed out.\n", ast_channel_name(bridge_channel->chan));
    ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END,
                                    AST_CAUSE_NORMAL_CLEARING);
    return -1;
}
/*! \brief Internal built in feature for hangup */
static int feature_hangup(struct ast_bridge_channel *bridge_channel, void *hook_pvt)
{
	/*
	 * This is very simple, we simply change the state on the
	 * bridge_channel to force the channel out of the bridge and the
	 * core takes care of the rest.
	 */
	ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END,
		AST_CAUSE_NORMAL_CLEARING);
	return 0;
}
/*!
 * \internal
 * \brief Push this channel into the Stasis bridge.
 * \since 12.5.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 did not get pushed.
 */
static int bridge_stasis_push(struct ast_bridge *self, struct ast_bridge_channel *bridge_channel, struct ast_bridge_channel *swap)
{
	struct stasis_app_control *control = stasis_app_control_find_by_channel(bridge_channel->chan);

	if (!control && !stasis_app_channel_is_internal(bridge_channel->chan)) {
		/* channel not in Stasis(), get it there */
		/* Attach after-bridge callback and pass ownership of swap_app to it */
		if (ast_bridge_set_after_callback(bridge_channel->chan,
			bridge_stasis_run_cb, NULL, NULL)) {
			ast_log(LOG_ERROR, "Failed to set after bridge callback\n");
			return -1;
		}

		bridge_stasis_queue_join_action(self, bridge_channel);
		if (swap) {
			/* nudge the swap channel out of the bridge */
			ast_bridge_channel_leave_bridge(swap, BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE, 0);
		}

		/* Return -1 so the push fails and the after-bridge callback gets called
		 * This keeps the bridging framework from putting the channel into the bridge
		 * until the Stasis thread gets started, and then the channel is put into the bridge.
		 */
		return -1;
	}

	/*
	 * If going into a holding bridge, default the role to participant, if
	 * it has no compatible role currently
	 */
	if ((self->technology->capabilities & AST_BRIDGE_CAPABILITY_HOLDING)
	    && !ast_channel_has_role(bridge_channel->chan, "announcer")
	    && !ast_channel_has_role(bridge_channel->chan, "holding_participant")) {
		if (ast_channel_add_bridge_role(bridge_channel->chan, "holding_participant")) {
			ast_log(LOG_ERROR, "Failed to set holding participant on %s\n", ast_channel_name(bridge_channel->chan));
			return -1;
		}

		if (ast_channel_set_bridge_role_option(bridge_channel->chan, "holding_participant", "idle_mode", "none")) {
			ast_log(LOG_ERROR, "Failed to set holding participant mode on %s\n", ast_channel_name(bridge_channel->chan));
			return -1;
		}
	}

	ao2_cleanup(control);
	if (self->allowed_capabilities & STASIS_BRIDGE_MIXING_CAPABILITIES) {
		ast_bridge_channel_update_linkedids(bridge_channel, swap);
		if (ast_test_flag(&self->feature_flags, AST_BRIDGE_FLAG_SMART)) {
			ast_bridge_channel_update_accountcodes(bridge_channel, swap);
		}
	}

	return ast_bridge_base_v_table.push(self, bridge_channel, swap);
}
static int bridge_features_duration_callback(struct ast_bridge_channel *bridge_channel, void *hook_pvt)
{
	struct ast_bridge_features_limits *limits = hook_pvt;

	if (!ast_strlen_zero(limits->duration_sound)) {
		ast_stream_and_wait(bridge_channel->chan, limits->duration_sound, AST_DIGIT_NONE);
	}

	ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END,
		AST_CAUSE_NORMAL_CLEARING);

	ast_test_suite_event_notify("BRIDGE_TIMELIMIT", "Channel1: %s",
		ast_channel_name(bridge_channel->chan));
	return -1;
}
/*! \internal
 * \brief Interval hook. Pulls a parked call from the parking bridge after the timeout is passed and sets the resolution to timeout.
 *
 * \param bridge_channel bridge channel this interval hook is being executed on
 * \param hook_pvt A pointer to the parked_user struct associated with the channel is stuffed in here
 */
static int parking_duration_callback(struct ast_bridge_channel *bridge_channel, void *hook_pvt)
{
	struct parked_user *user = hook_pvt;
	struct ast_channel *chan = user->chan;
	struct ast_context *park_dial_context;
	const char *dial_string;
	char *dial_string_flat;
	char parking_space[AST_MAX_EXTENSION];

	char returnexten[AST_MAX_EXTENSION];
	char *duplicate_returnexten;
	struct ast_exten *existing_exten;
	struct pbx_find_info pbx_finder = { .stacklen = 0 }; /* The rest is reset in pbx_find_extension */


	/* We are still in the bridge, so it's possible for other stuff to mess with the parked call before we leave the bridge
	   to deal with this, lock the parked user, check and set resolution. */
	ao2_lock(user);
	if (user->resolution != PARK_UNSET) {
		/* Abandon timeout since something else has resolved the parked user before we got to it. */
		ao2_unlock(user);
		return -1;
	}
	user->resolution = PARK_TIMEOUT;
	ao2_unlock(user);

	ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE,
		AST_CAUSE_NORMAL_CLEARING);

	dial_string = user->parker_dial_string;
	dial_string_flat = ast_strdupa(dial_string);
	flatten_dial_string(dial_string_flat);

	/* Set parking timeout channel variables */
	snprintf(parking_space, sizeof(parking_space), "%d", user->parking_space);
	ast_channel_lock(chan);
	ast_channel_stage_snapshot(chan);
	pbx_builtin_setvar_helper(chan, "PARKING_SPACE", parking_space);
	pbx_builtin_setvar_helper(chan, "PARKINGSLOT", parking_space); /* Deprecated version of PARKING_SPACE */
	pbx_builtin_setvar_helper(chan, "PARKEDLOT", user->lot->name);
	pbx_builtin_setvar_helper(chan, "PARKER", dial_string);
	pbx_builtin_setvar_helper(chan, "PARKER_FLAT", dial_string_flat);
	parking_timeout_set_caller_features(chan, user->lot->cfg);
	ast_channel_stage_snapshot_done(chan);
	ast_channel_unlock(chan);

	/* Dialplan generation for park-dial extensions */

	if (ast_wrlock_contexts()) {
		ast_log(LOG_ERROR, "Failed to lock the contexts list. Can't add the park-dial extension.\n");
		return -1;
	}

	if (!(park_dial_context = ast_context_find_or_create(NULL, NULL, PARK_DIAL_CONTEXT, BASE_REGISTRAR))) {
		ast_log(LOG_ERROR, "Parking dial context '%s' does not exist and unable to create\n", PARK_DIAL_CONTEXT);
		if (ast_unlock_contexts()) {
			ast_assert(0);
		}
		goto abandon_extension_creation;
	}

	if (ast_wrlock_context(park_dial_context)) {
		ast_log(LOG_ERROR, "failed to obtain write lock on context '%s'\n", PARK_DIAL_CONTEXT);
		if (ast_unlock_contexts()) {
			ast_assert(0);
		}
		goto abandon_extension_creation;
	}

	if (ast_unlock_contexts()) {
		ast_assert(0);
	}

	snprintf(returnexten, sizeof(returnexten), "%s,%u", dial_string,
		user->lot->cfg->comebackdialtime);

	duplicate_returnexten = ast_strdup(returnexten);
	if (!duplicate_returnexten) {
		ast_log(LOG_ERROR, "Failed to create parking redial parker extension %s@%s - Dial(%s)\n",
			dial_string_flat, PARK_DIAL_CONTEXT, returnexten);
	}

	/* If an extension already exists here because we registered it for another parked call timing out, then we may overwrite it. */
	if ((existing_exten = pbx_find_extension(NULL, NULL, &pbx_finder, PARK_DIAL_CONTEXT, dial_string_flat, 1, NULL, NULL, E_MATCH)) &&
	    (strcmp(ast_get_extension_registrar(existing_exten), BASE_REGISTRAR))) {
		ast_debug(3, "An extension for '%s@%s' was already registered by another registrar '%s'\n",
			dial_string_flat, PARK_DIAL_CONTEXT, ast_get_extension_registrar(existing_exten));
	} else if (ast_add_extension2_nolock(park_dial_context, 1, dial_string_flat, 1, NULL, NULL,
			"Dial", duplicate_returnexten, ast_free_ptr, BASE_REGISTRAR)) {
			ast_free(duplicate_returnexten);
		ast_log(LOG_ERROR, "Failed to create parking redial parker extension %s@%s - Dial(%s)\n",
			dial_string_flat, PARK_DIAL_CONTEXT, returnexten);
	}

	if (ast_unlock_context(park_dial_context)) {
		ast_assert(0);
	}

abandon_extension_creation:

	/* async_goto the proper PBX destination - this should happen when we come out of the bridge */
	if (!ast_strlen_zero(user->comeback)) {
		ast_async_parseable_goto(chan, user->comeback);
	} else {
		comeback_goto(user, user->lot);
	}

	return -1;
}

void say_parking_space(struct ast_bridge_channel *bridge_channel, const char *payload)
{
	unsigned int numeric_value;
	unsigned int hangup_after;

	if (sscanf(payload, "%u %u", &hangup_after, &numeric_value) != 2) {
		/* If say_parking_space is called with a non-numeric string, we have a problem. */
		ast_assert(0);
		ast_bridge_channel_leave_bridge(bridge_channel,
			BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE, AST_CAUSE_NORMAL_CLEARING);
		return;
	}

	ast_say_digits(bridge_channel->chan, numeric_value, "",
		ast_channel_language(bridge_channel->chan));

	if (hangup_after) {
		ast_bridge_channel_leave_bridge(bridge_channel,
			BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE, AST_CAUSE_NORMAL_CLEARING);
	}
}