Skip to content
Snippets Groups Projects
app_queue.c 107 KiB
Newer Older
  • Learn to ignore specific revisions
  • 	/* Prevent possible buffer overflow */
    	if (digitlen < sizeof(qe->digits) - 2) {
    		qe->digits[digitlen] = digit;
    		qe->digits[digitlen + 1] = '\0';
    	} else {
    		qe->digits[0] = '\0';
    		return 0;
    	}
    
     	/* If there's no context to goto, short-circuit */
    
    
    	/* If the extension is bad, then reset the digits to blank */
    	if (!ast_canmatch_extension(qe->chan, qe->context, qe->digits, 1, qe->chan->cid.cid_num)) {
    		qe->digits[0] = '\0';
    		return 0;
    	}
    
    	/* We have an exact match */
    
    	if (!ast_goto_if_exists(qe->chan, qe->context, qe->digits, 1)) {
    		/* Return 1 on a successful goto */
    
    static int say_position(struct queue_ent *qe)
    {
    
    	int res = 0, avgholdmins, avgholdsecs;
    
    	time_t now;
    
    	/* Check to see if this is ludicrous -- if we just announced position, don't do it again*/
    	time(&now);
    	if ( (now - qe->last_pos) < 15 )
    
    
    	/* If either our position has changed, or we are over the freq timer, say position */
    	if ( (qe->last_pos_said == qe->pos) && ((now - qe->last_pos) < qe->parent->announcefrequency) )
    
    
    	ast_moh_stop(qe->chan);
    	/* Say we're next, if we are */
    	if (qe->pos == 1) {
    
    		res = play_file(qe->chan, qe->parent->sound_next);
    		if (res && valid_exit(qe, res))
    			goto playout;
    		else
    			goto posout;
    
    		if (res && valid_exit(qe, res))
    
    			goto playout;
    		res = ast_say_number(qe->chan, qe->pos, AST_DIGIT_ANY, qe->chan->language, (char *) NULL); /* Needs gender */
    		if (res && valid_exit(qe, res))
    			goto playout;
    		res = play_file(qe->chan, qe->parent->sound_calls);
    		if (res && valid_exit(qe, res))
    			goto playout;
    
    	}
    	/* Round hold time to nearest minute */
    
    	avgholdmins = abs(( (qe->parent->holdtime + 30) - (now - qe->start) ) / 60);
    
    	/* If they have specified a rounding then round the seconds as well */
    	if(qe->parent->roundingseconds) {
    		avgholdsecs = (abs(( (qe->parent->holdtime + 30) - (now - qe->start) )) - 60 * avgholdmins) / qe->parent->roundingseconds;
    		avgholdsecs*= qe->parent->roundingseconds;
    	} else {
    		avgholdsecs=0;
    	}
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (option_verbose > 2)
    
    		ast_verbose(VERBOSE_PREFIX_3 "Hold time for %s is %d minutes %d seconds\n", qe->parent->name, avgholdmins, avgholdsecs);
    
    
    	/* If the hold time is >1 min, if it's enabled, and if it's not
    	   supposed to be only once and we have already said it, say it */
    
    	if ((avgholdmins+avgholdsecs) > 0 && (qe->parent->announceholdtime) &&
    	    (!(qe->parent->announceholdtime == ANNOUNCEHOLDTIME_ONCE) && qe->last_pos)) {
    
    		res = play_file(qe->chan, qe->parent->sound_holdtime);
    		if (res && valid_exit(qe, res))
    			goto playout;
    
    		if (avgholdmins>0) {
    
    				res = play_file(qe->chan, qe->parent->sound_lessthan);
    				if (res && valid_exit(qe, res))
    					goto playout;
    
    				res = ast_say_number(qe->chan, 2, AST_DIGIT_ANY, qe->chan->language, (char *)NULL);
    				if (res && valid_exit(qe, res))
    					goto playout;
    			} else {
    				res = ast_say_number(qe->chan, avgholdmins, AST_DIGIT_ANY, qe->chan->language, (char*) NULL);
    				if (res && valid_exit(qe, res))
    					goto playout;
    			}
    			
    			res = play_file(qe->chan, qe->parent->sound_minutes);
    			if (res && valid_exit(qe, res))
    				goto playout;
    
    		if (avgholdsecs>0) {
    			res = ast_say_number(qe->chan, avgholdsecs, AST_DIGIT_ANY, qe->chan->language, (char*) NULL);
    			if (res && valid_exit(qe, res))
    				goto playout;
    
    			res = play_file(qe->chan, qe->parent->sound_seconds);
    			if (res && valid_exit(qe, res))
    				goto playout;
    
     posout:
    	if (option_verbose > 2)
    		ast_verbose(VERBOSE_PREFIX_3 "Told %s in %s their queue position (which was %d)\n",
    			    qe->chan->name, qe->parent->name, qe->pos);
    	res = play_file(qe->chan, qe->parent->sound_thanks);
    
     playout:
    
    	/* Set our last_pos indicators */
     	qe->last_pos = now;
    	qe->last_pos_said = qe->pos;
    	ast_moh_start(qe->chan, qe->moh);
    
    
    }
    
    static void recalc_holdtime(struct queue_ent *qe)
    {
    	int oldvalue, newvalue;
    
    	/* Calculate holdtime using a recursive boxcar filter */
    	/* Thanks to SRT for this contribution */
    	/* 2^2 (4) is the filter coefficient; a higher exponent would give old entries more weight */
    
    	newvalue = time(NULL) - qe->start;
    
    	ast_mutex_lock(&qe->parent->lock);
    	if (newvalue <= qe->parent->servicelevel)
           		qe->parent->callscompletedinsl++;
    	oldvalue = qe->parent->holdtime;
    	qe->parent->holdtime = (((oldvalue << 2) - oldvalue) + newvalue) >> 2;
    	ast_mutex_unlock(&qe->parent->lock);
    }
    
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    static void leave_queue(struct queue_ent *qe)
    {
    	struct ast_call_queue *q;
    	struct queue_ent *cur, *prev = NULL;
    	int pos = 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	q = qe->parent;
    	if (!q)
    		return;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	prev = NULL;
    	cur = q->head;
    	while(cur) {
    		if (cur == qe) {
    			q->count--;
    
    
    			/* Take us out of the queue */
    			manager_event(EVENT_FLAG_CALL, "Leave",
    
    				"Channel: %s\r\nQueue: %s\r\nCount: %d\r\n",
    				qe->chan->name, q->name,  q->count);
    
    #if 0
    ast_log(LOG_NOTICE, "Queue '%s' Leave, Channel '%s'\n", q->name, qe->chan->name );
    #endif
    
    Mark Spencer's avatar
    Mark Spencer committed
    			/* Take us out of the queue */
    			if (prev)
    				prev->next = cur->next;
    			else
    				q->head = cur->next;
    		} else {
    
    			/* Renumber the people after us in the queue based on a new count */
    
    Mark Spencer's avatar
    Mark Spencer committed
    			cur->pos = ++pos;
    			prev = cur;
    		}
    		cur = cur->next;
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    		/* It's dead and nobody is in it, so kill it */
    		destroy_queue(q);
    	}
    }
    
    
    /* Hang up a list of outgoing calls */
    static void hangupcalls(struct localuser *outgoing, struct ast_channel *exception)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	struct localuser *oo;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	while(outgoing) {
    		/* Hangup any existing lines we have open */
    
    		if (outgoing->chan && (outgoing->chan != exception))
    
    Mark Spencer's avatar
    Mark Spencer committed
    			ast_hangup(outgoing->chan);
    		oo = outgoing;
    		outgoing=outgoing->next;
    		free(oo);
    	}
    }
    
    
    static int update_status(struct ast_call_queue *q, struct member *member, int status)
    {
    	struct member *cur;
    
    	/* Since a reload could have taken place, we have to traverse the list to
    		be sure it's still valid */
    	ast_mutex_lock(&q->lock);
    	cur = q->members;
    	while(cur) {
    		if (member == cur) {
    			cur->status = status;
    
    			if (!q->maskmemberstatus) {
    				manager_event(EVENT_FLAG_AGENT, "QueueMemberStatus",
    					"Queue: %s\r\n"
    					"Location: %s\r\n"
    					"Membership: %s\r\n"
    					"Penalty: %d\r\n"
    					"CallsTaken: %d\r\n"
    					"LastCall: %ld\r\n"
    					"Status: %d\r\n"
    					"Paused: %d\r\n",
    				q->name, cur->interface, cur->dynamic ? "dynamic" : "static",
    				cur->penalty, cur->calls, cur->lastcall, cur->status, cur->paused);
    			}
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int update_dial_status(struct ast_call_queue *q, struct member *member, int status)
    {
    	if (status == AST_CAUSE_BUSY)
    		status = AST_DEVICE_BUSY;
    	else if (status == AST_CAUSE_UNREGISTERED)
    		status = AST_DEVICE_UNAVAILABLE;
    	else if (status == AST_CAUSE_NOSUCHDRIVER)
    		status = AST_DEVICE_INVALID;
    	else
    		status = AST_DEVICE_UNKNOWN;
    	return update_status(q, member, status);
    }
    
    
    /* traverse all defined queues which have calls waiting and contain this member
       return 0 if no other queue has precedence (higher weight) or 1 if found  */
    
    static int compare_weight(struct ast_call_queue *rq, struct member *member)
    {
    
    	struct ast_call_queue *q;
    	struct member *mem;
    
    	int found = 0;
    
    	/* &qlock and &rq->lock already set by try_calling()
    	 * to solve deadlock */
    
    	for (q = queues; q; q = q->next) {
    
    		if (q == rq) /* don't check myself, could deadlock */
    
    			continue; 
    
    		ast_mutex_lock(&q->lock);
    		if (q->count && q->members) {
    			for (mem = q->members; mem; mem = mem->next) {
    				if (mem == member) {
    
    					ast_log(LOG_DEBUG, "Found matching member %s in queue '%s'\n", mem->interface, q->name);
    
    					if (q->weight > rq->weight) {
    						ast_log(LOG_DEBUG, "Queue '%s' (weight %d, calls %d) is preferred over '%s' (weight %d, calls %d)\n", q->name, q->weight, q->count, rq->name, rq->weight, rq->count);
    
    						found = 1;
    
    						break;
    
    					}
    				}
    			}
    		}
    		ast_mutex_unlock(&q->lock);
    		if (found) 
    			break;
    	}
    	ast_mutex_unlock(&qlock);
    	return found;
    }
    
    
    static int ring_entry(struct queue_ent *qe, struct localuser *tmp, int *busies)
    
    	char tech[256];
    	char *location;
    
    	if (qe->parent->wrapuptime && (time(NULL) - tmp->lastcall < qe->parent->wrapuptime)) {
    
    		if (option_debug)
    			ast_log(LOG_DEBUG, "Wrapuptime not yet expired for %s\n", tmp->interface);
    
    		if (qe->chan->cdr)
    			ast_cdr_busy(qe->chan->cdr);
    		tmp->stillgoing = 0;
    
    	if (tmp->member->paused) {
    		if (option_debug)
    			ast_log(LOG_DEBUG, "%s paused, can't receive call\n", tmp->interface);
    		if (qe->chan->cdr)
    			ast_cdr_busy(qe->chan->cdr);
    		tmp->stillgoing = 0;
    		return 0;
    	}
    
    	if (use_weight && compare_weight(qe->parent,tmp->member)) {
    		ast_log(LOG_DEBUG, "Priority queue delaying call to %s:%s\n", qe->parent->name, tmp->interface);
    		if (qe->chan->cdr)
    			ast_cdr_busy(qe->chan->cdr);
    		tmp->stillgoing = 0;
    		(*busies)++;
    		return 0;
    	}
    
    	if ((location = strchr(tech, '/')))
    		*location++ = '\0';
    	else
    		location = "";
    
    
    	tmp->chan = ast_request(tech, qe->chan->nativeformats, location, &status);
    
    	if (!tmp->chan) {			/* If we can't, just go on to the next call */
    #if 0
    
    		ast_log(LOG_NOTICE, "Unable to create channel of type '%s' for Queue\n", cur->tech);
    
    #endif			
    		if (qe->chan->cdr)
    			ast_cdr_busy(qe->chan->cdr);
    		tmp->stillgoing = 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		update_dial_status(qe->parent, tmp->member, status);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		update_dial_status(qe->parent, tmp->member, status);
    
    	tmp->chan->appl = "AppQueue";
    	tmp->chan->data = "(Outgoing Line)";
    	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 (qe->chan->cid.cid_num)
    		tmp->chan->cid.cid_num = strdup(qe->chan->cid.cid_num);
    	if (qe->chan->cid.cid_name)
    		tmp->chan->cid.cid_name = strdup(qe->chan->cid.cid_name);
    	if (qe->chan->cid.cid_ani)
    		tmp->chan->cid.cid_ani = strdup(qe->chan->cid.cid_ani);
    
    
    	/* Inherit specially named variables from parent channel */
    	ast_channel_inherit_variables(qe->chan, tmp->chan);
    
    
    	/* Presense of ADSI CPE on outgoing channel follows ours */
    	tmp->chan->adsicpe = qe->chan->adsicpe;
    
    	/* Place the call, but don't wait on the answer */
    
    	res = ast_call(tmp->chan, location, 0);
    
    	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)
    
    			ast_verbose(VERBOSE_PREFIX_3 "Couldn't call %s\n", tmp->interface);
    
    		ast_hangup(tmp->chan);
    		tmp->chan = NULL;
    		tmp->stillgoing = 0;
    
    			manager_event(EVENT_FLAG_AGENT, "AgentCalled",
    
    						"AgentCalled: %s\r\n"
    
    						"ChannelCalling: %s\r\n"
    						"CallerID: %s\r\n"
    
    						"CallerIDName: %s\r\n"
    
    						"Context: %s\r\n"
    						"Extension: %s\r\n"
    						"Priority: %d\r\n",
    
    						tmp->interface, qe->chan->name,
    
    						tmp->chan->cid.cid_num ? tmp->chan->cid.cid_num : "unknown",
    						tmp->chan->cid.cid_name ? tmp->chan->cid.cid_name : "unknown",
    
    						qe->chan->context, qe->chan->exten, qe->chan->priority);
    		}
    
    			ast_verbose(VERBOSE_PREFIX_3 "Called %s\n", tmp->interface);
    
    static int ring_one(struct queue_ent *qe, struct localuser *outgoing, int *busies)
    
    {
    	struct localuser *cur;
    	struct localuser *best;
    	int bestmetric=0;
    
    			if (cur->stillgoing &&					/* Not already done */
    				!cur->chan &&					/* Isn't already going */
    
    				(!best || (cur->metric < bestmetric))) {	/* We haven't found one yet, or it's better */
    					bestmetric = cur->metric;
    					best = cur;
    			}
    			cur = cur->next;
    		}
    		if (best) {
    
    			if (!qe->parent->strategy) {
    				/* Ring everyone who shares this best metric (for ringall) */
    				cur = outgoing;
    				while(cur) {
    
    					if (cur->stillgoing && !cur->chan && (cur->metric <= bestmetric)) {
    
    						if (option_debug)
    							ast_log(LOG_DEBUG, "(Parallel) Trying '%s' with metric %d\n", cur->interface, cur->metric);
    
    					}
    					cur = cur->next;
    				}
    			} else {
    				/* Ring just the best channel */
    
    					ast_log(LOG_DEBUG, "Trying '%s' with metric %d\n", best->interface, best->metric);
    
    		if (option_debug)
    			ast_log(LOG_DEBUG, "Nobody left to try ringing in queue\n");
    
    static int store_next(struct queue_ent *qe, struct localuser *outgoing)
    {
    	struct localuser *cur;
    	struct localuser *best;
    	int bestmetric=0;
    
    	best = NULL;
    	cur = outgoing;
    	while(cur) {
    
    		if (cur->stillgoing &&					/* Not already done */
    			!cur->chan &&					/* Isn't already going */
    
    			(!best || (cur->metric < bestmetric))) {	/* We haven't found one yet, or it's better */
    				bestmetric = cur->metric;
    				best = cur;
    		}
    		cur = cur->next;
    	}
    	if (best) {
    		/* Ring just the best channel */
    
    		if (option_debug)
    			ast_log(LOG_DEBUG, "Next is '%s' with metric %d\n", best->interface, best->metric);
    
    		qe->parent->rrpos = best->metric % 1000;
    	} else {
    		/* Just increment rrpos */
    
    			/* No more channels, start over */
    			qe->parent->rrpos = 0;
    		} else {
    			/* Prioritize next entry */
    			qe->parent->rrpos++;
    		}
    
    static int background_file(struct queue_ent *qe, struct ast_channel *chan, char *filename)
    {
    	int res;
    
    	ast_stopstream(chan);
    	res = ast_streamfile(chan, filename, chan->language);
    
    	if (!res) {
    		/* Wait for a keypress */
    		res = ast_waitstream(chan, AST_DIGIT_ANY);
    		if (res <= 0 || !valid_exit(qe, res))
    			res = 0;
    
    		/* Stop playback */
    		ast_stopstream(chan);
    	} else {
    		res = 0;
    	}
    	
    	/*if (res) {
    		ast_log(LOG_WARNING, "ast_streamfile failed on %s \n", chan->name);
    		res = 0;
    	}*/
    
    	return res;
    }
    
    static int say_periodic_announcement(struct queue_ent *qe)
    {
    	int res = 0;
    	time_t now;
    
    	/* Get the current time */
    	time(&now);
    
    	/* Check to see if it is time to announce */
    
    	if ((now - qe->last_periodic_announce_time) < qe->parent->periodicannouncefrequency)
    		return 0;
    
    
    	/* Stop the music on hold so we can play our own file */
    	ast_moh_stop(qe->chan);
    
    	if (option_verbose > 2)
    		ast_verbose(VERBOSE_PREFIX_3 "Playing periodic announcement\n");
    
    	/* play the announcement */
    	res = background_file(qe, qe->chan, qe->parent->sound_periodicannounce);
    
    	/* Resume Music on Hold */
    	ast_moh_start(qe->chan, qe->moh);
    
    	/* update last_periodic_announce_time */
    	qe->last_periodic_announce_time = now;
    
    
    }
    
    static void record_abandoned(struct queue_ent *qe)
    {
    	ast_mutex_lock(&qe->parent->lock);
    	qe->parent->callsabandoned++;
    	ast_mutex_unlock(&qe->parent->lock);
    }
    
    
    
    		o = outgoing; \
    		found = -1; \
    		pos = 1; \
    		numlines = 0; \
    		watchers[0] = in; \
    		while(o) { \
    			/* Keep track of important channels */ \
    			if (o->stillgoing) { \
    				stillgoing = 1; \
    				if (o->chan) { \
    					watchers[pos++] = o->chan; \
    					found = 1; \
    				} \
    			} \
    			o = o->next; \
    			numlines++; \
    		} \
    	} while(0)
    	
    
    static struct localuser *wait_for_answer(struct queue_ent *qe, struct localuser *outgoing, int *to, char *digit, int prebusies, int caller_disconnect)
    
    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 orig = *to;
    	struct ast_frame *f;
    
    	struct ast_channel *watchers[AST_MAX_WATCHERS];
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int pos;
    	struct ast_channel *winner;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	while(*to && !peer) {
    
    		if ((found < 0) && stillgoing && !qe->parent->strategy) {
    			/* On "ringall" strategy we only move to the next penalty level
    			   when *all* ringing phones are done in the current penalty level */
    			ring_one(qe, outgoing, &numbusies);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    		if (found < 0) {
    
    			if (numlines == (numbusies + numnochan)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    				ast_log(LOG_DEBUG, "Everyone is busy at this time\n");
    			} else {
    
    				ast_log(LOG_NOTICE, "No one is answering queue '%s' (%d/%d/%d)\n", queue, numlines, numbusies, numnochan);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			}
    			*to = 0;
    			return NULL;
    		}
    		winner = ast_waitfor_n(watchers, pos, to);
    		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);
    
    Mark Spencer's avatar
    Mark Spencer committed
    				}
    
    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;
    
    					ast_copy_string(tmpchan, o->chan->call_forward, sizeof(tmpchan));
    
    					if ((stuff = strchr(tmpchan, '/'))) {
    						*stuff = '\0';
    						stuff++;
    						tech = tmpchan;
    					} else {
    						snprintf(tmpchan, sizeof(tmpchan), "%s@%s", o->chan->call_forward, o->chan->context);
    						stuff = tmpchan;
    						tech = "Local";
    					}
    					/* Before processing channel, go ahead and check for forwarding */
    					if (option_verbose > 2)
    						ast_verbose(VERBOSE_PREFIX_3 "Now forwarding %s to '%s/%s' (thanks to %s)\n", in->name, tech, stuff, o->chan->name);
    					/* Setup parameters */
    
    					o->chan = ast_request(tech, in->nativeformats, stuff, &status);
    					if (status != o->oldstatus) 
    
    Mark Spencer's avatar
    Mark Spencer committed
    						update_dial_status(qe->parent, o->member, status);						
    
    					if (!o->chan) {
    						ast_log(LOG_NOTICE, "Unable to create local channel for call forward to '%s/%s'\n", tech, stuff);
    						o->stillgoing = 0;
    						numnochan++;
    					} else {
    						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;
    
    						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)
    								ast_log(LOG_WARNING, "Out of memory\n");	
    						}
    
    						ast_copy_string(o->chan->accountcode, in->accountcode, sizeof(o->chan->accountcode));
    
    						o->chan->cdrflags = in->cdrflags;
    
    						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);
    						if (!ast_strlen_zero(in->macroexten))
    							o->chan->cid.cid_rdnis = strdup(in->macroexten);
    						else
    							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);
    							o->stillgoing = 0;
    							ast_hangup(o->chan);
    							o->chan = NULL;
    							numnochan++;
    						}
    					}
    					/* Hangup the original channel now, in case we needed it */
    					ast_hangup(winner);
    					continue;
    				}
    
    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:
    
    Mark Spencer's avatar
    Mark Spencer committed
    							/* This is our guy if someone answered. */
    							if (!peer) {
    								if (option_verbose > 2)
    									ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name);
    
    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);
    							o->stillgoing = 0;
    							if (in->cdr)
    								ast_cdr_busy(in->cdr);
    
    							if (qe->parent->strategy) {
    
    									*to = orig;
    
    								ring_one(qe, outgoing, &numbusies);
    
    Mark Spencer's avatar
    Mark Spencer committed
    							numbusies++;
    							break;
    						case AST_CONTROL_CONGESTION:
    							if (option_verbose > 2)
    								ast_verbose( VERBOSE_PREFIX_3 "%s is circuit-busy\n", o->chan->name);
    							o->stillgoing = 0;
    							if (in->cdr)
    								ast_cdr_busy(in->cdr);
    
    							if (qe->parent->strategy) {
    
    									*to = orig;
    
    								ring_one(qe, outgoing, &numbusies);
    
    Mark Spencer's avatar
    Mark Spencer committed
    							numbusies++;
    							break;
    						case AST_CONTROL_RINGING:
    							if (option_verbose > 2)
    								ast_verbose( VERBOSE_PREFIX_3 "%s is ringing\n", o->chan->name);
    							if (!sentringing) {
    #if 0
    								ast_indicate(in, AST_CONTROL_RINGING);
    #endif								
    								sentringing++;
    							}
    							break;
    						case AST_CONTROL_OFFHOOK:
    							/* Ignore going off hook */
    							break;
    						default:
    							ast_log(LOG_DEBUG, "Dunno what to do with control type %d\n", f->subclass);
    						}
    					}
    					ast_frfree(f);
    				} else {
    					o->stillgoing = 0;
    
    					if (qe->parent->strategy) {
    
    							*to = orig;
    
    						ring_one(qe, outgoing, &numbusies);
    
    Mark Spencer's avatar
    Mark Spencer committed
    				}
    			}
    			o = o->next;
    		}
    		if (winner == in) {
    			f = ast_read(in);
    #if 0
    			if (f && (f->frametype != AST_FRAME_VOICE))
    					printf("Frame type: %d, %d\n", f->frametype, f->subclass);
    			else if (!f || (f->frametype != AST_FRAME_VOICE))
    				printf("Hangup received on %s\n", in->name);
    #endif
    			if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP))) {
    				/* Got hung up */
    				*to=-1;
    
    Mark Spencer's avatar
    Mark Spencer committed
    				return NULL;
    			}
    
    			if ((f->frametype == AST_FRAME_DTMF) && caller_disconnect && (f->subclass == '*')) {
    
    				if (option_verbose > 3)
    
    Mark Spencer's avatar
    Mark Spencer committed
    					ast_verbose(VERBOSE_PREFIX_3 "User hit %c to disconnect call.\n", f->subclass);
    
    Mark Spencer's avatar
    Mark Spencer committed
    				*to=0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    				return NULL;
    			}
    
    			if ((f->frametype == AST_FRAME_DTMF) && (f->subclass != '*') && valid_exit(qe, f->subclass)) {
    
    					ast_verbose(VERBOSE_PREFIX_3 "User pressed digit: %c\n", f->subclass);
    
    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);
    	}
    
    	return peer;
    	
    }
    
    
    static int is_our_turn(struct queue_ent *qe)
    {
    	struct queue_ent *ch;
    	int res;
    
    	/* Atomically read the parent head -- does not need a lock */
    	ch = qe->parent->head;
    	/* If we are now at the top of the head, break out */
    	if (ch == qe) {
    		if (option_debug)
    			ast_log(LOG_DEBUG, "It's our turn (%s).\n", qe->chan->name);
    		res = 1;
    	} else {
    		if (option_debug)
    			ast_log(LOG_DEBUG, "It's not our turn (%s).\n", qe->chan->name);
    		res = 0;
    	}
    	return res;
    }
    
    
    static int wait_our_turn(struct queue_ent *qe, int ringing, enum queue_result *reason)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	int res = 0;
    
    
    	/* This is the holding pen for callers 2 through maxlen */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	for (;;) {
    
    			break;
    
    		/* If we have timed out, break out */
    
    		if (qe->expire && (time(NULL) > qe->expire)) {
    			*reason = QUEUE_TIMEOUT;
    
    Mark Spencer's avatar
    Mark Spencer committed
    			break;
    
    		/* leave the queue if no agents, if enabled */
    
    		if (qe->parent->leavewhenempty && (stat == QUEUE_NO_MEMBERS)) {
    			*reason = QUEUE_LEAVEEMPTY;
    			leave_queue(qe);
    			break;
    		}
    
    		/* leave the queue if no reachable agents, if enabled */
    
    		if ((qe->parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS)) {
    
    		/* Make a position announcement, if enabled */
    
    		if (qe->parent->announcefrequency && !ringing)
    
    		/* Make a periodic announcement, if enabled */
    		if (qe->parent->periodicannouncefrequency && !ringing)
    			res = say_periodic_announcement(qe);
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    		/* Wait a second before checking again */
    
    		if (!res) res = ast_waitfordigit(qe->chan, RECHECK * 1000);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (res)
    			break;
    	}
    	return res;
    }
    
    
    static int update_queue(struct ast_call_queue *q, struct member *member)
    
    	/* Since a reload could have taken place, we have to traverse the list to
    		be sure it's still valid */
    
    		if (member == cur) {
    
    			time(&cur->lastcall);
    			cur->calls++;
    			break;
    		}
    		cur = cur->next;
    	}
    
    	q->callscompleted++;
    
    	return 0;
    }
    
    static int calc_metric(struct ast_call_queue *q, struct member *mem, int pos, struct queue_ent *qe, struct localuser *tmp)
    
    {
    	switch (q->strategy) {
    	case QUEUE_STRATEGY_RINGALL:
    
    		/* Everyone equal, except for penalty */
    		tmp->metric = mem->penalty * 1000000;
    
    		break;
    
    	case QUEUE_STRATEGY_ROUNDROBIN:
    		if (!pos) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    				/* No more channels, start over */
    				q->rrpos = 0;
    			} else {
    				/* Prioritize next entry */
    
    		/* Fall through */
    	case QUEUE_STRATEGY_RRMEMORY:
    
    		if (pos < q->rrpos) {
    			tmp->metric = 1000 + pos;
    		} else {
    
    Mark Spencer's avatar
    Mark Spencer committed
    				/* Indicate there is another priority */
    
    		tmp->metric += mem->penalty * 1000000;
    
    		break;
    	case QUEUE_STRATEGY_RANDOM:
    		tmp->metric = rand() % 1000;
    
    		tmp->metric += mem->penalty * 1000000;
    		break;
    	case QUEUE_STRATEGY_FEWESTCALLS:
    		tmp->metric = mem->calls;
    		tmp->metric += mem->penalty * 1000000;
    		break;
    	case QUEUE_STRATEGY_LEASTRECENT:
    		if (!mem->lastcall)
    			tmp->metric = 0;
    		else
    			tmp->metric = 1000000 - (time(NULL) - mem->lastcall);
    		tmp->metric += mem->penalty * 1000000;
    
    		break;
    	default:
    		ast_log(LOG_WARNING, "Can't calculate metric for unknown strategy %d\n", q->strategy);
    		break;
    
    static int try_calling(struct queue_ent *qe, const char *options, char *announceoverride, const char *url, int *go_on)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	struct member *cur;
    	struct localuser *outgoing=NULL, *tmp = NULL;
    	int to;
    	char restofit[AST_MAX_EXTENSION];
    
    	char oldexten[AST_MAX_EXTENSION]="";
    
    	char oldcontext[AST_MAX_CONTEXT]="";
    
    	char queuename[256]="";
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char *newnum;
    
    	char *monitorfilename;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	struct ast_channel *peer;
    
    	struct member *member;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int res = 0, bridge = 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char *announce = NULL;
    
    	time_t callstart;
    
    	struct ast_bridge_config bridge_config;
    	char nondataquality = 1;
    
    	memset(&bridge_config, 0, sizeof(bridge_config));
    
    		
    	for (; options && *options; options++)
    		switch (*options) {
    		case 't':
    			ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_REDIRECT);
    			break;
    		case 'T':
    			ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_REDIRECT);
    			break;
    		case 'd':
    			nondataquality = 0;
    			break;
    		case 'h':
    			ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_DISCONNECT);
    			break;
    		case 'H':
    			ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT);
    			break;
    		case 'n':
    			if ((now - qe->start >= qe->parent->timeout))
    				*go_on = 1;
    			break;