Skip to content
Snippets Groups Projects
app_dial.c 22.9 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>
    
    Mark Spencer's avatar
    Mark Spencer committed
    #include <asterisk/parking.h>
    
    Mark Spencer's avatar
    Mark Spencer committed
    #include <asterisk/musiconhold.h>
    
    Mark Spencer's avatar
    Mark Spencer committed
    #include <asterisk/callerid.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
    
    #include <pthread.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\n"
    "      'T' -- to allow the calling user to transfer the call.\n"
    "      'r' -- indicate ringing to the calling party, pass no audio until answered.\n"
    "      'm' -- provide hold music to the calling party until answered.\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"  	
    
    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\n"
    
    Mark Spencer's avatar
    Mark Spencer committed
    "it.\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;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int allowdisconnect;
    
    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);
    	}
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    #define MAX 256
    
    
    static struct ast_channel *wait_for_answer(struct ast_channel *in, struct localuser *outgoing, int *to, int *allowredir_in, int *allowredir_out, int *allowdisconnect)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	struct localuser *o;
    	int found;
    	int numlines;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int sentringing = 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int numbusies = 0;
    	int orig = *to;
    	struct ast_frame *f;
    	struct ast_channel *peer = NULL;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	struct ast_channel *watchers[MAX];
    	int pos;
    	int single;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int moh=0;
    	int ringind=0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	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 */
    		ast_indicate(in, -1);
    
    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
    	if (outgoing) {
    		moh = outgoing->musiconhold;
    		ringind = outgoing->ringbackonly;
    		if (outgoing->musiconhold) {
    			ast_moh_start(in, NULL);
    		} else if (outgoing->ringbackonly) {
    			ast_indicate(in, AST_CONTROL_RINGING);
    		}
    	}
    	
    
    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) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			if (numlines == numbusies) {
    				if (option_verbose > 2)
    					ast_verbose( VERBOSE_PREFIX_2 "Everyone is busy at this time\n");
    				/* 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
    			/* if no one available we'd better stop MOH/ringing to */
    			if (moh) {
    				ast_moh_stop(in);
    			} else if (ringind) {
    				ast_indicate(in, -1);
    			}
    
    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;
    
    Mark Spencer's avatar
    Mark Spencer committed
    					*allowdisconnect = o->allowdisconnect;
    				}
    
    			} else if (o->chan && (o->chan == winner)) {
    
    				if (strlen(o->chan->call_forward)) {
    
    					/* 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, o->chan->call_forward, o->chan->context, o->chan->name);
    					/* Setup parameters */
    
    					snprintf(tmpchan, sizeof(tmpchan),"%s@%s", o->chan->call_forward, o->chan->context);
    					ast_hangup(o->chan);
    					o->chan = ast_request("Local", in->nativeformats, tmpchan);
    					if (!o->chan) {
    						ast_log(LOG_NOTICE, "Unable to create local channel for call forward to '%s'\n", tmpchan);
    						o->stillgoing = 0;
    						numbusies++;
    
    						if (in->callerid) {
    							if (o->chan->callerid)
    								free(o->chan->callerid);
    
    							o->chan->callerid = malloc(strlen(in->callerid) + 1);
    
    							if (o->chan->callerid)
    								strncpy(o->chan->callerid, in->callerid, strlen(in->callerid) + 1);
    							else
    								ast_log(LOG_WARNING, "Out of memory\n");
    
    						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) + 1);
    							else
    								ast_log(LOG_WARNING, "Out of memory\n");
    
    						}
    						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;
    							numbusies++;
    						}
    
    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;
    
    Mark Spencer's avatar
    Mark Spencer committed
    								*allowdisconnect = o->allowdisconnect;
    
    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
    							numbusies++;
    							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
    							numbusies++;
    							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 && !moh) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    								ast_indicate(in, AST_CONTROL_RINGING);
    								sentringing++;
    
    Mark Spencer's avatar
    Mark Spencer committed
    								ringind++;
    
    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;
    
    						case -1:
    							if (option_verbose > 2)
    								ast_verbose( VERBOSE_PREFIX_3 "%s stopped sounds\n", o->chan->name);
    							ast_indicate(in, -1);
    							break;
    
    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;
    				return NULL;
    			}
    
    Mark Spencer's avatar
    Mark Spencer committed
    			if (f && (f->frametype == AST_FRAME_DTMF) && *allowdisconnect &&
    
    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;
    				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
    	if (moh) {
    		ast_moh_stop(in);
    	} else if (ringind) {
    		ast_indicate(in, -1);
    	}
    
    
    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;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	struct ast_channel *peer;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int to;
    
    	int allowredir_in=0;
    	int allowredir_out=0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int allowdisconnect=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;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char callerid[256], *l, *n;
    	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;
    
    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 || !strlen(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);			
    		} 
    		
    
    		/* 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;
    			}
    		}
    
    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);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (!strlen(privdb) && privacy) {
    		/* 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));
    		else
    			strcpy(callerid, "");
    		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'))
    
    Mark Spencer's avatar
    Mark Spencer committed
    				allowdisconnect = tmp->allowdisconnect = 1;
                            else    allowdisconnect = tmp->allowdisconnect = 0;
    
    			if(strchr(transfer, 'g'))
    				go_on=1;
    
    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;
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (strlen(tmp->chan->call_forward)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			if (option_verbose > 2)
    				ast_verbose(VERBOSE_PREFIX_3 "Forwarding call to '%s@%s'\n", tmp->chan->call_forward, tmp->chan->context);
    
    			snprintf(tmpchan, sizeof(tmpchan),"%s@%s", tmp->chan->call_forward, tmp->chan->context);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			ast_hangup(tmp->chan);
    
    			tmp->chan = ast_request("Local", chan->nativeformats, tmpchan);
    			if (!tmp->chan) {
    				ast_log(LOG_NOTICE, "Unable to create local channel for call forward to '%s'\n", tmpchan);
    				free(tmp);
    				cur = rest;
    				continue;
    			}
    
    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.                                          */
    		if (strcasecmp(tech,"SIP")==0)
    		{
    			headp=&chan->varshead;
    			AST_LIST_TRAVERSE(headp,current,entries) {
    				if (strcasecmp(ast_var_name(current),"VXML_URL")==0)
    				{
    					newvar=ast_var_assign(ast_var_name(current),ast_var_value(current));
    
    					newheadp=&tmp->chan->varshead;
    					AST_LIST_INSERT_HEAD(newheadp,newvar,entries);
    					break;
    				}
    			}
    		}
    		/* 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-         */
    
    
    		if (strcasecmp(tech,"SIP")==0)
    		{
    			headp=&chan->varshead;
    			AST_LIST_TRAVERSE(headp,current,entries) {
    				/* Search for ALERT_INFO */
    				if (strcasecmp(ast_var_name(current),"ALERT_INFO")==0)
    				{
    					newvar=ast_var_assign(ast_var_name(current),ast_var_value(current));
    
    Mark Spencer's avatar
    Mark Spencer committed
    					newheadp=&tmp->chan->varshead;
    					AST_LIST_INSERT_HEAD(newheadp,newvar,entries);
    					break;
    				}
    			}
    		}
    		
    
    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 */
    		strcpy(tmp->chan->language, chan->language);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (!strlen(tmp->chan->musicclass))
    			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 restuls 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);
    	
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (timeout && strlen(timeout))
    
    Mark Spencer's avatar
    Mark Spencer committed
    		to = atoi(timeout) * 1000;
    	else
    		to = -1;
    
    	peer = wait_for_answer(chan, outgoing, &to, &allowredir_in, &allowredir_out, &allowdisconnect);
    
    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) {
    		/* 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
    		/* 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);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			ast_hangup(peer);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			return -1;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
     		/* JDG: sendurl */
     		if( url && strlen(url) && ast_channel_supports_html(peer) ) {
     			ast_log(LOG_DEBUG, "app_dial: sendurl=%s.\n", url);
     			ast_channel_sendurl( peer, url );
     		} /* /JDG */
    
    		if (announce && announcemsg)
    		{
    			int res2;
    			// Start autoservice on the other chan
    			res2 = ast_autoservice_start(chan);
    			// Now Stream the File
    			if (!res2)
    				res2 = ast_streamfile(peer,announcemsg,peer->language);
    			if (!res2)
    				res2 = ast_waitstream(peer,"");
    			// Ok, done. stop autoservice
    			res2 = ast_autoservice_stop(chan);
    		}
    
    		if (calldurationlimit > 0) {
    			time(&now);
    			chan->whentohangup = now + calldurationlimit;
    		}
    
    		res = ast_bridge_call(chan, peer, allowredir_in, allowredir_out, allowdisconnect);
    
    
    		if (res != AST_PBX_NO_HANGUP_PEER)
    			ast_hangup(peer);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}	
    out:
    	hanguptree(outgoing, NULL);
    	LOCAL_USER_REMOVE(u);
    
    	
    	if((go_on>0) && (!chan->_softhangup))
    	    res=0;
    	    
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return res;
    }
    
    int unload_module(void)
    {
    	STANDARD_HANGUP_LOCALUSERS;
    	return ast_unregister_application(app);
    }
    
    int load_module(void)
    {
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int res;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	res = ast_register_application(app, dial_exec, synopsis, descrip);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return res;
    
    Mark Spencer's avatar
    Mark Spencer committed
    }
    
    char *description(void)
    {
    	return tdesc;
    }
    
    int usecount(void)
    {
    	int res;
    	STANDARD_USECOUNT(res);
    	return res;
    }
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    char *key()
    {
    	return ASTERISK_GPL_KEY;
    }