static void stop_automonitor(struct ast_bridge_channel *bridge_channel, struct ast_channel *peer_chan, struct ast_features_general_config *features_cfg, const char *stop_message)
{
	ast_verb(3, "AutoMonitor used to stop recording call.\n");

	ast_channel_lock(peer_chan);
	if (ast_channel_monitor(peer_chan)) {
		if (ast_channel_monitor(peer_chan)->stop(peer_chan, 1)) {
			ast_verb(3, "Cannot stop AutoMonitor for %s\n", ast_channel_name(bridge_channel->chan));
			if (features_cfg && !(ast_strlen_zero(features_cfg->recordingfailsound))) {
				ast_bridge_channel_queue_playfile(bridge_channel, NULL, features_cfg->recordingfailsound, NULL);
			}
			ast_channel_unlock(peer_chan);
			return;
		}
	} else {
		/* Something else removed the Monitor before we got to it. */
		ast_channel_unlock(peer_chan);
		return;
	}

	ast_channel_unlock(peer_chan);

	if (features_cfg && !(ast_strlen_zero(features_cfg->courtesytone))) {
		ast_bridge_channel_queue_playfile(bridge_channel, NULL, features_cfg->courtesytone, NULL);
		ast_bridge_channel_write_playfile(bridge_channel, NULL, features_cfg->courtesytone, NULL);
	}

	if (!ast_strlen_zero(stop_message)) {
		ast_bridge_channel_queue_playfile(bridge_channel, NULL, stop_message, NULL);
		ast_bridge_channel_write_playfile(bridge_channel, NULL, stop_message, NULL);
	}
}
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);
	}
}
static void stop_automixmonitor(struct ast_bridge_channel *bridge_channel, struct ast_channel *peer_chan, struct ast_features_general_config *features_cfg, const char *stop_message)
{
	ast_verb(3, "AutoMixMonitor used to stop recording call.\n");

	if (ast_stop_mixmonitor(peer_chan, NULL)) {
		ast_verb(3, "Failed to stop Mixmonitor for %s.\n", ast_channel_name(bridge_channel->chan));
		if (features_cfg && !(ast_strlen_zero(features_cfg->recordingfailsound))) {
			ast_bridge_channel_queue_playfile(bridge_channel, NULL, features_cfg->recordingfailsound, NULL);
		}
		return;
	}

	if (features_cfg && !ast_strlen_zero(features_cfg->courtesytone)) {
		ast_bridge_channel_queue_playfile(bridge_channel, NULL, features_cfg->courtesytone, NULL);
		ast_bridge_channel_write_playfile(bridge_channel, NULL, features_cfg->courtesytone, NULL);
	}

	if (!ast_strlen_zero(stop_message)) {
		ast_bridge_channel_queue_playfile(bridge_channel, NULL, stop_message, NULL);
		ast_bridge_channel_write_playfile(bridge_channel, NULL, stop_message, NULL);
	}
}
static int feature_automixmonitor(struct ast_bridge_channel *bridge_channel, void *hook_pvt)
{
	static const char *mixmonitor_spy_type = "MixMonitor";
	const char *stop_message;
	const char *start_message;
	struct ast_bridge_features_automixmonitor *options = hook_pvt;
	enum ast_bridge_features_monitor start_stop = options ? options->start_stop : AUTO_MONITOR_TOGGLE;
	int is_monitoring;

	RAII_VAR(struct ast_channel *, peer_chan, NULL, ast_channel_cleanup);
	RAII_VAR(struct ast_features_general_config *, features_cfg, NULL, ao2_cleanup);

	features_cfg = ast_get_chan_features_general_config(bridge_channel->chan);
	ast_bridge_channel_lock_bridge(bridge_channel);
	peer_chan = ast_bridge_peer_nolock(bridge_channel->bridge, bridge_channel->chan);
	ast_bridge_unlock(bridge_channel->bridge);

	if (!peer_chan) {
		ast_verb(3, "Cannot do AutoMixMonitor for %s - cannot determine peer in bridge.\n",
			ast_channel_name(bridge_channel->chan));
		if (features_cfg && !ast_strlen_zero(features_cfg->recordingfailsound)) {
			ast_bridge_channel_queue_playfile(bridge_channel, NULL, features_cfg->recordingfailsound, NULL);
		}
		return 0;
	}

	ast_channel_lock(bridge_channel->chan);
	start_message = pbx_builtin_getvar_helper(bridge_channel->chan,
		"TOUCH_MIXMONITOR_MESSAGE_START");
	start_message = ast_strdupa(S_OR(start_message, ""));
	stop_message = pbx_builtin_getvar_helper(bridge_channel->chan,
		"TOUCH_MIXMONITOR_MESSAGE_STOP");
	stop_message = ast_strdupa(S_OR(stop_message, ""));
	ast_channel_unlock(bridge_channel->chan);

	is_monitoring =
		0 < ast_channel_audiohook_count_by_source(peer_chan, mixmonitor_spy_type, AST_AUDIOHOOK_TYPE_SPY);
	switch (start_stop) {
	case AUTO_MONITOR_TOGGLE:
		if (is_monitoring) {
			stop_automixmonitor(bridge_channel, peer_chan, features_cfg, stop_message);
		} else {
			start_automixmonitor(bridge_channel, peer_chan, features_cfg, start_message);
		}
		return 0;
	case AUTO_MONITOR_START:
		if (!is_monitoring) {
			start_automixmonitor(bridge_channel, peer_chan, features_cfg, start_message);
			return 0;
		}
		ast_verb(3, "AutoMixMonitor already recording call.\n");
		break;
	case AUTO_MONITOR_STOP:
		if (is_monitoring) {
			stop_automixmonitor(bridge_channel, peer_chan, features_cfg, stop_message);
			return 0;
		}
		ast_verb(3, "AutoMixMonitor already not recording call.\n");
		break;
	}

	/*
	 * Fake start/stop to invoker so will think it did something but
	 * was already in that mode.
	 */
	if (features_cfg && !ast_strlen_zero(features_cfg->courtesytone)) {
		ast_bridge_channel_queue_playfile(bridge_channel, NULL, features_cfg->courtesytone, NULL);
	}
	if (is_monitoring) {
		if (!ast_strlen_zero(start_message)) {
			ast_bridge_channel_queue_playfile(bridge_channel, NULL, start_message, NULL);
		}
	} else {
		if (!ast_strlen_zero(stop_message)) {
			ast_bridge_channel_queue_playfile(bridge_channel, NULL, stop_message, NULL);
		}
	}
	return 0;
}
static void start_automixmonitor(struct ast_bridge_channel *bridge_channel, struct ast_channel *peer_chan, struct ast_features_general_config *features_cfg, const char *start_message)
{
	char *touch_filename;
	size_t len;
	int x;
	enum set_touch_variables_res set_touch_res;

	RAII_VAR(char *, touch_format, NULL, ast_free);
	RAII_VAR(char *, touch_monitor, NULL, ast_free);
	RAII_VAR(char *, touch_monitor_prefix, NULL, ast_free);

	set_touch_res = set_touch_variables(bridge_channel->chan, 1, &touch_format,
		&touch_monitor, &touch_monitor_prefix);
	switch (set_touch_res) {
	case SET_TOUCH_SUCCESS:
		break;
	case SET_TOUCH_UNSET:
		set_touch_res = set_touch_variables(peer_chan, 1, &touch_format, &touch_monitor,
			&touch_monitor_prefix);
		if (set_touch_res == SET_TOUCH_ALLOC_FAILURE) {
			return;
		}
		break;
	case SET_TOUCH_ALLOC_FAILURE:
		return;
	}

	if (!ast_strlen_zero(touch_monitor)) {
		len = strlen(touch_monitor) + 50;
		touch_filename = ast_alloca(len);
		snprintf(touch_filename, len, "%s-%ld-%s.%s",
			S_OR(touch_monitor_prefix, "auto"),
			(long) time(NULL),
			touch_monitor,
			S_OR(touch_format, "wav"));
	} else {
		char *caller_chan_id;
		char *peer_chan_id;

		caller_chan_id = ast_strdupa(S_COR(ast_channel_caller(bridge_channel->chan)->id.number.valid,
			ast_channel_caller(bridge_channel->chan)->id.number.str, ast_channel_name(bridge_channel->chan)));
		peer_chan_id = ast_strdupa(S_COR(ast_channel_caller(peer_chan)->id.number.valid,
			ast_channel_caller(peer_chan)->id.number.str, ast_channel_name(peer_chan)));
		len = strlen(caller_chan_id) + strlen(peer_chan_id) + 50;
		touch_filename = ast_alloca(len);
		snprintf(touch_filename, len, "%s-%ld-%s-%s.%s",
			S_OR(touch_monitor_prefix, "auto"),
			(long) time(NULL),
			caller_chan_id,
			peer_chan_id,
			S_OR(touch_format, "wav"));
	}

	for (x = 0; x < strlen(touch_filename); x++) {
		if (touch_filename[x] == '/') {
			touch_filename[x] = '-';
		}
	}

	ast_verb(3, "AutoMixMonitor used to record call. Filename: %s\n", touch_filename);

	if (ast_start_mixmonitor(peer_chan, touch_filename, "b")) {
		ast_verb(3, "automixmon feature was tried by '%s' but mixmonitor failed to start.\n",
			ast_channel_name(bridge_channel->chan));

		if (features_cfg && !ast_strlen_zero(features_cfg->recordingfailsound)) {
			ast_bridge_channel_queue_playfile(bridge_channel, NULL, features_cfg->recordingfailsound, NULL);
		}
		return;
	}

	if (features_cfg && !ast_strlen_zero(features_cfg->courtesytone)) {
		ast_bridge_channel_queue_playfile(bridge_channel, NULL, features_cfg->courtesytone, NULL);
		ast_bridge_channel_write_playfile(bridge_channel, NULL, features_cfg->courtesytone, NULL);
	}

	if (!ast_strlen_zero(start_message)) {
		ast_bridge_channel_queue_playfile(bridge_channel, NULL, start_message, NULL);
		ast_bridge_channel_write_playfile(bridge_channel, NULL, start_message, NULL);
	}

	pbx_builtin_setvar_helper(peer_chan, "TOUCH_MIXMONITOR_OUTPUT", touch_filename);
}
Exemple #6
0
/*!
 * \internal
 * \brief ast_bridge parking pull method.
 * \since 12.0.0
 *
 * \param self Bridge to operate upon.
 * \param bridge_channel Bridge channel to pull.
 *
 * \note On entry, self is already locked.
 *
 * \return Nothing
 */
