Skip to content
Snippets Groups Projects
app_queue.c 315 KiB
Newer Older
  • Learn to ignore specific revisions
  • 				ast_copy_string(membername, o->member->membername, sizeof(membername));
    
    
    				/* Before processing channel, go ahead and check for forwarding */
    
    				if (!ast_strlen_zero(ast_channel_call_forward(o->chan)) && !forwardsallowed) {
    					ast_verb(3, "Forwarding %s to '%s' prevented.\n", inchan_name, ast_channel_call_forward(o->chan));
    
    					numnochan++;
    					do_hang(o);
    					winner = NULL;
    					continue;
    
    				} else if (!ast_strlen_zero(ast_channel_call_forward(o->chan))) {
    
    					ast_copy_string(tmpchan, ast_channel_call_forward(o->chan), sizeof(tmpchan));
    
    					if ((stuff = strchr(tmpchan, '/'))) {
    
    						snprintf(tmpchan, sizeof(tmpchan), "%s@%s", ast_channel_call_forward(o->chan), ast_channel_context(o->chan));
    
    					if (!strcasecmp(tech, "Local")) {
    						/*
    						 * Drop the connected line update block for local channels since
    						 * this is going to run dialplan and the user can change his
    						 * mind about what connected line information he wants to send.
    						 */
    						o->block_connected_update = 0;
    					}
    
    					ast_cel_report_event(in, AST_CEL_FORWARD, NULL, ast_channel_call_forward(o->chan), NULL);
    
    					ast_verb(3, "Now forwarding %s to '%s/%s' (thanks to %s)\n", inchan_name, tech, stuff, ochan_name);
    
    					o->chan = ast_request(tech, ast_channel_nativeformats(in), in, stuff, &status);
    
    						ast_log(LOG_NOTICE,
    							"Forwarding failed to create channel to dial '%s/%s'\n",
    							tech, stuff);
    
    						ast_channel_lock_both(o->chan, original);
    						ast_party_redirecting_copy(ast_channel_redirecting(o->chan),
    							ast_channel_redirecting(original));
    						ast_channel_unlock(o->chan);
    						ast_channel_unlock(original);
    
    						ast_channel_lock_both(o->chan, in);
    
    						ast_channel_inherit_variables(in, o->chan);
    
    						ast_channel_datastore_inherit(in, o->chan);
    
    						if (o->pending_connected_update) {
    							/*
    							 * Re-seed the callattempt's connected line information with
    							 * previously acquired connected line info from the queued
    							 * channel.  The previously acquired connected line info could
    							 * have been set through the CONNECTED_LINE dialplan function.
    							 */
    							o->pending_connected_update = 0;
    							ast_party_connected_line_copy(&o->connected, ast_channel_connected(in));
    						}
    
    
    						ast_channel_accountcode_set(o->chan, ast_channel_accountcode(in));
    
    						if (!ast_channel_redirecting(o->chan)->from.number.valid
    							|| ast_strlen_zero(ast_channel_redirecting(o->chan)->from.number.str)) {
    
    							/*
    							 * The call was not previously redirected so it is
    							 * now redirected from this number.
    							 */
    
    							ast_party_number_free(&ast_channel_redirecting(o->chan)->from.number);
    							ast_party_number_init(&ast_channel_redirecting(o->chan)->from.number);
    							ast_channel_redirecting(o->chan)->from.number.valid = 1;
    							ast_channel_redirecting(o->chan)->from.number.str =
    
    								ast_strdup(S_OR(ast_channel_macroexten(in), ast_channel_exten(in)));
    
    						ast_channel_dialed(o->chan)->transit_network_select = ast_channel_dialed(in)->transit_network_select;
    
    						o->dial_callerid_absent = !ast_channel_caller(o->chan)->id.number.valid
    							|| ast_strlen_zero(ast_channel_caller(o->chan)->id.number.str);
    						ast_connected_line_copy_from_caller(ast_channel_connected(o->chan),
    							ast_channel_caller(in));
    
    						ast_channel_unlock(in);
    
    						if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL
    							&& !o->block_connected_update) {
    							struct ast_party_redirecting redirecting;
    
    							/*
    							 * Redirecting updates to the caller make sense only on single
    							 * call at a time strategies.
    							 *
    							 * We must unlock o->chan before calling
    							 * ast_channel_redirecting_macro, because we put o->chan into
    							 * autoservice there.  That is pretty much a guaranteed
    							 * deadlock.  This is why the handling of o->chan's lock may
    							 * seem a bit unusual here.
    							 */
    							ast_party_redirecting_init(&redirecting);
    							ast_party_redirecting_copy(&redirecting, ast_channel_redirecting(o->chan));
    							ast_channel_unlock(o->chan);
    							if (ast_channel_redirecting_sub(o->chan, in, &redirecting, 0) &&
    								ast_channel_redirecting_macro(o->chan, in, &redirecting, 1, 0)) {
    								ast_channel_update_redirecting(in, &redirecting, NULL);
    							}
    							ast_party_redirecting_free(&redirecting);
    						} else {
    							ast_channel_unlock(o->chan);
    						}
    
    						if (ast_call(o->chan, stuff, 0)) {
    							ast_log(LOG_NOTICE, "Forwarding failed to dial '%s/%s'\n",
    								tech, stuff);
    
    							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.integer) {
    
    Olle Johansson's avatar
    Olle Johansson committed
    						case AST_CONTROL_ANSWER:
    
    Mark Spencer's avatar
    Mark Spencer committed
    							/* This is our guy if someone answered. */
    							if (!peer) {
    
    								ast_verb(3, "%s answered %s\n", ochan_name, inchan_name);
    
    								if (!o->block_connected_update) {
    
    										if (ast_channel_connected_line_sub(o->chan, in, &o->connected, 0) &&
    											ast_channel_connected_line_macro(o->chan, in, &o->connected, 1, 0)) {
    
    											ast_channel_update_connected_line(in, &o->connected, NULL);
    
    									} else if (!o->dial_callerid_absent) {
    
    										ast_connected_line_copy_from_caller(&connected_caller, ast_channel_caller(o->chan));
    
    										ast_channel_unlock(o->chan);
    										connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER;
    
    										if (ast_channel_connected_line_sub(o->chan, in, &connected_caller, 0) &&
    											ast_channel_connected_line_macro(o->chan, in, &connected_caller, 1, 0)) {
    											ast_channel_update_connected_line(in, &connected_caller, NULL);
    										}
    
    										ast_party_connected_line_free(&connected_caller);
    									}
    								}
    
    								if (o->aoc_s_rate_list) {
    									size_t encoded_size;
    									struct ast_aoc_encoded *encoded;
    									if ((encoded = ast_aoc_encode(o->aoc_s_rate_list, &encoded_size, o->chan))) {
    										ast_indicate_data(in, AST_CONTROL_AOC, encoded, encoded_size);
    										ast_aoc_destroy_encoded(encoded);
    									}
    								}
    
    Mark Spencer's avatar
    Mark Spencer committed
    							}
    							break;
    						case AST_CONTROL_BUSY:
    
    Olle Johansson's avatar
    Olle Johansson committed
    							if (ast_channel_cdr(in)) {
    
    								ast_cdr_busy(ast_channel_cdr(in));
    
    							rna(endtime * 1000, qe, on, membername, qe->parent->autopausebusy);
    
    							if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
    
    Olle Johansson's avatar
    Olle Johansson committed
    								if (qe->parent->timeoutrestart) {
    
    									*to = orig;
    
    								/* Have enough time for a queue member to answer? */
    								if (*to > 500) {
    									ring_one(qe, outgoing, &numbusies);
    									starttime = (long) time(NULL);
    								}
    
    Mark Spencer's avatar
    Mark Spencer committed
    							numbusies++;
    							break;
    						case AST_CONTROL_CONGESTION:
    
    							ast_verb(3, "%s is circuit-busy\n", ochan_name);
    
    Olle Johansson's avatar
    Olle Johansson committed
    							if (ast_channel_cdr(in)) {
    
    								ast_cdr_busy(ast_channel_cdr(in));
    
    							rna(endtime * 1000, qe, on, membername, qe->parent->autopauseunavail);
    
    							if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
    
    Olle Johansson's avatar
    Olle Johansson committed
    								if (qe->parent->timeoutrestart) {
    
    									*to = orig;
    
    								if (*to > 500) {
    									ring_one(qe, outgoing, &numbusies);
    									starttime = (long) time(NULL);
    								}
    
    Mark Spencer's avatar
    Mark Spencer committed
    							numbusies++;
    							break;
    						case AST_CONTROL_RINGING:
    
    							ast_verb(3, "%s is ringing\n", ochan_name);
    
    
    							/* Start ring indication when the channel is ringing, if specified */
    							if (qe->ring_when_ringing) {
    								ast_moh_stop(qe->chan);
    								ast_indicate(qe->chan, AST_CONTROL_RINGING);
    							}
    
    Mark Spencer's avatar
    Mark Spencer committed
    							break;
    						case AST_CONTROL_OFFHOOK:
    							/* Ignore going off hook */
    							break;
    
    							if (o->block_connected_update) {
    
    								ast_verb(3, "Connected line update to %s prevented.\n", inchan_name);
    
    								break;
    							}
    							if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) {
    
    								struct ast_party_connected_line connected;
    
    								ast_verb(3, "%s connected line has changed. Saving it until answer for %s\n", ochan_name, inchan_name);
    								ast_party_connected_line_set_init(&connected, &o->connected);
    								ast_connected_line_parse_data(f->data.ptr, f->datalen, &connected);
    
    								ast_party_connected_line_set(&o->connected, &connected, NULL);
    
    								ast_party_connected_line_free(&connected);
    
    								break;
    							}
    
    							/*
    							 * Prevent using the CallerID from the outgoing channel since we
    							 * got a connected line update from it.
    							 */
    							o->dial_callerid_absent = 1;
    
    							if (ast_channel_connected_line_sub(o->chan, in, f, 1) &&
    								ast_channel_connected_line_macro(o->chan, in, f, 1, 1)) {
    								ast_indicate_data(in, AST_CONTROL_CONNECTED_LINE, f->data.ptr, f->datalen);
    
    						case AST_CONTROL_AOC:
    							{
    								struct ast_aoc_decoded *decoded = ast_aoc_decode(f->data.ptr, f->datalen, o->chan);
    								if (decoded && (ast_aoc_get_msg_type(decoded) == AST_AOC_S)) {
    									ast_aoc_destroy_decoded(o->aoc_s_rate_list);
    									o->aoc_s_rate_list = decoded;
    								} else {
    									ast_aoc_destroy_decoded(decoded);
    								}
    							}
    							break;
    
    							if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) {
    								/*
    								 * Redirecting updates to the caller make sense only on single
    								 * call at a time strategies.
    								 */
    								break;
    							}
    							if (o->block_connected_update) {
    								ast_verb(3, "Redirecting update to %s prevented\n",
    									inchan_name);
    								break;
    							}
    							ast_verb(3, "%s redirecting info has changed, passing it to %s\n",
    								ochan_name, inchan_name);
    							if (ast_channel_redirecting_sub(o->chan, in, f, 1) &&
    								ast_channel_redirecting_macro(o->chan, in, f, 1, 1)) {
    								ast_indicate_data(in, AST_CONTROL_REDIRECTING, f->data.ptr, f->datalen);
    
    						case AST_CONTROL_PVT_CAUSE_CODE:
    							ast_indicate_data(in, AST_CONTROL_PVT_CAUSE_CODE, f->data.ptr, f->datalen);
    							break;
    
    Mark Spencer's avatar
    Mark Spencer committed
    						default:
    
    							ast_debug(1, "Dunno what to do with control type %d\n", f->subclass.integer);
    
    Mark Spencer's avatar
    Mark Spencer committed
    						}
    					}
    					ast_frfree(f);
    
    				} else { /* ast_read() returned NULL */
    
    					endtime = (long) time(NULL) - starttime;
    
    					rna(endtime * 1000, qe, on, membername, 1);
    
    					if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
    
    Olle Johansson's avatar
    Olle Johansson committed
    						if (qe->parent->timeoutrestart) {
    
    							*to = orig;
    
    						if (*to > 500) {
    							ring_one(qe, outgoing, &numbusies);
    							starttime = (long) time(NULL);
    						}
    
    
    		/* If we received an event from the caller, deal with it. */
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (winner == in) {
    			f = ast_read(in);
    
    			if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass.integer == AST_CONTROL_HANGUP))) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    				/* Got hung up */
    
    						ast_channel_hangupcause_set(in, f->data.uint32);
    
    Mark Spencer's avatar
    Mark Spencer committed
    				return NULL;
    			}
    
    
    			/*!
    			 * \todo
    			 * XXX Queue like Dial really should send any connected line
    			 * updates (AST_CONTROL_CONNECTED_LINE) from the caller to each
    			 * ringing queue member.
    			 */
    
    
    			if ((f->frametype == AST_FRAME_DTMF) && caller_disconnect && (f->subclass.integer == '*')) {
    				ast_verb(3, "User hit %c to disconnect call.\n", f->subclass.integer);
    
    Mark Spencer's avatar
    Mark Spencer committed
    				return NULL;
    			}
    
    			if ((f->frametype == AST_FRAME_DTMF) && valid_exit(qe, f->subclass.integer)) {
    				ast_verb(3, "User pressed digit: %c\n", f->subclass.integer);
    
    				*digit = f->subclass.integer;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    
    Olle Johansson's avatar
    Olle Johansson committed
    			for (o = start; o; o = o->call_next) {
    
    				rna(orig, qe, o->interface, o->member->membername, 1);
    
    	for (epollo = outgoing; epollo; epollo = epollo->q_next) {
    
    Olle Johansson's avatar
    Olle Johansson committed
    		if (epollo->chan) {
    
    			ast_poll_channel_del(in, epollo->chan);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return peer;
    }
    
    
    /*! 
     * \brief Check if we should start attempting to call queue members.
    
     * A simple process, really. Count the number of members who are available
     * to take our call and then see if we are in a position in the queue at
     * which a member could accept our call.
     *
     * \param[in] qe The caller who wants to know if it is his turn
     * \retval 0 It is not our turn
     * \retval 1 It is our turn
    
    static int is_our_turn(struct queue_ent *qe)
    {
    	struct queue_ent *ch;
    	int res;
    
    	int avl;
    	int idx = 0;
    	/* This needs a lock. How many members are available to be served? */
    	ao2_lock(qe->parent);
    
    	avl = num_available_members(qe->parent);
    
    	ch = qe->parent->head;
    
    	ast_debug(1, "There %s %d available %s.\n", avl != 1 ? "are" : "is", avl, avl != 1 ? "members" : "member");
    
    	while ((idx < avl) && (ch) && (ch != qe)) {
    
    Olle Johansson's avatar
    Olle Johansson committed
    		if (!ch->pending) {
    
    		ch = ch->next;			
    	}
    
    	ao2_unlock(qe->parent);
    
    	/* If the queue entry is within avl [the number of available members] calls from the top ... 
    	 * Autofill and position check added to support autofill=no (as only calls
    	 * from the front of the queue are valid when autofill is disabled)
    	 */
    	if (ch && idx < avl && (qe->parent->autofill || qe->pos == 1)) {
    
    		ast_debug(1, "It's our turn (%s).\n", ast_channel_name(qe->chan));
    
    		ast_debug(1, "It's not our turn (%s).\n", ast_channel_name(qe->chan));
    
    
    /*!
     * \brief update rules for queues
     *
     * Calculate min/max penalties making sure if relative they stay within bounds.
     * Update queues penalty and set dialplan vars, goto next list entry.
    */
    
    static void update_qe_rule(struct queue_ent *qe)
    {
    	int max_penalty = qe->pr->max_relative ? qe->max_penalty + qe->pr->max_value : qe->pr->max_value;
    	int min_penalty = qe->pr->min_relative ? qe->min_penalty + qe->pr->min_value : qe->pr->min_value;
    	char max_penalty_str[20], min_penalty_str[20]; 
    	/* a relative change to the penalty could put it below 0 */
    
    Olle Johansson's avatar
    Olle Johansson committed
    	if (max_penalty < 0) {
    
    Olle Johansson's avatar
    Olle Johansson committed
    	}
    	if (min_penalty < 0) {
    
    Olle Johansson's avatar
    Olle Johansson committed
    	}
    	if (min_penalty > max_penalty) {
    
    	snprintf(max_penalty_str, sizeof(max_penalty_str), "%d", max_penalty);
    	snprintf(min_penalty_str, sizeof(min_penalty_str), "%d", min_penalty);
    
    	pbx_builtin_setvar_helper(qe->chan, "QUEUE_MAX_PENALTY", max_penalty_str);
    	pbx_builtin_setvar_helper(qe->chan, "QUEUE_MIN_PENALTY", min_penalty_str);
    	qe->max_penalty = max_penalty;
    	qe->min_penalty = min_penalty;
    
    	ast_debug(3, "Setting max penalty to %d and min penalty to %d for caller %s since %d seconds have elapsed\n", qe->max_penalty, qe->min_penalty, ast_channel_name(qe->chan), qe->pr->time);
    
    /*! \brief The waiting areas for callers who are not actively calling members
     *
     * This function is one large loop. This function will return if a caller
     * either exits the queue or it becomes that caller's turn to attempt calling
     * queue members. Inside the loop, we service the caller with periodic announcements,
     * holdtime announcements, etc. as configured in queues.conf
     *
    
     * \retval  0 if the caller's turn has arrived
     * \retval -1 if the caller should exit the queue.
    
    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 (;;) {
    
    Olle Johansson's avatar
    Olle Johansson committed
    		if (is_our_turn(qe)) {
    
    
    		/* If we have timed out, break out */
    
    		if (qe->expire && (time(NULL) >= qe->expire)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			break;
    
    		if (qe->parent->leavewhenempty) {
    
    			if ((status = get_member_status(qe->parent, qe->max_penalty, qe->min_penalty, qe->parent->leavewhenempty))) {
    
    				*reason = QUEUE_LEAVEEMPTY;
    
    				ast_queue_log(qe->parent->name, ast_channel_uniqueid(qe->chan), "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long) time(NULL) - qe->start);
    
    		/* Make a position announcement, if enabled */
    
    Olle Johansson's avatar
    Olle Johansson committed
    			(res = say_position(qe,ringing))) {
    
    		/* If we have timed out, break out */
    
    		if (qe->expire && (time(NULL) >= qe->expire)) {
    
    			*reason = QUEUE_TIMEOUT;
    			break;
    		}
    
    
    		/* Make a periodic announcement, if enabled */
    
    		if (qe->parent->periodicannouncefrequency &&
    			(res = say_periodic_announcement(qe,ringing)))
    
    		
    		/* see if we need to move to the next penalty level for this queue */
    
    		while (qe->pr && ((time(NULL) - qe->start) >= qe->pr->time)) {
    
    		/* If we have timed out, break out */
    
    		if (qe->expire && (time(NULL) >= qe->expire)) {
    
    			*reason = QUEUE_TIMEOUT;
    			break;
    		}
    		
    
    Mark Spencer's avatar
    Mark Spencer committed
    		/* Wait a second before checking again */
    
    		if ((res = ast_waitfordigit(qe->chan, RECHECK * 1000))) {
    
    Olle Johansson's avatar
    Olle Johansson committed
    			if (res > 0 && !valid_exit(qe, res)) {
    
    Olle Johansson's avatar
    Olle Johansson committed
    			} else {
    
    		
    		/* If we have timed out, break out */
    
    		if (qe->expire && (time(NULL) >= qe->expire)) {
    
    			*reason = QUEUE_TIMEOUT;
    			break;
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return res;
    }
    
    
    /*!
     * \brief update the queue status
     * \retval Always 0
    */
    
    static int update_queue(struct call_queue *q, struct member *member, int callcompletedinsl, int newtalktime)
    
    	struct ao2_iterator queue_iter;
    
    
    	if (shared_lastcall) {
    		queue_iter = ao2_iterator_init(queues, 0);
    
    		while ((qtmp = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
    
    			if ((mem = ao2_find(qtmp->members, member, OBJ_POINTER))) {
    
    				time(&mem->lastcall);
    				mem->calls++;
    				mem->lastqueue = q;
    				ao2_ref(mem, -1);
    			}
    			ao2_unlock(qtmp);
    
    		ao2_iterator_destroy(&queue_iter);
    
    	} else {
    		ao2_lock(q);
    		time(&member->lastcall);
    		member->calls++;
    		member->lastqueue = q;
    		ao2_unlock(q);
    	}	
    
    	q->callscompleted++;
    
    Olle Johansson's avatar
    Olle Johansson committed
    	if (callcompletedinsl) {
    
    		q->callscompletedinsl++;
    
    	/* Calculate talktime using the same exponential average as holdtime code*/
    	oldtalktime = q->talktime;
    	q->talktime = (((oldtalktime << 2) - oldtalktime) + newtalktime) >> 2;
    
    /*! \brief Calculate the metric of each member in the outgoing callattempts
     *
     * A numeric metric is given to each member depending on the ring strategy used
     * by the queue. Members with lower metrics will be called before members with
     * higher metrics
    
     * \retval -1 if penalties are exceeded
     * \retval 0 otherwise
    
    static int calc_metric(struct call_queue *q, struct member *mem, int pos, struct queue_ent *qe, struct callattempt *tmp)
    
    David Vossel's avatar
     
    David Vossel committed
    	/* disregarding penalty on too few members? */
    
    	int membercount = ao2_container_count(q->members);
    
    	unsigned char usepenalty = (membercount <= q->penaltymemberslimit) ? 0 : 1;
    
    David Vossel's avatar
     
    David Vossel committed
    
    	if (usepenalty) {
    		if ((qe->max_penalty && (mem->penalty > qe->max_penalty)) ||
    			(qe->min_penalty && (mem->penalty < qe->min_penalty))) {
    			return -1;
    		}
    	} else {
    		ast_debug(1, "Disregarding penalty, %d members and %d in penaltymemberslimit.\n",
    
    			  membercount, q->penaltymemberslimit);
    
    David Vossel's avatar
     
    David Vossel committed
    	}
    
    	switch (q->strategy) {
    	case QUEUE_STRATEGY_RINGALL:
    
    		/* Everyone equal, except for penalty */
    
    David Vossel's avatar
     
    David Vossel committed
    		tmp->metric = mem->penalty * 1000000 * usepenalty;
    
    		break;
    
    	case QUEUE_STRATEGY_LINEAR:
    		if (pos < qe->linpos) {
    			tmp->metric = 1000 + pos;
    		} else {
    
    Olle Johansson's avatar
    Olle Johansson committed
    			if (pos > qe->linpos) {
    
    				/* Indicate there is another priority */
    				qe->linwrapped = 1;
    
    David Vossel's avatar
     
    David Vossel committed
    		tmp->metric += mem->penalty * 1000000 * usepenalty;
    
    	case QUEUE_STRATEGY_RRORDERED:
    
    	case QUEUE_STRATEGY_RRMEMORY:
    
    		if (pos < q->rrpos) {
    			tmp->metric = 1000 + pos;
    		} else {
    
    Olle Johansson's avatar
    Olle Johansson committed
    			if (pos > q->rrpos) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    				/* Indicate there is another priority */
    
    David Vossel's avatar
     
    David Vossel committed
    		tmp->metric += mem->penalty * 1000000 * usepenalty;
    
    David Vossel's avatar
     
    David Vossel committed
    		tmp->metric += mem->penalty * 1000000 * usepenalty;
    
    	case QUEUE_STRATEGY_WRANDOM:
    		tmp->metric = ast_random() % ((1 + mem->penalty) * 1000);
    		break;
    
    	case QUEUE_STRATEGY_FEWESTCALLS:
    		tmp->metric = mem->calls;
    
    David Vossel's avatar
     
    David Vossel committed
    		tmp->metric += mem->penalty * 1000000 * usepenalty;
    
    Olle Johansson's avatar
    Olle Johansson committed
    		if (!mem->lastcall) {
    
    Olle Johansson's avatar
    Olle Johansson committed
    		} else {
    
    			tmp->metric = 1000000 - (time(NULL) - mem->lastcall);
    
    David Vossel's avatar
     
    David Vossel committed
    		tmp->metric += mem->penalty * 1000000 * usepenalty;
    
    		break;
    	default:
    		ast_log(LOG_WARNING, "Can't calculate metric for unknown strategy %d\n", q->strategy);
    		break;
    
    /*! \brief Send out AMI message with member call completion status information */
    
    static void send_agent_complete(const struct queue_ent *qe, const char *queuename,
    	const struct ast_channel *peer, const struct member *member, time_t callstart,
    	char *vars, size_t vars_len, enum agent_complete_reason rsn)
    {
    
    	const char *reason = NULL;	/* silence dumb compilers */
    
    Olle Johansson's avatar
    Olle Johansson committed
    	if (!qe->parent->eventwhencalled) {
    
    
    	switch (rsn) {
    	case CALLER:
    		reason = "caller";
    		break;
    	case AGENT:
    		reason = "agent";
    		break;
    	case TRANSFER:
    		reason = "transfer";
    		break;
    	}
    
    
    	/*** DOCUMENTATION
    	<managerEventInstance>
    		<synopsis>Raised when an agent has finished servicing a member in the queue.</synopsis>
    		<syntax>
    			<xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Queue'])" />
    			<xi:include xpointer="xpointer(/docs/managerEvent[@name='AgentRingNoAnswer']/managerEventInstance/syntax/parameter[@name='Member'])" />
    			<xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='MemberName'])" />
    			<xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueCallerAbandon']/managerEventInstance/syntax/parameter[@name='HoldTime'])" />
    			<xi:include xpointer="xpointer(/docs/managerEvent[@name='AgentCalled']/managerEventInstance/syntax/parameter[@name='Variable'])" />
    			<parameter name="TalkTime">
    				<para>The time the agent talked with the member in the queue, expressed in seconds since 00:00, Jan 1, 1970 UTC.</para>
    			</parameter>
    			<parameter name="Reason">
    				<enumlist>
    					<enum name="caller"/>
    					<enum name="agent"/>
    					<enum name="transfer"/>
    				</enumlist>
    			</parameter>
    		</syntax>
    		<see-also>
    			<ref type="managerEvent">AgentCalled</ref>
    			<ref type="managerEvent">AgentConnect</ref>
    		</see-also>
    	</managerEventInstance>
    	***/
    
    	manager_event(EVENT_FLAG_AGENT, "AgentComplete",
    		"Queue: %s\r\n"
    		"Uniqueid: %s\r\n"
    		"Channel: %s\r\n"
    		"Member: %s\r\n"
    		"MemberName: %s\r\n"
    		"HoldTime: %ld\r\n"
    		"TalkTime: %ld\r\n"
    		"Reason: %s\r\n"
    		"%s",
    
    		queuename, ast_channel_uniqueid(qe->chan), ast_channel_name(peer), member->interface, member->membername,
    
    		(long)(callstart - qe->start), (long)(time(NULL) - callstart), reason,
    		qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, vars_len) : "");
    }
    
    struct queue_transfer_ds {
    	struct queue_ent *qe;
    	struct member *member;
    
    	time_t starttime;
    
    	int callcompletedinsl;
    
    static void queue_transfer_destroy(void *data)
    {
    	struct queue_transfer_ds *qtds = data;
    	ast_free(qtds);
    }
    
    
    /*! \brief a datastore used to help correctly log attended transfers of queue callers
     */
    static const struct ast_datastore_info queue_transfer_info = {
    	.type = "queue_transfer",
    	.chan_fixup = queue_transfer_fixup,
    
    	.destroy = queue_transfer_destroy,
    
    };
    
    /*! \brief Log an attended transfer when a queue caller channel is masqueraded
     *
     * When a caller is masqueraded, we want to log a transfer. Fixup time is the closest we can come to when
     * the actual transfer occurs. This happens during the masquerade after datastores are moved from old_chan
     * to new_chan. This is why new_chan is referenced for exten, context, and datastore information.
     *
     * At the end of this, we want to remove the datastore so that this fixup function is not called on any
     * future masquerades of the caller during the current call.
     */
    
    static void queue_transfer_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan)
    
    {
    	struct queue_transfer_ds *qtds = data;
    	struct queue_ent *qe = qtds->qe;
    	struct member *member = qtds->member;
    
    	time_t callstart = qtds->starttime;
    
    	int callcompletedinsl = qtds->callcompletedinsl;
    
    	struct ast_datastore *datastore;
    
    	ast_queue_log(qe->parent->name, ast_channel_uniqueid(qe->chan), member->membername, "TRANSFER", "%s|%s|%ld|%ld|%d",
    
    				ast_channel_exten(new_chan), ast_channel_context(new_chan), (long) (callstart - qe->start),
    
    				(long) (time(NULL) - callstart), qe->opos);
    
    	update_queue(qe->parent, member, callcompletedinsl, (time(NULL) - callstart));
    
    	/* No need to lock the channels because they are already locked in ast_do_masquerade */
    	if ((datastore = ast_channel_datastore_find(old_chan, &queue_transfer_info, NULL))) {
    		ast_channel_datastore_remove(old_chan, datastore);
    
    		ast_log(LOG_WARNING, "Can't find the queue_transfer datastore.\n");
    	}
    }
    
    /*! \brief mechanism to tell if a queue caller was atxferred by a queue member.
     *
     * When a caller is atxferred, then the queue_transfer_info datastore
     * is removed from the channel. If it's still there after the bridge is
     * broken, then the caller was not atxferred.
    
     *
     * \note Only call this with chan locked
    
     */
    static int attended_transfer_occurred(struct ast_channel *chan)
    {
    	return ast_channel_datastore_find(chan, &queue_transfer_info, NULL) ? 0 : 1;
    }
    
    /*! \brief create a datastore for storing relevant info to log attended transfers in the queue_log
     */
    
    static struct ast_datastore *setup_transfer_datastore(struct queue_ent *qe, struct member *member, time_t starttime, int callcompletedinsl)
    
    	struct queue_transfer_ds *qtds = ast_calloc(1, sizeof(*qtds));
    
    
    	if (!qtds) {
    		ast_log(LOG_WARNING, "Memory allocation error!\n");
    
    	if (!(ds = ast_datastore_alloc(&queue_transfer_info, NULL))) {
    
    		ast_channel_unlock(qe->chan);
    
    		ast_free(qtds);
    
    		ast_log(LOG_WARNING, "Unable to create transfer datastore. queue_log will not show attended transfer\n");
    
    	/* This member is refcounted in try_calling, so no need to add it here, too */
    
    	qtds->member = member;
    	qtds->starttime = starttime;
    
    	qtds->callcompletedinsl = callcompletedinsl;
    
    	ast_channel_datastore_add(qe->chan, ds);
    	ast_channel_unlock(qe->chan);
    
    struct queue_end_bridge {
    	struct call_queue *q;
    	struct ast_channel *chan;
    };
    
    static void end_bridge_callback_data_fixup(struct ast_bridge_config *bconfig, struct ast_channel *originator, struct ast_channel *terminator)
    {
    	struct queue_end_bridge *qeb = bconfig->end_bridge_callback_data;
    	ao2_ref(qeb, +1);
    	qeb->chan = originator;
    }
    
    
    static void end_bridge_callback(void *data)
    {
    
    	struct queue_end_bridge *qeb = data;
    	struct call_queue *q = qeb->q;
    	struct ast_channel *chan = qeb->chan;
    
    	if (ao2_ref(qeb, -1) == 1) {
    		set_queue_variables(q, chan);
    		/* This unrefs the reference we made in try_calling when we allocated qeb */
    
    		queue_t_unref(q, "Expire bridge_config reference");
    
    /*!
     * \internal
     * \brief A large function which calls members, updates statistics, and bridges the caller and a member
    
     * Here is the process of this function
     * 1. Process any options passed to the Queue() application. Options here mean the third argument to Queue()
     * 2. Iterate trough the members of the queue, creating a callattempt corresponding to each member. During this
     *    iteration, we also check the dialed_interfaces datastore to see if we have already attempted calling this
     *    member. If we have, we do not create a callattempt. This is in place to prevent call forwarding loops. Also
     *    during each iteration, we call calc_metric to determine which members should be rung when.
     * 3. Call ring_one to place a call to the appropriate member(s)
     * 4. Call wait_for_answer to wait for an answer. If no one answers, return.
     * 5. Take care of any holdtime announcements, member delays, or other options which occur after a call has been answered.
     * 6. Start the monitor or mixmonitor if the option is set
     * 7. Remove the caller from the queue to allow other callers to advance
     * 8. Bridge the call.
     * 9. Do any post processing after the call has disconnected.
     *
     * \param[in] qe the queue_ent structure which corresponds to the caller attempting to reach members
    
     * \param[in] opts the options passed as the third parameter to the Queue() application
     * \param[in] opt_args the options passed as the third parameter to the Queue() application
    
     * \param[in] announceoverride filename to play to user when waiting 
    
     * \param[in] url the url passed as the fourth parameter to the Queue() application
     * \param[in,out] tries the number of times we have tried calling queue members
     * \param[out] noption set if the call to Queue() has the 'n' option set.
     * \param[in] agi the agi passed as the fifth parameter to the Queue() application
     * \param[in] macro the macro passed as the sixth parameter to the Queue() application
     * \param[in] gosub the gosub passed as the seventh parameter to the Queue() application
     * \param[in] ringing 1 if the 'r' option is set, otherwise 0
     */
    
    static int try_calling(struct queue_ent *qe, const struct ast_flags opts, char **opt_args, char *announceoverride, const char *url, int *tries, int *noption, const char *agi, const char *macro, const char *gosub, int ringing)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	struct member *cur;
    
    	struct callattempt *outgoing = NULL; /* the list of calls we are building */
    
    	char oldexten[AST_MAX_EXTENSION]="";
    
    	char oldcontext[AST_MAX_CONTEXT]="";
    
    	char queuename[256]="";
    
    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;
    
    	const char *monitorfilename;
    	const char *monitor_exec;
    	const char *monitor_options;
    	char tmpid[256], tmpid2[256];
    	char meid[1024], meid2[1024];
    	char mixmonargs[1512];
    	struct ast_app *mixmonapp = NULL;
    	char *p;
    
    	int callcompletedinsl;
    
    	struct ao2_iterator memi;
    
    	struct ast_datastore *datastore, *transfer_ds;
    
    	struct queue_end_bridge *queue_end_bridge = NULL;
    
    
    	ast_channel_lock(qe->chan);
    	datastore = ast_channel_datastore_find(qe->chan, &dialed_interface_info, NULL);
    	ast_channel_unlock(qe->chan);
    
    	memset(&bridge_config, 0, sizeof(bridge_config));
    
    
    	/* If we've already exceeded our timeout, then just stop
    	 * This should be extremely rare. queue_exec will take care
    	 * of removing the caller and reporting the timeout as the reason.
    	 */
    
    	if (qe->expire && now >= qe->expire) {
    
    
    	if (ast_test_flag(&opts, OPT_CALLEE_TRANSFER)) {
    		ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_REDIRECT);
    	}
    	if (ast_test_flag(&opts, OPT_CALLER_TRANSFER)) {
    		ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_REDIRECT);
    	}
    	if (ast_test_flag(&opts, OPT_CALLEE_AUTOMON)) {
    		ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_AUTOMON);
    	}
    	if (ast_test_flag(&opts, OPT_CALLER_AUTOMON)) {
    		ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_AUTOMON);
    	}
    	if (ast_test_flag(&opts, OPT_GO_ON)) {
    		ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_NO_H_EXTEN);
    	}
    	if (ast_test_flag(&opts, OPT_DATA_QUALITY)) {
    		nondataquality = 0;
    	}
    	if (ast_test_flag(&opts, OPT_CALLEE_HANGUP)) {
    		ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_DISCONNECT);
    	}
    	if (ast_test_flag(&opts, OPT_CALLER_HANGUP)) {
    		ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT);
    	}
    	if (ast_test_flag(&opts, OPT_CALLEE_PARK)) {
    		ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_PARKCALL);
    	}
    	if (ast_test_flag(&opts, OPT_CALLER_PARK)) {
    		ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_PARKCALL);
    	}
    	if (ast_test_flag(&opts, OPT_NO_RETRY)) {
    
    Olle Johansson's avatar
    Olle Johansson committed
    		if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY || qe->parent->strategy == QUEUE_STRATEGY_LINEAR 
    			|| qe->parent->strategy == QUEUE_STRATEGY_RRORDERED) {
    
    Olle Johansson's avatar
    Olle Johansson committed
    		} else {
    
    			*tries = ao2_container_count(qe->parent->members);
    
    		*noption = 1;
    	}
    	if (ast_test_flag(&opts, OPT_IGNORE_CALL_FW)) {
    		forwardsallowed = 0;
    	}
    
    	if (ast_test_flag(&opts, OPT_IGNORE_CONNECTEDLINE)) {
    		block_connected_line = 1;
    
    	}
    	if (ast_test_flag(&opts, OPT_CALLEE_AUTOMIXMON)) {
    		ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_AUTOMIXMON);
    	}
    	if (ast_test_flag(&opts, OPT_CALLER_AUTOMIXMON)) {
    		ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_AUTOMIXMON);
    	}
    	if (ast_test_flag(&opts, OPT_MARK_AS_ANSWERED)) {
    		qe->cancel_answered_elsewhere = 1;
    	}
    
    	/* if the calling channel has AST_CAUSE_ANSWERED_ELSEWHERE set, make sure this is inherited.
    
    	if (ast_channel_hangupcause(qe->chan) == AST_CAUSE_ANSWERED_ELSEWHERE) {
    
    	ast_debug(1, "%s is trying to call a queue member.\n",
    
    							ast_channel_name(qe->chan));
    
    	ast_copy_string(queuename, qe->parent->name, sizeof(queuename));
    
    Olle Johansson's avatar
    Olle Johansson committed
    	if (!ast_strlen_zero(qe->announce)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		announce = qe->announce;
    
    Olle Johansson's avatar
    Olle Johansson committed
    	}
    	if (!ast_strlen_zero(announceoverride)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		announce = announceoverride;