/*! \brief Internal built in feature for blind transfers */
static int feature_blind_transfer(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
{
	char exten[AST_MAX_EXTENSION] = "";
	struct ast_channel *chan = NULL;
	struct ast_bridge_features_blind_transfer *blind_transfer = hook_pvt;
	const char *context = (blind_transfer && !ast_strlen_zero(blind_transfer->context) ? blind_transfer->context : bridge_channel->chan->context);

	/* Grab the extension to transfer to */
	if (!grab_transfer(bridge_channel->chan, exten, sizeof(exten), context)) {
		ast_stream_and_wait(bridge_channel->chan, "pbx-invalid", AST_DIGIT_ANY);
		ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_WAIT);
		return 0;
	}

	/* Get a channel that is the destination we wish to call */
	if (!(chan = dial_transfer(bridge_channel->chan, exten, context))) {
		ast_stream_and_wait(bridge_channel->chan, "beeperr", AST_DIGIT_ANY);
		ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_WAIT);
		return 0;
	}

	/* This is sort of the fun part. We impart the above channel onto the bridge, and have it take our place. */
	ast_bridge_impart(bridge, chan, bridge_channel->chan, NULL);

	return 0;
}
static void limits_interval_playback(struct ast_bridge_channel *bridge_channel, struct ast_bridge_features_limits *limits, const char *file)
{
	if (!strcasecmp(file, "timeleft")) {
		unsigned int remaining = ast_tvdiff_ms(limits->quitting_time, ast_tvnow()) / 1000;
		unsigned int min;
		unsigned int sec;

		if (remaining <= 0) {
			return;
		}

		if ((remaining / 60) > 1) {
			min = remaining / 60;
			sec = remaining % 60;
		} else {
			min = 0;
			sec = remaining;
		}

		ast_stream_and_wait(bridge_channel->chan, "vm-youhave", AST_DIGIT_NONE);
		if (min) {
			ast_say_number(bridge_channel->chan, min, AST_DIGIT_NONE,
				ast_channel_language(bridge_channel->chan), NULL);
			ast_stream_and_wait(bridge_channel->chan, "queue-minutes", AST_DIGIT_NONE);
		}
		if (sec) {
			ast_say_number(bridge_channel->chan, sec, AST_DIGIT_NONE,
				ast_channel_language(bridge_channel->chan), NULL);
			ast_stream_and_wait(bridge_channel->chan, "queue-seconds", AST_DIGIT_NONE);
		}
	} else {
		ast_stream_and_wait(bridge_channel->chan, file, AST_DIGIT_NONE);
	}

	/*
	 * It may be necessary to resume music on hold after we finish
	 * playing the announcment.
	 */
	if (ast_test_flag(ast_channel_flags(bridge_channel->chan), AST_FLAG_MOH)) {
		const char *latest_musicclass;

		ast_channel_lock(bridge_channel->chan);
		latest_musicclass = ast_strdupa(ast_channel_latest_musicclass(bridge_channel->chan));
		ast_channel_unlock(bridge_channel->chan);
		ast_moh_start(bridge_channel->chan, latest_musicclass, NULL);
	}
}
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;
}
Beispiel #4
0
/*!
 * \brief Pickup a call
 * \param chan channel that initiated pickup.
 *
 * Walk list of channels, checking it is not itself, channel is pbx one,
 * check that the callgroup for both channels are the same and the channel is ringing.
 * Answer calling channel, flag channel as answered on queue, masq channels together.
 */
