Skip to content
Snippets Groups Projects
app_dial.c 127 KiB
Newer Older
  • Learn to ignore specific revisions
  • 			ast_channel_lock(peer);
    			ast_channel_stage_snapshot(peer);
    			ast_clear_flag(ast_channel_flags(peer), AST_FLAG_OUTGOING);
    
    			ast_channel_context_set(peer, ast_channel_context(chan));
    			ast_channel_exten_set(peer, ast_channel_exten(chan));
    
    			ast_channel_priority_set(peer, ast_channel_priority(chan) + 2);
    
    			ast_channel_stage_snapshot_done(peer);
    
    				ast_autoservice_chan_hangup_peer(chan, peer);
    
    			if (continue_exec)
    				*continue_exec = 1;
    			res = 0;
    
    			ast_channel_publish_dial(chan, peer, NULL, "ANSWER");
    
    		if (ast_test_flag64(&opts, OPT_CALLEE_MACRO) && !ast_strlen_zero(opt_args[OPT_ARG_CALLEE_MACRO])) {
    
    			const char *macro_result_peer;
    
    			/* Set peer->exten and peer->context so that MACRO_EXTEN and MACRO_CONTEXT get set */
    			ast_channel_lock_both(chan, peer);
    			ast_channel_context_set(peer, ast_channel_context(chan));
    			ast_channel_exten_set(peer, ast_channel_exten(chan));
    			ast_channel_unlock(peer);
    			ast_channel_unlock(chan);
    			ast_replace_subargument_delimiter(opt_args[OPT_ARG_CALLEE_MACRO]);
    
    			macro_res = ast_app_exec_macro(chan, peer, opt_args[OPT_ARG_CALLEE_MACRO]);
    
    			if (!macro_res && (macro_result_peer = pbx_builtin_getvar_helper(peer, "MACRO_RESULT"))) {
    
    				char *macro_result = ast_strdupa(macro_result_peer);
    
    				if (!strcasecmp(macro_result, "BUSY")) {
    
    					ast_copy_string(pa.status, macro_result, sizeof(pa.status));
    
    					ast_set_flag64(peerflags, OPT_GO_ON);
    
    				} else if (!strcasecmp(macro_result, "CONGESTION") || !strcasecmp(macro_result, "CHANUNAVAIL")) {
    
    					ast_copy_string(pa.status, macro_result, sizeof(pa.status));
    
    Michiel van Baak's avatar
    Michiel van Baak committed
    					ast_set_flag64(peerflags, OPT_GO_ON);
    
    				} else if (!strcasecmp(macro_result, "CONTINUE")) {
    
    Michiel van Baak's avatar
    Michiel van Baak committed
    					/* hangup peer and keep chan alive assuming the macro has changed
    					   the context / exten / priority or perhaps
    
    					   the next priority in the current exten is desired.
    					*/
    
    Michiel van Baak's avatar
    Michiel van Baak committed
    					ast_set_flag64(peerflags, OPT_GO_ON);
    
    				} else if (!strcasecmp(macro_result, "ABORT")) {
    					/* Hangup both ends unless the caller has the g flag */
    
    				} else if (!strncasecmp(macro_result, "GOTO:", 5)) {
    					macro_transfer_dest = macro_result + 5;
    
    					/* perform a transfer to a new extension */
    					if (strchr(macro_transfer_dest, '^')) { /* context^exten^priority*/
    
    						ast_replace_subargument_delimiter(macro_transfer_dest);
    
    					}
    					if (!ast_parseable_goto(chan, macro_transfer_dest)) {
    						ast_set_flag64(peerflags, OPT_GO_ON);
    
    				if (macro_res && !dial_end_raised) {
    					ast_channel_publish_dial(chan, peer, NULL, macro_result);
    					dial_end_raised = 1;
    				}
    
    			} else {
    				ast_channel_unlock(peer);
    
    		if (ast_test_flag64(&opts, OPT_CALLEE_GOSUB) && !ast_strlen_zero(opt_args[OPT_ARG_CALLEE_GOSUB])) {
    
    			const char *gosub_result_peer;
    			char *gosub_argstart;
    			char *gosub_args = NULL;
    
    			ast_replace_subargument_delimiter(opt_args[OPT_ARG_CALLEE_GOSUB]);
    			gosub_argstart = strchr(opt_args[OPT_ARG_CALLEE_GOSUB], ',');
    			if (gosub_argstart) {
    				const char *what_is_s = "s";
    				*gosub_argstart = 0;
    				if (!ast_exists_extension(peer, opt_args[OPT_ARG_CALLEE_GOSUB], "s", 1, S_COR(ast_channel_caller(peer)->id.number.valid, ast_channel_caller(peer)->id.number.str, NULL)) &&
    					 ast_exists_extension(peer, opt_args[OPT_ARG_CALLEE_GOSUB], "~~s~~", 1, S_COR(ast_channel_caller(peer)->id.number.valid, ast_channel_caller(peer)->id.number.str, NULL))) {
    					what_is_s = "~~s~~";
    
    				if (ast_asprintf(&gosub_args, "%s,%s,1(%s)", opt_args[OPT_ARG_CALLEE_GOSUB], what_is_s, gosub_argstart + 1) < 0) {
    
    					gosub_args = NULL;
    				}
    				*gosub_argstart = ',';
    			} else {
    				const char *what_is_s = "s";
    				if (!ast_exists_extension(peer, opt_args[OPT_ARG_CALLEE_GOSUB], "s", 1, S_COR(ast_channel_caller(peer)->id.number.valid, ast_channel_caller(peer)->id.number.str, NULL)) &&
    					 ast_exists_extension(peer, opt_args[OPT_ARG_CALLEE_GOSUB], "~~s~~", 1, S_COR(ast_channel_caller(peer)->id.number.valid, ast_channel_caller(peer)->id.number.str, NULL))) {
    					what_is_s = "~~s~~";
    				}
    
    				if (ast_asprintf(&gosub_args, "%s,%s,1", opt_args[OPT_ARG_CALLEE_GOSUB], what_is_s) < 0) {
    
    				gosub_res = ast_app_exec_sub(chan, peer, gosub_args, 0);
    
    				ast_free(gosub_args);
    			} else {
    				ast_log(LOG_ERROR, "Could not Allocate string for Gosub arguments -- Gosub Call Aborted!\n");
    
    			ast_channel_lock_both(chan, peer);
    
    
    			if (!gosub_res && (gosub_result_peer = pbx_builtin_getvar_helper(peer, "GOSUB_RESULT"))) {
    
    				char *gosub_result = ast_strdupa(gosub_result_peer);
    
    				const char *gosub_retval = pbx_builtin_getvar_helper(peer, "GOSUB_RETVAL");
    
    				/* Inherit return value from the peer, so it can be used in the master */
    				if (gosub_retval) {
    					pbx_builtin_setvar_helper(chan, "GOSUB_RETVAL", gosub_retval);
    				}
    
    				ast_channel_unlock(peer);
    				ast_channel_unlock(chan);
    
    
    				if (!strcasecmp(gosub_result, "BUSY")) {
    					ast_copy_string(pa.status, gosub_result, sizeof(pa.status));
    
    					ast_set_flag64(peerflags, OPT_GO_ON);
    
    				} else if (!strcasecmp(gosub_result, "CONGESTION") || !strcasecmp(gosub_result, "CHANUNAVAIL")) {
    					ast_copy_string(pa.status, gosub_result, sizeof(pa.status));
    
    Michiel van Baak's avatar
    Michiel van Baak committed
    					ast_set_flag64(peerflags, OPT_GO_ON);
    
    				} else if (!strcasecmp(gosub_result, "CONTINUE")) {
    
    					/* Hangup peer and continue with the next extension priority. */
    
    Michiel van Baak's avatar
    Michiel van Baak committed
    					ast_set_flag64(peerflags, OPT_GO_ON);
    
    				} else if (!strcasecmp(gosub_result, "ABORT")) {
    					/* Hangup both ends unless the caller has the g flag */
    
    				} else if (!strncasecmp(gosub_result, "GOTO:", 5)) {
    					gosub_transfer_dest = gosub_result + 5;
    
    					/* perform a transfer to a new extension */
    					if (strchr(gosub_transfer_dest, '^')) { /* context^exten^priority*/
    
    						ast_replace_subargument_delimiter(gosub_transfer_dest);
    
    					}
    					if (!ast_parseable_goto(chan, gosub_transfer_dest)) {
    						ast_set_flag64(peerflags, OPT_GO_ON);
    
    				if (gosub_res) {
    					res = gosub_res;
    					if (!dial_end_raised) {
    						ast_channel_publish_dial(chan, peer, NULL, gosub_result);
    						dial_end_raised = 1;
    					}
    				}
    
    			} else {
    				ast_channel_unlock(peer);
    				ast_channel_unlock(chan);
    
    James Golovich's avatar
    James Golovich committed
    		if (!res) {
    
    
    			/* None of the Dial options changed our status; inform
    			 * everyone that this channel answered
    			 */
    
    			if (!dial_end_raised) {
    				ast_channel_publish_dial(chan, peer, NULL, "ANSWER");
    				dial_end_raised = 1;
    			}
    
    			if (!ast_tvzero(calldurationlimit)) {
    
    				struct timeval whentohangup = ast_tvadd(ast_tvnow(), calldurationlimit);
    
    				ast_channel_whentohangup_set(peer, &whentohangup);
    
    James Golovich's avatar
    James Golovich committed
    			}
    
    Michiel van Baak's avatar
    Michiel van Baak committed
    			if (!ast_strlen_zero(dtmfcalled)) {
    
    				ast_verb(3, "Sending DTMF '%s' to the called party.\n", dtmfcalled);
    
    				res = ast_dtmf_stream(peer, chan, dtmfcalled, 250, 0);
    
    			if (!ast_strlen_zero(dtmfcalling)) {
    
    				ast_verb(3, "Sending DTMF '%s' to the calling party.\n", dtmfcalling);
    
    				res = ast_dtmf_stream(chan, peer, dtmfcalling, 250, 0);
    
    Michiel van Baak's avatar
    Michiel van Baak committed
    		if (res) { /* some error */
    
    			if (!ast_check_hangup(chan) && ast_check_hangup(peer)) {
    				ast_channel_hangupcause_set(chan, ast_channel_hangupcause(peer));
    			}
    			setup_peer_after_bridge_goto(chan, peer, &opts, opt_args);
    
    			if (ast_bridge_setup_after_goto(peer)
    
    				|| ast_pbx_start(peer)) {
    				ast_autoservice_chan_hangup_peer(chan, peer);
    			}
    
    			if (ast_test_flag64(peerflags, OPT_CALLEE_TRANSFER))
    
    				ast_set_flag(&(config.features_callee), AST_FEATURE_REDIRECT);
    
    			if (ast_test_flag64(peerflags, OPT_CALLER_TRANSFER))
    
    				ast_set_flag(&(config.features_caller), AST_FEATURE_REDIRECT);
    
    			if (ast_test_flag64(peerflags, OPT_CALLEE_HANGUP))
    
    				ast_set_flag(&(config.features_callee), AST_FEATURE_DISCONNECT);
    
    			if (ast_test_flag64(peerflags, OPT_CALLER_HANGUP))
    
    				ast_set_flag(&(config.features_caller), AST_FEATURE_DISCONNECT);
    
    			if (ast_test_flag64(peerflags, OPT_CALLEE_MONITOR))
    
    				ast_set_flag(&(config.features_callee), AST_FEATURE_AUTOMON);
    
    Michiel van Baak's avatar
    Michiel van Baak committed
    			if (ast_test_flag64(peerflags, OPT_CALLER_MONITOR))
    
    				ast_set_flag(&(config.features_caller), AST_FEATURE_AUTOMON);
    
    			if (ast_test_flag64(peerflags, OPT_CALLEE_PARK))
    
    				ast_set_flag(&(config.features_callee), AST_FEATURE_PARKCALL);
    
    			if (ast_test_flag64(peerflags, OPT_CALLER_PARK))
    
    				ast_set_flag(&(config.features_caller), AST_FEATURE_PARKCALL);
    
    			if (ast_test_flag64(peerflags, OPT_CALLEE_MIXMONITOR))
    				ast_set_flag(&(config.features_callee), AST_FEATURE_AUTOMIXMON);
    			if (ast_test_flag64(peerflags, OPT_CALLER_MIXMONITOR))
    				ast_set_flag(&(config.features_caller), AST_FEATURE_AUTOMIXMON);
    
    			config.end_bridge_callback = end_bridge_callback;
    
    			config.end_bridge_callback_data = chan;
    
    			config.end_bridge_callback_data_fixup = end_bridge_callback_data_fixup;
    
    			if (moh) {
    				moh = 0;
    				ast_moh_stop(chan);
    			} else if (sentringing) {
    				sentringing = 0;
    				ast_indicate(chan, -1);
    			}
    
    			/* Be sure no generators are left on it and reset the visible indication */
    
    			ast_channel_visible_indication_set(chan, 0);
    
    			/* Make sure channels are compatible */
    			res = ast_channel_make_compatible(chan, peer);
    			if (res < 0) {
    
    				ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", ast_channel_name(chan), ast_channel_name(peer));
    
    				ast_autoservice_chan_hangup_peer(chan, peer);
    
    				res = -1;
    				goto done;
    
    				struct oprmode oprmode;
    
    				oprmode.peer = peer;
    				oprmode.mode = opermode;
    
    
    				ast_channel_setoption(chan, AST_OPTION_OPRMODE, &oprmode, sizeof(oprmode), 0);
    
    			setup_peer_after_bridge_goto(chan, peer, &opts, opt_args);
    
    Mark Spencer's avatar
    Mark Spencer committed
    out:
    
    	if (moh) {
    		moh = 0;
    		ast_moh_stop(chan);
    	} else if (sentringing) {
    		sentringing = 0;
    		ast_indicate(chan, -1);
    	}
    
    
    	if (delprivintro && ast_fileexists(pa.privintro, NULL, NULL) > 0) {
    		ast_filedelete(pa.privintro, NULL);
    		if (ast_fileexists(pa.privintro, NULL, NULL) > 0) {
    			ast_log(LOG_NOTICE, "privacy: ast_filedelete didn't do its job on %s\n", pa.privintro);
    		} else {
    			ast_verb(3, "Successfully deleted %s intro file\n", pa.privintro);
    		}
    	}
    
    
    	ast_channel_early_bridge(chan, NULL);
    
    	 /* forward 'answered elsewhere' if we received it */
    	hanguptree(&out_chans, NULL,
    		ast_channel_hangupcause(chan) == AST_CAUSE_ANSWERED_ELSEWHERE
    		|| ast_test_flag64(&opts, OPT_CANCEL_ELSEWHERE)
    		? AST_CAUSE_ANSWERED_ELSEWHERE : -1);
    
    	pbx_builtin_setvar_helper(chan, "DIALSTATUS", pa.status);
    	ast_debug(1, "Exiting with DIALSTATUS=%s.\n", pa.status);
    
    	if ((ast_test_flag64(peerflags, OPT_GO_ON)) && !ast_check_hangup(chan) && (res != AST_PBX_INCOMPLETE)) {
    		if (!ast_tvzero(calldurationlimit))
    
    			memset(ast_channel_whentohangup(chan), 0, sizeof(*ast_channel_whentohangup(chan)));
    
    	if (config.warning_sound) {
    		ast_free((char *)config.warning_sound);
    	}
    	if (config.end_sound) {
    		ast_free((char *)config.end_sound);
    	}
    	if (config.start_sound) {
    		ast_free((char *)config.start_sound);
    	}
    
    	ast_ignore_cc(chan);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return res;
    }
    
    
    static int dial_exec(struct ast_channel *chan, const char *data)
    
    	memset(&peerflags, 0, sizeof(peerflags));
    
    	return dial_exec_full(chan, data, &peerflags, NULL);
    
    static int retrydial_exec(struct ast_channel *chan, const char *data)
    
    	int sleepms = 0, loops = 0, res = -1;
    
    	struct ast_flags64 peerflags = { 0, };
    	AST_DECLARE_APP_ARGS(args,
    		AST_APP_ARG(announce);
    		AST_APP_ARG(sleep);
    		AST_APP_ARG(retries);
    		AST_APP_ARG(dialdata);
    	);
    
    
    	if (ast_strlen_zero(data)) {
    
    		ast_log(LOG_WARNING, "RetryDial requires an argument!\n");
    		return -1;
    
    	parse = ast_strdupa(data);
    	AST_STANDARD_APP_ARGS(args, parse);
    
    	if (!ast_strlen_zero(args.sleep) && (sleepms = atoi(args.sleep)))
    
    	if (!ast_strlen_zero(args.retries)) {
    		loops = atoi(args.retries);
    	}
    
    
    	if (!args.dialdata) {
    		ast_log(LOG_ERROR, "%s requires a 4th argument (dialdata)\n", rapp);
    
    	if (sleepms < 1000)
    		sleepms = 10000;
    
    Michiel van Baak's avatar
    Michiel van Baak committed
    		loops = -1; /* run forever */
    
    
    	context = pbx_builtin_getvar_helper(chan, "EXITCONTEXT");
    
    	context = !ast_strlen_zero(context) ? ast_strdupa(context) : NULL;
    	ast_channel_unlock(chan);
    
    		ast_channel_data_set(chan, "Retrying");
    
    		if (ast_test_flag(ast_channel_flags(chan), AST_FLAG_MOH))
    
    		res = dial_exec_full(chan, args.dialdata, &peerflags, &continue_exec);
    
    		if (continue_exec)
    
    		if (res == 0) {
    
    			if (ast_test_flag64(&peerflags, OPT_DTMF_EXIT)) {
    
    					if (ast_fileexists(args.announce, NULL, ast_channel_language(chan)) > 0) {
    						if (!(res = ast_streamfile(chan, args.announce, ast_channel_language(chan))))
    
    							ast_waitstream(chan, AST_DIGIT_ANY);
    					} else
    
    						ast_log(LOG_WARNING, "Announce file \"%s\" specified in Retrydial does not exist\n", args.announce);
    
    					if (!ast_test_flag(ast_channel_flags(chan), AST_FLAG_MOH))
    
    					res = ast_waitfordigit(chan, sleepms);
    
    					if (ast_fileexists(args.announce, NULL, ast_channel_language(chan)) > 0) {
    						if (!(res = ast_streamfile(chan, args.announce, ast_channel_language(chan))))
    
    							res = ast_waitstream(chan, "");
    					} else
    
    						ast_log(LOG_WARNING, "Announce file \"%s\" specified in Retrydial does not exist\n", args.announce);
    
    					if (!ast_test_flag(ast_channel_flags(chan), AST_FLAG_MOH))
    
    						res = ast_waitfordigit(chan, sleepms);
    
    Mark Spencer's avatar
    Mark Spencer committed
    				}
    
    		if (res < 0 || res == AST_PBX_INCOMPLETE) {
    
    		} else if (res > 0) { /* Trying to send the call elsewhere (1 digit ext) */
    
    			if (onedigit_goto(chan, context, (char) res, 1)) {
    
    	else if (res == 1)
    		res = 0;
    
    
    	if (ast_test_flag(ast_channel_flags(chan), AST_FLAG_MOH))
    
    		ast_moh_stop(chan);
    
    static int unload_module(void)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    	int res;
    
    	res = ast_unregister_application(app);
    	res |= ast_unregister_application(rapp);
    
    	return res;
    
    static int load_module(void)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int res;
    
    	res = ast_register_application_xml(app, dial_exec);
    	res |= ast_register_application_xml(rapp, retrydial_exec);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return res;
    
    AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Dialing Application",
    	.support_level = AST_MODULE_SUPPORT_CORE,
    	.load = load_module,
    	.unload = unload_module,
    	.requires = "ccss",
    );