Skip to content
Snippets Groups Projects
app_dial.c 47.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
     * 
    
     * Copyright (C) 1999 - 2005, Digium, Inc.
    
    Mark Spencer's avatar
    Mark Spencer committed
     *
    
     * Mark Spencer <markster@digium.com>
    
    Mark Spencer's avatar
    Mark Spencer committed
     *
     * 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"
    
    "      'd' -- allow the calling user to dial a 1 digit extension while waiting for a call to\n"
    "             be answered exiting to that extension if it exists in the context defined by\n"
    "             ${EXITCONTEXT} or the current context.\n"
    
    "      't' -- allow the called user to transfer the calling user by hitting #.\n"
    
    "      'T' -- allow the calling user to transfer the call by hitting #.\n"
    
    "      'w' -- allow the called user to write the conversation to disk via app_monitor\n"
    "      'W' -- allow the calling user to write the conversation to disk via app_monitor\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"
    
    "      'o' -- Original (inbound) Caller*ID should be placed on the outbound leg of the call\n" 
    
    "             instead of using the destination extension (old style asterisk behavior)\n"
    
    Mark Spencer's avatar
    Mark Spencer committed
    "      'r' -- indicate ringing to the calling party, pass no audio until answered.\n"
    
    "      'm[(class)]' -- provide hold music to the calling party until answered (optionally\n"
    "                      with the specified class.\n"
    
    "      'M(x[^arg]) -- Executes the macro (x with ^ delim arg list) upon connect of the call.\n"
    "                     Also, the macro can set the MACRO_RESULT variable to do the following:\n"
    "                     -- ABORT - Hangup both legs of the call.\n"
    "                     -- CONGESTION - Behave as if line congestion was encountered.\n"
    "                     -- BUSY - Behave as if a busy signal was encountered. (n+101)\n"
    "                     -- CONTINUE - Hangup the called party and continue on in the dialplan.\n"
    "                     -- GOTO:<context>^<exten>^<priority> - Transfer 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"
    
    "  If the OUTBOUND_GROUP variable is set, all peer channels created by this\n"
    "  application will be put into that group (as in SetGroup).\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"
    "";
    
    /* RetryDial App by Anthony Minessale II <anthmct@yahoo.com> Jan/2005 */
    static char *rapp = "RetryDial";
    static char *rsynopsis = "Place a call, retrying on failure allowing optional exit extension.\n";
    static char *rdescrip =
    "  RetryDial(announce|sleep|loops|Technology/resource[&Technology2/resource2...][|timeout][|options][|URL]):\n"
    "Attempt to place a call.  If no channel can be reached, play the file defined by 'announce'\n"
    "waiting 'sleep' seconds to retry the call.  If the specified number of attempts matches \n"
    "'loops' the call will continue in the dialplan.  If 'loops' is set to 0, the call will retry endlessly.\n\n"
    "While waiting, a 1 digit extension may be dialed.  If that extension exists in either\n"
    "the context defined in ${EXITCONTEXT} or the current one, The call will transfer\n"
    "to that extension immmediately.\n\n"
    
    "All arguments after 'loops' are passed directly to the Dial() application.\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. */
    
    
    #define DIAL_STILLGOING				(1 << 0)
    
    #define DIAL_ALLOWREDIRECT_IN		(1 << 1)
    #define DIAL_ALLOWREDIRECT_OUT		(1 << 2)
    #define DIAL_ALLOWDISCONNECT_IN		(1 << 3)
    #define DIAL_ALLOWDISCONNECT_OUT	(1 << 4)
    
    #define DIAL_RINGBACKONLY			(1 << 5)
    #define DIAL_MUSICONHOLD			(1 << 6)
    #define DIAL_FORCECALLERID			(1 << 7)
    #define DIAL_MONITOR_IN				(1 << 8)
    #define DIAL_MONITOR_OUT			(1 << 9)
    #define DIAL_GO_ON					(1 << 10)
    #define DIAL_HALT_ON_DTMF 			(1 << 11)
    
    #define DIAL_PRESERVE_CALLERID		(1 << 12)
    
    #define DIAL_NOFORWARDHTML			(1 << 13)
    
    Mark Spencer's avatar
    Mark Spencer committed
    struct localuser {
    	struct ast_channel *chan;
    
    	unsigned int flags;
    
    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) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		/* 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);
    	}
    }
    
    
    #define HANDLE_CAUSE(cause, chan) do { \
    
    		if (chan->cdr) \
    			ast_cdr_busy(chan->cdr); \
    
    Mark Spencer's avatar
    Mark Spencer committed
    		break; \
    
    	case AST_CAUSE_CONGESTION: \
    	case AST_CAUSE_UNREGISTERED: \
    
    		if (chan->cdr) \
    			ast_cdr_busy(chan->cdr); \
    
    Mark Spencer's avatar
    Mark Spencer committed
    		break; \
    
    Mark Spencer's avatar
    Mark Spencer committed
    		break; \
    
    } while (0)
    
    
    static int ast_onedigit_goto(struct ast_channel *chan, char *context, char exten, int pri, char *cid) 
    {
    	char rexten[2];
    	snprintf(rexten, 2, "%c", exten);
    	if (context) {
    		if (ast_exists_extension(chan, context, rexten, pri, cid)) {
    			ast_explicit_goto(chan, context, rexten, pri-1);
    			return 1;
    		}
    	} else {
    		if (ast_exists_extension(chan, chan->context, rexten, pri, cid)) {
    			ast_explicit_goto(chan, chan->context, rexten, pri-1);
    			return 1;
    		} else if (!ast_strlen_zero(chan->macrocontext)) {
    
    			if (ast_exists_extension(chan, chan->macrocontext, rexten, pri, cid)) {
    				ast_explicit_goto(chan, chan->macrocontext, rexten, pri-1);
    
    static char *get_cid_name(char *name, int namelen, struct ast_channel *chan)
    {
    	char *context;
    	char *exten;
    	if (!ast_strlen_zero(chan->macrocontext))
    		context = chan->macrocontext;
    	else
    		context = chan->context;
    
    	if (!ast_strlen_zero(chan->macroexten))
    		exten = chan->macroexten;
    	else
    		exten = chan->exten;
    
    	if (ast_get_hint(NULL, 0, name, namelen, chan, context, exten))
    		return name;
    	else
    		return "";
    }
    
    
    static void senddialevent(struct ast_channel *src, struct ast_channel *dst)
    {
    	manager_event(EVENT_FLAG_CALL, "Dial", 
    								   "Source: %s\r\n"
    								   "Destination: %s\r\n"
    								   "CallerID: %s\r\n"
    								   "CallerIDName: %s\r\n"
    								   "SrcUniqueID: %s\r\n"
    								   "DestUniqueID: %s\r\n",
    								   src->name, dst->name, src->cid.cid_num ? src->cid.cid_num : "<unknown>",
    								   src->cid.cid_name ? src->cid.cid_name : "<unknown>", src->uniqueid,
    								   dst->uniqueid);
    }
    
    
    static struct ast_channel *wait_for_answer(struct ast_channel *in, struct localuser *outgoing, int *to, struct ast_flags *peerflags, int *sentringing, char *status, size_t statussize, int busystart, int nochanstart, int congestionstart, int *result)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	struct localuser *o;
    	int found;
    	int numlines;
    
    	int numbusy = busystart;
    	int numcongestion = congestionstart;
    	int numnochan = nochanstart;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int prestart = busystart + congestionstart + nochanstart;
    
    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;
    
    	char *context = NULL;
    
    	char cidname[AST_MAX_EXTENSION];
    
    	single = (outgoing && !outgoing->next && !ast_test_flag(outgoing, DIAL_MUSICONHOLD | DIAL_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);
    	}
    	
    
    	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 = prestart;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		watchers[0] = in;
    
    Mark Spencer's avatar
    Mark Spencer committed
    			/* Keep track of important channels */
    
    			if (ast_test_flag(o, DIAL_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 (%d:%d/%d/%d)\n", numlines, numbusy, numcongestion, numnochan);
    
    					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 */
    
    				if (ast_exists_extension(in, in->context, in->exten, in->priority + 101, in->cid.cid_num)) 
    
    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 (%d:%d/%d/%d)\n", numlines, numbusy, numcongestion, numnochan);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			}
    
    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;
    
    			if (ast_test_flag(o, DIAL_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;
    
    					ast_copy_flags(peerflags, o, DIAL_ALLOWREDIRECT_IN|DIAL_ALLOWREDIRECT_OUT|DIAL_ALLOWDISCONNECT_IN|DIAL_ALLOWDISCONNECT_OUT|DIAL_NOFORWARDHTML);
    
    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 */
    
    					o->forwards++;
    					if (o->forwards < AST_MAX_FORWARDS) {
    						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);
    						/* Setup parameters */
    						o->chan = ast_request(tech, in->nativeformats, stuff, &cause);
    						if (!o->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", o->chan->name);
    						cause = AST_CAUSE_CONGESTION;
    						o->chan = NULL;
    					}
    
    						ast_clear_flag(o, DIAL_STILLGOING);	
    
    						if (o->chan->cid.cid_num)
    							free(o->chan->cid.cid_num);
    						o->chan->cid.cid_num = NULL;
    						if (o->chan->cid.cid_name)
    							free(o->chan->cid.cid_name);
    						o->chan->cid.cid_name = NULL;
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    
    						if (ast_test_flag(o, DIAL_FORCECALLERID)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    							char *newcid = NULL;
    
    							if (strlen(in->macroexten))
    								newcid = in->macroexten;
    
    Mark Spencer's avatar
    Mark Spencer committed
    								newcid = in->exten;
    
    							o->chan->cid.cid_num = strdup(newcid);
    
    							strncpy(o->chan->accountcode, winner->accountcode, sizeof(o->chan->accountcode) - 1);
    							o->chan->cdrflags = winner->cdrflags;
    
    							if (!o->chan->cid.cid_num)
    
    Mark Spencer's avatar
    Mark Spencer committed
    								ast_log(LOG_WARNING, "Out of memory\n");
    						} else {
    
    							if (in->cid.cid_num) {
    								o->chan->cid.cid_num = strdup(in->cid.cid_num);
    								if (!o->chan->cid.cid_num)
    									ast_log(LOG_WARNING, "Out of memory\n");	
    							}
    							if (in->cid.cid_name) {
    								o->chan->cid.cid_name = strdup(in->cid.cid_name);
    								if (!o->chan->cid.cid_name)
    
    							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->cid.cid_ani) {
    							if (o->chan->cid.cid_ani)
    								free(o->chan->cid.cid_ani);
    							o->chan->cid.cid_ani = malloc(strlen(in->cid.cid_ani) + 1);
    							if (o->chan->cid.cid_ani)
    								strncpy(o->chan->cid.cid_ani, in->cid.cid_ani, strlen(in->cid.cid_ani) + 1);
    
    							else
    								ast_log(LOG_WARNING, "Out of memory\n");
    
    						if (o->chan->cid.cid_rdnis) 
    							free(o->chan->cid.cid_rdnis);
    
    							o->chan->cid.cid_rdnis = strdup(in->macroexten);
    
    							o->chan->cid.cid_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);
    
    							ast_clear_flag(o, DIAL_STILLGOING);	
    
    							/* After calling, set callerid to extension */
    
    							if (!ast_test_flag(peerflags, DIAL_PRESERVE_CALLERID))
    								ast_set_callerid(o->chan, ast_strlen_zero(in->macroexten) ? in->exten : in->macroexten, get_cid_name(cidname, sizeof(cidname), in), 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;
    
    								ast_copy_flags(peerflags, o, DIAL_ALLOWREDIRECT_IN|DIAL_ALLOWREDIRECT_OUT|DIAL_ALLOWDISCONNECT_IN|DIAL_ALLOWDISCONNECT_OUT|DIAL_NOFORWARDHTML);
    
    Mark Spencer's avatar
    Mark Spencer committed
    							}
    
    							/* If call has been answered, then the eventual hangup is likely to be normal hangup */
    							in->hangupcause = AST_CAUSE_NORMAL_CLEARING;
    							o->chan->hangupcause = AST_CAUSE_NORMAL_CLEARING;
    
    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);
    
    							ast_clear_flag(o, DIAL_STILLGOING);	
    
    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);
    
    							ast_clear_flag(o, DIAL_STILLGOING);
    
    							HANDLE_CAUSE(AST_CAUSE_CONGESTION, in);
    
    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);
    
    							if (!(*sentringing) && !ast_test_flag(outgoing, DIAL_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);
    
    							if (!ast_test_flag(outgoing, DIAL_RINGBACKONLY))
    
    								ast_indicate(in, AST_CONTROL_PROGRESS);
    
    						case AST_CONTROL_HOLD:
    							if (option_verbose > 2)
    								ast_verbose(VERBOSE_PREFIX_3 "Call on %s placed on hold\n", o->chan->name);
    							ast_indicate(in, AST_CONTROL_HOLD);
    							break;
    						case AST_CONTROL_UNHOLD:
    							if (option_verbose > 2)
    								ast_verbose(VERBOSE_PREFIX_3 "Call on %s left from hold\n", o->chan->name);
    							ast_indicate(in, AST_CONTROL_UNHOLD);
    							break;
    
    Mark Spencer's avatar
    Mark Spencer committed
    						case AST_CONTROL_OFFHOOK:
    
    Mark Spencer's avatar
    Mark Spencer committed
    						case AST_CONTROL_FLASH:
    							/* Ignore going off hook and flash */
    
    Mark Spencer's avatar
    Mark Spencer committed
    							break;
    
    							if (!ast_test_flag(outgoing, DIAL_RINGBACKONLY | DIAL_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) && 
    
    								!(ast_test_flag(outgoing, DIAL_RINGBACKONLY|DIAL_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) && 
    
    								!(ast_test_flag(outgoing, DIAL_RINGBACKONLY|DIAL_MUSICONHOLD))) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    						if (ast_write(in, f))
    							ast_log(LOG_WARNING, "Unable to forward image\n");
    
    					} else if (single && (f->frametype == AST_FRAME_HTML) && !ast_test_flag(outgoing, DIAL_NOFORWARDHTML))
    						ast_channel_sendhtml(in, f->subclass, f->data, f->datalen);
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    					ast_frfree(f);
    				} else {
    
    Martin Pycko's avatar
    Martin Pycko committed
    					ast_hangup(o->chan);
    
    					ast_clear_flag(o, DIAL_STILLGOING);
    
    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)) {
    				if (ast_test_flag(peerflags, DIAL_HALT_ON_DTMF)) {
    					context = pbx_builtin_getvar_helper(in, "EXITCONTEXT");
    					if (ast_onedigit_goto(in, context, (char) f->subclass, 1, in->cid.cid_num)) {
    						if (option_verbose > 3)
    							ast_verbose(VERBOSE_PREFIX_3 "User hit %c to disconnect call.\n", f->subclass);
    						*to=0;
    						*result = f->subclass;
    						strcpy(status, "CANCEL");
    						ast_frfree(f);
    						return NULL;
    					}
    				}
    
    				if (ast_test_flag(peerflags, DIAL_ALLOWDISCONNECT_OUT) && 
    						  (f->subclass == '*')) { /* hmm it it not guarenteed to be '*' anymore. */
    					if (option_verbose > 3)
    						ast_verbose(VERBOSE_PREFIX_3 "User hit %c to disconnect call.\n", f->subclass);
    					*to=0;
    					strcpy(status, "CANCEL");
    					ast_frfree(f);
    					return NULL;
    				}
    
    Mark Spencer's avatar
    Mark Spencer committed
    			}
    
    			/* Forward HTML stuff */
    			if (single && f && (f->frametype == AST_FRAME_HTML) && !ast_test_flag(outgoing, DIAL_NOFORWARDHTML)) 
    				ast_channel_sendhtml(outgoing->chan, f->subclass, f->data, f->datalen);
    			
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    			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;
    
    
    static int dial_exec_full(struct ast_channel *chan, void *data, struct ast_flags *peerflags)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	int res=-1;
    	struct localuser *u;
    
    	char *info, *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 hasmacro = 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int privacy=0;
    
    	int announce=0;
    
    	int resetcdr=0;
    
    	int numbusy = 0;
    	int numcongestion = 0;
    	int numnochan = 0;
    	int cause;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char numsubst[AST_MAX_EXTENSION];
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char restofit[AST_MAX_EXTENSION];
    
    	char cidname[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 *url=NULL; /* JDG */
    
    	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 = NULL;
    
    	int play_to_caller=0,play_to_callee=0;
    
    	int playargs=0, sentringing=0, moh=0;
    
    	char *mohclass = NULL;
    
    	char *outbound_group = NULL;
    
    	char *macro_result = NULL, *macro_transfer_dest = NULL;
    
    	int digit = 0, result = 0;
    
    	time_t start_time, answer_time, end_time;
    
    	struct ast_app *app = NULL;
    
    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;
    	}
    
    
    	if (!(info = ast_strdupa(data))) {
    		ast_log(LOG_WARNING, "Unable to dupe data :(\n");
    		return -1;
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	LOCAL_USER_ADD(u);
    	
    
    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++;
    
    					if (option_debug)
    						ast_log(LOG_DEBUG, "DIAL WITH URL=%s_\n", url);
    
    Mark Spencer's avatar
    Mark Spencer committed
    				} else 
    
    					if (option_debug) {
    						ast_log(LOG_DEBUG, "SIMPLE DIAL (NO URL)\n");
    					}
    
    Mark Spencer's avatar
    Mark Spencer committed
    				/* /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 != ')')) 
    
    James Golovich's avatar
    James Golovich committed
    				*(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, ":");
    
    James Golovich's avatar
    James Golovich committed
    						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;
    
    			macroname = ast_strdupa(mac + 2);
    
    			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;
    			}
    		}
    
    		/* Get music on hold class */
    		if ((mac = strstr(transfer, "m("))) {
    			mohclass = ast_strdupa(mac + 2);
    			mac++; /* Leave the "m" in the string */
    			while (*mac && (*mac != ')'))
    				*(mac++) = 'X';
    
    			if (*mac) {
    
    				mac = strchr(mohclass, ')');
    				if (mac)
    					*mac = '\0';
    				else {
    					ast_log(LOG_WARNING, "Music on hold class specified without trailing ')'\n");
    					mohclass = NULL;
    				}
    			} else {
    
    				ast_log(LOG_WARNING, "Could not find music on hold class to use, assuming default.\n");
    				mohclass=NULL;
    			}
    		}
    
    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 != ')')) 
    
    Mark Spencer's avatar
    Mark Spencer committed
    				*(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) {
    
    		l = chan->cid.cid_num;
    		if (!l)
    
    Mark Spencer's avatar
    Mark Spencer committed
    			l = "";
    		ast_log(LOG_NOTICE, "Privacy DB is '%s', privacy is %d, clid is '%s'\n", privdb, privacy, l);
    	}
    
    
    	/* If a channel group has been specified, get it for use when we create peer channels */
    	outbound_group = pbx_builtin_getvar_helper(chan, "OUTBOUND_GROUP");
    
    
    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) {
    
    			ast_set2_flag(tmp, strchr(transfer, 't'), DIAL_ALLOWREDIRECT_IN);
    
    			ast_set2_flag(tmp, strchr(transfer, 'T'), DIAL_ALLOWREDIRECT_OUT);
    
    			ast_set2_flag(tmp, strchr(transfer, 'r'), DIAL_RINGBACKONLY);	
    			ast_set2_flag(tmp, strchr(transfer, 'm'), DIAL_MUSICONHOLD);	
    			ast_set2_flag(tmp, strchr(transfer, 'H'), DIAL_ALLOWDISCONNECT_OUT);	
    
    			ast_set2_flag(peerflags, strchr(transfer, 'H'), DIAL_ALLOWDISCONNECT_OUT);	
    
    			ast_set2_flag(tmp, strchr(transfer, 'h'), DIAL_ALLOWDISCONNECT_IN);
    
    			ast_set2_flag(peerflags, strchr(transfer, 'h'), DIAL_ALLOWDISCONNECT_IN);
    
    			ast_set2_flag(tmp, strchr(transfer, 'f'), DIAL_FORCECALLERID);	
    
    			ast_set2_flag(tmp, url, DIAL_NOFORWARDHTML);	
    
    			ast_set2_flag(peerflags, strchr(transfer, 'w'), DIAL_MONITOR_IN);	
    			ast_set2_flag(peerflags, strchr(transfer, 'W'), DIAL_MONITOR_OUT);	
    			ast_set2_flag(peerflags, strchr(transfer, 'd'), DIAL_HALT_ON_DTMF);	
    
    Mark Spencer's avatar
    Mark Spencer committed
    			ast_set2_flag(peerflags, strchr(transfer, 'g'), DIAL_GO_ON);	
    
    			ast_set2_flag(peerflags, strchr(transfer, 'o'), DIAL_PRESERVE_CALLERID);	
    
    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 */
    
    		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 */
    
    Mark Spencer's avatar
    Mark Spencer committed
    			ast_log(LOG_NOTICE, "Unable to create channel of type '%s' (cause %d)\n", tech, cause);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			cur = rest;
    
    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 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";
    			}
    
    			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;
    			}
    
    Mark Spencer's avatar
    Mark Spencer committed
    				cur = rest;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    
    		/* 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);
    		tmp->chan->cid.cid_num = NULL;
    		if (tmp->chan->cid.cid_name)
    			free(tmp->chan->cid.cid_name);
    		tmp->chan->cid.cid_name = NULL;
    		if (tmp->chan->cid.cid_ani)
    			free(tmp->chan->cid.cid_ani);
    		tmp->chan->cid.cid_ani = NULL;
    
    		if (chan->cid.cid_num) 
    			tmp->chan->cid.cid_num = strdup(chan->cid.cid_num);
    		if (chan->cid.cid_name) 
    			tmp->chan->cid.cid_name = strdup(chan->cid.cid_name);
    		if (chan->cid.cid_ani) 
    			tmp->chan->cid.cid_ani = strdup(chan->cid.cid_ani);