int ast_pickup_call(struct ast_channel *chan)
{
	struct ast_channel *target;/*!< Potential pickup target */
	int res = -1;
	RAII_VAR(struct ast_features_pickup_config *, pickup_cfg, NULL, ao2_cleanup);
	const char *pickup_sound;
	const char *fail_sound;

	ast_debug(1, "Pickup attempt by %s\n", ast_channel_name(chan));
	ast_channel_lock(chan);
	pickup_cfg = ast_get_chan_features_pickup_config(chan);
	if (!pickup_cfg) {
		ast_log(LOG_ERROR, "Unable to retrieve pickup configuration. Unable to play pickup sounds\n");
	}
	pickup_sound = ast_strdupa(pickup_cfg ? pickup_cfg->pickupsound : "");
	fail_sound = ast_strdupa(pickup_cfg ? pickup_cfg->pickupfailsound : "");
	ast_channel_unlock(chan);

	/* The found channel is already locked. */
	target = ast_pickup_find_by_group(chan);
	if (target) {
		ast_log(LOG_NOTICE, "Pickup %s attempt by %s\n", ast_channel_name(target), ast_channel_name(chan));

		res = ast_do_pickup(chan, target);
		ast_channel_unlock(target);
		if (!res) {
			if (!ast_strlen_zero(pickup_sound)) {
				pbx_builtin_setvar_helper(target, "BRIDGE_PLAY_SOUND", pickup_sound);
			}
		} else {
			ast_log(LOG_WARNING, "Pickup %s failed by %s\n", ast_channel_name(target), ast_channel_name(chan));
		}
		target = ast_channel_unref(target);
	}

	if (res < 0) {
		ast_debug(1, "No call pickup possible... for %s\n", ast_channel_name(chan));
		if (!ast_strlen_zero(fail_sound)) {
			ast_answer(chan);
			ast_stream_and_wait(chan, fail_sound, "");
		}
	}

	return res;
}
/*! \brief Helper function that presents dialtone and grabs extension */
static int grab_transfer(struct ast_channel *chan, char *exten, size_t exten_len, const char *context)
{
	int res;

	/* Play the simple "transfer" prompt out and wait */
	res = ast_stream_and_wait(chan, "pbx-transfer", AST_DIGIT_ANY);
	ast_stopstream(chan);

	/* If the person hit a DTMF digit while the above played back stick it into the buffer */
	if (res) {
		exten[0] = (char)res;
	}

	/* Drop to dialtone so they can enter the extension they want to transfer to */
	res = ast_app_dtget(chan, context, exten, exten_len, 100, 1000);

	return res;
}
/* play name of mailbox owner.
 * returns:  -1 for bad or missing extension
 *           '1' for selected entry from directory
 *           '*' for skipped entry from directory
 */
