Skip to content
Snippets Groups Projects
pbx.c 158 KiB
Newer Older
  • Learn to ignore specific revisions
  • Mark Spencer's avatar
    Mark Spencer committed
    		/* In order to do it when the channel doesn't really exist within
    		   the PBX, we have to make a new channel, masquerade, and start the PBX
    		   at the new location */
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    		struct ast_channel *tmpchan = ast_channel_alloc(0);
    		if (!tmpchan)
    			res = -1;
    		else {
    
    			ast_string_field_build(tmpchan, name, "AsyncGoto/%s", chan->name);
    
    			ast_setstate(tmpchan, chan->_state);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			/* Make formats okay */
    			tmpchan->readformat = chan->readformat;
    			tmpchan->writeformat = chan->writeformat;
    			/* Setup proper location */
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    				S_OR(context, chan->context), S_OR(exten, chan->exten), priority);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			/* Masquerade into temp channel */
    			ast_channel_masquerade(tmpchan, chan);
    
    Malcolm Davenport's avatar
    Malcolm Davenport committed
    		
    
    			/* Grab the locks and get going */
    
    			ast_channel_lock(tmpchan);
    
    			ast_channel_unlock(tmpchan);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			/* Start the PBX going on our stolen channel */
    			if (ast_pbx_start(tmpchan)) {
    				ast_log(LOG_WARNING, "Unable to start PBX on %s\n", tmpchan->name);
    				ast_hangup(tmpchan);
    				res = -1;
    			}
    		}
    	}
    
    	ast_channel_unlock(chan);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return res;
    }
    
    
    int ast_async_goto_by_name(const char *channame, const char *context, const char *exten, int priority)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	struct ast_channel *chan;
    
    	chan = ast_get_channel_by_name_locked(channame);
    
    	if (chan) {
    		res = ast_async_goto(chan, context, exten, priority);
    
    		ast_channel_unlock(chan);
    
    /*! \brief copy a string skipping whitespace */
    
    static int ext_strncpy(char *dst, const char *src, int len)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	int count=0;
    
    Russell Bryant's avatar
    Russell Bryant committed
    	while (*src && (count < len - 1)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		switch(*src) {
    		case ' ':
    
    			/*	otherwise exten => [a-b],1,... doesn't work */
    			/*		case '-': */
    
    Mark Spencer's avatar
    Mark Spencer committed
    			/* Ignore */
    			break;
    		default:
    			*dst = *src;
    			dst++;
    		}
    		src++;
    		count++;
    	}
    	*dst = '\0';
    
    /*! \brief
     * Main interface to add extensions to the list for out context.
     *
     * We sort extensions in order of matching preference, so that we can
     * stop the search as soon as we find a suitable match.
     * This ordering also takes care of wildcards such as '.' (meaning
     * "one or more of any character") and '!' (which is 'earlymatch',
     * meaning "zero or more of any character" but also impacts the
     * return value from CANMATCH and EARLYMATCH.
     * 
     * The extension match rules defined in the devmeeting 2006.05.05 are
     * quite simple: WE SELECT THE LONGEST MATCH.
     * In detail, "longest" means the number of matched characters in
     * the extension. In case of ties (e.g. _XXX and 333) in the length
     * of a pattern, we give priority to entries with the smallest cardinality
     * (e.g, [5-9] comes before [2-8] before the former has only 5 elements,
     * while the latter has 7, etc.
     * In case of same cardinality, the first element in the range counts.
     * If we still have a tie, any final '!' will make this as a possibly
     * less specific pattern.
     *
    
    Mark Spencer's avatar
    Mark Spencer committed
     * EBUSY - can't lock
     * EEXIST - extension with the same priority exist and no replace is set
     *
     */
    
    Mark Spencer's avatar
    Mark Spencer committed
    int ast_add_extension2(struct ast_context *con,
    
    	int replace, const char *extension, int priority, const char *label, const char *callerid,
    	const char *application, void *data, void (*datad)(void *),
    	const char *registrar)
    
    Mark Spencer's avatar
    Mark Spencer committed
    #define LOG do { 	if (option_debug) {\
    		if (tmp->matchcid) { \
    			ast_log(LOG_DEBUG, "Added extension '%s' priority %d (CID match '%s') to %s\n", tmp->exten, tmp->priority, tmp->cidmatch, con->name); \
    		} else { \
    			ast_log(LOG_DEBUG, "Added extension '%s' priority %d to %s\n", tmp->exten, tmp->priority, con->name); \
    		} \
    	} else if (option_verbose > 2) { \
    		if (tmp->matchcid) { \
    			ast_verbose( VERBOSE_PREFIX_3 "Added extension '%s' priority %d (CID match '%s')to %s\n", tmp->exten, tmp->priority, tmp->cidmatch, con->name); \
    		} else {  \
    			ast_verbose( VERBOSE_PREFIX_3 "Added extension '%s' priority %d to %s\n", tmp->exten, tmp->priority, con->name); \
    		} \
    	} } while(0)
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    	/*
    	 * This is a fairly complex routine.  Different extensions are kept
    	 * in order by the extension number.  Then, extensions of different
    	 * priorities (same extension) are kept in a list, according to the
    	 * peer pointer.
    	 */
    	struct ast_exten *tmp, *e, *el = NULL, *ep = NULL;
    	int res;
    
    	char expand_buf[VAR_BUF_SIZE] = { 0, };
    
    
    	/* if we are adding a hint, and there are global variables, and the hint
    	   contains variable references, then expand them
    	*/
    
    	ast_mutex_lock(&globalslock);
    
    	if ((priority == PRIORITY_HINT) && AST_LIST_FIRST(&globals) && strstr(application, "${")) {
    		pbx_substitute_variables_varshead(&globals, application, expand_buf, sizeof(expand_buf));
    		application = expand_buf;
    	}
    
    	ast_mutex_unlock(&globalslock);
    
    	length = sizeof(struct ast_exten);
    	length += strlen(extension) + 1;
    	length += strlen(application) + 1;
    	if (label)
    		length += strlen(label) + 1;
    	if (callerid)
    		length += strlen(callerid) + 1;
    	else
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    		length ++;	/* just the '\0' */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Be optimistic:  Build the extension structure first */
    
    	if (datad == NULL)
    		datad = null_datad;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return -1;
    
    		
    	/* use p as dst in assignments, as the fields are const char * */
    	p = tmp->stuff;
    	if (label) {
    		tmp->label = p;
    		strcpy(p, label);
    		p += strlen(label) + 1;
    	}
    	tmp->exten = p;
    	p += ext_strncpy(p, extension, strlen(extension) + 1) + 1;
    	tmp->priority = priority;
    	tmp->cidmatch = p;	/* but use p for assignments below */
    	if (callerid) {
    		p += ext_strncpy(p, callerid, strlen(callerid) + 1) + 1;
    		tmp->matchcid = 1;
    	} else {
    		*p++ = '\0';
    		tmp->matchcid = 0;
    	}
    	tmp->app = p;
    	strcpy(p, application);
    	tmp->parent = con;
    	tmp->data = data;
    	tmp->datad = datad;
    	tmp->registrar = registrar;
    	
    
    Russell Bryant's avatar
    Russell Bryant committed
    	for (e = con->root; e; e = e->next) {
    
    		/* Make sure patterns are always last! */
    		if ((e->exten[0] != '_') && (extension[0] == '_'))
    			res = -1;
    		else if ((e->exten[0] == '_') && (extension[0] != '_'))
    			res = 1;
    		else
    			res= strcmp(e->exten, extension);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (!res) {
    			if (!e->matchcid && !tmp->matchcid)
    				res = 0;
    			else if (tmp->matchcid && !e->matchcid)
    				res = 1;
    			else if (e->matchcid && !tmp->matchcid)
    				res = -1;
    			else
    				res = strcasecmp(e->cidmatch, tmp->cidmatch);
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (res == 0) {
    			/* We have an exact match, now we find where we are
    			   and be sure there's no duplicates */
    			while(e) {
    				if (e->priority == tmp->priority) {
    					/* Can't have something exactly the same.  Is this a
    					   replacement?  If so, replace, otherwise, bonk. */
    					if (replace) {
    						if (ep) {
    							/* We're in the peer list, insert ourselves */
    							ep->peer = tmp;
    							tmp->peer = e->peer;
    						} else if (el) {
    							/* We're the first extension. Take over e's functions */
    							el->next = tmp;
    							tmp->next = e->next;
    							tmp->peer = e->peer;
    						} else {
    							/* We're the very first extension.  */
    							con->root = tmp;
    							tmp->next = e->next;
    							tmp->peer = e->peer;
    						}
    
    						if (tmp->priority == PRIORITY_HINT)
    						    ast_change_hint(e,tmp);
    
    Mark Spencer's avatar
    Mark Spencer committed
    						/* Destroy the old one */
    						e->datad(e->data);
    						free(e);
    
    						if (tmp->priority == PRIORITY_HINT)
    						    ast_change_hint(e, tmp);
    
    Mark Spencer's avatar
    Mark Spencer committed
    						/* And immediately return success. */
    						LOG;
    						return 0;
    					} else {
    						ast_log(LOG_WARNING, "Unable to register extension '%s', priority %d in '%s', already in use\n", tmp->exten, tmp->priority, con->name);
    						tmp->datad(tmp->data);
    						free(tmp);
    
    Mark Spencer's avatar
    Mark Spencer committed
    						errno = EEXIST;
    
    Mark Spencer's avatar
    Mark Spencer committed
    						return -1;
    					}
    				} else if (e->priority > tmp->priority) {
    					/* Slip ourselves in just before e */
    					if (ep) {
    						/* Easy enough, we're just in the peer list */
    						ep->peer = tmp;
    						tmp->peer = e;
    					} else if (el) {
    						/* We're the first extension in this peer list */
    						el->next = tmp;
    						tmp->next = e->next;
    						e->next = NULL;
    						tmp->peer = e;
    					} else {
    						/* We're the very first extension altogether */
    
    Mark Spencer's avatar
    Mark Spencer committed
    						/* Con->root must always exist or we couldn't get here */
    
    Mark Spencer's avatar
    Mark Spencer committed
    						con->root = tmp;
    					}
    
    Mark Spencer's avatar
    Mark Spencer committed
    					/* And immediately return success. */
    
    					if (tmp->priority == PRIORITY_HINT)
    
    						 ast_add_hint(tmp);
    
    Mark Spencer's avatar
    Mark Spencer committed
    					LOG;
    					return 0;
    				}
    				ep = e;
    				e = e->peer;
    			}
    			/* If we make it here, then it's time for us to go at the very end.
    			   ep *must* be defined or we couldn't have gotten here. */
    			ep->peer = tmp;
    
    			if (tmp->priority == PRIORITY_HINT)
    
    				ast_add_hint(tmp);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			/* And immediately return success. */
    			LOG;
    			return 0;
    				
    		} else if (res > 0) {
    			/* Insert ourselves just before 'e'.  We're the first extension of
    			   this kind */
    			tmp->next = e;
    			if (el) {
    				/* We're in the list somewhere */
    				el->next = tmp;
    			} else {
    				/* We're at the top of the list */
    				con->root = tmp;
    			}
    
    			if (tmp->priority == PRIORITY_HINT)
    
    				ast_add_hint(tmp);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			/* And immediately return success. */
    			LOG;
    			return 0;
    		}			
    			
    		el = e;
    	}
    	/* If we fall all the way through to here, then we need to be on the end. */
    	if (el)
    		el->next = tmp;
    	else
    		con->root = tmp;
    
    	if (tmp->priority == PRIORITY_HINT)
    
    		ast_add_hint(tmp);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	LOG;
    	return 0;	
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    struct async_stat {
    	pthread_t p;
    	struct ast_channel *chan;
    
    	char context[AST_MAX_CONTEXT];
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char exten[AST_MAX_EXTENSION];
    	int priority;
    	int timeout;
    	char app[AST_MAX_EXTENSION];
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char appdata[1024];
    
    Mark Spencer's avatar
    Mark Spencer committed
    };
    
    static void *async_wait(void *data) 
    {
    	struct async_stat *as = data;
    	struct ast_channel *chan = as->chan;
    	int timeout = as->timeout;
    	int res;
    	struct ast_frame *f;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	struct ast_app *app;
    
    Russell Bryant's avatar
    Russell Bryant committed
    	while (timeout && (chan->_state != AST_STATE_UP)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		res = ast_waitfor(chan, timeout);
    		if (res < 1) 
    			break;
    		if (timeout > -1)
    			timeout = res;
    		f = ast_read(chan);
    		if (!f)
    			break;
    		if (f->frametype == AST_FRAME_CONTROL) {
    			if ((f->subclass == AST_CONTROL_BUSY)  ||
    				(f->subclass == AST_CONTROL_CONGESTION) )
    					break;
    		}
    		ast_frfree(f);
    	}
    	if (chan->_state == AST_STATE_UP) {
    
    		if (!ast_strlen_zero(as->app)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			app = pbx_findapp(as->app);
    			if (app) {
    				if (option_verbose > 2)
    
    					ast_verbose(VERBOSE_PREFIX_3 "Launching %s(%s) on %s\n", as->app, as->appdata, chan->name);
    
    				pbx_exec(chan, app, as->appdata);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			} else
    				ast_log(LOG_WARNING, "No such application '%s'\n", as->app);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		} else {
    
    			if (!ast_strlen_zero(as->context))
    
    				ast_copy_string(chan->context, as->context, sizeof(chan->context));
    
    			if (!ast_strlen_zero(as->exten))
    
    				ast_copy_string(chan->exten, as->exten, sizeof(chan->exten));
    
    Mark Spencer's avatar
    Mark Spencer committed
    			if (as->priority > 0)
    				chan->priority = as->priority;
    			/* Run the PBX */
    			if (ast_pbx_run(chan)) {
    
    				ast_log(LOG_ERROR, "Failed to start PBX on %s\n", chan->name);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			} else {
    				/* PBX will have taken care of this */
    				chan = NULL;
    			}
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    			
    	}
    	free(as);
    	if (chan)
    		ast_hangup(chan);
    	return NULL;
    }
    
    
    /*! Function to post an empty cdr after a spool call fails.
    
     *  This function posts an empty cdr for a failed spool call
    
     *
     */
    int ast_pbx_outgoing_cdr_failed(void)
    {
    	/* allocate a channel */
    	struct ast_channel *chan = ast_channel_alloc(0);
    
    		return -1;  /* failure */
    
    	chan->cdr = ast_cdr_alloc();   /* allocate a cdr for the channel */
    
    
    Russell Bryant's avatar
    Russell Bryant committed
    	if (!chan->cdr) {
    
    		/* allocation of the cdr failed */
    		ast_channel_free(chan);   /* free the channel */
    		return -1;                /* return failure */
    	}
    	
    	/* allocation of the cdr was successful */
    	ast_cdr_init(chan->cdr, chan);  /* initilize our channel's cdr */
    	ast_cdr_start(chan->cdr);       /* record the start and stop time */
    	ast_cdr_end(chan->cdr);
    	ast_cdr_failed(chan->cdr);      /* set the status to failed */
    
    	ast_cdr_detach(chan->cdr);      /* post and free the record */
    
    	ast_channel_free(chan);         /* free the channel */
    	
    	return 0;  /* success */
    }
    
    
    int ast_pbx_outgoing_exten(const char *type, int format, void *data, int timeout, const char *context, const char *exten, int priority, int *reason, int sync, const char *cid_num, const char *cid_name, struct ast_variable *vars, const char *account, struct ast_channel **channel)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	struct ast_channel *chan;
    	struct async_stat *as;
    
    	int res = -1, cdr_res = -1;
    
    	pthread_attr_t attr;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (sync) {
    
    		chan = __ast_request_and_dial(type, format, data, timeout, reason, cid_num, cid_name, &oh);
    
    		if (channel) {
    			*channel = chan;
    			if (chan)
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (chan) {
    
    			if (chan->cdr) { /* check if the channel already has a cdr record, if not give it one */
    
    				ast_log(LOG_WARNING, "%s already has a call record??\n", chan->name);
    			} else {
    				chan->cdr = ast_cdr_alloc();   /* allocate a cdr for the channel */
    
    					/* allocation of the cdr failed */
    					free(chan->pbx);
    
    				}
    				/* allocation of the cdr was successful */
    				ast_cdr_init(chan->cdr, chan);  /* initilize our channel's cdr */
    				ast_cdr_start(chan->cdr);
    			}
    
    Mark Spencer's avatar
    Mark Spencer committed
    			if (chan->_state == AST_STATE_UP) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    				if (option_verbose > 3)
    					ast_verbose(VERBOSE_PREFIX_4 "Channel %s was answered.\n", chan->name);
    
    Mark Spencer's avatar
    Mark Spencer committed
    				if (sync > 1) {
    
    						ast_channel_unlock(chan);
    
    Mark Spencer's avatar
    Mark Spencer committed
    					if (ast_pbx_run(chan)) {
    
    						ast_log(LOG_ERROR, "Unable to run PBX on %s\n", chan->name);
    
    						if (channel)
    							*channel = NULL;
    
    Mark Spencer's avatar
    Mark Spencer committed
    						ast_hangup(chan);
    						res = -1;
    					}
    				} else {
    					if (ast_pbx_start(chan)) {
    
    						ast_log(LOG_ERROR, "Unable to start PBX on %s\n", chan->name);
    
    						if (channel) {
    
    							ast_channel_unlock(chan);
    
    Mark Spencer's avatar
    Mark Spencer committed
    						ast_hangup(chan);
    						res = -1;
    					} 
    				}
    
    Mark Spencer's avatar
    Mark Spencer committed
    			} else {
    				if (option_verbose > 3)
    					ast_verbose(VERBOSE_PREFIX_4 "Channel %s was never answered.\n", chan->name);
    
    
    				if(chan->cdr) { /* update the cdr */
    					/* here we update the status of the call, which sould be busy.
    					 * if that fails then we set the status to failed */
    
    					if (ast_cdr_disposition(chan->cdr, chan->hangupcause))
    
    						ast_cdr_failed(chan->cdr);
    				}
    			
    
    				if (channel) {
    
    					ast_channel_unlock(chan);
    
    Mark Spencer's avatar
    Mark Spencer committed
    				ast_hangup(chan);
    			}
    
    		if (res < 0) { /* the call failed for some reason */
    
    			if (*reason == 0) { /* if the call failed (not busy or no answer)
    
    				            * update the cdr with the failed message */
    				cdr_res = ast_pbx_outgoing_cdr_failed();
    
    				if (cdr_res != 0) {
    					res = cdr_res;
    					goto outgoing_exten_cleanup;
    				}
    
    Martin Pycko's avatar
     
    Martin Pycko committed
    			/* create a fake channel and execute the "failed" extension (if it exists) within the requested context */
    			/* check if "failed" exists */
    			if (ast_exists_extension(chan, context, "failed", 1, NULL)) {
    				chan = ast_channel_alloc(0);
    
    					ast_string_field_set(chan, name, "OutgoingSpoolFailed");
    
    						ast_copy_string(chan->context, context, sizeof(chan->context));
    
    					set_ext_pri(chan, "failed", 1);
    
    					if (account)
    						ast_cdr_setaccount(chan, account);
    
    Martin Pycko's avatar
     
    Martin Pycko committed
    					ast_pbx_run(chan);	
    
    Martin Pycko's avatar
     
    Martin Pycko committed
    			}
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    	} else {
    
    		chan = ast_request_and_dial(type, format, data, timeout, reason, cid_num, cid_name);
    
    		if (channel) {
    			*channel = chan;
    			if (chan)
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (!chan) {
    			free(as);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    		as->chan = chan;
    
    		ast_copy_string(as->context, context, sizeof(as->context));
    
    		set_ext_pri(as->chan,  exten, priority);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		as->timeout = timeout;
    
    		if (account)
    			ast_cdr_setaccount(chan, account);
    
    		pthread_attr_init(&attr);
    		pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
    
    		if (ast_pthread_create(&as->p, &attr, async_wait, as)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			ast_log(LOG_WARNING, "Failed to start async wait\n");
    			free(as);
    
    			if (channel) {
    
    				ast_channel_unlock(chan);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			ast_hangup(chan);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    		res = 0;
    	}
    
    outgoing_exten_cleanup:
    	ast_variables_destroy(vars);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return res;
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    struct app_tmp {
    	char app[256];
    	char data[256];
    	struct ast_channel *chan;
    	pthread_t t;
    };
    
    
    /*! \brief run the application and free the descriptor once done */
    
    Mark Spencer's avatar
    Mark Spencer committed
    static void *ast_pbx_run_app(void *data)
    {
    	struct app_tmp *tmp = data;
    	struct ast_app *app;
    	app = pbx_findapp(tmp->app);
    	if (app) {
    		if (option_verbose > 3)
    
    			ast_verbose(VERBOSE_PREFIX_4 "Launching %s(%s) on %s\n", tmp->app, tmp->data, tmp->chan->name);
    
    		pbx_exec(tmp->chan, app, tmp->data);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	} else
    		ast_log(LOG_WARNING, "No such application '%s'\n", tmp->app);
    	ast_hangup(tmp->chan);
    	free(tmp);
    	return NULL;
    }
    
    
    int ast_pbx_outgoing_app(const char *type, int format, void *data, int timeout, const char *app, const char *appdata, int *reason, int sync, const char *cid_num, const char *cid_name, struct ast_variable *vars, const char *account, struct ast_channel **locked_channel)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	struct ast_channel *chan;
    	struct app_tmp *tmp;
    
    	int res = -1, cdr_res = -1;
    
    	pthread_attr_t attr;
    
    	oh.vars = vars;
    	oh.account = account;	
    
    	if (locked_channel) 
    		*locked_channel = NULL;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (sync) {
    
    		chan = __ast_request_and_dial(type, format, data, timeout, reason, cid_num, cid_name, &oh);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (chan) {
    
    			if (chan->cdr) { /* check if the channel already has a cdr record, if not give it one */
    
    				ast_log(LOG_WARNING, "%s already has a call record??\n", chan->name);
    			} else {
    				chan->cdr = ast_cdr_alloc();   /* allocate a cdr for the channel */
    				if(!chan->cdr) {
    					/* allocation of the cdr failed */
    					free(chan->pbx);
    
    				}
    				/* allocation of the cdr was successful */
    				ast_cdr_init(chan->cdr, chan);  /* initilize our channel's cdr */
    				ast_cdr_start(chan->cdr);
    			}
    
    			if (account)
    				ast_cdr_setaccount(chan, account);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			if (chan->_state == AST_STATE_UP) {
    				res = 0;
    				if (option_verbose > 3)
    					ast_verbose(VERBOSE_PREFIX_4 "Channel %s was answered.\n", chan->name);
    
    				tmp = ast_calloc(1, sizeof(*tmp));
    				if (!tmp)
    					res = -1;
    				else {
    
    					ast_copy_string(tmp->app, app, sizeof(tmp->app));
    
    					if (appdata)
    						ast_copy_string(tmp->data, appdata, sizeof(tmp->data));
    
    Mark Spencer's avatar
    Mark Spencer committed
    					tmp->chan = chan;
    					if (sync > 1) {
    
    						if (locked_channel)
    
    							ast_channel_unlock(chan);
    
    Mark Spencer's avatar
    Mark Spencer committed
    						ast_pbx_run_app(tmp);
    					} else {
    
    						pthread_attr_init(&attr);
    						pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
    
    						if (ast_pthread_create(&tmp->t, &attr, ast_pbx_run_app, tmp)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    							ast_log(LOG_WARNING, "Unable to spawn execute thread on %s: %s\n", chan->name, strerror(errno));
    							free(tmp);
    
    								ast_channel_unlock(chan);
    
    Mark Spencer's avatar
    Mark Spencer committed
    							ast_hangup(chan);
    							res = -1;
    
    						} else {
    							if (locked_channel) 
    								*locked_channel = chan;
    
    Mark Spencer's avatar
    Mark Spencer committed
    						}
    					}
    				}
    			} else {
    				if (option_verbose > 3)
    					ast_verbose(VERBOSE_PREFIX_4 "Channel %s was never answered.\n", chan->name);
    
    				if (chan->cdr) { /* update the cdr */
    
    					/* here we update the status of the call, which sould be busy.
    					 * if that fails then we set the status to failed */
    
    					if (ast_cdr_disposition(chan->cdr, chan->hangupcause))
    
    						ast_cdr_failed(chan->cdr);
    				}
    
    Mark Spencer's avatar
    Mark Spencer committed
    				ast_hangup(chan);
    			}
    		}
    
    		if (res < 0) { /* the call failed for some reason */
    			if (*reason == 0) { /* if the call failed (not busy or no answer)
    
    				            * update the cdr with the failed message */
    				cdr_res = ast_pbx_outgoing_cdr_failed();
    
    				if (cdr_res != 0) {
    					res = cdr_res;
    					goto outgoing_app_cleanup;
    				}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	} else {
    
    		chan = __ast_request_and_dial(type, format, data, timeout, reason, cid_num, cid_name, &oh);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (!chan) {
    			free(as);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    		as->chan = chan;
    
    		ast_copy_string(as->app, app, sizeof(as->app));
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (appdata)
    
    			ast_copy_string(as->appdata,  appdata, sizeof(as->appdata));
    
    Mark Spencer's avatar
    Mark Spencer committed
    		as->timeout = timeout;
    
    		if (account)
    			ast_cdr_setaccount(chan, account);
    
    		/* Start a new thread, and get something handling this channel. */
    		pthread_attr_init(&attr);
    		pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
    
    		if (ast_pthread_create(&as->p, &attr, async_wait, as)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			ast_log(LOG_WARNING, "Failed to start async wait\n");
    			free(as);
    
    				ast_channel_unlock(chan);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			ast_hangup(chan);
    
    		} else {
    			if (locked_channel)
    				*locked_channel = chan;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    		res = 0;
    	}
    
    outgoing_app_cleanup:
    	ast_variables_destroy(vars);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return res;
    }
    
    
    static void destroy_exten(struct ast_exten *e)
    {
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (e->priority == PRIORITY_HINT)
    		ast_remove_hint(e);
    
    
    void __ast_context_destroy(struct ast_context *con, const char *registrar)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	struct ast_context *tmp, *tmpl=NULL;
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    	struct ast_include *tmpi;
    
    	struct ast_exten *e, *el, *en;
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    	struct ast_ignorepat *ipi;
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    	for (tmp = contexts; tmp; ) {
    		struct ast_context *next;	/* next starting point */
    		for (; tmp; tmpl = tmp, tmp = tmp->next) {
    			if ( (!registrar || !strcasecmp(registrar, tmp->registrar)) &&
    			     (!con || !strcasecmp(tmp->name, con->name)) )
    				break;	/* found it */
    		}
    		if (!tmp)	/* not found, we are done */
    			break;
    		ast_mutex_lock(&tmp->lock);
    		next = tmp->next;
    		if (tmpl)
    			tmpl->next = next;
    		else
    			contexts = next;
    		/* Okay, now we're safe to let it go -- in a sense, we were
    		   ready to let it go as soon as we locked it. */
    		ast_mutex_unlock(&tmp->lock);
    		for (tmpi = tmp->includes; tmpi; ) { /* Free includes */
    			struct ast_include *tmpil = tmpi;
    			tmpi = tmpi->next;
    			free(tmpil);
    		}
    		for (ipi = tmp->ignorepats; ipi; ) { /* Free ignorepats */
    			struct ast_ignorepat *ipl = ipi;
    			ipi = ipi->next;
    			free(ipl);
    		}
    		while ((sw = AST_LIST_REMOVE_HEAD(&tmp->alts, list)))
    			free(sw);
    		for (e = tmp->root; e;) {
    			for (en = e->peer; en;) {
    				el = en;
    				en = en->peer;
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    			el = e;
    			e = e->next;
    			destroy_exten(el);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    		ast_mutex_destroy(&tmp->lock);
    		free(tmp);
    		/* if we have a specific match, we are done, otherwise continue */
    		tmp = con ? NULL : next;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    void ast_context_destroy(struct ast_context *con, const char *registrar)
    
    static void wait_for_hangup(struct ast_channel *chan, void *data)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	int res;
    	struct ast_frame *f;
    
    	if (ast_strlen_zero(data) || (sscanf(data, "%d", &waittime) != 1) || (waittime < 0))
    
    		waittime = -1;
    	if (waittime > -1) {
    		ast_safe_sleep(chan, waittime * 1000);
    	} else do {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		res = ast_waitfor(chan, -1);
    		if (res < 0)
    			return;
    		f = ast_read(chan);
    		if (f)
    			ast_frfree(f);
    	} while(f);
    }
    
    
    /*!
     * \ingroup applications
     */
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int pbx_builtin_progress(struct ast_channel *chan, void *data)
    {
    	ast_indicate(chan, AST_CONTROL_PROGRESS);
    	return 0;
    }
    
    
    /*!
     * \ingroup applications
     */
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int pbx_builtin_ringing(struct ast_channel *chan, void *data)
    {
    	ast_indicate(chan, AST_CONTROL_RINGING);
    	return 0;
    }
    
    
    /*!
     * \ingroup applications
     */
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int pbx_builtin_busy(struct ast_channel *chan, void *data)
    {
    	ast_indicate(chan, AST_CONTROL_BUSY);		
    
    	wait_for_hangup(chan, data);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return -1;
    }
    
    
    /*!
     * \ingroup applications
     */
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int pbx_builtin_congestion(struct ast_channel *chan, void *data)
    {
    	ast_indicate(chan, AST_CONTROL_CONGESTION);
    
    	wait_for_hangup(chan, data);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return -1;
    }
    
    
    /*!
     * \ingroup applications
     */
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int pbx_builtin_answer(struct ast_channel *chan, void *data)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    	if (chan->_state == AST_STATE_UP)
    		delay = 0;
    
    	else if (!ast_strlen_zero(data))
    		delay = atoi(data);
    
    
    	res = ast_answer(chan);
    	if (res)
    		return res;
    
    	if (delay)
    		res = ast_safe_sleep(chan, delay);
    
    AST_APP_OPTIONS(resetcdr_opts, {
    	AST_APP_OPTION('w', AST_CDR_FLAG_POSTED),
    	AST_APP_OPTION('a', AST_CDR_FLAG_LOCKED),
    	AST_APP_OPTION('v', AST_CDR_FLAG_KEEP_VARS),
    });
    
    
    /*!
     * \ingroup applications
     */
    
    static int pbx_builtin_resetcdr(struct ast_channel *chan, void *data)
    {
    
    	char *args;
    	struct ast_flags flags = { 0 };
    	
    	if (!ast_strlen_zero(data)) {
    
    		if (!(args = ast_strdupa(data)))
    
    		ast_app_parse_options(resetcdr_opts, &flags, NULL, args);
    	}
    
    	ast_cdr_reset(chan->cdr, &flags);
    
    
    /*!
     * \ingroup applications
     */
    
    static int pbx_builtin_setamaflags(struct ast_channel *chan, void *data)
    {
    	/* Copy the AMA Flags as specified */
    
    	ast_cdr_setamaflags(chan, data ? data : "");
    
    /*!
     * \ingroup applications
     */
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int pbx_builtin_hangup(struct ast_channel *chan, void *data)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	/* Just return non-zero and it will hang up */
    
    	if (!chan->hangupcause)
    		chan->hangupcause = AST_CAUSE_NORMAL_CLEARING;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return -1;
    }
    
    
    /*!
     * \ingroup applications
     */
    
    static int pbx_builtin_gotoiftime(struct ast_channel *chan, void *data)
    {
    
    	char *s, *ts;
    
    	struct ast_timing timing;
    
    		ast_log(LOG_WARNING, "GotoIfTime requires an argument:\n  <time range>|<days of week>|<days of month>|<months>?[[context|]extension|]priority\n");
    		return -1;
    	}
    
    
    		/* Separate the Goto path */
    		strsep(&ts,"?");
    
    		/* struct ast_include include contained garbage here, fixed by zeroing it on get_timerange */
    		if (ast_build_timing(&timing, s) && ast_check_timing(&timing))
    			res = pbx_builtin_goto(chan, (void *)ts);
    	}
    
    /*!
     * \ingroup applications
     */
    
    static int pbx_builtin_execiftime(struct ast_channel *chan, void *data)
    {
    
    	char *s, *appname;
    
    	struct ast_timing timing;
    
    	static const char *usage = "ExecIfTime requires an argument:\n  <time range>|<days of week>|<days of month>|<months>?<appname>[|<appargs>]";
    
    		ast_log(LOG_WARNING, "%s\n", usage);	
    
    	if (!(appname = ast_strdupa(data)))
    
    	s = strsep(&appname,"?");	/* Separate the timerange and application name/data */
    	if (!appname) {	/* missing application */
    		ast_log(LOG_WARNING, "%s\n", usage);
    		return -1;
    	}
    
    	if (!ast_build_timing(&timing, s)) {
    		ast_log(LOG_WARNING, "Invalid Time Spec: %s\nCorrect usage: %s\n", s, usage);
    		return -1;
    
    	if (!ast_check_timing(&timing))	/* outside the valid time window, just return */
    		return 0;
    
    	/* now split appname|appargs */
    	if ((s = strchr(appname, '|')))
    		*s++ = '\0';
    
    	if ((app = pbx_findapp(appname))) {
    		return pbx_exec(chan, app, S_OR(s, ""));
    	} else {
    		ast_log(LOG_WARNING, "Cannot locate application %s\n", appname);
    		return -1;