Skip to content
Snippets Groups Projects
app_dial.c 33.3 KiB
Newer Older
  • Learn to ignore specific revisions
  • Mark Spencer's avatar
    Mark Spencer committed
    /*
     * Asterisk -- A telephony toolkit for Linux.
     *
    
    Mark Spencer's avatar
    Mark Spencer committed
     * Trivial application to dial a channel and send an URL on answer
    
    Mark Spencer's avatar
    Mark Spencer committed
     * 
    
    Mark Spencer's avatar
    Mark Spencer committed
     * Copyright (C) 1999, Mark Spencer
    
    Mark Spencer's avatar
    Mark Spencer committed
     *
     * Mark Spencer <markster@linux-support.net>
     *
     * This program is free software, distributed under the terms of
     * the GNU General Public License
     */
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    #include <asterisk/lock.h>
    
    Mark Spencer's avatar
    Mark Spencer committed
    #include <asterisk/file.h>
    #include <asterisk/logger.h>
    #include <asterisk/channel.h>
    #include <asterisk/pbx.h>
    #include <asterisk/options.h>
    #include <asterisk/module.h>
    
    Mark Spencer's avatar
    Mark Spencer committed
    #include <asterisk/translate.h>
    
    Mark Spencer's avatar
    Mark Spencer committed
    #include <asterisk/say.h>
    
    #include <asterisk/config.h>
    
    Mark Spencer's avatar
    Mark Spencer committed
    #include <asterisk/musiconhold.h>
    
    Mark Spencer's avatar
    Mark Spencer committed
    #include <asterisk/callerid.h>
    
    James Golovich's avatar
    James Golovich committed
    #include <asterisk/app.h>
    
    Mark Spencer's avatar
    Mark Spencer committed
    #include <stdlib.h>
    #include <errno.h>
    #include <unistd.h>
    #include <string.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <sys/time.h>
    
    Mark Spencer's avatar
    Mark Spencer committed
    #include <sys/signal.h>
    
    Mark Spencer's avatar
    Mark Spencer committed
    #include <netinet/in.h>
    
    Mark Spencer's avatar
    Mark Spencer committed
    static char *tdesc = "Dialing Application";
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    static char *app = "Dial";
    
    
    static char *synopsis = "Place a call and connect to the current channel";
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    static char *descrip =
    
    Mark Spencer's avatar
    Mark Spencer committed
    "  Dial(Technology/resource[&Technology2/resource2...][|timeout][|options][|URL]):\n"
    
    "Requests one or more channels and places specified outgoing calls on them.\n"
    "As soon as a channel answers, the Dial app will answer the originating\n"
    
    Mark Spencer's avatar
    Mark Spencer committed
    "channel (if it needs to be answered) and will bridge a call with the channel\n"
    
    "which first answered. All other calls placed by the Dial app will be hung up\n"
    "If a timeout is not specified, the Dial application will wait indefinitely\n"
    "until either one of the called channels answers, the user hangs up, or all\n"
    
    "channels return busy or error. In general, the dialer will return 0 if it\n"
    
    "was unable to place the call, or the timeout expired. However, if all\n"
    
    Mark Spencer's avatar
    Mark Spencer committed
    "channels were busy, and there exists an extension with priority n+101 (where\n"
    
    "n is the priority of the dialer instance), then it will be the next\n"
    
    Mark Spencer's avatar
    Mark Spencer committed
    "executed extension (this allows you to setup different behavior on busy from\n"
    "no-answer).\n"
    "  This application returns -1 if the originating channel hangs up, or if the\n"
    
    "call is bridged and either of the parties in the bridge terminate the call.\n"
    
    Mark Spencer's avatar
    Mark Spencer committed
    "The option string may contain zero or more of the following characters:\n"
    
    "      't' -- allow the called user transfer the calling user by hitting #.\n"
    "      'T' -- allow the calling user to transfer the call by hitting #.\n"
    
    "      'f' -- Forces callerid to be set as the extension of the line \n"
    "             making/redirecting the outgoing call. For example, some PSTNs\n"
    "             don't allow callerids from other extensions then the ones\n"
    
    Mark Spencer's avatar
    Mark Spencer committed
    "             that are assigned to you.\n"
    
    Mark Spencer's avatar
    Mark Spencer committed
    "      'r' -- indicate ringing to the calling party, pass no audio until answered.\n"
    "      'm' -- provide hold music to the calling party until answered.\n"
    
    "      'M(x) -- Executes the macro (x) upon connect of the call\n"
    
    "      'h' -- allow callee to hang up by hitting *.\n"
    
    Mark Spencer's avatar
    Mark Spencer committed
    "      'H' -- allow caller to hang up by hitting *.\n"
    
    "      'C' -- reset call detail record for this call.\n"
    
    Mark Spencer's avatar
    Mark Spencer committed
    "      'P[(x)]' -- privacy mode, using 'x' as database if provided.\n"
    
    "      'g' -- goes on in context if the destination channel hangs up\n"
    
    "      'A(x)' -- play an announcement to the called party, using x as file\n"
    
    "      'S(x)' -- hangup the call after x seconds AFTER called party picked up\n"  	
    
    "      'D([digits])'  -- Send DTMF digit string *after* called party has answered\n"
    
    "             but before the bridge. (w=500ms sec pause)\n"
    "      'L(x[:y][:z])' -- Limit the call to 'x' ms warning when 'y' ms are left\n"
    "             repeated every 'z' ms) Only 'x' is required, 'y' and 'z' are optional.\n"
    "             The following special variables are optional:\n"
    "             * LIMIT_PLAYAUDIO_CALLER    yes|no (default yes)\n"
    "                                         Play sounds to the caller.\n"
    "             * LIMIT_PLAYAUDIO_CALLEE    yes|no\n"
    "                                         Play sounds to the callee.\n"
    "             * LIMIT_TIMEOUT_FILE        File to play when time is up.\n"
    "             * LIMIT_CONNECT_FILE        File to play when call begins.\n"
    "             * LIMIT_WARNING_FILE        File to play as warning if 'y' is defined.\n"
    "                        'timeleft' is a special sound macro to auto-say the time \n"
    "                        left and is the default.\n\n"
    
    Mark Spencer's avatar
    Mark Spencer committed
    "  In addition to transferring the call, a call may be parked and then picked\n"
    
    Mark Spencer's avatar
    Mark Spencer committed
    "up by another user.\n"
    
    "  The optional URL will be sent to the called party if the channel supports it.\n"
    "  This application sets the following channel variables upon completion:\n"
    "      DIALEDTIME    Time from dial to answer\n" 
    "      ANSWEREDTIME  Time for actual call\n"
    "      DIALSTATUS    The status of the call as a text string, one of\n"
    "             CHANUNAVAIL | CONGESTION | NOANSWER | BUSY | ANSWER | CANCEL\n"
    "";
    
    Mark Spencer's avatar
    Mark Spencer committed
    /* We define a customer "local user" structure because we
       use it not only for keeping track of what is in use but
       also for keeping track of who we're dialing. */
    
    struct localuser {
    	struct ast_channel *chan;
    	int stillgoing;
    
    	int allowredirect_in;
    	int allowredirect_out;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int ringbackonly;
    	int musiconhold;
    
    	int allowdisconnect_in;
    	int allowdisconnect_out;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int forcecallerid;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	struct localuser *next;
    };
    
    LOCAL_USER_DECL;
    
    static void hanguptree(struct localuser *outgoing, struct ast_channel *exception)
    {
    	/* Hang up a tree of stuff */
    	struct localuser *oo;
    	while(outgoing) {
    		/* Hangup any existing lines we have open */
    
    		if (outgoing->chan && (outgoing->chan != exception))
    
    Mark Spencer's avatar
    Mark Spencer committed
    			ast_hangup(outgoing->chan);
    		oo = outgoing;
    		outgoing=outgoing->next;
    		free(oo);
    	}
    }
    
    
    static struct ast_channel *wait_for_answer(struct ast_channel *in, struct localuser *outgoing, int *to, int *allowredir_in, int *allowredir_out, int *allowdisconnect_in, int *allowdisconnect_out, int *sentringing, char *status, size_t statussize)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	struct localuser *o;
    	int found;
    	int numlines;
    
    	int numbusy = 0;
    	int numcongestion = 0;
    	int numnochan = 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int orig = *to;
    	struct ast_frame *f;
    	struct ast_channel *peer = NULL;
    
    	struct ast_channel *watchers[AST_MAX_WATCHERS];
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int pos;
    	int single;
    	struct ast_channel *winner;
    	
    
    	single = (outgoing && !outgoing->next && !outgoing->musiconhold && !outgoing->ringbackonly);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	
    	if (single) {
    
    		/* Turn off hold music, etc */
    
    Mark Spencer's avatar
    Mark Spencer committed
    		/* If we are calling a single channel, make them compatible for in-band tone purpose */
    		ast_channel_make_compatible(outgoing->chan, in);
    	}
    	
    
    Mark Spencer's avatar
    Mark Spencer committed
    	while(*to && !peer) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		o = outgoing;
    		found = -1;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		pos = 1;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		numlines = 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		watchers[0] = in;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		while(o) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			/* Keep track of important channels */
    
    			if (o->stillgoing && o->chan) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    				watchers[pos++] = o->chan;
    				found = 1;
    
    Mark Spencer's avatar
    Mark Spencer committed
    			}
    			o = o->next;
    
    Mark Spencer's avatar
    Mark Spencer committed
    			numlines++;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (found < 0) {
    
    			if (numlines == (numbusy + numcongestion + numnochan)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    				if (option_verbose > 2)
    
    					ast_verbose( VERBOSE_PREFIX_2 "Everyone is busy/congested at this time\n");
    				if (numbusy)
    
    					strncpy(status, "BUSY", statussize - 1);
    
    					strncpy(status, "CONGESTION", statussize - 1);
    
    					strncpy(status, "CHANUNAVAIL", statussize - 1);
    
    Mark Spencer's avatar
    Mark Spencer committed
    				/* See if there is a special busy message */
    
    Mark Spencer's avatar
    Mark Spencer committed
    				if (ast_exists_extension(in, in->context, in->exten, in->priority + 101, in->callerid)) 
    
    Mark Spencer's avatar
    Mark Spencer committed
    					in->priority+=100;
    			} else {
    				if (option_verbose > 2)
    					ast_verbose( VERBOSE_PREFIX_2 "No one is available to answer at this time\n");
    			}
    
    Mark Spencer's avatar
    Mark Spencer committed
    			*to = 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    			return NULL;
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    		winner = ast_waitfor_n(watchers, pos, to);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		o = outgoing;
    		while(o) {
    
    			if (o->stillgoing && o->chan && (o->chan->_state == AST_STATE_UP)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    				if (!peer) {
    					if (option_verbose > 2)
    						ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name);
    					peer = o->chan;
    
    					*allowredir_in = o->allowredirect_in;
    					*allowredir_out = o->allowredirect_out;
    
    					*allowdisconnect_in = o->allowdisconnect_in;
    					*allowdisconnect_out = o->allowdisconnect_out;
    
    Mark Spencer's avatar
    Mark Spencer committed
    				}
    
    			} else if (o->chan && (o->chan == winner)) {
    
    				if (!ast_strlen_zero(o->chan->call_forward)) {
    
    					char tmpchan[256]="";
    					char *stuff;
    					char *tech;
    					strncpy(tmpchan, o->chan->call_forward, sizeof(tmpchan) - 1);
    					if ((stuff = strchr(tmpchan, '/'))) {
    						*stuff = '\0';
    						stuff++;
    						tech = tmpchan;
    					} else {
    						snprintf(tmpchan, sizeof(tmpchan), "%s@%s", o->chan->call_forward, o->chan->context);
    						stuff = tmpchan;
    						tech = "Local";
    					}
    
    					/* Before processing channel, go ahead and check for forwarding */
    					if (option_verbose > 2)
    
    						ast_verbose(VERBOSE_PREFIX_3 "Now forwarding %s to '%s/%s' (thanks to %s)\n", in->name, tech, stuff, o->chan->name);
    
    					o->chan = ast_request(tech, in->nativeformats, stuff);
    
    						ast_log(LOG_NOTICE, "Unable to create local channel for call forward to '%s/%s'\n", tech, stuff);
    
    Mark Spencer's avatar
    Mark Spencer committed
    						if (o->chan->callerid)
    							free(o->chan->callerid);
    
    						o->chan->callerid = NULL;
    
    						if (o->forcecallerid) {
    							char *newcid = NULL;
    
    							if (strlen(in->macroexten))
    								newcid = in->macroexten;
    
    Mark Spencer's avatar
    Mark Spencer committed
    								newcid = in->exten;
    							o->chan->callerid = strdup(newcid);
    
    							strncpy(o->chan->accountcode, winner->accountcode, sizeof(o->chan->accountcode) - 1);
    							o->chan->cdrflags = winner->cdrflags;
    
    Mark Spencer's avatar
    Mark Spencer committed
    							if (!o->chan->callerid)
    								ast_log(LOG_WARNING, "Out of memory\n");
    						} else {
    
    Mark Spencer's avatar
    Mark Spencer committed
    								o->chan->callerid = strdup(in->callerid);
    
    								if (!o->chan->callerid)
    									ast_log(LOG_WARNING, "Out of memory\n");	
    							}
    
    							strncpy(o->chan->accountcode, in->accountcode, sizeof(o->chan->accountcode) - 1);
    							o->chan->cdrflags = in->cdrflags;
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    
    						if (in->ani) {
    							if (o->chan->ani)
    								free(o->chan->ani);
    
    							o->chan->ani = malloc(strlen(in->ani) + 1);
    
    							if (o->chan->ani)
    
    								strncpy(o->chan->ani, in->ani, strlen(in->ani));
    
    							else
    								ast_log(LOG_WARNING, "Out of memory\n");
    
    Mark Spencer's avatar
    Mark Spencer committed
    						if (o->chan->rdnis) 
    							free(o->chan->rdnis);
    
    							o->chan->rdnis = strdup(in->macroexten);
    						else
    							o->chan->rdnis = strdup(in->exten);
    
    						if (ast_call(o->chan, tmpchan, 0)) {
    							ast_log(LOG_NOTICE, "Failed to dial on local channel for call forward to '%s'\n", tmpchan);
    							o->stillgoing = 0;
    							ast_hangup(o->chan);
    							o->chan = NULL;
    
    					/* Hangup the original channel now, in case we needed it */
    					ast_hangup(winner);
    
    Mark Spencer's avatar
    Mark Spencer committed
    				f = ast_read(winner);
    				if (f) {
    					if (f->frametype == AST_FRAME_CONTROL) {
    						switch(f->subclass) {
    					    case AST_CONTROL_ANSWER:
    							/* This is our guy if someone answered. */
    							if (!peer) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    								if (option_verbose > 2)
    
    Mark Spencer's avatar
    Mark Spencer committed
    									ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name);
    								peer = o->chan;
    
    								*allowredir_in = o->allowredirect_in;
    								*allowredir_out = o->allowredirect_out;
    
    								*allowdisconnect_in = o->allowdisconnect_in;
    								*allowdisconnect_out = o->allowdisconnect_out;
    
    Mark Spencer's avatar
    Mark Spencer committed
    							}
    
    Mark Spencer's avatar
    Mark Spencer committed
    							break;
    						case AST_CONTROL_BUSY:
    							if (option_verbose > 2)
    								ast_verbose( VERBOSE_PREFIX_3 "%s is busy\n", o->chan->name);
    
    Martin Pycko's avatar
    Martin Pycko committed
    							ast_hangup(o->chan);
    
    Mark Spencer's avatar
    Mark Spencer committed
    							o->stillgoing = 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    							if (in->cdr)
    								ast_cdr_busy(in->cdr);
    
    Mark Spencer's avatar
    Mark Spencer committed
    							break;
    
    Mark Spencer's avatar
    Mark Spencer committed
    						case AST_CONTROL_CONGESTION:
    							if (option_verbose > 2)
    								ast_verbose( VERBOSE_PREFIX_3 "%s is circuit-busy\n", o->chan->name);
    
    Martin Pycko's avatar
    Martin Pycko committed
    							ast_hangup(o->chan);
    
    Mark Spencer's avatar
    Mark Spencer committed
    							o->stillgoing = 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    							if (in->cdr)
    								ast_cdr_busy(in->cdr);
    
    Mark Spencer's avatar
    Mark Spencer committed
    							break;
    
    Mark Spencer's avatar
    Mark Spencer committed
    						case AST_CONTROL_RINGING:
    							if (option_verbose > 2)
    								ast_verbose( VERBOSE_PREFIX_3 "%s is ringing\n", o->chan->name);
    
    Mark Spencer's avatar
    Mark Spencer committed
    							if (!(*sentringing) && !outgoing->musiconhold) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    								ast_indicate(in, AST_CONTROL_RINGING);
    
    Mark Spencer's avatar
    Mark Spencer committed
    								(*sentringing)++;
    
    Mark Spencer's avatar
    Mark Spencer committed
    							}
    							break;
    
    						case AST_CONTROL_PROGRESS:
    							if (option_verbose > 2)
    								ast_verbose ( VERBOSE_PREFIX_3 "%s is making progress passing it to %s\n", o->chan->name,in->name);
    							ast_indicate(in, AST_CONTROL_PROGRESS);
    							break;
    
    Mark Spencer's avatar
    Mark Spencer committed
    						case AST_CONTROL_OFFHOOK:
    							/* Ignore going off hook */
    							break;
    
    							if (!outgoing->ringbackonly && !outgoing->musiconhold) {
    
    								if (option_verbose > 2)
    									ast_verbose( VERBOSE_PREFIX_3 "%s stopped sounds\n", o->chan->name);
    								ast_indicate(in, -1);
    
    Mark Spencer's avatar
    Mark Spencer committed
    								(*sentringing) = 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    						default:
    							ast_log(LOG_DEBUG, "Dunno what to do with control type %d\n", f->subclass);
    
    Mark Spencer's avatar
    Mark Spencer committed
    						}
    
    Mark Spencer's avatar
    Mark Spencer committed
    					} else if (single && (f->frametype == AST_FRAME_VOICE) && 
    								!(outgoing->ringbackonly || outgoing->musiconhold)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    						if (ast_write(in, f)) 
    							ast_log(LOG_WARNING, "Unable to forward frame\n");
    
    Mark Spencer's avatar
    Mark Spencer committed
    					} else if (single && (f->frametype == AST_FRAME_IMAGE) && 
    								!(outgoing->ringbackonly || outgoing->musiconhold)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    						if (ast_write(in, f))
    							ast_log(LOG_WARNING, "Unable to forward image\n");
    
    Mark Spencer's avatar
    Mark Spencer committed
    					}
    
    Mark Spencer's avatar
    Mark Spencer committed
    					ast_frfree(f);
    				} else {
    
    Martin Pycko's avatar
    Martin Pycko committed
    					ast_hangup(o->chan);
    
    Mark Spencer's avatar
    Mark Spencer committed
    					o->stillgoing = 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    				}
    			}
    			o = o->next;
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (winner == in) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			f = ast_read(in);
    #if 0
    			if (f && (f->frametype != AST_FRAME_VOICE))
    					printf("Frame type: %d, %d\n", f->frametype, f->subclass);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			else if (!f || (f->frametype != AST_FRAME_VOICE))
    				printf("Hangup received on %s\n", in->name);
    
    Mark Spencer's avatar
    Mark Spencer committed
    #endif
    
    Mark Spencer's avatar
    Mark Spencer committed
    			if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP))) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    				/* Got hung up */
    				*to=-1;
    
    				strncpy(status, "CANCEL", statussize - 1);
    
    Mark Spencer's avatar
    Mark Spencer committed
    				return NULL;
    			}
    
    			if (f && (f->frametype == AST_FRAME_DTMF) && *allowdisconnect_out &&
    
    Mark Spencer's avatar
    Mark Spencer committed
    				(f->subclass == '*')) {
    			    if (option_verbose > 3)
    				ast_verbose(VERBOSE_PREFIX_3 "User hit %c to disconnect call.\n", f->subclass);
    				*to=0;
    
    				strcpy(status, "CANCEL");
    
    Mark Spencer's avatar
    Mark Spencer committed
    				return NULL;
    			}
    			if (single && ((f->frametype == AST_FRAME_VOICE) || (f->frametype == AST_FRAME_DTMF)))  {
    				if (ast_write(outgoing->chan, f))
    					ast_log(LOG_WARNING, "Unable to forward voice\n");
    
    Mark Spencer's avatar
    Mark Spencer committed
    				ast_frfree(f);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			}
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (!*to && (option_verbose > 2))
    			ast_verbose( VERBOSE_PREFIX_3 "Nobody picked up in %d ms\n", orig);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return peer;
    
    Mark Spencer's avatar
    Mark Spencer committed
    }
    
    static int dial_exec(struct ast_channel *chan, void *data)
    {
    	int res=-1;
    	struct localuser *u;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char info[256], *peers, *timeout, *tech, *number, *rest, *cur;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char  privdb[256] = "", *s;
    
    	char  announcemsg[256] = "", *ann;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	struct localuser *outgoing=NULL, *tmp;
    	struct ast_channel *peer;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int to;
    
    	int allowredir_in=0;
    	int allowredir_out=0;
    
    	int allowdisconnect_in=0;
    	int allowdisconnect_out=0;
    
    	int hasmacro = 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int privacy=0;
    
    	int announce=0;
    
    	int resetcdr=0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char numsubst[AST_MAX_EXTENSION];
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char restofit[AST_MAX_EXTENSION];
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char *transfer = NULL;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char *newnum;
    
    	char callerid[256] = "", *l, *n;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char *url=NULL; /* JDG */
    	struct ast_var_t *current;
    	struct varshead *headp, *newheadp;
    	struct ast_var_t *newvar;
    
    	unsigned int calldurationlimit=0;
    	char *cdl;
    	time_t now;
    
    	struct ast_bridge_config config;
    	long timelimit = 0;
    	long play_warning = 0;
    	long warning_freq=0;
    	char *warning_sound=NULL;
    	char *end_sound=NULL;
    	char *start_sound=NULL;
    	char *limitptr;
    	char limitdata[256];
    
    	char *sdtmfptr;
    	char sdtmfdata[256] = "";
    
    	char *mac = NULL, macroname[256] = "";
    
    	int play_to_caller=0,play_to_callee=0;
    
    	int playargs=0, sentringing=0, moh=0;
    
    	int digit = 0;
    
    	time_t start_time, answer_time, end_time;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (!data) {
    
    		ast_log(LOG_WARNING, "Dial requires an argument (technology1/number1&technology2/number2...|optional timeout|options)\n");
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return -1;
    	}
    	
    	LOCAL_USER_ADD(u);
    	
    
    	strncpy(info, (char *)data, sizeof(info) - 1);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	peers = info;
    	if (peers) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		timeout = strchr(info, '|');
    		if (timeout) {
    			*timeout = '\0';
    			timeout++;
    
    Mark Spencer's avatar
    Mark Spencer committed
    			transfer = strchr(timeout, '|');
    			if (transfer) {
    				*transfer = '\0';
    				transfer++;
    
    Mark Spencer's avatar
    Mark Spencer committed
    				/* JDG */
    				url = strchr(transfer, '|');
    				if (url) {
    					*url = '\0';
    					url++;
    					ast_log(LOG_DEBUG, "DIAL WITH URL=%s_\n", url);
    				} else 
    					ast_log(LOG_DEBUG, "SIMPLE DIAL (NO URL)\n");
    				/* /JDG */
    
    Mark Spencer's avatar
    Mark Spencer committed
    			}
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    	} else
    		timeout = NULL;
    
    	if (!peers || ast_strlen_zero(peers)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_log(LOG_WARNING, "Dial argument takes format (technology1/number1&technology2/number2...|optional timeout)\n");
    		goto out;
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    	if (transfer) {
    
    		/* Extract call duration limit */
    		if ((cdl = strstr(transfer, "S("))) {
    			calldurationlimit=atoi(cdl+2);
    			if (option_verbose > 2)
    				ast_verbose(VERBOSE_PREFIX_3 "Setting call duration limit to %i seconds.\n",calldurationlimit);			
    		} 
    
    		/* DTMF SCRIPT*/
    		if ((sdtmfptr = strstr(transfer, "D("))) {
    
    James Golovich's avatar
    James Golovich committed
    			strncpy(sdtmfdata, sdtmfptr + 2, sizeof(sdtmfdata) - 1);
    			/* Overwrite with X's what was the sdtmf info */
    			while (*sdtmfptr && (*sdtmfptr != ')')) 
    				*(sdtmfptr++) = 'X';
    			if (*sdtmfptr)
    				*sdtmfptr = 'X';
    			/* Now find the end  */
    			sdtmfptr = strchr(sdtmfdata, ')');
    			if (sdtmfptr)
    				*sdtmfptr = '\0';
    			else 
    				ast_log(LOG_WARNING, "D( Data lacking trailing ')'\n");
    
    		/* XXX LIMIT SUPPORT */
    		if ((limitptr = strstr(transfer, "L("))) {
    
    James Golovich's avatar
    James Golovich committed
    			strncpy(limitdata, limitptr + 2, sizeof(limitdata) - 1);
    			/* Overwrite with X's what was the limit info */
    			while(*limitptr && (*limitptr != ')')) 
    				*(limitptr++) = 'X';
    			if (*limitptr)
    				*limitptr = 'X';
    			/* Now find the end */
    			limitptr = strchr(limitdata, ')');
    			if (limitptr)
    				*limitptr = '\0';
    			else
    				ast_log(LOG_WARNING, "Limit Data lacking trailing ')'\n");
    
    			var = pbx_builtin_getvar_helper(chan,"LIMIT_PLAYAUDIO_CALLER");
    			play_to_caller = var ? ast_true(var) : 1;
    
    James Golovich's avatar
    James Golovich committed
    			var = pbx_builtin_getvar_helper(chan,"LIMIT_PLAYAUDIO_CALLEE");
    			play_to_callee = var ? ast_true(var) : 0;
    
    James Golovich's avatar
    James Golovich committed
    			if (!play_to_caller && !play_to_callee)
    				play_to_caller=1;
    
    James Golovich's avatar
    James Golovich committed
    			var = pbx_builtin_getvar_helper(chan,"LIMIT_WARNING_FILE");
    			warning_sound = var ? var : "timeleft";
    
    			var = pbx_builtin_getvar_helper(chan,"LIMIT_TIMEOUT_FILE");
    			end_sound = var ? var : NULL;
    
    			var = pbx_builtin_getvar_helper(chan,"LIMIT_CONNECT_FILE");
    			start_sound = var ? var : NULL;
    
    			var=stack=limitdata;
    
    			var = strsep(&stack, ":");
    			if (var) {
    				timelimit = atol(var);
    				playargs++;
    				var = strsep(&stack, ":");
    				if (var) {
    					play_warning = atol(var);
    					playargs++;
    					var = strsep(&stack, ":");
    					if(var) {
    						warning_freq = atol(var);
    						playargs++;
    					}
    				}
    			}
    
    James Golovich's avatar
    James Golovich committed
    			if (!timelimit) {
    				timelimit=play_to_caller=play_to_callee=play_warning=warning_freq=0;
    				warning_sound=NULL;
    			}
    			/* undo effect of S(x) in case they are both used */
    			calldurationlimit=0; 
    			/* more efficient do it like S(x) does since no advanced opts*/
    			if (!play_warning && !start_sound && !end_sound && timelimit) { 
    				calldurationlimit=timelimit/1000;
    				timelimit=play_to_caller=play_to_callee=play_warning=warning_freq=0;
    			} else if (option_verbose > 2) {
    				ast_verbose(VERBOSE_PREFIX_3"Limit Data:\n");
    				ast_verbose(VERBOSE_PREFIX_3"timelimit=%ld\n",timelimit);
    				ast_verbose(VERBOSE_PREFIX_3"play_warning=%ld\n",play_warning);
    				ast_verbose(VERBOSE_PREFIX_3"play_to_caller=%s\n",play_to_caller ? "yes" : "no");
    				ast_verbose(VERBOSE_PREFIX_3"play_to_callee=%s\n",play_to_callee ? "yes" : "no");
    				ast_verbose(VERBOSE_PREFIX_3"warning_freq=%ld\n",warning_freq);
    				ast_verbose(VERBOSE_PREFIX_3"start_sound=%s\n",start_sound ? start_sound : "UNDEF");
    				ast_verbose(VERBOSE_PREFIX_3"warning_sound=%s\n",warning_sound ? warning_sound : "UNDEF");
    				ast_verbose(VERBOSE_PREFIX_3"end_sound=%s\n",end_sound ? end_sound : "UNDEF");
    			}
    
    		/* XXX ANNOUNCE SUPPORT */
    		if ((ann = strstr(transfer, "A("))) {
    			announce = 1;
    			strncpy(announcemsg, ann + 2, sizeof(announcemsg) - 1);
    			/* Overwrite with X's what was the announce info */
    			while(*ann && (*ann != ')')) 
    				*(ann++) = 'X';
    			if (*ann)
    				*ann = 'X';
    			/* Now find the end of the privdb */
    			ann = strchr(announcemsg, ')');
    			if (ann)
    				*ann = '\0';
    			else {
    				ast_log(LOG_WARNING, "Transfer with Announce spec lacking trailing ')'\n");
    				announce = 0;
    			}
    		}
    
    		
    		/* Get the macroname from the dial option string */
    		if ((mac = strstr(transfer, "M("))) {
    			hasmacro = 1;
    			strncpy(macroname, mac + 2, sizeof(macroname) - 1);
    			while (*mac && (*mac != ')'))
    				*(mac++) = 'X';
    			if (*mac)
    				*mac = 'X';
    			else {
    				ast_log(LOG_WARNING, "Could not find macro to which we should jump.\n");
    				hasmacro = 0;
    			}
    			mac = strchr(macroname, ')');
    			if (mac)
    				*mac = '\0';
    			else {
    				ast_log(LOG_WARNING, "Macro flag set without trailing ')'\n");
    				hasmacro = 0;
    			}
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    		/* Extract privacy info from transfer */
    		if ((s = strstr(transfer, "P("))) {
    			privacy = 1;
    			strncpy(privdb, s + 2, sizeof(privdb) - 1);
    			/* Overwrite with X's what was the privacy info */
    			while(*s && (*s != ')')) 
    				*(s++) = 'X';
    			if (*s)
    				*s = 'X';
    			/* Now find the end of the privdb */
    			s = strchr(privdb, ')');
    			if (s)
    				*s = '\0';
    			else {
    
    				ast_log(LOG_WARNING, "Transfer with privacy lacking trailing ')'\n");
    
    Mark Spencer's avatar
    Mark Spencer committed
    				privacy = 0;
    			}
    		} else if (strchr(transfer, 'P')) {
    			/* No specified privdb */
    			privacy = 1;
    
    		} else if (strchr(transfer, 'C')) {
    			resetcdr = 1;
    
    	if (resetcdr && chan->cdr)
    		ast_cdr_reset(chan->cdr, 0);
    
    	if (ast_strlen_zero(privdb) && privacy) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		/* If privdb is not specified and we are using privacy, copy from extension */
    		strncpy(privdb, chan->exten, sizeof(privdb) - 1);
    	}
    	if (privacy) {
    		if (chan->callerid)
    
    			strncpy(callerid, chan->callerid, sizeof(callerid) - 1);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		else
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_callerid_parse(callerid, &n, &l);
    		if (l) {
    			ast_shrink_phone_number(l);
    		} else
    			l = "";
    		ast_log(LOG_NOTICE, "Privacy DB is '%s', privacy is %d, clid is '%s'\n", privdb, privacy, l);
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	cur = peers;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	do {
    		/* Remember where to start next time */
    
    Mark Spencer's avatar
    Mark Spencer committed
    		rest = strchr(cur, '&');
    		if (rest) {
    			*rest = 0;
    			rest++;
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    		/* Get a technology/[device:]number pair */
    
    Mark Spencer's avatar
    Mark Spencer committed
    		tech = cur;
    		number = strchr(tech, '/');
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (!number) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			ast_log(LOG_WARNING, "Dial argument takes format (technology1/[device:]number1&technology2/[device:]number2...|optional timeout)\n");
    
    Mark Spencer's avatar
    Mark Spencer committed
    			goto out;
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    		*number = '\0';
    		number++;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		tmp = malloc(sizeof(struct localuser));
    		if (!tmp) {
    			ast_log(LOG_WARNING, "Out of memory\n");
    			goto out;
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    		memset(tmp, 0, sizeof(struct localuser));
    		if (transfer) {
    			if (strchr(transfer, 't'))
    
    				tmp->allowredirect_in = 1;
                            else    tmp->allowredirect_in = 0;
    			if (strchr(transfer, 'T'))
    				tmp->allowredirect_out = 1;
                            else    tmp->allowredirect_out = 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    			if (strchr(transfer, 'r'))
    				tmp->ringbackonly = 1;
    
    Mark Spencer's avatar
    Mark Spencer committed
                            else    tmp->ringbackonly = 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    			if (strchr(transfer, 'm'))
    				tmp->musiconhold = 1;
    
    Mark Spencer's avatar
    Mark Spencer committed
                            else    tmp->musiconhold = 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    			if (strchr(transfer, 'H'))
    
    				allowdisconnect_out = tmp->allowdisconnect_out = 1;
                            else    allowdisconnect_out = tmp->allowdisconnect_out = 0;
    			if(strchr(transfer, 'h'))
    				allowdisconnect_in = tmp->allowdisconnect_in = 1;
    			else	allowdisconnect_in = tmp->allowdisconnect_in = 0;
    
    			if(strchr(transfer, 'g'))
    				go_on=1;
    
    Mark Spencer's avatar
    Mark Spencer committed
    			if (strchr(transfer, 'f'))
    				tmp->forcecallerid = 1;
    			else	tmp->forcecallerid = 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    		strncpy(numsubst, number, sizeof(numsubst)-1);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		/* If we're dialing by extension, look at the extension to know what to dial */
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if ((newnum = strstr(numsubst, "BYEXTENSION"))) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			strncpy(restofit, newnum + strlen("BYEXTENSION"), sizeof(restofit)-1);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			snprintf(newnum, sizeof(numsubst) - (newnum - numsubst), "%s%s", chan->exten,restofit);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			if (option_debug)
    				ast_log(LOG_DEBUG, "Dialing by extension %s\n", numsubst);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    		/* Request the peer */
    
    Mark Spencer's avatar
    Mark Spencer committed
    		tmp->chan = ast_request(tech, chan->nativeformats, numsubst);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (!tmp->chan) {
    			/* If we can't, just go on to the next call */
    
    Mark Spencer's avatar
    Mark Spencer committed
    			ast_log(LOG_NOTICE, "Unable to create channel of type '%s'\n", tech);
    			if (chan->cdr)
    				ast_cdr_busy(chan->cdr);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			free(tmp);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			cur = rest;
    
    Mark Spencer's avatar
    Mark Spencer committed
    			continue;
    		}
    
    		if (!ast_strlen_zero(tmp->chan->call_forward)) {
    
    			char tmpchan[256]="";
    			char *stuff;
    			char *tech;
    
    Mark Spencer's avatar
    Mark Spencer committed
    			strncpy(tmpchan, tmp->chan->call_forward, sizeof(tmpchan) - 1);
    
    			if ((stuff = strchr(tmpchan, '/'))) {
    				*stuff = '\0';
    				stuff++;
    				tech = tmpchan;
    			} else {
    				snprintf(tmpchan, sizeof(tmpchan), "%s@%s", tmp->chan->call_forward, tmp->chan->context);
    				stuff = tmpchan;
    				tech = "Local";
    			}
    			/* Before processing channel, go ahead and check for forwarding */
    
    Mark Spencer's avatar
    Mark Spencer committed
    			if (option_verbose > 2)
    
    Mark Spencer's avatar
    Mark Spencer committed
    				ast_verbose(VERBOSE_PREFIX_3 "Forwarding %s to '%s/%s' (thanks to %s)\n", chan->name, tech, stuff, tmp->chan->name);
    
    			/* Setup parameters */
    
    Mark Spencer's avatar
    Mark Spencer committed
    			ast_hangup(tmp->chan);
    			tmp->chan = ast_request(tech, chan->nativeformats, stuff);
    
    				ast_log(LOG_NOTICE, "Unable to create local channel for call forward to '%s/%s'\n", tech, stuff);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    		/* If creating a SIP channel, look for a variable called */
    		/* VXML_URL in the calling channel and copy it to the    */
    		/* new channel.                                          */
    
    		/* Check for ALERT_INFO in the SetVar list.  This is for   */
    		/* SIP distinctive ring as per the RFC.  For Cisco 7960s,  */
    
    		/* SetVar(ALERT_INFO=<x>) where x is an integer value 1-5. */
    		/* However, the RFC says it should be a URL.  -km-         */
    
    		headp=&chan->varshead;
    		AST_LIST_TRAVERSE(headp,current,entries) {
    			if (!strcasecmp(ast_var_name(current),"VXML_URL") ||
    			    !strcasecmp(ast_var_name(current), "ALERT_INFO") ||
    				!strcasecmp(ast_var_name(current), "OSPTOKEN") ||
    				!strcasecmp(ast_var_name(current), "OSPHANDLE"))
    			{
    				newvar=ast_var_assign(ast_var_name(current),ast_var_value(current));
    				newheadp=&tmp->chan->varshead;
    				AST_LIST_INSERT_HEAD(newheadp,newvar,entries);
    
    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;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (tmp->chan->callerid)
    			free(tmp->chan->callerid);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (tmp->chan->ani)
    			free(tmp->chan->ani);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (chan->callerid)
    			tmp->chan->callerid = strdup(chan->callerid);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		else
    			tmp->chan->callerid = NULL;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		/* Copy language from incoming to outgoing */
    
    		strncpy(tmp->chan->language, chan->language, sizeof(tmp->chan->language) - 1);
    
    		strncpy(tmp->chan->accountcode, chan->accountcode, sizeof(tmp->chan->accountcode) - 1);
    		tmp->chan->cdrflags = chan->cdrflags;
    
    		if (ast_strlen_zero(tmp->chan->musicclass))
    
    Mark Spencer's avatar
    Mark Spencer committed
    			strncpy(tmp->chan->musicclass, chan->musicclass, sizeof(tmp->chan->musicclass) - 1);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (chan->ani)
    			tmp->chan->ani = strdup(chan->ani);
    		else
    			tmp->chan->ani = NULL;
    
    		/* Pass hidecallerid setting */
    		tmp->chan->restrictcid = chan->restrictcid;
    
    		/* Pass callingpres setting */
    		tmp->chan->callingpres = chan->callingpres;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		/* Presense of ADSI CPE on outgoing channel follows ours */
    		tmp->chan->adsicpe = chan->adsicpe;
    
    		/* pass the digital flag */
    		ast_dup_flag(tmp->chan, chan, AST_FLAG_DIGITAL);
    
    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);
    			free(tmp);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			cur = rest;
    
    Mark Spencer's avatar
    Mark Spencer committed
    			continue;
    		} else
    			if (option_verbose > 2)
    
    Mark Spencer's avatar
    Mark Spencer committed
    				ast_verbose(VERBOSE_PREFIX_3 "Called %s\n", numsubst);
    
    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 */
    		tmp->stillgoing = -1;
    		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;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		cur = rest;
    	} while(cur);
    	
    
    	if (timeout && !ast_strlen_zero(timeout)) {
    
    		to = atoi(timeout);
    		if (to > 0)
    			to *= 1000;
    		else
    			ast_log(LOG_WARNING, "Invalid timeout specified: '%s'\n", timeout);
    	} else
    
    Mark Spencer's avatar
    Mark Spencer committed
    		to = -1;
    
    	if (outgoing) {
    
    		/* Our status will at least be NOANSWER */
    
    		strncpy(status, "NOANSWER", sizeof(status) - 1);
    
    		if (outgoing->musiconhold) {
    			moh=1;
    			ast_moh_start(chan, NULL);
    		} else if (outgoing->ringbackonly) {
    			ast_indicate(chan, AST_CONTROL_RINGING);
    			sentringing++;
    		}
    
    		strncpy(status, "CHANUNAVAIL", sizeof(status) - 1);
    
    	peer = wait_for_answer(chan, outgoing, &to, &allowredir_in, &allowredir_out, &allowdisconnect_in, &allowdisconnect_out, &sentringing, status, sizeof(status));
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (!peer) {
    		if (to) 
    			/* Musta gotten hung up */
    			res = -1;
    		 else 
    		 	/* Nobody answered, next please? */
    			res=0;
    		
    		goto out;
    	}
    	if (peer) {
    
    #ifdef OSP_SUPPORT
    		/* Once call is answered, ditch the OSP Handle */
    		pbx_builtin_setvar_helper(chan, "OSPHANDLE", "");
    #endif		
    
    		strncpy(status, "ANSWER", sizeof(status) - 1);
    
    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);
    		if (numsubst)
    			pbx_builtin_setvar_helper(chan, "DIALEDPEERNUMBER", numsubst);
    
    Mark Spencer's avatar
    Mark Spencer committed
     		/* JDG: sendurl */
    
     		if( url && !ast_strlen_zero(url) && ast_channel_supports_html(peer) ) {
    
    Mark Spencer's avatar
    Mark Spencer committed
     			ast_log(LOG_DEBUG, "app_dial: sendurl=%s.\n", url);
     			ast_channel_sendurl( peer, url );
     		} /* /JDG */
    
    		if (announce && announcemsg) {
    
    			// Start autoservice on the other chan
    
    			res = ast_autoservice_start(chan);
    
    			// Now Stream the File
    
    			if (!res)
    				res = ast_streamfile(peer,announcemsg,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;
    
    
    James Golovich's avatar
    James Golovich committed
    		} else
    			res = 0;
    
    		if (hasmacro && macroname) {
    			void *app = NULL;
    
    			res = ast_autoservice_start(chan);
    			if (res) {
    				ast_log(LOG_ERROR, "Unable to start autoservice on calling channel\n");
    				res = -1;
    			}
    
    			app = pbx_findapp("Macro");
    
    			if (app && !res) {
    				res = pbx_exec(peer, app, macroname, 1);
    				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;
    			}
    		}
    
    
    James Golovich's avatar
    James Golovich committed
    		if (!res) {
    			if (calldurationlimit > 0) {
    				time(&now);
    				chan->whentohangup = now + calldurationlimit;
    			}
    			if (!ast_strlen_zero(sdtmfdata)) 
    
    				res = ast_dtmf_stream(peer,chan,sdtmfdata,250);
    
    James Golovich's avatar
    James Golovich committed
    		if (!res) {
    			memset(&config,0,sizeof(struct ast_bridge_config));
    			config.play_to_caller=play_to_caller;
    			config.play_to_callee=play_to_callee;
    			config.allowredirect_in = allowredir_in;
    			config.allowredirect_out = allowredir_out;
    
    			config.allowdisconnect_in = allowdisconnect_in;
    			config.allowdisconnect_out = allowdisconnect_out;
    
    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);
    				return -1;
    			}
    
    James Golovich's avatar
    James Golovich committed
    			res = ast_bridge_call(chan,peer,&config);