Skip to content
Snippets Groups Projects
app_dial.c 60.5 KiB
Newer Older
  • Learn to ignore specific revisions
  • 			ast_copy_flags(tmp, &opts,
    				       OPT_CALLEE_TRANSFER | OPT_CALLER_TRANSFER |
    				       OPT_CALLEE_HANGUP | OPT_CALLER_HANGUP |
    				       OPT_CALLEE_MONITOR | OPT_CALLER_MONITOR |
    				       OPT_RINGBACK | OPT_MUSICBACK | OPT_FORCECLID);
    
    			ast_set2_flag(tmp, args.url, DIAL_NOFORWARDHTML);	
    
    		ast_copy_string(numsubst, number, sizeof(numsubst));
    
    Mark Spencer's avatar
    Mark Spencer committed
    		/* Request the peer */
    
    		tmp->chan = ast_request(tech, chan->nativeformats, numsubst, &cause);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (!tmp->chan) {
    			/* If we can't, just go on to the next call */
    
    Olle Johansson's avatar
    Olle Johansson committed
    			ast_log(LOG_WARNING, "Unable to create channel of type '%s' (cause %d - %s)\n", tech, cause, ast_cause2str(cause));
    
    			if (!rest)	/* we are on the last destination */
    
    Mark Spencer's avatar
    Mark Spencer committed
    			continue;
    		}
    
    		pbx_builtin_setvar_helper(tmp->chan, "DIALEDPEERNUMBER", numsubst);
    
    		if (!ast_strlen_zero(tmp->chan->call_forward)) {
    
    			char *stuff;
    			char *tech;
    
    			ast_copy_string(tmpchan, tmp->chan->call_forward, sizeof(tmpchan));
    
    			if ((stuff = strchr(tmpchan, '/'))) {
    
    				tech = tmpchan;
    			} else {
    				snprintf(tmpchan, sizeof(tmpchan), "%s@%s", tmp->chan->call_forward, tmp->chan->context);
    				stuff = tmpchan;
    				tech = "Local";
    			}
    
    			tmp->forwards++;
    			if (tmp->forwards < AST_MAX_FORWARDS) {
    				if (option_verbose > 2)
    					ast_verbose(VERBOSE_PREFIX_3 "Now forwarding %s to '%s/%s' (thanks to %s)\n", chan->name, tech, stuff, tmp->chan->name);
    				ast_hangup(tmp->chan);
    				/* Setup parameters */
    				tmp->chan = ast_request(tech, chan->nativeformats, stuff, &cause);
    				if (!tmp->chan)
    					ast_log(LOG_NOTICE, "Unable to create local channel for call forward to '%s/%s' (cause = %d)\n", tech, stuff, cause);
    			} else {
    				if (option_verbose > 2)
    					ast_verbose(VERBOSE_PREFIX_3 "Too many forwards from %s\n", tmp->chan->name);
    				ast_hangup(tmp->chan);
    				tmp->chan = NULL;
    				cause = AST_CAUSE_CONGESTION;
    			}
    
    		/* Setup outgoing SDP to match incoming one */
    		ast_rtp_make_compatible(tmp->chan, chan);
    		
    
    		/* Inherit specially named variables from parent channel */
    		ast_channel_inherit_variables(chan, tmp->chan);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		tmp->chan->appl = "AppDial";
    		tmp->chan->data = "(Outgoing Line)";
    
    Mark Spencer's avatar
    Mark Spencer committed
    		tmp->chan->whentohangup = 0;
    
    		if (tmp->chan->cid.cid_num)
    			free(tmp->chan->cid.cid_num);
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    		tmp->chan->cid.cid_num = ast_strdup(chan->cid.cid_num);
    
    
    		if (tmp->chan->cid.cid_name)
    			free(tmp->chan->cid.cid_name);
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    		tmp->chan->cid.cid_name = ast_strdup(chan->cid.cid_name);
    
    
    		if (tmp->chan->cid.cid_ani)
    			free(tmp->chan->cid.cid_ani);
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    		tmp->chan->cid.cid_ani = ast_strdup(chan->cid.cid_ani);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		/* Copy language from incoming to outgoing */
    
    		ast_string_field_set(tmp->chan, language, chan->language);
    		ast_string_field_set(tmp->chan, accountcode, chan->accountcode);
    
    		if (ast_strlen_zero(tmp->chan->musicclass))
    
    			ast_string_field_set(tmp->chan, musicclass, chan->musicclass);
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    		/* XXX don't we free previous values ? */
    		tmp->chan->cid.cid_rdnis = ast_strdup(chan->cid.cid_rdnis);
    
    		tmp->chan->cid.cid_pres = chan->cid.cid_pres;
    
    		/* Pass type of number */
    		tmp->chan->cid.cid_ton = chan->cid.cid_ton;
    		/* Pass type of tns */
    		tmp->chan->cid.cid_tns = chan->cid.cid_tns;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		/* Presense of ADSI CPE on outgoing channel follows ours */
    		tmp->chan->adsicpe = chan->adsicpe;
    
    		/* Pass the transfer capability */
    		tmp->chan->transfercapability = chan->transfercapability;
    
    
    		/* If we have an outbound group, set this peer channel to it */
    		if (outbound_group)
    			ast_app_group_set_channel(tmp->chan, outbound_group);
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    		/* Place the call, but don't wait on the answer */
    
    Mark Spencer's avatar
    Mark Spencer committed
    		res = ast_call(tmp->chan, numsubst, 0);
    
    
    		/* Save the info in cdr's that we called them */
    		if (chan->cdr)
    			ast_cdr_setdestchan(chan->cdr, tmp->chan->name);
    
    
    		/* check the results of ast_call */
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (res) {
    			/* Again, keep going even if there's an error */
    			if (option_debug)
    				ast_log(LOG_DEBUG, "ast call on peer returned %d\n", res);
    			else if (option_verbose > 2)
    
    Mark Spencer's avatar
    Mark Spencer committed
    				ast_verbose(VERBOSE_PREFIX_3 "Couldn't call %s\n", numsubst);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			ast_hangup(tmp->chan);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			continue;
    
    Mark Spencer's avatar
    Mark Spencer committed
    			if (option_verbose > 2)
    
    Mark Spencer's avatar
    Mark Spencer committed
    				ast_verbose(VERBOSE_PREFIX_3 "Called %s\n", numsubst);
    
    			if (!ast_test_flag(peerflags, OPT_ORIGINAL_CLID))
    
    				ast_set_callerid(tmp->chan, S_OR(chan->macroexten, chan->exten), get_cid_name(cidname, sizeof(cidname), chan), NULL);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		/* Put them in the list of outgoing thingies...  We're ready now. 
    		   XXX If we're forcibly removed, these outgoing calls won't get
    		   hung up XXX */
    
    		ast_set_flag(tmp, DIAL_STILLGOING);	
    
    Mark Spencer's avatar
    Mark Spencer committed
    		tmp->next = outgoing;
    		outgoing = tmp;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		/* If this line is up, don't try anybody else */
    		if (outgoing->chan->_state == AST_STATE_UP)
    			break;
    
    	if (ast_strlen_zero(args.timeout)) {
    		to = -1;
    	} else {
    
    			ast_log(LOG_WARNING, "Invalid timeout specified: '%s'\n", args.timeout);
    
    	if (!outgoing) {
    		strcpy(status, "CHANUNAVAIL");
    	} else {
    
    		/* Our status will at least be NOANSWER */
    
    		strcpy(status, "NOANSWER");
    
    		if (ast_test_flag(outgoing, OPT_MUSICBACK)) {
    
    			ast_moh_start(chan, opt_args[OPT_ARG_MUSICBACK]);
    
    		} else if (ast_test_flag(outgoing, OPT_RINGBACK)) {
    
    			ast_indicate(chan, AST_CONTROL_RINGING);
    			sentringing++;
    		}
    
    	peer = wait_for_answer(chan, outgoing, &to, peerflags, &sentringing, status, sizeof(status), numbusy, numnochan, numcongestion, ast_test_flag(&opts, OPT_PRIORITY_JUMP), &result);
    
    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? */
    
    		/* almost done, although the 'else' block is 400 lines */
    
    		const char *number;
    		time_t end_time, answer_time = time(NULL);
    
    
    		strcpy(status, "ANSWER");
    
    Mark Spencer's avatar
    Mark Spencer committed
    		/* Ah ha!  Someone answered within the desired timeframe.  Of course after this
    		   we will always return with -1 so that it is hung up properly after the 
    		   conversation.  */
    		hanguptree(outgoing, peer);
    		outgoing = NULL;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		/* If appropriate, log that we have a destination channel */
    		if (chan->cdr)
    			ast_cdr_setdestchan(chan->cdr, peer->name);
    
    		if (peer->name)
    			pbx_builtin_setvar_helper(chan, "DIALEDPEERNAME", peer->name);
    
    		number = pbx_builtin_getvar_helper(peer, "DIALEDPEERNUMBER");
    
    		if (!number)
    			number = numsubst;
    		pbx_builtin_setvar_helper(chan, "DIALEDPEERNUMBER", number);
    
     		if (!ast_strlen_zero(args.url) && ast_channel_supports_html(peer) ) {
    
    Olle Johansson's avatar
    Olle Johansson committed
    			if (option_debug)
     				ast_log(LOG_DEBUG, "app_dial: sendurl=%s.\n", args.url);
    
     			ast_channel_sendurl( peer, args.url );
    
    		if ( (ast_test_flag(&opts, OPT_PRIVACY) || ast_test_flag(&opts, OPT_SCREENING)) && privdb_val == AST_PRIVACY_UNKNOWN) {
    
    			int res2;
    			int loopcount = 0;
    
    			/* 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  */
    
    			/* all ring indications and moh for the caller has been halted as soon as the 
    			   target extension was picked up. We are going to have to kill some
    			   time and make the caller believe the peer hasn't picked up yet */
    
    			if (ast_test_flag(&opts, OPT_MUSICBACK) && !ast_strlen_zero(opt_args[OPT_ARG_MUSICBACK])) {
    				ast_indicate(chan, -1);
    				ast_moh_start(chan, opt_args[OPT_ARG_MUSICBACK]);
    			} else if (ast_test_flag(&opts, OPT_RINGBACK)) {
    				ast_indicate(chan, AST_CONTROL_RINGING);
    				sentringing++;
    			}
    
    			/* Start autoservice on the other chan ?? */
    			res2 = ast_autoservice_start(chan);
    			/* Now Stream the File */
    			for (loopcount = 0; loopcount < 3; loopcount++) {
    				if (res2 && loopcount == 0)	/* error in ast_autoservice_start() */
    					break;
    				if (!res2)	/* on timeout, play the message again */
    					res2 = ast_play_and_wait(peer,"priv-callpending");
    				if (!valid_priv_reply(&opts, res2))
    					res2 = 0;
    				
    				/* priv-callpending script: 
    				   "I have a caller waiting, who introduces themselves as:"
    				*/
    				if (!res2)
    					res2 = ast_play_and_wait(peer,privintro);
    				if (!valid_priv_reply(&opts, res2))
    					res2 = 0;
    				/* now get input from the called party, as to their choice */
    				if( !res2 ) {
    					/* XXX can we have both, or they are mutually exclusive ? */
    					if( ast_test_flag(&opts, OPT_PRIVACY) )
    						res2 = ast_play_and_wait(peer,"priv-callee-options");
    					if( ast_test_flag(&opts, OPT_SCREENING) )
    						res2 = ast_play_and_wait(peer,"screen-callee-options");
    				}
    				/*! \page DialPrivacy Dial Privacy scripts
    				\par priv-callee-options script:
    					"Dial 1 if you wish this caller to reach you directly in the future,
    						and immediately connect to their incoming call
    					 Dial 2 if you wish to send this caller to voicemail now and 
    						forevermore.
    					 Dial 3 to send this caller to the torture menus, now and forevermore.
    					 Dial 4 to send this caller to a simple "go away" menu, now and forevermore.
    					 Dial 5 to allow this caller to come straight thru to you in the future,
    						but right now, just this once, send them to voicemail."
    				\par screen-callee-options script:
    					"Dial 1 if you wish to immediately connect to the incoming call
    					 Dial 2 if you wish to send this caller to voicemail.
    					 Dial 3 to send this caller to the torture menus.
    					 Dial 4 to send this caller to a simple "go away" menu.
    				*/
    				if (valid_priv_reply(&opts, res2))
    					break;
    				/* invalid option */
    				res2 = ast_play_and_wait(peer, "vm-sorry");
    			}
    
    
    			if (ast_test_flag(&opts, OPT_MUSICBACK)) {
    				ast_moh_stop(chan);
    			} else if (ast_test_flag(&opts, OPT_RINGBACK)) {
    				ast_indicate(chan, -1);
    				sentringing=0;
    			}
    			ast_autoservice_stop(chan);
    
    			switch (res2) {
    
    			case '1':
    				if( ast_test_flag(&opts, OPT_PRIVACY) ) {
    					if (option_verbose > 2)
    						ast_verbose(VERBOSE_PREFIX_3 "--Set privacy database entry %s/%s to ALLOW\n",
    							     opt_args[OPT_ARG_PRIVACY], privcid);
    					ast_privacy_set(opt_args[OPT_ARG_PRIVACY], privcid, AST_PRIVACY_ALLOW);
    				}
    				break;
    			case '2':
    				if( ast_test_flag(&opts, OPT_PRIVACY) ) {
    					if (option_verbose > 2)
    						ast_verbose(VERBOSE_PREFIX_3 "--Set privacy database entry %s/%s to DENY\n",
    							     opt_args[OPT_ARG_PRIVACY], privcid);
    					ast_privacy_set(opt_args[OPT_ARG_PRIVACY], privcid, AST_PRIVACY_DENY);
    				}
    				ast_hangup(peer); /* hang up on the callee -- he didn't want to talk anyway! */
    				res=0;
    				goto out;
    			case '3':
    				if( ast_test_flag(&opts, OPT_PRIVACY) ) {
    					if (option_verbose > 2)
    						ast_verbose(VERBOSE_PREFIX_3 "--Set privacy database entry %s/%s to TORTURE\n",
    							     opt_args[OPT_ARG_PRIVACY], privcid);
    					ast_privacy_set(opt_args[OPT_ARG_PRIVACY], privcid, AST_PRIVACY_TORTURE);
    				}
    				ast_copy_string(status, "TORTURE", sizeof(status));
    				
    				res = 0;
    				ast_hangup(peer); /* hang up on the caller -- he didn't want to talk anyway! */
    				goto out; /* Is this right? */
    			case '4':
    				if( ast_test_flag(&opts, OPT_PRIVACY) ) {
    					if (option_verbose > 2)
    						ast_verbose(VERBOSE_PREFIX_3 "--Set privacy database entry %s/%s to KILL\n",
    							     opt_args[OPT_ARG_PRIVACY], privcid);
    					ast_privacy_set(opt_args[OPT_ARG_PRIVACY], privcid, AST_PRIVACY_KILL);
    
    				ast_copy_string(status, "DONTCALL", sizeof(status));
    				res = 0;
    				ast_hangup(peer); /* hang up on the caller -- he didn't want to talk anyway! */
    				goto out; /* Is this right? */
    			case '5':
    				if( ast_test_flag(&opts, OPT_PRIVACY) ) {
    					if (option_verbose > 2)
    						ast_verbose(VERBOSE_PREFIX_3 "--Set privacy database entry %s/%s to ALLOW\n",
    							     opt_args[OPT_ARG_PRIVACY], privcid);
    					ast_privacy_set(opt_args[OPT_ARG_PRIVACY], privcid, AST_PRIVACY_ALLOW);
    
    					ast_hangup(peer); /* hang up on the caller -- he didn't want to talk anyway! */
    					res=0;
    					goto out;
    
    				} /* if not privacy, then 5 is the same as "default" case */
    			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");
    				ast_hangup(peer); /* hang up on the callee -- he didn't want to talk anyway! */
    				res=0;
    				goto out;
    			}
    
    
    			/* XXX once again, this path is only taken in the case '1', so it could be
    			 * moved there, although i am not really sure that this is correct - maybe
    			 * the check applies to other cases as well.
    			 */
    
    			/* 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(privcid,"NOCALLERID",10) == 0 || ast_test_flag(&opts, OPT_SCREEN_NOINTRO) ) {
    				ast_filedelete(privintro, NULL);
    				if( ast_fileexists(privintro, NULL, NULL ) > 0 )
    					ast_log(LOG_NOTICE, "privacy: ast_filedelete didn't do its job on %s\n", privintro);
    				else if (option_verbose > 2)
    					ast_verbose(VERBOSE_PREFIX_3 "Successfully deleted %s intro file\n", privintro);
    			}
    
    		if (!ast_test_flag(&opts, OPT_ANNOUNCE) || ast_strlen_zero(opt_args[OPT_ARG_ANNOUNCE])) {
    			res = 0;
    		} else {
    
    			int digit = 0;
    
    			/* Start autoservice on the other chan */
    
    			res = ast_autoservice_start(chan);
    
    			/* Now Stream the File */
    
    				res = ast_streamfile(peer, opt_args[OPT_ARG_ANNOUNCE], peer->language);
    
    			if (!res) {
    				digit = ast_waitstream(peer, AST_DIGIT_ANY); 
    			}
    
    			/* Ok, done. stop autoservice */
    
    			res = ast_autoservice_stop(chan);
    
    			if (digit > 0 && !res)
    				res = ast_senddigit(chan, digit); 
    			else
    				res = digit;
    
    
    		if (chan && peer && ast_test_flag(&opts, OPT_GOTO) && !ast_strlen_zero(opt_args[OPT_ARG_GOTO])) {
    
    			replace_macro_delimiter(opt_args[OPT_ARG_GOTO]);
    
    			ast_parseable_goto(chan, opt_args[OPT_ARG_GOTO]);
    			ast_parseable_goto(peer, opt_args[OPT_ARG_GOTO]);
    
    			peer->priority++;
    
    			ast_pbx_start(peer);
    			hanguptree(outgoing, NULL);
    
    		if (ast_test_flag(&opts, OPT_CALLEE_MACRO) && !ast_strlen_zero(opt_args[OPT_ARG_CALLEE_MACRO])) {
    
    			struct ast_app *theapp;
    
    			const char *macro_result;
    
    			res = ast_autoservice_start(chan);
    			if (res) {
    				ast_log(LOG_ERROR, "Unable to start autoservice on calling channel\n");
    				res = -1;
    			}
    
    
    			theapp = pbx_findapp("Macro");
    
    			if (theapp && !res) {	/* XXX why check res here ? */
    
    				replace_macro_delimiter(opt_args[OPT_ARG_CALLEE_MACRO]);
    
    				res = pbx_exec(peer, theapp, opt_args[OPT_ARG_CALLEE_MACRO]);
    
    				ast_log(LOG_DEBUG, "Macro exited with status %d\n", res);
    				res = 0;
    			} else {
    				ast_log(LOG_ERROR, "Could not find application Macro\n");
    				res = -1;
    			}
    
    			if (ast_autoservice_stop(chan) < 0) {
    				ast_log(LOG_ERROR, "Could not stop autoservice on calling channel\n");
    				res = -1;
    			}
    
    			if (!res && (macro_result = pbx_builtin_getvar_helper(peer, "MACRO_RESULT"))) {
    
    					char *macro_transfer_dest;
    
    
    					if (!strcasecmp(macro_result, "BUSY")) {
    
    						ast_copy_string(status, macro_result, sizeof(status));
    
    						if (ast_opt_priority_jumping || ast_test_flag(&opts, OPT_PRIORITY_JUMP)) {
    
    							if (!ast_goto_if_exists(chan, NULL, NULL, chan->priority + 101)) {
    								ast_set_flag(peerflags, OPT_GO_ON);
    							}
    						} else
    
    							ast_set_flag(peerflags, OPT_GO_ON);
    
    					} else if (!strcasecmp(macro_result, "CONGESTION") || !strcasecmp(macro_result, "CHANUNAVAIL")) {
    
    						ast_copy_string(status, macro_result, sizeof(status));
    
    						ast_set_flag(peerflags, OPT_GO_ON);	
    
    					} else if (!strcasecmp(macro_result, "CONTINUE")) {
    
    						/* 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.
    						*/
    
    						ast_set_flag(peerflags, OPT_GO_ON);	
    
    						res = -1;
    					} else if (!strcasecmp(macro_result, "ABORT")) {
    						/* Hangup both ends unless the caller has the g flag */
    						res = -1;
    
    Olle Johansson's avatar
    Olle Johansson committed
    					} else if (!strncasecmp(macro_result, "GOTO:", 5) && (macro_transfer_dest = ast_strdupa(macro_result + 5))) {
    
    						res = -1;
    						/* perform a transfer to a new extension */
    
    Olle Johansson's avatar
    Olle Johansson committed
    						if (strchr(macro_transfer_dest, '^')) { /* context^exten^priority*/
    
    							replace_macro_delimiter(macro_transfer_dest);
    
    							if (!ast_parseable_goto(chan, macro_transfer_dest))
    
    								ast_set_flag(peerflags, OPT_GO_ON);
    
    James Golovich's avatar
    James Golovich committed
    		if (!res) {
    			if (calldurationlimit > 0) {
    
    				chan->whentohangup = time(NULL) + calldurationlimit;
    
    James Golovich's avatar
    James Golovich committed
    			}
    
    			if (!ast_strlen_zero(dtmfcalled)) { 
    
    				if (option_verbose > 2)
    
    Olle Johansson's avatar
    Olle Johansson committed
    					ast_verbose(VERBOSE_PREFIX_3 "Sending DTMF '%s' to the called party.\n", dtmfcalled);
    
    				res = ast_dtmf_stream(peer,chan,dtmfcalled,250);
    			}
    
    			if (!ast_strlen_zero(dtmfcalling)) {
    
    				if (option_verbose > 2)
    
    Olle Johansson's avatar
    Olle Johansson committed
    					ast_verbose(VERBOSE_PREFIX_3 "Sending DTMF '%s' to the calling party.\n", dtmfcalling);
    
    				res = ast_dtmf_stream(chan,peer,dtmfcalling,250);
    			}
    
    James Golovich's avatar
    James Golovich committed
    		if (!res) {
    
    			struct ast_bridge_config config;
    
    
    James Golovich's avatar
    James Golovich committed
    			memset(&config,0,sizeof(struct ast_bridge_config));
    
    				ast_set_flag(&(config.features_caller), AST_FEATURE_PLAY_WARNING);
    
    				ast_set_flag(&(config.features_callee), AST_FEATURE_PLAY_WARNING);
    
    			if (ast_test_flag(peerflags, OPT_CALLEE_TRANSFER))
    
    				ast_set_flag(&(config.features_callee), AST_FEATURE_REDIRECT);
    
    			if (ast_test_flag(peerflags, OPT_CALLER_TRANSFER))
    
    				ast_set_flag(&(config.features_caller), AST_FEATURE_REDIRECT);
    
    			if (ast_test_flag(peerflags, OPT_CALLEE_HANGUP))
    
    				ast_set_flag(&(config.features_callee), AST_FEATURE_DISCONNECT);
    
    			if (ast_test_flag(peerflags, OPT_CALLER_HANGUP))
    
    				ast_set_flag(&(config.features_caller), AST_FEATURE_DISCONNECT);
    
    			if (ast_test_flag(peerflags, OPT_CALLEE_MONITOR))
    
    				ast_set_flag(&(config.features_callee), AST_FEATURE_AUTOMON);
    
    			if (ast_test_flag(peerflags, OPT_CALLER_MONITOR)) 
    
    				ast_set_flag(&(config.features_caller), AST_FEATURE_AUTOMON);
    
    James Golovich's avatar
    James Golovich committed
    			config.timelimit = timelimit;
    			config.play_warning = play_warning;
    			config.warning_freq = warning_freq;
    			config.warning_sound = warning_sound;
    			config.end_sound = end_sound;
    			config.start_sound = start_sound;
    
    			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 */
    			ast_deactivate_generator(chan);
    			/* 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", chan->name, peer->name);
    				ast_hangup(peer);
    
    				res = -1;
    				goto done;
    
    			if (opermode && (!strncmp(chan->name,"Zap",3)) &&
    				(!strncmp(peer->name,"Zap",3)))
    			{
    				struct oprmode oprmode;
    
    				oprmode.peer = peer;
    				oprmode.mode = opermode;
    
    				ast_channel_setoption(chan,
    					AST_OPTION_OPRMODE,&oprmode,sizeof(struct oprmode),0);
    			}
    
    James Golovich's avatar
    James Golovich committed
    			res = ast_bridge_call(chan,peer,&config);
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    			{
    				char toast[80];
    				snprintf(toast, sizeof(toast), "%ld", (long)(end_time - answer_time));
    				pbx_builtin_setvar_helper(chan, "ANSWEREDTIME", toast);
    			}
    
    James Golovich's avatar
    James Golovich committed
    			res = -1;
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    		{
    			char toast[80];
    			snprintf(toast, sizeof(toast), "%ld", (long)(end_time - start_time));
    			pbx_builtin_setvar_helper(chan, "DIALEDTIME", toast);
    		}
    
    		if (res != AST_PBX_NO_HANGUP_PEER) {
    			if (!chan->_softhangup)
    				chan->hangupcause = peer->hangupcause;
    
    James Golovich's avatar
    James Golovich committed
    			ast_hangup(peer);
    
    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);
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	hanguptree(outgoing, NULL);
    
    	pbx_builtin_setvar_helper(chan, "DIALSTATUS", status);
    
    Olle Johansson's avatar
    Olle Johansson committed
    	if (option_debug)
    		ast_log(LOG_DEBUG, "Exiting with DIALSTATUS=%s.\n", status);
    
    	if ((ast_test_flag(peerflags, OPT_GO_ON)) && (!chan->_softhangup) && (res != AST_PBX_KEEPALIVE))
    
    Olle Johansson's avatar
    Olle Johansson committed
    		res = 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return res;
    }
    
    
    static int dial_exec(struct ast_channel *chan, void *data)
    {
    	struct ast_flags peerflags;
    	memset(&peerflags, 0, sizeof(peerflags));
    	return dial_exec_full(chan, data, &peerflags);
    }
    
    static int retrydial_exec(struct ast_channel *chan, void *data)
    {
    
    	char *announce = NULL, *dialdata = NULL;
    	const char *context = NULL;
    
    	int sleep = 0, loops = 0, res = -1;
    
    	struct ast_flags peerflags;
    	
    
    	if (ast_strlen_zero(data)) {
    
    		ast_log(LOG_WARNING, "RetryDial requires an argument!\n");
    		return -1;
    	}	
    
    	if (!(announce = ast_strdupa(data)))
    		goto done;
    
    
    	if ((dialdata = strchr(announce, '|'))) {
    
    		if ((sleep = atoi(dialdata))) {
    			sleep *= 1000;
    		} else {
    
    			ast_log(LOG_ERROR, "%s requires the numerical argument <sleep>\n",rapp);
    
    		}
    		if ((dialdata = strchr(dialdata, '|'))) {
    
    			if (!(loops = atoi(dialdata))) {
    
    				ast_log(LOG_ERROR, "%s requires the numerical argument <loops>\n",rapp);
    
    			}
    		}
    	}
    	
    	if ((dialdata = strchr(dialdata, '|'))) {
    
    		ast_log(LOG_ERROR, "%s requires more arguments\n",rapp);
    
    	}
    		
    	if (sleep < 1000)
    		sleep = 10000;
    
    	
    	context = pbx_builtin_getvar_helper(chan, "EXITCONTEXT");
    
    	while (loops) {
    		chan->data = "Retrying";
    		if (ast_test_flag(chan, AST_FLAG_MOH))
    			ast_moh_stop(chan);
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if ((res = dial_exec_full(chan, dialdata, &peerflags)) == 0) {
    
    			if (ast_test_flag(&peerflags, OPT_DTMF_EXIT)) {
    
    				if (!(res = ast_streamfile(chan, announce, chan->language)))
    					res = ast_waitstream(chan, AST_DIGIT_ANY);
    
    Mark Spencer's avatar
    Mark Spencer committed
    				if (!res && sleep) {
    
    					if (!ast_test_flag(chan, AST_FLAG_MOH))
    						ast_moh_start(chan, NULL);
    					res = ast_waitfordigit(chan, sleep);
    				}
    			} else {
    				if (!(res = ast_streamfile(chan, announce, chan->language)))
    					res = ast_waitstream(chan, "");
    
    Mark Spencer's avatar
    Mark Spencer committed
    				if (sleep) {
    					if (!ast_test_flag(chan, AST_FLAG_MOH))
    						ast_moh_start(chan, NULL);
    					if (!res) 
    
    						res = ast_waitfordigit(chan, sleep);
    
    Mark Spencer's avatar
    Mark Spencer committed
    				}
    
    			}
    		}
    
    		if (res < 0)
    			break;
    		else if (res > 0) { /* Trying to send the call elsewhere (1 digit ext) */
    
    			if (onedigit_goto(chan, context, (char) res, 1)) {
    
    	
    	if (ast_test_flag(chan, AST_FLAG_MOH))
    		ast_moh_stop(chan);
    
    	LOCAL_USER_REMOVE(u);
    
    static int unload_module(void *mod)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    	int res;
    
    	res = ast_unregister_application(app);
    	res |= ast_unregister_application(rapp);
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    	STANDARD_HANGUP_LOCALUSERS;
    
    static int load_module(void *mod)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int res;
    
    
    	res = ast_register_application(app, dial_exec, synopsis, descrip);
    	res |= ast_register_application(rapp, retrydial_exec, rsynopsis, rdescrip);
    	
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return res;
    
    static const char *description(void)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    	return "Dialing Application";
    
    static const char *key(void)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	return ASTERISK_GPL_KEY;
    }
    
    
    STD_MOD(MOD_1, NULL, NULL, NULL);