Skip to content
Snippets Groups Projects
app_dial.c 119 KiB
Newer Older
  • Learn to ignore specific revisions
  • 	time(&end);
    
    	ast_channel_lock(chan);
    
    	if (ast_channel_cdr(chan)->answer.tv_sec) {
    		snprintf(buf, sizeof(buf), "%ld", (long) end - ast_channel_cdr(chan)->answer.tv_sec);
    
    		pbx_builtin_setvar_helper(chan, "ANSWEREDTIME", buf);
    	}
    
    
    	if (ast_channel_cdr(chan)->start.tv_sec) {
    		snprintf(buf, sizeof(buf), "%ld", (long) end - ast_channel_cdr(chan)->start.tv_sec);
    
    		pbx_builtin_setvar_helper(chan, "DIALEDTIME", buf);
    	}
    	ast_channel_unlock(chan);
    }
    
    
    static void end_bridge_callback_data_fixup(struct ast_bridge_config *bconfig, struct ast_channel *originator, struct ast_channel *terminator) {
    	bconfig->end_bridge_callback_data = originator;
    }
    
    
    static int dial_handle_playtones(struct ast_channel *chan, const char *data)
    {
    	struct ast_tone_zone_sound *ts = NULL;
    	int res;
    	const char *str = data;
    
    	if (ast_strlen_zero(str)) {
    		ast_debug(1,"Nothing to play\n");
    		return -1;
    	}
    
    
    	ts = ast_get_indication_tone(ast_channel_zone(chan), str);
    
    
    	if (ts && ts->data[0]) {
    		res = ast_playtones_start(chan, 0, ts->data, 0);
    	} else {
    		res = -1;
    	}
    
    	if (ts) {
    		ts = ast_tone_zone_sound_unref(ts);
    	}
    
    	if (res) {
    		ast_log(LOG_WARNING, "Unable to start playtone \'%s\'\n", str);
    	}
    
    	return res;
    }
    
    
    static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast_flags64 *peerflags, int *continue_exec)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    Michiel van Baak's avatar
    Michiel van Baak committed
    	int res = -1; /* default: error */
    	char *rest, *cur; /* scan the list of destinations */
    
    	struct dial_head out_chans = AST_LIST_HEAD_NOLOCK_INIT_VALUE; /* list of destinations */
    	struct chanlist *outgoing;
    	struct chanlist *tmp;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	struct ast_channel *peer;
    
    Michiel van Baak's avatar
    Michiel van Baak committed
    	int to; /* timeout */
    
    	struct cause_args num = { chan, 0, 0, 0 };
    
    	struct timeval calldurationlimit = { 0, };
    
    	char *dtmfcalled = NULL, *dtmfcalling = NULL, *dtmf_progress=NULL;
    
    	struct privacy_args pa = {
    		.sentringing = 0,
    		.privdb_val = 0,
    
    		.status = "INVALIDARGS",
    
    	int sentringing = 0, moh = 0;
    
    	int result = 0;
    
    	int delprivintro = 0;
    
    Michiel van Baak's avatar
    Michiel van Baak committed
    		AST_APP_ARG(peers);
    		AST_APP_ARG(timeout);
    		AST_APP_ARG(options);
    		AST_APP_ARG(url);
    
    	char *opt_args[OPT_ARG_ARRAY_SIZE];
    
    	int fulldial = 0, num_dialed = 0;
    
    	int ignore_cc = 0;
    	char device_name[AST_CHANNEL_NAME];
    
    	char forced_clid_name[AST_MAX_EXTENSION];
    	char stored_clid_name[AST_MAX_EXTENSION];
    	int force_forwards_only;	/*!< TRUE if force CallerID on call forward only. Legacy behaviour.*/
    	/*!
    	 * \brief Forced CallerID party information to send.
    	 * \note This will not have any malloced strings so do not free it.
    	 */
    	struct ast_party_id forced_clid;
    	/*!
    	 * \brief Stored CallerID information if needed.
    	 *
    	 * \note If OPT_ORIGINAL_CLID set then this is the o option
    	 * CallerID.  Otherwise it is the dialplan extension and hint
    	 * name.
    	 *
    	 * \note This will not have any malloced strings so do not free it.
    	 */
    	struct ast_party_id stored_clid;
    	/*!
    	 * \brief CallerID party information to store.
    	 * \note This will not have any malloced strings so do not free it.
    	 */
    	struct ast_party_caller caller;
    
    	/* Reset all DIAL variables back to blank, to prevent confusion (in case we don't reset all of them). */
    	pbx_builtin_setvar_helper(chan, "DIALSTATUS", "");
    	pbx_builtin_setvar_helper(chan, "DIALEDPEERNUMBER", "");
    	pbx_builtin_setvar_helper(chan, "DIALEDPEERNAME", "");
    	pbx_builtin_setvar_helper(chan, "ANSWEREDTIME", "");
    	pbx_builtin_setvar_helper(chan, "DIALEDTIME", "");
    
    
    	if (ast_strlen_zero(data)) {
    
    		ast_log(LOG_WARNING, "Dial requires an argument (technology/resource)\n");
    
    		pbx_builtin_setvar_helper(chan, "DIALSTATUS", pa.status);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return -1;
    	}
    
    	AST_STANDARD_APP_ARGS(args, parse);
    
    
    	if (!ast_strlen_zero(args.options) &&
    
    Michiel van Baak's avatar
    Michiel van Baak committed
    		ast_app_parse_options64(dial_exec_options, &opts, opt_args, args.options)) {
    
    		pbx_builtin_setvar_helper(chan, "DIALSTATUS", pa.status);
    
    	if (ast_strlen_zero(args.peers)) {
    
    		ast_log(LOG_WARNING, "Dial requires an argument (technology/resource)\n");
    
    		pbx_builtin_setvar_helper(chan, "DIALSTATUS", pa.status);
    
    	if (ast_cc_call_init(chan, &ignore_cc)) {
    		goto done;
    	}
    
    
    	if (ast_test_flag64(&opts, OPT_SCREEN_NOINTRO) && !ast_strlen_zero(opt_args[OPT_ARG_SCREEN_NOINTRO])) {
    		delprivintro = atoi(opt_args[OPT_ARG_SCREEN_NOINTRO]);
    
    		if (delprivintro < 0 || delprivintro > 1) {
    			ast_log(LOG_WARNING, "Unknown argument %d specified to n option, ignoring\n", delprivintro);
    			delprivintro = 0;
    		}
    	}
    
    
    	if (!ast_test_flag64(&opts, OPT_RINGBACK)) {
    		opt_args[OPT_ARG_RINGBACK] = NULL;
    	}
    
    
    	if (ast_test_flag64(&opts, OPT_OPERMODE)) {
    
    		opermode = ast_strlen_zero(opt_args[OPT_ARG_OPERMODE]) ? 1 : atoi(opt_args[OPT_ARG_OPERMODE]);
    
    		ast_verb(3, "Setting operator services mode to %d.\n", opermode);
    
    	if (ast_test_flag64(&opts, OPT_DURATION_STOP) && !ast_strlen_zero(opt_args[OPT_ARG_DURATION_STOP])) {
    
    		calldurationlimit.tv_sec = atoi(opt_args[OPT_ARG_DURATION_STOP]);
    		if (!calldurationlimit.tv_sec) {
    
    			ast_log(LOG_WARNING, "Dial does not accept S(%s), hanging up.\n", opt_args[OPT_ARG_DURATION_STOP]);
    
    			pbx_builtin_setvar_helper(chan, "DIALSTATUS", pa.status);
    
    		ast_verb(3, "Setting call duration limit to %.3lf seconds.\n", calldurationlimit.tv_sec + calldurationlimit.tv_usec / 1000000.0);
    
    	if (ast_test_flag64(&opts, OPT_SENDDTMF) && !ast_strlen_zero(opt_args[OPT_ARG_SENDDTMF])) {
    
    		dtmf_progress = opt_args[OPT_ARG_SENDDTMF];
    		dtmfcalled = strsep(&dtmf_progress, ":");
    		dtmfcalling = strsep(&dtmf_progress, ":");
    
    	if (ast_test_flag64(&opts, OPT_DURATION_LIMIT) && !ast_strlen_zero(opt_args[OPT_ARG_DURATION_LIMIT])) {
    
    		if (ast_bridge_timelimit(chan, &config, opt_args[OPT_ARG_DURATION_LIMIT], &calldurationlimit))
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    	/* Setup the forced CallerID information to send if used. */
    	ast_party_id_init(&forced_clid);
    	force_forwards_only = 0;
    	if (ast_test_flag64(&opts, OPT_FORCECLID)) {
    		if (ast_strlen_zero(opt_args[OPT_ARG_FORCECLID])) {
    			ast_channel_lock(chan);
    
    			forced_clid.number.str = ast_strdupa(S_OR(ast_channel_macroexten(chan), ast_channel_exten(chan)));
    
    			ast_channel_unlock(chan);
    			forced_clid_name[0] = '\0';
    			forced_clid.name.str = (char *) get_cid_name(forced_clid_name,
    				sizeof(forced_clid_name), chan);
    			force_forwards_only = 1;
    		} else {
    			/* Note: The opt_args[OPT_ARG_FORCECLID] string value is altered here. */
    			ast_callerid_parse(opt_args[OPT_ARG_FORCECLID], &forced_clid.name.str,
    				&forced_clid.number.str);
    		}
    		if (!ast_strlen_zero(forced_clid.name.str)) {
    			forced_clid.name.valid = 1;
    		}
    		if (!ast_strlen_zero(forced_clid.number.str)) {
    			forced_clid.number.valid = 1;
    		}
    	}
    	if (ast_test_flag64(&opts, OPT_FORCE_CID_TAG)
    		&& !ast_strlen_zero(opt_args[OPT_ARG_FORCE_CID_TAG])) {
    		forced_clid.tag = opt_args[OPT_ARG_FORCE_CID_TAG];
    	}
    	forced_clid.number.presentation = AST_PRES_ALLOWED_USER_NUMBER_PASSED_SCREEN;
    	if (ast_test_flag64(&opts, OPT_FORCE_CID_PRES)
    		&& !ast_strlen_zero(opt_args[OPT_ARG_FORCE_CID_PRES])) {
    		int pres;
    
    		pres = ast_parse_caller_presentation(opt_args[OPT_ARG_FORCE_CID_PRES]);
    		if (0 <= pres) {
    			forced_clid.number.presentation = pres;
    		}
    	}
    
    	/* Setup the stored CallerID information if needed. */
    	ast_party_id_init(&stored_clid);
    	if (ast_test_flag64(&opts, OPT_ORIGINAL_CLID)) {
    		if (ast_strlen_zero(opt_args[OPT_ARG_ORIGINAL_CLID])) {
    			ast_channel_lock(chan);
    
    			ast_party_id_set_init(&stored_clid, &ast_channel_caller(chan)->id);
    			if (!ast_strlen_zero(ast_channel_caller(chan)->id.name.str)) {
    				stored_clid.name.str = ast_strdupa(ast_channel_caller(chan)->id.name.str);
    
    			if (!ast_strlen_zero(ast_channel_caller(chan)->id.number.str)) {
    				stored_clid.number.str = ast_strdupa(ast_channel_caller(chan)->id.number.str);
    
    			if (!ast_strlen_zero(ast_channel_caller(chan)->id.subaddress.str)) {
    				stored_clid.subaddress.str = ast_strdupa(ast_channel_caller(chan)->id.subaddress.str);
    
    			if (!ast_strlen_zero(ast_channel_caller(chan)->id.tag)) {
    				stored_clid.tag = ast_strdupa(ast_channel_caller(chan)->id.tag);
    
    			}
    			ast_channel_unlock(chan);
    		} else {
    			/* Note: The opt_args[OPT_ARG_ORIGINAL_CLID] string value is altered here. */
    			ast_callerid_parse(opt_args[OPT_ARG_ORIGINAL_CLID], &stored_clid.name.str,
    				&stored_clid.number.str);
    			if (!ast_strlen_zero(stored_clid.name.str)) {
    				stored_clid.name.valid = 1;
    			}
    			if (!ast_strlen_zero(stored_clid.number.str)) {
    				stored_clid.number.valid = 1;
    			}
    		}
    	} else {
    		/*
    		 * In case the new channel has no preset CallerID number by the
    		 * channel driver, setup the dialplan extension and hint name.
    		 */
    		stored_clid_name[0] = '\0';
    		stored_clid.name.str = (char *) get_cid_name(stored_clid_name,
    			sizeof(stored_clid_name), chan);
    		if (ast_strlen_zero(stored_clid.name.str)) {
    			stored_clid.name.str = NULL;
    		} else {
    			stored_clid.name.valid = 1;
    		}
    		ast_channel_lock(chan);
    
    		stored_clid.number.str = ast_strdupa(S_OR(ast_channel_macroexten(chan), ast_channel_exten(chan)));
    
    		stored_clid.number.valid = 1;
    		ast_channel_unlock(chan);
    	}
    
    
    	if (ast_test_flag64(&opts, OPT_RESETCDR) && ast_channel_cdr(chan))
    		ast_cdr_reset(ast_channel_cdr(chan), NULL);
    
    	if (ast_test_flag64(&opts, OPT_PRIVACY) && ast_strlen_zero(opt_args[OPT_ARG_PRIVACY]))
    
    		opt_args[OPT_ARG_PRIVACY] = ast_strdupa(ast_channel_exten(chan));
    
    	if (ast_test_flag64(&opts, OPT_PRIVACY) || ast_test_flag64(&opts, OPT_SCREENING)) {
    
    		res = setup_privacy_args(&pa, &opts, opt_args, chan);
    		if (res <= 0)
    
    Michiel van Baak's avatar
    Michiel van Baak committed
    		res = -1; /* reset default */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    	if (continue_exec)
    		*continue_exec = 0;
    
    	/* If a channel group has been specified, get it for use when we create peer channels */
    
    	if ((outbound_group = pbx_builtin_getvar_helper(chan, "OUTBOUND_GROUP_ONCE"))) {
    
    		outbound_group = ast_strdupa(outbound_group);
    
    		pbx_builtin_setvar_helper(chan, "OUTBOUND_GROUP_ONCE", NULL);
    
    	} else if ((outbound_group = pbx_builtin_getvar_helper(chan, "OUTBOUND_GROUP"))) {
    		outbound_group = ast_strdupa(outbound_group);
    
    
    	/* Set per dial instance flags.  These flags are also passed back to RetryDial. */
    	ast_copy_flags64(peerflags, &opts, OPT_DTMF_EXIT | OPT_GO_ON | OPT_ORIGINAL_CLID
    		| OPT_CALLER_HANGUP | OPT_IGNORE_FORWARDING | OPT_CANCEL_TIMEOUT
    		| OPT_ANNOUNCE | OPT_CALLEE_MACRO | OPT_CALLEE_GOSUB | OPT_FORCECLID);
    
    	/* PREDIAL: Run gosub on the caller's channel */
    	if (ast_test_flag64(&opts, OPT_PREDIAL_CALLER)
    		&& !ast_strlen_zero(opt_args[OPT_ARG_PREDIAL_CALLER])) {
    		ast_replace_subargument_delimiter(opt_args[OPT_ARG_PREDIAL_CALLER]);
    
    		ast_app_exec_sub(NULL, chan, opt_args[OPT_ARG_PREDIAL_CALLER], 0);
    
    	/* loop through the list of dial destinations */
    	rest = args.peers;
    	while ((cur = strsep(&rest, "&")) ) {
    
    Michiel van Baak's avatar
    Michiel van Baak committed
    		struct ast_channel *tc; /* channel for this destination */
    
    		char *number = cur;
    		char *tech = strsep(&number, "/");
    
    		/* find if we already dialed this interface */
    		struct ast_dialed_interface *di;
    
    		AST_LIST_HEAD(,ast_dialed_interface) *dialed_interfaces;
    
    		if (ast_strlen_zero(number)) {
    
    			ast_log(LOG_WARNING, "Dial argument takes format (technology/resource)\n");
    
    Mark Spencer's avatar
    Mark Spencer committed
    			goto out;
    		}
    
    
    		tech_len = strlen(tech) + 1;
    		number_len = strlen(number) + 1;
    		tmp = ast_calloc(1, sizeof(*tmp) + (2 * tech_len) + number_len);
    		if (!tmp) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			goto out;
    
    		}
    
    		/* Save tech, number, and interface. */
    		cur = tmp->stuff;
    		strcpy(cur, tech);
    		tmp->tech = cur;
    		cur += tech_len;
    		strcpy(cur, tech);
    		cur[tech_len - 1] = '/';
    		tmp->interface = cur;
    		cur += tech_len;
    		strcpy(cur, number);
    		tmp->number = cur;
    
    
    			/* Set per outgoing call leg options. */
    
    Michiel van Baak's avatar
    Michiel van Baak committed
    				OPT_CANCEL_ELSEWHERE |
    				OPT_CALLEE_TRANSFER | OPT_CALLER_TRANSFER |
    				OPT_CALLEE_HANGUP | OPT_CALLER_HANGUP |
    				OPT_CALLEE_MONITOR | OPT_CALLER_MONITOR |
    				OPT_CALLEE_PARK | OPT_CALLER_PARK |
    				OPT_CALLEE_MIXMONITOR | OPT_CALLER_MIXMONITOR |
    
    				OPT_RINGBACK | OPT_MUSICBACK | OPT_FORCECLID | OPT_IGNORE_CONNECTEDLINE);
    
    Michiel van Baak's avatar
    Michiel van Baak committed
    			ast_set2_flag64(tmp, args.url, DIAL_NOFORWARDHTML);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		/* Request the peer */
    
    
    		ast_channel_lock(chan);
    		datastore = ast_channel_datastore_find(chan, &dialed_interface_info, NULL);
    
    		/*
    		 * Seed the chanlist's connected line information with previously
    		 * acquired connected line info from the incoming channel.  The
    		 * previously acquired connected line info could have been set
    		 * through the CONNECTED_LINE dialplan function.
    
    		ast_party_connected_line_copy(&tmp->connected, ast_channel_connected(chan));
    
    		ast_channel_unlock(chan);
    
    		if (datastore)
    			dialed_interfaces = datastore->data;
    		else {
    
    			if (!(datastore = ast_datastore_alloc(&dialed_interface_info, NULL))) {
    
    Michiel van Baak's avatar
    Michiel van Baak committed
    				ast_log(LOG_WARNING, "Unable to create channel datastore for dialed interfaces. Aborting!\n");
    
    				chanlist_free(tmp);
    
    			datastore->inheritance = DATASTORE_INHERIT_FOREVER;
    
    			if (!(dialed_interfaces = ast_calloc(1, sizeof(*dialed_interfaces)))) {
    
    				ast_datastore_free(datastore);
    				chanlist_free(tmp);
    
    
    			datastore->data = dialed_interfaces;
    			AST_LIST_HEAD_INIT(dialed_interfaces);
    
    			ast_channel_lock(chan);
    			ast_channel_datastore_add(chan, datastore);
    			ast_channel_unlock(chan);
    		}
    
    
    		AST_LIST_LOCK(dialed_interfaces);
    		AST_LIST_TRAVERSE(dialed_interfaces, di, list) {
    
    			if (!strcasecmp(di->interface, tmp->interface)) {
    
    Michiel van Baak's avatar
    Michiel van Baak committed
    				ast_log(LOG_WARNING, "Skipping dialing interface '%s' again since it has already been dialed\n",
    
    		AST_LIST_UNLOCK(dialed_interfaces);
    		if (di) {
    			fulldial++;
    
    			chanlist_free(tmp);
    
    			continue;
    		}
    
    		/* It is always ok to dial a Local interface.  We only keep track of
    		 * which "real" interfaces have been dialed.  The Local channel will
    		 * inherit this list so that if it ends up dialing a real interface,
    		 * it won't call one that has already been called. */
    
    		if (strcasecmp(tmp->tech, "Local")) {
    			if (!(di = ast_calloc(1, sizeof(*di) + strlen(tmp->interface)))) {
    
    				chanlist_free(tmp);
    
    
    			AST_LIST_LOCK(dialed_interfaces);
    
    			AST_LIST_INSERT_TAIL(dialed_interfaces, di, list);
    
    			AST_LIST_UNLOCK(dialed_interfaces);
    
    		tc = ast_request(tmp->tech, ast_channel_nativeformats(chan), chan, tmp->number, &cause);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			/* If we can't, just go on to the next call */
    
    			ast_log(LOG_WARNING, "Unable to create channel of type '%s' (cause %d - %s)\n",
    
    				tmp->tech, cause, ast_cause2str(cause));
    
    			handle_cause(cause, &num);
    
    			if (!rest) {
    				/* we are on the last destination */
    
    				ast_channel_hangupcause_set(chan, cause);
    
    			if (!ignore_cc && (cause == AST_CAUSE_BUSY || cause == AST_CAUSE_CONGESTION)) {
    
    				if (!ast_cc_callback(chan, tmp->tech, tmp->number, ast_cc_busy_interface)) {
    					ast_cc_extension_monitor_add_dialstring(chan, tmp->interface, "");
    
    Mark Spencer's avatar
    Mark Spencer committed
    			continue;
    		}
    
    		ast_channel_get_device_name(tc, device_name, sizeof(device_name));
    		if (!ignore_cc) {
    
    			ast_cc_extension_monitor_add_dialstring(chan, tmp->interface, device_name);
    
    		pbx_builtin_setvar_helper(tc, "DIALEDPEERNUMBER", tmp->number);
    
    		ast_channel_lock_both(tc, chan);
    
    
    		/* Setup outgoing SDP to match incoming one */
    
    		if (!AST_LIST_FIRST(&out_chans) && !rest && CAN_EARLY_BRIDGE(peerflags, chan, tc)) {
    			/* We are on the only destination. */
    
    			ast_rtp_instance_early_bridge_make_compatible(tc, chan);
    		}
    
    		/* Inherit specially named variables from parent channel */
    
    		ast_channel_inherit_variables(chan, tc);
    
    		ast_channel_datastore_inherit(chan, tc);
    
    		ast_channel_appl_set(tc, "AppDial");
    		ast_channel_data_set(tc, "(Outgoing Line)");
    
    		memset(ast_channel_whentohangup(tc), 0, sizeof(*ast_channel_whentohangup(tc)));
    
    		/* Determine CallerID to store in outgoing channel. */
    
    		ast_party_caller_set_init(&caller, ast_channel_caller(tc));
    
    		if (ast_test_flag64(peerflags, OPT_ORIGINAL_CLID)) {
    			caller.id = stored_clid;
    			ast_channel_set_caller_event(tc, &caller, NULL);
    			ast_set_flag64(tmp, DIAL_CALLERID_ABSENT);
    
    		} else if (ast_strlen_zero(S_COR(ast_channel_caller(tc)->id.number.valid,
    			ast_channel_caller(tc)->id.number.str, NULL))) {
    
    			/*
    			 * The new channel has no preset CallerID number by the channel
    			 * driver.  Use the dialplan extension and hint name.
    			 */
    			caller.id = stored_clid;
    			if (!caller.id.name.valid
    
    				&& !ast_strlen_zero(S_COR(ast_channel_connected(chan)->id.name.valid,
    					ast_channel_connected(chan)->id.name.str, NULL))) {
    
    				/*
    				 * No hint name available.  We have a connected name supplied by
    				 * the dialplan we can use instead.
    				 */
    
    				caller.id.name.valid = 1;
    
    				caller.id.name = ast_channel_connected(chan)->id.name;
    
    			ast_channel_set_caller_event(tc, &caller, NULL);
    
    			ast_set_flag64(tmp, DIAL_CALLERID_ABSENT);
    
    		} else if (ast_strlen_zero(S_COR(ast_channel_caller(tc)->id.name.valid, ast_channel_caller(tc)->id.name.str,
    
    			NULL))) {
    			/* The new channel has no preset CallerID name by the channel driver. */
    
    			if (!ast_strlen_zero(S_COR(ast_channel_connected(chan)->id.name.valid,
    				ast_channel_connected(chan)->id.name.str, NULL))) {
    
    				/*
    				 * We have a connected name supplied by the dialplan we can
    				 * use instead.
    				 */
    
    				caller.id.name.valid = 1;
    
    				caller.id.name = ast_channel_connected(chan)->id.name;
    
    				ast_channel_set_caller_event(tc, &caller, NULL);
    			}
    
    		/* Determine CallerID for outgoing channel to send. */
    		if (ast_test_flag64(peerflags, OPT_FORCECLID) && !force_forwards_only) {
    
    			struct ast_party_connected_line connected;
    
    
    			ast_party_connected_line_set_init(&connected, ast_channel_connected(tc));
    
    			connected.id = forced_clid;
    
    			ast_channel_set_connected_line(tc, &connected, NULL);
    
    			ast_connected_line_copy_from_caller(ast_channel_connected(tc), ast_channel_caller(chan));
    
    		ast_party_redirecting_copy(ast_channel_redirecting(tc), ast_channel_redirecting(chan));
    
    		ast_channel_dialed(tc)->transit_network_select = ast_channel_dialed(chan)->transit_network_select;
    
    		if (!ast_strlen_zero(ast_channel_accountcode(chan))) {
    
    			ast_channel_accountcode_set(tc, ast_channel_accountcode(chan));
    
    		if (ast_strlen_zero(ast_channel_musicclass(tc))) {
    
    			ast_channel_musicclass_set(tc, ast_channel_musicclass(chan));
    
    		ast_channel_adsicpe_set(tc, ast_channel_adsicpe(chan));
    		ast_channel_transfercapability_set(tc, ast_channel_transfercapability(chan));
    
    
    		/* If we have an outbound group, set this peer channel to it */
    		if (outbound_group)
    
    			ast_app_group_set_channel(tc, outbound_group);
    
    		/* If the calling channel has the ANSWERED_ELSEWHERE flag set, inherit it. This is to support local channels */
    
    		if (ast_channel_hangupcause(chan) == AST_CAUSE_ANSWERED_ELSEWHERE)
    			ast_channel_hangupcause_set(tc, AST_CAUSE_ANSWERED_ELSEWHERE);
    
    
    		/* Check if we're forced by configuration */
    		if (ast_test_flag64(&opts, OPT_CANCEL_ELSEWHERE))
    
    			 ast_channel_hangupcause_set(tc, AST_CAUSE_ANSWERED_ELSEWHERE);
    
    		/* Inherit context and extension */
    
    		ast_channel_dialcontext_set(tc, ast_strlen_zero(ast_channel_macrocontext(chan)) ? ast_channel_context(chan) : ast_channel_macrocontext(chan));
    		if (!ast_strlen_zero(ast_channel_macroexten(chan)))
    			ast_channel_exten_set(tc, ast_channel_macroexten(chan));
    
    			ast_channel_exten_set(tc, ast_channel_exten(chan));
    
    		ast_channel_unlock(chan);
    
    
    		/* Put channel in the list of outgoing thingies. */
    		tmp->chan = tc;
    		AST_LIST_INSERT_TAIL(&out_chans, tmp, node);
    	}
    
    	/*
    	 * PREDIAL: Run gosub on all of the callee channels
    	 *
    	 * We run the callee predial before ast_call() in case the user
    	 * wishes to do something on the newly created channels before
    	 * the channel does anything important.
    	 *
    	 * Inside the target gosub we will be able to do something with
    	 * the newly created channel name ie: now the calling channel
    	 * can know what channel will be used to call the destination
    	 * ex: now we will know that SIP/abc-123 is calling SIP/def-124
    	 */
    	if (ast_test_flag64(&opts, OPT_PREDIAL_CALLEE)
    		&& !ast_strlen_zero(opt_args[OPT_ARG_PREDIAL_CALLEE])
    		&& !AST_LIST_EMPTY(&out_chans)) {
    
    		ast_replace_subargument_delimiter(opt_args[OPT_ARG_PREDIAL_CALLEE]);
    
    		predial_callee = ast_app_expand_sub_args(chan, opt_args[OPT_ARG_PREDIAL_CALLEE]);
    		if (predial_callee) {
    			ast_autoservice_start(chan);
    			AST_LIST_TRAVERSE(&out_chans, tmp, node) {
    				ast_pre_call(tmp->chan, predial_callee);
    			}
    			ast_autoservice_stop(chan);
    			ast_free((char *) predial_callee);
    
    		}
    	}
    
    	/* Start all outgoing calls */
    	AST_LIST_TRAVERSE_SAFE_BEGIN(&out_chans, tmp, node) {
    		res = ast_call(tmp->chan, tmp->number, 0); /* Place the call, but don't wait on the answer */
    
    		ast_channel_lock(chan);
    
    
    		/* Save the info in cdr's that we called them */
    
    		if (ast_channel_cdr(chan))
    
    			ast_cdr_setdestchan(ast_channel_cdr(chan), ast_channel_name(tmp->chan));
    
    		/* check the results of ast_call */
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (res) {
    			/* Again, keep going even if there's an error */
    
    			ast_debug(1, "ast call on peer returned %d\n", res);
    
    			ast_verb(3, "Couldn't call %s\n", tmp->interface);
    			if (ast_channel_hangupcause(tmp->chan)) {
    				ast_channel_hangupcause_set(chan, ast_channel_hangupcause(tmp->chan));
    
    			ast_cc_call_failed(chan, tmp->chan, tmp->interface);
    			ast_hangup(tmp->chan);
    			tmp->chan = NULL;
    			AST_LIST_REMOVE_CURRENT(node);
    
    			chanlist_free(tmp);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			continue;
    
    
    		senddialevent(chan, tmp->chan, tmp->number);
    		ast_channel_unlock(chan);
    
    		ast_verb(3, "Called %s\n", tmp->interface);
    
    Michiel van Baak's avatar
    Michiel van Baak committed
    		ast_set_flag64(tmp, DIAL_STILLGOING);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		/* If this line is up, don't try anybody else */
    
    		if (ast_channel_state(tmp->chan) == AST_STATE_UP) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			break;
    
    	if (ast_strlen_zero(args.timeout)) {
    		to = -1;
    	} else {
    
    		else {
    			ast_log(LOG_WARNING, "Invalid timeout specified: '%s'. Setting timeout to infinite\n", args.timeout);
    			to = -1;
    		}
    
    		strcpy(pa.status, "CHANUNAVAIL");
    
    		/* Our status will at least be NOANSWER */
    
    		strcpy(pa.status, "NOANSWER");
    
    		if (ast_test_flag64(outgoing, OPT_MUSICBACK)) {
    
    			if (!ast_strlen_zero(opt_args[OPT_ARG_MUSICBACK])) {
    
    				char *original_moh = ast_strdupa(ast_channel_musicclass(chan));
    				ast_channel_musicclass_set(chan, opt_args[OPT_ARG_MUSICBACK]);
    
    				ast_moh_start(chan, opt_args[OPT_ARG_MUSICBACK], NULL);
    
    				ast_channel_musicclass_set(chan, original_moh);
    
    			} else {
    				ast_moh_start(chan, NULL, NULL);
    			}
    
    			ast_indicate(chan, AST_CONTROL_PROGRESS);
    
    		} else if (ast_test_flag64(outgoing, OPT_RINGBACK)) {
    
    			if (!ast_strlen_zero(opt_args[OPT_ARG_RINGBACK])) {
    				if (dial_handle_playtones(chan, opt_args[OPT_ARG_RINGBACK])){
    					ast_indicate(chan, AST_CONTROL_RINGING);
    					sentringing++;
    				} else {
    					ast_indicate(chan, AST_CONTROL_PROGRESS);
    				}
    			} else {
    				ast_indicate(chan, AST_CONTROL_RINGING);
    				sentringing++;
    			}
    
    	peer = wait_for_answer(chan, &out_chans, &to, peerflags, opt_args, &pa, &num, &result,
    
    		dtmf_progress, ignore_cc, &forced_clid, &stored_clid);
    
    	/* The ast_channel_datastore_remove() function could fail here if the
    	 * datastore was moved to another channel during a masquerade. If this is
    	 * the case, don't free the datastore here because later, when the channel
    	 * to which the datastore was moved hangs up, it will attempt to free this
    	 * datastore again, causing a crash
    	 */
    	ast_channel_lock(chan);
    
    	datastore = ast_channel_datastore_find(chan, &dialed_interface_info, NULL); /* make sure we weren't cleaned up already */
    	if (datastore && !ast_channel_datastore_remove(chan, datastore)) {
    
    		ast_datastore_free(datastore);
    	}
    	ast_channel_unlock(chan);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (!peer) {
    
    		if (result) {
    			res = result;
    
    		} else if (to) { /* Musta gotten hung up */
    
    Mark Spencer's avatar
    Mark Spencer committed
    			res = -1;
    
    		} else { /* Nobody answered, next please? */
    
    		const char *number;
    
    
    		if (ast_test_flag64(&opts, OPT_CALLER_ANSWER))
    			ast_answer(chan);
    
    
    		strcpy(pa.status, "ANSWER");
    
    		pbx_builtin_setvar_helper(chan, "DIALSTATUS", pa.status);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		/* Ah ha!  Someone answered within the desired timeframe.  Of course after this
    
    Michiel van Baak's avatar
    Michiel van Baak committed
    		   we will always return with -1 so that it is hung up properly after the
    
    Mark Spencer's avatar
    Mark Spencer committed
    		   conversation.  */
    
    		/* If appropriate, log that we have a destination channel and set the answer time */
    
    		if (ast_channel_cdr(chan)) {
    			ast_cdr_setdestchan(ast_channel_cdr(chan), ast_channel_name(peer));
    			ast_cdr_setanswer(ast_channel_cdr(chan), ast_channel_cdr(peer)->answer);
    
    		if (ast_channel_name(peer))
    			pbx_builtin_setvar_helper(chan, "DIALEDPEERNAME", ast_channel_name(peer));
    
    		number = pbx_builtin_getvar_helper(peer, "DIALEDPEERNUMBER");
    		if (ast_strlen_zero(number)) {
    			number = NULL;
    		} else {
    			number = ast_strdupa(number);
    		}
    
    		pbx_builtin_setvar_helper(chan, "DIALEDPEERNUMBER", number);
    
    Michiel van Baak's avatar
    Michiel van Baak committed
    		if (!ast_strlen_zero(args.url) && ast_channel_supports_html(peer) ) {
    			ast_debug(1, "app_dial: sendurl=%s.\n", args.url);
    			ast_channel_sendurl( peer, args.url );
    		}
    
    		if ( (ast_test_flag64(&opts, OPT_PRIVACY) || ast_test_flag64(&opts, OPT_SCREENING)) && pa.privdb_val == AST_PRIVACY_UNKNOWN) {
    
    			if (do_privacy(chan, peer, &opts, opt_args, &pa)) {
    
    				res = 0;
    				goto out;
    			}
    
    		if (!ast_test_flag64(&opts, OPT_ANNOUNCE) || ast_strlen_zero(opt_args[OPT_ARG_ANNOUNCE])) {
    
    			int digit = 0;
    
    			struct ast_channel *chans[2];
    			struct ast_channel *active_chan;
    
    			chans[0] = chan;
    			chans[1] = peer;
    
    			/* we need to stream the announcment while monitoring the caller for a hangup */
    
    			/* stream the file */
    
    			res = ast_streamfile(peer, opt_args[OPT_ARG_ANNOUNCE], ast_channel_language(peer));
    
    			if (res) {
    				res = 0;
    				ast_log(LOG_ERROR, "error streaming file '%s' to callee\n", opt_args[OPT_ARG_ANNOUNCE]);
    
    			ast_set_flag(ast_channel_flags(peer), AST_FLAG_END_DTMF_ONLY);
    
    			while (ast_channel_stream(peer)) {
    
    				ms = ast_sched_wait(ast_channel_sched(peer));
    
    				if (ms < 0 && !ast_channel_timingfunc(peer)) {
    
    					ast_stopstream(peer);
    					break;
    				}
    				if (ms < 0)
    					ms = 1000;
    
    				active_chan = ast_waitfor_n(chans, 2, &ms);
    				if (active_chan) {
    					struct ast_frame *fr = ast_read(active_chan);
    					if (!fr) {
    
    						ast_autoservice_chan_hangup_peer(chan, peer);
    
    						res = -1;
    						goto done;
    					}
    					switch(fr->frametype) {
    						case AST_FRAME_DTMF_END:
    							digit = fr->subclass.integer;
    							if (active_chan == peer && strchr(AST_DIGIT_ANY, res)) {
    								ast_stopstream(peer);
    								res = ast_senddigit(chan, digit, 0);
    							}
    							break;
    						case AST_FRAME_CONTROL:
    							switch (fr->subclass.integer) {
    								case AST_CONTROL_HANGUP:
    									ast_frfree(fr);
    
    									ast_autoservice_chan_hangup_peer(chan, peer);
    
    									res = -1;
    									goto done;
    								default:
    									break;
    							}
    							break;
    						default:
    							/* Ignore all others */
    							break;
    					}
    					ast_frfree(fr);
    				}
    
    				ast_sched_runq(ast_channel_sched(peer));
    
    			ast_clear_flag(ast_channel_flags(peer), AST_FLAG_END_DTMF_ONLY);
    
    		if (chan && peer && ast_test_flag64(&opts, OPT_GOTO) && !ast_strlen_zero(opt_args[OPT_ARG_GOTO])) {
    
    			/* chan and peer are going into the PBX, they both
    			 * should probably get CDR records. */
    
    			ast_clear_flag(ast_channel_cdr(chan), AST_CDR_FLAG_DIALED);
    			ast_clear_flag(ast_channel_cdr(peer), AST_CDR_FLAG_DIALED);
    
    			ast_replace_subargument_delimiter(opt_args[OPT_ARG_GOTO]);
    
    			ast_parseable_goto(chan, opt_args[OPT_ARG_GOTO]);
    
    			/* peer goes to the same context and extension as chan, so just copy info from chan*/
    
    			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_autoservice_chan_hangup_peer(chan, peer);
    
    			hanguptree(&out_chans, NULL, ast_test_flag64(&opts, OPT_CANCEL_ELSEWHERE) ? 1 : 0);
    
    			if (continue_exec)
    				*continue_exec = 1;
    			res = 0;
    
    		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]);
    			res = ast_app_exec_macro(chan, peer, opt_args[OPT_ARG_CALLEE_MACRO]);
    
    			if (!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);
    
    					res = -1;
    				} 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);
    
    					res = -1;
    				} 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);
    
    					res = -1;
    				} else if (!strcasecmp(macro_result, "ABORT")) {
    					/* Hangup both ends unless the caller has the g flag */
    					res = -1;
    
    				} else if (!strncasecmp(macro_result, "GOTO:", 5)) {
    					macro_transfer_dest = macro_result + 5;
    
    					res = -1;
    					/* 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);
    
    			} 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 (asprintf(&gosub_args, "%s,%s,1(%s)", opt_args[OPT_ARG_CALLEE_GOSUB], what_is_s, gosub_argstart + 1) < 0) {
    					ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
    					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 (asprintf(&gosub_args, "%s,%s,1", opt_args[OPT_ARG_CALLEE_GOSUB], what_is_s) < 0) {
    					ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
    					gosub_args = NULL;
    
    				res9 = 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 (!res9 && (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);
    
    			} else {
    				ast_channel_unlock(peer);
    				ast_channel_unlock(chan);
    
    James Golovich's avatar
    James Golovich committed
    		if (!res) {
    
    			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_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))