static int play_mailbox_owner(struct ast_channel *chan, char *context,
		char *dialcontext, char *ext, char *name, int readext,
		int fromappvm)
{
	int res = 0;
	int loop;
	char fn[256];

	/* Check for the VoiceMail2 greeting first */
	snprintf(fn, sizeof(fn), "%s/voicemail/%s/%s/greet",
		ast_config_AST_SPOOL_DIR, context, ext);
#ifdef ODBC_STORAGE
	retrieve_file(fn);
#endif

	if (ast_fileexists(fn, NULL, chan->language) <= 0) {
		/* no file, check for an old-style Voicemail greeting */
		snprintf(fn, sizeof(fn), "%s/vm/%s/greet",
			ast_config_AST_SPOOL_DIR, ext);
	}
#ifdef ODBC_STORAGE
	retrieve_file(fn);
#endif

	if (ast_fileexists(fn, NULL, chan->language) > 0) {
		res = ast_stream_and_wait(chan, fn, chan->language, AST_DIGIT_ANY);
		ast_stopstream(chan);
		/* If Option 'e' was specified, also read the extension number with the name */
		if (readext) {
			ast_stream_and_wait(chan, "vm-extension", chan->language, AST_DIGIT_ANY);
			res = ast_say_character_str(chan, ext, AST_DIGIT_ANY, chan->language);
		}
	} else {
		res = ast_say_character_str(chan, S_OR(name, ext), AST_DIGIT_ANY, chan->language);
		if (!ast_strlen_zero(name) && readext) {
			ast_stream_and_wait(chan, "vm-extension", chan->language, AST_DIGIT_ANY);
			res = ast_say_character_str(chan, ext, AST_DIGIT_ANY, chan->language);
		}
	}
#ifdef ODBC_STORAGE
	ast_filedelete(fn, NULL);	
#endif

	for (loop = 3 ; loop > 0; loop--) {
		if (!res)
			res = ast_stream_and_wait(chan, "dir-instr", chan->language, AST_DIGIT_ANY);
		if (!res)
			res = ast_waitfordigit(chan, 3000);
		ast_stopstream(chan);
	
		if (res < 0) /* User hungup, so jump out now */
			break;
		if (res == '1') {	/* Name selected */
			if (fromappvm) {
				/* We still want to set the exten though */
				ast_copy_string(chan->exten, ext, sizeof(chan->exten));
			} else {
				if (ast_goto_if_exists(chan, dialcontext, ext, 1)) {
					ast_log(LOG_WARNING,
						"Can't find extension '%s' in context '%s'.  "
						"Did you pass the wrong context to Directory?\n",
						ext, dialcontext);
					res = -1;
				}
			}
			break;
		}
		if (res == '*') /* Skip to next match in list */
			break;

		/* Not '1', or '*', so decrement number of tries */
		res = 0;
	}

	return(res);
}
/*! \brief Internal built in feature for attended transfers */
static int feature_attended_transfer(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
{
	char exten[AST_MAX_EXTENSION] = "";
	struct ast_channel *chan = NULL;
	struct ast_bridge *attended_bridge = NULL;
	struct ast_bridge_features caller_features, called_features;
	enum ast_bridge_channel_state attended_bridge_result;
	struct ast_bridge_features_attended_transfer *attended_transfer = hook_pvt;
	const char *context = (attended_transfer && !ast_strlen_zero(attended_transfer->context) ? attended_transfer->context : bridge_channel->chan->context);

	/* Grab the extension to transfer to */
	if (!grab_transfer(bridge_channel->chan, exten, sizeof(exten), context)) {
		ast_stream_and_wait(bridge_channel->chan, "pbx-invalid", AST_DIGIT_ANY);
		ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_WAIT);
		return 0;
	}

	/* Get a channel that is the destination we wish to call */
	if (!(chan = dial_transfer(bridge_channel->chan, exten, context))) {
		ast_stream_and_wait(bridge_channel->chan, "beeperr", AST_DIGIT_ANY);
		ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_WAIT);
		return 0;
	}

	/* Create a bridge to use to talk to the person we are calling */
	if (!(attended_bridge = ast_bridge_new(AST_BRIDGE_CAPABILITY_1TO1MIX, 0))) {
		ast_hangup(chan);
		ast_stream_and_wait(bridge_channel->chan, "beeperr", AST_DIGIT_ANY);
		ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_WAIT);
		return 0;
	}

	/* Setup our called features structure so that if they hang up we immediately get thrown out of the bridge */
	ast_bridge_features_init(&called_features);
	ast_bridge_features_set_flag(&called_features, AST_BRIDGE_FLAG_DISSOLVE);

	/* This is how this is going down, we are imparting the channel we called above into this bridge first */
	ast_bridge_impart(attended_bridge, chan, NULL, &called_features);

	/* Before we join setup a features structure with the hangup option, just in case they want to use DTMF */
	ast_bridge_features_init(&caller_features);
	ast_bridge_features_enable(&caller_features, AST_BRIDGE_BUILTIN_HANGUP,
				   (attended_transfer && !ast_strlen_zero(attended_transfer->complete) ? attended_transfer->complete : "*1"), NULL);
	ast_bridge_features_hook(&caller_features, (attended_transfer && !ast_strlen_zero(attended_transfer->threeway) ? attended_transfer->threeway : "*2"),
				 attended_threeway_transfer, NULL);
	ast_bridge_features_hook(&caller_features, (attended_transfer && !ast_strlen_zero(attended_transfer->abort) ? attended_transfer->abort : "*3"),
				 attended_abort_transfer, NULL);

	/* But for the caller we want to join the bridge in a blocking fashion so we don't spin around in this function doing nothing while waiting */
	attended_bridge_result = ast_bridge_join(attended_bridge, bridge_channel->chan, NULL, &caller_features);

	/* Since the above returned the caller features structure is of no more use */
	ast_bridge_features_cleanup(&caller_features);

	/* Drop the channel we are transferring to out of the above bridge since it has ended */
	if ((attended_bridge_result != AST_BRIDGE_CHANNEL_STATE_HANGUP) && !ast_bridge_depart(attended_bridge, chan)) {
		/* If the user wants to turn this into a threeway transfer then do so, otherwise they take our place */
		if (attended_bridge_result == AST_BRIDGE_CHANNEL_STATE_DEPART) {
			/* We want to impart them upon the bridge and just have us return to it as normal */
			ast_bridge_impart(bridge, chan, NULL, NULL);
		} else {
			ast_bridge_impart(bridge, chan, bridge_channel->chan, NULL);
		}
	} else {
		ast_stream_and_wait(bridge_channel->chan, "beeperr", AST_DIGIT_ANY);
		ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_WAIT);
	}

	/* Now that all channels are out of it we can destroy the bridge and the called features structure */
	ast_bridge_features_cleanup(&called_features);
	ast_bridge_destroy(attended_bridge);

	return 0;
}