static void bridge_parking_pull(struct ast_bridge_parking *self, struct ast_bridge_channel *bridge_channel)
{
	RAII_VAR(struct parked_user *, pu, NULL, ao2_cleanup);

	ast_bridge_base_v_table.pull(&self->base, bridge_channel);

	/* Take over the bridge channel's pu reference. It will be released when we are done. */
	pu = bridge_channel->bridge_pvt;
	bridge_channel->bridge_pvt = NULL;

	/* This should only happen if the exiting channel was swapped out */
	if (!pu) {
		return;
	}

	/* If we got here without the resolution being set, that's because the call was hung up for some reason without
	 * timing out or being picked up. There may be some forcible park removals later, but the resolution should be
	 * handled in those cases */
	ao2_lock(pu);
	if (pu->resolution == PARK_UNSET) {
		pu->resolution = PARK_ABANDON;
	}
	ao2_unlock(pu);

	/* Pull can still happen after the bridge starts dissolving, so make sure we still have a lot before trying to notify metermaids. */
	if (self->lot) {
		parking_notify_metermaids(pu->parking_space, self->lot->cfg->parking_con, AST_DEVICE_NOT_INUSE);
	}

	switch (pu->resolution) {
	case PARK_UNSET:
		/* This should be impossible now since the resolution is forcibly set to abandon if it was unset at this point. Resolution
		   isn't allowed to be changed when it isn't currently PARK_UNSET. */
		break;
	case PARK_ABANDON:
		/* Since the call was abandoned without additional handling, we need to issue the give up event and unpark the user. */
		publish_parked_call(pu, PARKED_CALL_GIVEUP);
		unpark_parked_user(pu);
		break;
	case PARK_FORCED:
		/* PARK_FORCED is currently unused, but it is expected that it would be handled similar to PARK_ANSWERED.
		 * There is currently no event related to forced parked calls either */
		break;
	case PARK_ANSWERED:
		/* If answered or forced, the channel should be pulled from the bridge as part of that process and unlinked from
		 * the parking lot afterwards. We do need to apply bridge features though and play the courtesy tone if set. */
		publish_parked_call(pu, PARKED_CALL_UNPARKED);
		parked_call_retrieve_enable_features(bridge_channel->chan, pu->lot, AST_FEATURE_FLAG_BYCALLEE);

		if (pu->lot->cfg->parkedplay & AST_FEATURE_FLAG_BYCALLEE) {
			ast_bridge_channel_queue_playfile(bridge_channel, NULL, pu->lot->cfg->courtesytone, NULL);
		}
		break;
	case PARK_TIMEOUT:
		/* Timeout is similar to abandon because it simply sets the bridge state to end and doesn't
		 * actually pull the channel. Because of that, unpark should happen in here. */
		publish_parked_call(pu, PARKED_CALL_TIMEOUT);
		parked_call_retrieve_enable_features(bridge_channel->chan, pu->lot, AST_FEATURE_FLAG_BYCALLEE);
		unpark_parked_user(pu);
		break;
	}
}
Exemple #7
0
/*!
 * \internal
 * \brief ast_bridge parking push method.
 * \since 12.0.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
 */
