Skip to content
Snippets Groups Projects
app_dial.c 127 KiB
Newer Older
  • Learn to ignore specific revisions
  • Michiel van Baak's avatar
    Michiel van Baak committed
    	default: /* bad input or -1 if failure to start autoservice */
    
    		/* well, if the user messes up, ... he had his chance... What Is The Best Thing To Do?  */
    		/* well, there seems basically two choices. Just patch the caller thru immediately,
    			  or,... put 'em thru to voicemail. */
    		/* since the callee may have hung up, let's do the voicemail thing, no database decision */
    		ast_log(LOG_NOTICE, "privacy: no valid response from the callee. Sending the caller to voicemail, the callee isn't responding\n");
    		/* XXX should we set status to DENY ? */
    		/* XXX what about the privacy flags ? */
    		break;
    	}
    
    
    Michiel van Baak's avatar
    Michiel van Baak committed
    	if (res2 == '1') { /* the only case where we actually connect */
    		/* if the intro is NOCALLERID, then there's no reason to leave it on disk, it'll
    
    		   just clog things up, and it's not useful information, not being tied to a CID */
    
    		if (strncmp(pa->privcid, "NOCALLERID", 10) == 0 || ast_test_flag64(opts, OPT_SCREEN_NOINTRO)) {
    
    			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);
    
    Michiel van Baak's avatar
    Michiel van Baak committed
    		return 0; /* the good exit path */
    
    /*! \brief returns 1 if successful, 0 or <0 if the caller should 'goto out' */
    static int setup_privacy_args(struct privacy_args *pa,
    
    	struct ast_flags64 *opts, char *opt_args[], struct ast_channel *chan)
    
    	if (ast_channel_caller(chan)->id.number.valid
    		&& !ast_strlen_zero(ast_channel_caller(chan)->id.number.str)) {
    		l = ast_strdupa(ast_channel_caller(chan)->id.number.str);
    
    		if (ast_test_flag64(opts, OPT_PRIVACY) ) {
    
    			ast_verb(3, "Privacy DB is '%s', clid is '%s'\n", opt_args[OPT_ARG_PRIVACY], l);
    
    			pa->privdb_val = ast_privacy_check(opt_args[OPT_ARG_PRIVACY], l);
    		} else {
    
    			ast_verb(3, "Privacy Screening, clid is '%s'\n", l);
    
    			pa->privdb_val = AST_PRIVACY_UNKNOWN;
    		}
    	} else {
    		char *tnam, *tn2;
    
    
    		tnam = ast_strdupa(ast_channel_name(chan));
    
    		/* clean the channel name so slashes don't try to end up in disk file name */
    		for (tn2 = tnam; *tn2; tn2++) {
    
    			if (*tn2 == '/')  /* any other chars to be afraid of? */
    
    		ast_verb(3, "Privacy-- callerid is empty\n");
    
    		snprintf(callerid, sizeof(callerid), "NOCALLERID_%s%s", ast_channel_exten(chan), tnam);
    
    		l = callerid;
    		pa->privdb_val = AST_PRIVACY_UNKNOWN;
    	}
    
    	ast_copy_string(pa->privcid, l, sizeof(pa->privcid));
    
    	if (strncmp(pa->privcid, "NOCALLERID", 10) != 0 && ast_test_flag64(opts, OPT_SCREEN_NOCALLERID)) {
    		/* if callerid is set and OPT_SCREEN_NOCALLERID is set also */
    
    		ast_verb(3, "CallerID set (%s); N option set; Screening should be off\n", pa->privcid);
    
    		pa->privdb_val = AST_PRIVACY_ALLOW;
    
    	} else if (ast_test_flag64(opts, OPT_SCREEN_NOCALLERID) && strncmp(pa->privcid, "NOCALLERID", 10) == 0) {
    
    		ast_verb(3, "CallerID blank; N option set; Screening should happen; dbval is %d\n", pa->privdb_val);
    
    		ast_verb(3, "Privacy DB reports PRIVACY_DENY for this callerid. Dial reports unavailable\n");
    
    		ast_copy_string(pa->status, "NOANSWER", sizeof(pa->status));
    		return 0;
    
    	} else if (pa->privdb_val == AST_PRIVACY_KILL) {
    
    		ast_copy_string(pa->status, "DONTCALL", sizeof(pa->status));
    		return 0; /* Is this right? */
    
    	} else if (pa->privdb_val == AST_PRIVACY_TORTURE) {
    
    		ast_copy_string(pa->status, "TORTURE", sizeof(pa->status));
    		return 0; /* is this right??? */
    
    	} else if (pa->privdb_val == AST_PRIVACY_UNKNOWN) {
    
    Michiel van Baak's avatar
    Michiel van Baak committed
    		/* Get the user's intro, store it in priv-callerintros/$CID,
    		   unless it is already there-- this should be done before the
    
    		   call is actually dialed  */
    
    		/* make sure the priv-callerintros dir actually exists */
    		snprintf(pa->privintro, sizeof(pa->privintro), "%s/sounds/priv-callerintros", ast_config_AST_DATA_DIR);
    
    		if ((res = ast_mkdir(pa->privintro, 0755))) {
    			ast_log(LOG_WARNING, "privacy: can't create directory priv-callerintros: %s\n", strerror(res));
    
    		snprintf(pa->privintro, sizeof(pa->privintro), "priv-callerintros/%s", pa->privcid);
    		if (ast_fileexists(pa->privintro, NULL, NULL ) > 0 && strncmp(pa->privcid, "NOCALLERID", 10) != 0) {
    
    			/* the DELUX version of this code would allow this caller the
    			   option to hear and retape their previously recorded intro.
    			*/
    		} else {
    			int duration; /* for feedback from play_and_wait */
    			/* the file doesn't exist yet. Let the caller submit his
    			   vocal intro for posterity */
    			/* priv-recordintro script:
    
    			   "At the tone, please say your name:"
    
    			*/
    
    			int silencethreshold = ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE);
    
    			ast_answer(chan);
    
    			res = ast_play_and_record(chan, "priv-recordintro", pa->privintro, 4, "sln", &duration, NULL, silencethreshold, 2000, 0);  /* NOTE: I've reduced the total time to 4 sec */
    
    									/* don't think we'll need a lock removed, we took care of
    									   conflicts by naming the pa.privintro file */
    			if (res == -1) {
    				/* Delete the file regardless since they hung up during recording */
    				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);
    
    			if (!ast_streamfile(chan, "vm-dialout", ast_channel_language(chan)) )
    
    Michiel van Baak's avatar
    Michiel van Baak committed
    	return 1; /* success */
    
    static void end_bridge_callback(void *data)
    {
    	struct ast_channel *chan = data;
    
    	ast_channel_lock(chan);
    
    	ast_channel_stage_snapshot(chan);
    
    	set_duration_var(chan, "ANSWEREDTIME", ast_channel_get_up_time_ms(chan));
    	set_duration_var(chan, "DIALEDTIME", ast_channel_get_duration_ms(chan));
    
    	ast_channel_stage_snapshot_done(chan);
    
    	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;
    }
    
    
    /*!
     * \internal
     * \brief Setup the after bridge goto location on the peer.
     * \since 12.0.0
     *
     * \param chan Calling channel for bridge.
     * \param peer Peer channel for bridge.
     * \param opts Dialing option flags.
     * \param opt_args Dialing option argument strings.
     *
     * \return Nothing
     */
    static void setup_peer_after_bridge_goto(struct ast_channel *chan, struct ast_channel *peer, struct ast_flags64 *opts, char *opt_args[])
    {
    	const char *context;
    	const char *extension;
    	int priority;
    
    	if (ast_test_flag64(opts, OPT_PEER_H)) {
    		ast_channel_lock(chan);
    		context = ast_strdupa(ast_channel_context(chan));
    		ast_channel_unlock(chan);
    
    		ast_bridge_set_after_h(peer, context);
    
    	} else if (ast_test_flag64(opts, OPT_CALLEE_GO_ON)) {
    		ast_channel_lock(chan);
    		context = ast_strdupa(ast_channel_context(chan));
    		extension = ast_strdupa(ast_channel_exten(chan));
    		priority = ast_channel_priority(chan);
    		ast_channel_unlock(chan);
    
    		ast_bridge_set_after_go_on(peer, context, extension, priority,
    
    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). */
    
    	ast_channel_stage_snapshot(chan);
    
    	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, "ANSWEREDTIME_MS", "");
    
    	pbx_builtin_setvar_helper(chan, "DIALEDTIME", "");
    
    	pbx_builtin_setvar_helper(chan, "DIALEDTIME_MS", "");
    	pbx_builtin_setvar_helper(chan, "RINGTIME", "");
    	pbx_builtin_setvar_helper(chan, "RINGTIME_MS", "");
    	pbx_builtin_setvar_helper(chan, "PROGRESSTIME", "");
    	pbx_builtin_setvar_helper(chan, "PROGRESSTIME_MS", "");
    
    	ast_channel_stage_snapshot_done(chan);
    
    	max_forwards = ast_max_forwards_get(chan);
    
    	if (max_forwards <= 0) {
    		ast_log(LOG_WARNING, "Cannot place outbound call from channel '%s'. Max forwards exceeded\n",
    				ast_channel_name(chan));
    		pbx_builtin_setvar_helper(chan, "DIALSTATUS", "BUSY");
    		return -1;
    	}
    
    
    	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;
    	}
    
    	if (ast_check_hangup_locked(chan)) {
    		/*
    		 * Caller hung up before we could dial.  If dial is executed
    		 * within an AGI then the AGI has likely eaten all queued
    		 * frames before executing the dial in DeadAGI mode.  With
    		 * the caller hung up and no pending frames from the caller's
    		 * read queue, dial would not know that the call has hung up
    		 * until a called channel answers.  It is rather annoying to
    		 * whoever just answered the non-existent call.
    		 *
    		 * Dial should not continue execution in DeadAGI mode, hangup
    		 * handlers, or the h exten.
    		 */
    		ast_verb(3, "Caller hung up before dial.\n");
    		pbx_builtin_setvar_helper(chan, "DIALSTATUS", "CANCEL");
    		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_cdr_reset(ast_channel_name(chan), 0);
    	}
    
    	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, "/");
    
    		struct ast_stream_topology *topology;
    
    		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 |
    				OPT_RING_WITH_EARLY_MEDIA);
    
    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);
    
    		/*
    		 * 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));
    
    		topology = ast_stream_topology_clone(ast_channel_get_stream_topology(chan));
    
    		ast_channel_unlock(chan);
    
    
    		tc = ast_request_with_stream_topology(tmp->tech, topology, NULL, 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);
    
    		ast_channel_lock_both(tc, chan);
    
    		ast_channel_stage_snapshot(tc);
    
    
    		pbx_builtin_setvar_helper(tc, "DIALEDPEERNUMBER", tmp->number);
    
    		/* 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_max_forwards_decrement(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;
    
    		ast_channel_req_accountcodes(tc, chan, AST_CHANNEL_REQUESTOR_BRIDGE_PEER);
    
    		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_stage_snapshot_done(tc);
    
    
    		/* Save the original channel name to detect call pickup masquerading in. */
    		tmp->orig_chan_name = ast_strdup(ast_channel_name(tc));
    
    
    		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);
    
    		/* 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;
    
    		ast_channel_publish_dial(chan, tmp->chan, tmp->number, NULL);
    
    		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) || ast_test_flag64(outgoing, OPT_RING_WITH_EARLY_MEDIA)) {
    
    			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);
    
    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)) {
    
    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 (ast_test_flag64(&opts, OPT_HANGUPCAUSE)
    			&& !ast_strlen_zero(opt_args[OPT_ARG_HANGUPCAUSE])) {
    			cause = ast_str2cause(opt_args[OPT_ARG_HANGUPCAUSE]);
    			if (cause <= 0) {
    				if (!strcasecmp(opt_args[OPT_ARG_HANGUPCAUSE], "NONE")) {
    					cause = 0;
    				} else if (sscanf(opt_args[OPT_ARG_HANGUPCAUSE], "%30d", &cause) != 1
    					|| cause < 0) {
    					ast_log(LOG_WARNING, "Invalid cause given to Dial(...Q(<cause>)): \"%s\"\n",
    						opt_args[OPT_ARG_HANGUPCAUSE]);
    					cause = -1;
    				}
    			}
    		}
    		hanguptree(&out_chans, peer, cause >= 0 ? cause : AST_CAUSE_ANSWERED_ELSEWHERE);
    
    
    		/* If appropriate, log that we have a destination channel and set the answer time */
    
    		name = ast_strdupa(ast_channel_name(peer));
    
    
    		number = pbx_builtin_getvar_helper(peer, "DIALEDPEERNUMBER");
    		if (ast_strlen_zero(number)) {
    			number = NULL;
    		} else {
    			number = ast_strdupa(number);
    		}
    
    		ast_channel_stage_snapshot(chan);
    
    		strcpy(pa.status, "ANSWER");
    		pbx_builtin_setvar_helper(chan, "DIALSTATUS", pa.status);
    
    		pbx_builtin_setvar_helper(chan, "DIALEDPEERNAME", name);
    
    		pbx_builtin_setvar_helper(chan, "DIALEDPEERNUMBER", number);
    
    		ast_channel_stage_snapshot_done(chan);
    
    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)) {
    
    				ast_channel_publish_dial(chan, peer, NULL, pa.status);
    
    				/* hang up on the callee -- he didn't want to talk anyway! */
    				ast_autoservice_chan_hangup_peer(chan, peer);
    
    				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 announcement to the called party when the OPT_ARG_ANNOUNCE (-A) is setted */
    
    			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_channel_set_flag(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_channel *other_chan;
    
    					struct ast_frame *fr = ast_read(active_chan);
    
    						ast_autoservice_chan_hangup_peer(chan, peer);
    
    					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;
    						case AST_CONTROL_CONNECTED_LINE:
    							/* Pass COLP update to the other channel. */
    							if (active_chan == chan) {
    								other_chan = peer;
    							} else {
    								other_chan = chan;
    
    							if (ast_channel_connected_line_sub(active_chan, other_chan, fr, 1)
    								&& ast_channel_connected_line_macro(active_chan,
    									other_chan, fr, other_chan == chan, 1)) {
    								ast_indicate_data(other_chan, fr->subclass.integer,
    									fr->data.ptr, fr->datalen);
    
    						}
    						break;
    					default:
    						/* Ignore all others */
    						break;
    
    				ast_sched_runq(ast_channel_sched(peer));
    
    			ast_channel_clear_flag(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; as such neither are considered
    			 * outgoing channels any longer */
    
    			ast_channel_clear_flag(chan, AST_FLAG_OUTGOING);
    
    			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*/