static int bridge_parking_push(struct ast_bridge_parking *self, struct ast_bridge_channel *bridge_channel, struct ast_bridge_channel *swap)
{
	struct parked_user *pu;
	const char *blind_transfer;
	RAII_VAR(struct ast_channel *, parker, NULL, ao2_cleanup); /* XXX replace with ast_channel_cleanup when available */
	RAII_VAR(struct park_common_datastore *, park_datastore, NULL, park_common_datastore_free);

	ast_bridge_base_v_table.push(&self->base, bridge_channel, swap);

	ast_assert(self->lot != NULL);

	/* Answer the channel if needed */
	if (ast_channel_state(bridge_channel->chan) != AST_STATE_UP) {
		ast_answer(bridge_channel->chan);
	}

	if (swap) {
		int use_ringing = 0;

		ast_bridge_channel_lock(swap);
		pu = swap->bridge_pvt;
		if (!pu) {
			/* This should be impossible since the only way a channel can enter in the first place
			 * is if it has a parked user associated with it */
			publish_parked_call_failure(bridge_channel->chan);
			ast_bridge_channel_unlock(swap);
			return -1;
		}

		/* Give the swap channel's parked user reference to the incoming channel */
		pu->chan = bridge_channel->chan;
		bridge_channel->bridge_pvt = pu;
		swap->bridge_pvt = NULL;

		if (ast_bridge_channel_has_role(swap, "holding_participant")) {
			const char *idle_mode = ast_bridge_channel_get_role_option(swap, "holding_participant", "idle_mode");

			if (!ast_strlen_zero(idle_mode) && !strcmp(idle_mode, "ringing")) {
				use_ringing = 1;
			}
		}

		ast_bridge_channel_unlock(swap);

		parking_set_duration(bridge_channel->features, pu);

		if (parking_channel_set_roles(bridge_channel->chan, self->lot, use_ringing)) {
			ast_log(LOG_WARNING, "Failed to apply holding bridge roles to %s while joining the parking lot.\n",
				ast_channel_name(bridge_channel->chan));
		}

		publish_parked_call(pu, PARKED_CALL_SWAP);

		return 0;
	}

	if (!(park_datastore = get_park_common_datastore_copy(bridge_channel->chan))) {
		/* There was either a failure to apply the datastore when performing park common setup or else we had alloc failures while cloning. Abort. */
		return -1;
	}
	parker = ast_channel_get_by_name(park_datastore->parker_uuid);

	/* If the parker and the parkee are the same channel pointer, then the channel entered using
	 * the park application. It's possible that the channel that transferred it is still alive (particularly
	 * when a multichannel bridge is parked), so try to get the real parker if possible. */
	ast_channel_lock(bridge_channel->chan);
	blind_transfer = S_OR(pbx_builtin_getvar_helper(bridge_channel->chan, "BLINDTRANSFER"),
		ast_channel_name(bridge_channel->chan));
	if (blind_transfer) {
		blind_transfer = ast_strdupa(blind_transfer);
	}
	ast_channel_unlock(bridge_channel->chan);

	if (parker == bridge_channel->chan) {
		struct ast_channel *real_parker = ast_channel_get_by_name(blind_transfer);

		if (real_parker) {
			ao2_cleanup(parker);
			parker = real_parker;
		}
	}

	pu = generate_parked_user(self->lot, bridge_channel->chan, parker,
		park_datastore->parker_dial_string, park_datastore->randomize, park_datastore->time_limit);
	if (!pu) {
		publish_parked_call_failure(bridge_channel->chan);
		return -1;
	}

	/* If a comeback_override was provided, set it for the parked user's comeback string. */
	if (park_datastore->comeback_override) {
		ast_copy_string(pu->comeback, park_datastore->comeback_override, sizeof(pu->comeback));
	}

	/* Generate ParkedCall Stasis Message */
	publish_parked_call(pu, PARKED_CALL);

	/* If the parkee and the parker are the same and silence_announce isn't set, play the announcement to the parkee */
	if (!strcmp(blind_transfer, ast_channel_name(bridge_channel->chan)) && !park_datastore->silence_announce) {
		char saynum_buf[16];

		snprintf(saynum_buf, sizeof(saynum_buf), "%u %u", 0, pu->parking_space);
		ast_bridge_channel_queue_playfile(bridge_channel, say_parking_space, saynum_buf, NULL);
	}

	/* Apply parking duration limits */
	parking_set_duration(bridge_channel->features, pu);

	/* Set this to the bridge pvt so that we don't have to refind the parked user associated with this bridge channel again. */
	bridge_channel->bridge_pvt = pu;

	ast_verb(3, "Parking '" COLORIZE_FMT "' in '" COLORIZE_FMT "' at space %d\n",
		COLORIZE(COLOR_BRMAGENTA, 0, ast_channel_name(bridge_channel->chan)),
		COLORIZE(COLOR_BRMAGENTA, 0, self->lot->name),
		pu->parking_space);

	parking_notify_metermaids(pu->parking_space, self->lot->cfg->parking_con, AST_DEVICE_INUSE);

	return 0;
}