Skip to content
Snippets Groups Projects
app_queue.c 55.3 KiB
Newer Older
  • Learn to ignore specific revisions
  • 			record_abandoned(qe);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			res = -1;
    
    		} else {
    			if (digit && valid_exit(qe, digit))
    				res=digit;
    			else
    				/* Nobody answered, next please? */
    				res=0;
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    		goto out;
    	}
    	if (peer) {
    		/* Ah ha!  Someone answered within the desired timeframe.  Of course after this
    		   we will always return with -1 so that it is hung up properly after the 
    		   conversation.  */
    
    		if (!strcmp(qe->chan->type,"Zap")) {
    			if (tmp->dataquality) zapx = 0;
    			ast_channel_setoption(qe->chan,AST_OPTION_TONE_VERIFY,&zapx,sizeof(char),0);
    		}			
    		if (!strcmp(peer->type,"Zap")) {
    			if (tmp->dataquality) zapx = 0;
    			ast_channel_setoption(peer,AST_OPTION_TONE_VERIFY,&zapx,sizeof(char),0);
    		}
    
    		/* Update parameters for the queue */
    
    		recalc_holdtime(qe);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		hanguptree(outgoing, peer);
    		outgoing = NULL;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (announce) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			int res2;
    
    			res2 = ast_autoservice_start(qe->chan);
    
    				res2 = ast_streamfile(peer, announce, peer->language);
    
    				if (!res2)
    					res2 = ast_waitstream(peer, "");
    				else {
    					ast_log(LOG_WARNING, "Announcement file '%s' is unavailable, continuing anyway...\n", announce);
    					res2 = 0;
    				}
    			}
    
    			res2 |= ast_autoservice_stop(qe->chan);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			if (res2) {
    				/* Agent must have hung up */
    				ast_log(LOG_WARNING, "Agent on %s hungup on the customer.  They're going to be pissed.\n", peer->name);
    
    				ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "AGENTDUMP", "%s", "");
    
    Mark Spencer's avatar
    Mark Spencer committed
    				ast_hangup(peer);
    				return -1;
    			}
    		}
    
    		/* Stop music on hold */
    		ast_moh_stop(qe->chan);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		/* If appropriate, log that we have a destination channel */
    		if (qe->chan->cdr)
    			ast_cdr_setdestchan(qe->chan->cdr, peer->name);
    		/* Make sure channels are compatible */
    		res = ast_channel_make_compatible(qe->chan, peer);
    		if (res < 0) {
    
    			ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "SYSCOMPAT", "%s", "");
    
    Mark Spencer's avatar
    Mark Spencer committed
    			ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", qe->chan->name, peer->name);
    			ast_hangup(peer);
    			return -1;
    		}
    
    		/* Begin Monitoring */
    		if (qe->parent->monfmt && *qe->parent->monfmt) {
    			ast_monitor_start( peer, qe->parent->monfmt, qe->chan->cdr->uniqueid, 1 );
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    		/* Drop out of the queue at this point, to prepare for next caller */
    		leave_queue(qe);			
    
     		if( url && !ast_strlen_zero(url) && ast_channel_supports_html(peer) ) {
    
    Mark Spencer's avatar
    Mark Spencer committed
     			ast_log(LOG_DEBUG, "app_queue: sendurl=%s.\n", url);
     			ast_channel_sendurl( peer, url );
    
    		ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "CONNECT", "%ld", (long)time(NULL) - qe->start);
    		strncpy(oldcontext, qe->chan->context, sizeof(oldcontext) - 1);
    		strncpy(oldexten, qe->chan->exten, sizeof(oldexten) - 1);
    		time(&callstart);
    
    
    		memset(&config,0,sizeof(struct ast_bridge_config));
            config.allowredirect_in = allowredir_in;
            config.allowredirect_out = allowredir_out;
            config.allowdisconnect = allowdisconnect;
            bridge = ast_bridge_call(qe->chan,peer,&config);
    
    
    		if (strcasecmp(oldcontext, qe->chan->context) || strcasecmp(oldexten, qe->chan->exten)) {
    			ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "TRANSFER", "%s|%s", qe->chan->exten, qe->chan->context);
    		} else if (qe->chan->_softhangup) {
    			ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "COMPLETECALLER", "%ld|%ld", (long)(callstart - qe->start), (long)(time(NULL) - callstart));
    		} else {
    			ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "COMPLETEAGENT", "%ld|%ld", (long)(callstart - qe->start), (long)(time(NULL) - callstart));
    		}
    
    
    		if(bridge != AST_PBX_NO_HANGUP_PEER)
    			ast_hangup(peer);
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if( bridge == 0 ) res=1; /* JDG: bridge successfull, leave app_queue */
    		else res = bridge; /* bridge error, stay in the queue */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}	
    out:
    	hanguptree(outgoing, NULL);
    	return res;
    }
    
    static int wait_a_bit(struct queue_ent *qe)
    {
    
    	/* Don't need to hold the lock while we setup the outgoing calls */
    	int retrywait = qe->parent->retry * 1000;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return ast_waitfordigit(qe->chan, retrywait);
    }
    
    
    // [PHM 06/26/03]
    
    static struct member * interface_exists( struct ast_call_queue * q, char * interface )
    {
    	struct member * ret = NULL ;
    	struct member *mem;
    	char buf[500] ;
    
    	if( q != NULL )
    	{
    		mem = q->members ;
    
    		while( mem != NULL ) {
    			sprintf( buf, "%s/%s", mem->tech, mem->loc);
    
    			if( strcmp( buf, interface ) == 0 ) {
    				ret = mem ;
    				break ;
    			}
    			else
    				mem = mem->next ;
    		}
    	}
    
    	return( ret ) ;
    }
    
    
    static struct member * create_queue_node( char * interface )
    {
    	struct member * cur ;
    	char * tmp ;
    	
    	/* Add a new member */
    
    	cur = malloc(sizeof(struct member));
    
    	if (cur) {
    		memset(cur, 0, sizeof(struct member));
    		strncpy(cur->tech, interface, sizeof(cur->tech) - 1);
    		if ((tmp = strchr(cur->tech, '/')))
    			*tmp = '\0';
    		if ((tmp = strchr(interface, '/'))) {
    			tmp++;
    			strncpy(cur->loc, tmp, sizeof(cur->loc) - 1);
    		} else
    			ast_log(LOG_WARNING, "No location at interface '%s'\n", interface);
    	}
    
    	return( cur ) ;
    }
    
    
    static int rqm_exec(struct ast_channel *chan, void *data)
    {
    	int res=-1;
    	struct localuser *u;
    	char *queuename;
    	struct member * node ;
    	struct member * look ;
    	char info[512];
    
    	char tmpchan[256]="";
    
    	char *interface=NULL;
    	struct ast_call_queue *q;
    	int found=0 ;
    
    	if (!data) {
    		ast_log(LOG_WARNING, "RemoveQueueMember requires an argument (queuename|optional interface)\n");
    		return -1;
    	}
    	
    	LOCAL_USER_ADD(u); // not sure if we need this, but better be safe than sorry ;-)
    	
    	/* Parse our arguments XXX Check for failure XXX */
    	strncpy(info, (char *)data, strlen((char *)data) + AST_MAX_EXTENSION-1);
    	queuename = info;
    	if (queuename) {
    		interface = strchr(queuename, '|');
    		if (interface) {
    			*interface = '\0';
    			interface++;
    		}
    
    		else {
    			strncpy(tmpchan, chan->name, sizeof(tmpchan) - 1);
    			interface = strrchr(tmpchan, '-');
    			if (interface)
    				*interface = '\0';
    			interface = tmpchan;
    		}
    
    	}
    
    	if( ( q = queues) != NULL )
    	{
    		while( q && ( res != 0 ) && (!found) ) 
    		{
    
    			if( strcmp( q->name, queuename) == 0 )
    			{
    				// found queue, try to remove  interface
    				found=1 ;
    
    				if( ( node = interface_exists( q, interface ) ) != NULL )
    				{
    					if( ( look = q->members ) == node )
    					{
    						// 1st
    						q->members = node->next;
    					}
    					else
    					{
    						while( look != NULL )
    							if( look->next == node )
    							{
    								look->next = node->next ;
    								break ;
    							}
    							else
    								look = look->next ;
    					}
    
    					free( node ) ;
    
    					ast_log(LOG_NOTICE, "Removed interface '%s' to queue '%s'\n", 
    						interface, queuename);
    					res = 0 ;
    				}
    				else
    
    					ast_log(LOG_WARNING, "Unable to remove interface '%s' from queue '%s': "
    						"Not there\n", interface, queuename);
    
    	                                if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 101, chan->callerid))
    						{
    						chan->priority += 100;
    						res = 0 ;
    						}
    				}
    
    			q = q->next;
    		}
    	}
    
    	if( ! found )
    		ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': No such queue\n", queuename);
    
    	LOCAL_USER_REMOVE(u);
    	return res;
    }
    
    
    
    static int aqm_exec(struct ast_channel *chan, void *data)
    {
    	int res=-1;
    	struct localuser *u;
    	char *queuename;
    	char info[512];
    
    	char tmpchan[512]="";
    
    	char *interface=NULL;
    	struct ast_call_queue *q;
    	struct member *save;
    	int found=0 ;
    
    	if (!data) {
    		ast_log(LOG_WARNING, "AddQueueMember requires an argument (queuename|optional interface)\n");
    		return -1;
    	}
    	
    	LOCAL_USER_ADD(u); // not sure if we need this, but better be safe than sorry ;-)
    	
    	/* Parse our arguments XXX Check for failure XXX */
    	strncpy(info, (char *)data, strlen((char *)data) + AST_MAX_EXTENSION-1);
    	queuename = info;
    	if (queuename) {
    		interface = strchr(queuename, '|');
    		if (interface) {
    			*interface = '\0';
    			interface++;
    		}
    
    		else {
    			strncpy(tmpchan, chan->name, sizeof(tmpchan) - 1);
    			interface = strrchr(tmpchan, '-');
    			if (interface)
    				*interface = '\0';
    			interface = tmpchan;
    		}
    
    	}
    
    	if( ( q = queues) != NULL )
    	{
    		while( q && ( res != 0 ) && (!found) ) 
    		{
    
    			if( strcmp( q->name, queuename) == 0 )
    			{
    				// found queue, try to enable interface
    				found=1 ;
    
    				if( interface_exists( q, interface ) == NULL )
    				{
    					save = q->members ;
    					q->members = create_queue_node( interface ) ;
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    					if( q->members != NULL ) {
    						q->members->dynamic = 1;
    
    						q->members->next = save ;
    
    Mark Spencer's avatar
    Mark Spencer committed
    					} else
    
    						q->members = save ;
    
    					ast_log(LOG_NOTICE, "Added interface '%s' to queue '%s'\n", interface, queuename);
    					res = 0 ;
    				}
    				else
    
    					ast_log(LOG_WARNING, "Unable to add interface '%s' to queue '%s': "
    						"Already there\n", interface, queuename);
    
    			                if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 101, chan->callerid))
                                            {
                                                    chan->priority += 100;
                                                    res = 0 ;
                                            }
    				}
    
    			q = q->next;
    		}
    	}
    
    	if( ! found )
    		ast_log(LOG_WARNING, "Unable to add interface to queue '%s': No such queue\n", queuename);
    
    	LOCAL_USER_REMOVE(u);
    	return res;
    }
    
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int queue_exec(struct ast_channel *chan, void *data)
    {
    	int res=-1;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	struct localuser *u;
    	char *queuename;
    	char info[512];
    	char *options = NULL;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char *url = NULL;
    	char *announceoverride = NULL;
    
    	char *queuetimeoutstr = NULL;
    
    	/* whether to exit Queue application after the timeout hits */
    	int go_on = 0;
    
    
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Our queue entry */
    	struct queue_ent qe;
    	
    	if (!data) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_log(LOG_WARNING, "Queue requires an argument (queuename|optional timeout|optional URL)\n");
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return -1;
    	}
    	
    	LOCAL_USER_ADD(u);
    
    
    	/* Setup our queue entry */
    	memset(&qe, 0, sizeof(qe));
    
    Mark Spencer's avatar
    Mark Spencer committed
    	
    	/* Parse our arguments XXX Check for failure XXX */
    	strncpy(info, (char *)data, strlen((char *)data) + AST_MAX_EXTENSION-1);
    	queuename = info;
    	if (queuename) {
    		options = strchr(queuename, '|');
    		if (options) {
    			*options = '\0';
    			options++;
    
    Mark Spencer's avatar
    Mark Spencer committed
    			url = strchr(options, '|');
    			if (url) {
    				*url = '\0';
    				url++;
    				announceoverride = strchr(url, '|');
    				if (announceoverride) {
    					*announceoverride = '\0';
    					announceoverride++;
    
    					queuetimeoutstr = strchr(announceoverride, '|');
    					if (queuetimeoutstr) {
    						*queuetimeoutstr = '\0';
    						queuetimeoutstr++;
    						qe.queuetimeout = atoi(queuetimeoutstr);
    					} else {
    						qe.queuetimeout = 0;
    
    	if (options) {
    		if (strchr(options, 'r')) {
    			ringing = 1;
    	  	}
            }
    
    
    	printf("queue: %s, options: %s, url: %s, announce: %s, timeout: %d\n",
    		queuename, options, url, announceoverride, qe.queuetimeout);
    
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    	qe.chan = chan;
    	qe.start = time(NULL);
    
    	qe.last_pos_said = 0;
    	qe.last_pos = 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (!join_queue(queuename, &qe)) {
    
    		ast_queue_log(queuename, chan->uniqueid, "NONE", "ENTERQUEUE", "%s|%s", url ? url : "", chan->callerid ? chan->callerid : "");
    
    Mark Spencer's avatar
    Mark Spencer committed
    		/* Start music on hold */
    
    		if (ringing) {
    			ast_indicate(chan, AST_CONTROL_RINGING);
    		} else {              
    			ast_moh_start(chan, qe.moh);
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    		for (;;) {
    
    			/* This is the wait loop for callers 2 through maxlen */
    
    
    			res = wait_our_turn(&qe, ringing);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			/* If they hungup, return immediately */
    			if (res < 0) {
    
    				/* Record this abandoned call */
    				record_abandoned(&qe);
    
    				ast_queue_log(queuename, chan->uniqueid, "NONE", "ABANDON", "%d|%d|%ld", qe.pos, qe.opos, (long)time(NULL) - qe.start);
    
    Mark Spencer's avatar
    Mark Spencer committed
    				if (option_verbose > 2) {
    					ast_verbose(VERBOSE_PREFIX_3 "User disconnected while waiting their turn\n");
    					res = -1;
    				}
    				break;
    			}
    
    Mark Spencer's avatar
    Mark Spencer committed
    				break;
    
    			if (valid_exit(&qe, res)) {
    				ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHKEY", "%c|%d", res, qe.pos);
    
    Mark Spencer's avatar
    Mark Spencer committed
    				break;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    		if (!res) {
    			for (;;) {
    
    				/* This is the wait loop for the head caller*/
    				/* To exit, they may get their call answered; */
    				/* they may dial a digit from the queue context; */
    				/* or, they may may timeout. */
    
    				/* Leave if we have exceeded our queuetimeout */
    				if (qe.queuetimeout && ( (time(NULL) - qe.start) >= qe.queuetimeout) ) {
    					res = 0;
    					break;
    				}
    
    				/* Make a position announcement, if enabled */
    
    				if (qe.parent->announcefrequency && !ringing)
    
    					say_position(&qe);
    
    				/* Try calling all queue members for 'timeout' seconds */
    
    				res = try_calling(&qe, options, announceoverride, url, &go_on);
    
    				if (res) {
    					if (res < 0) {
    						if (!qe.handled)
    							ast_queue_log(queuename, chan->uniqueid, "NONE", "ABANDON", "%d|%d|%ld", qe.pos, qe.opos, (long)time(NULL) - qe.start);
    					} else if (res > 0)
    						ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHKEY", "%c|%d", res, qe.pos);
    
    Mark Spencer's avatar
    Mark Spencer committed
    					break;
    
    
    				/* Leave if we have exceeded our queuetimeout */
    				if (qe.queuetimeout && ( (time(NULL) - qe.start) >= qe.queuetimeout) ) {
    					res = 0;
    					break;
    				}
    
    				/* OK, we didn't get anybody; wait for 'retry' seconds; may get a digit to exit with */
    
    Mark Spencer's avatar
    Mark Spencer committed
    				res = wait_a_bit(&qe);
    				if (res < 0) {
    
    					ast_queue_log(queuename, chan->uniqueid, "NONE", "ABANDON", "%d|%d|%ld", qe.pos, qe.opos, (long)time(NULL) - qe.start);
    
    Mark Spencer's avatar
    Mark Spencer committed
    					if (option_verbose > 2) {
    						ast_verbose(VERBOSE_PREFIX_3 "User disconnected when they almost made it\n");
    						res = -1;
    					}
    					break;
    				}
    
    				if (res && valid_exit(&qe, res)) {
    					ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHKEY", "%c|%d", res, qe.pos);
    
    Mark Spencer's avatar
    Mark Spencer committed
    					break;
    
    				/* exit after 'timeout' cycle if 'n' option enabled */
    
    				if (go_on) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    					if (option_verbose > 2) {
    						ast_verbose(VERBOSE_PREFIX_3 "Exiting on time-out cycle\n");
    						res = -1;
    					}
    
    					ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
    
    					res = 0;
    					break;
    				}
    
    Mark Spencer's avatar
    Mark Spencer committed
    			}
    		}
    		/* Don't allow return code > 0 */
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (res > 0 && res != AST_PBX_KEEPALIVE) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			res = 0;	
    
    			if (ringing) {
    				ast_indicate(chan, -1);
    			} else {
    				ast_moh_stop(chan);
    			}			
    
    			ast_stopstream(chan);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		leave_queue(&qe);
    	} else {
    		ast_log(LOG_WARNING, "Unable to join queue '%s'\n", queuename);
    		res =  0;
    	}
    	LOCAL_USER_REMOVE(u);
    	return res;
    }
    
    static void reload_queues(void)
    {
    	struct ast_call_queue *q, *ql, *qn;
    	struct ast_config *cfg;
    	char *cat, *tmp;
    	struct ast_variable *var;
    	struct member *prev, *cur;
    	int new;
    	cfg = ast_load("queues.conf");
    	if (!cfg) {
    		ast_log(LOG_NOTICE, "No call queueing config file, so no call queues\n");
    		return;
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Mark all queues as dead for the moment */
    	q = queues;
    	while(q) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		q = q->next;
    	}
    	/* Chug through config file */
    	cat = ast_category_browse(cfg, NULL);
    	while(cat) {
    		if (strcasecmp(cat, "general")) {
    			/* Look for an existing one */
    			q = queues;
    			while(q) {
    				if (!strcmp(q->name, cat))
    					break;
    				q = q->next;
    			}
    			if (!q) {
    				/* Make one then */
    				q = malloc(sizeof(struct ast_call_queue));
    				if (q) {
    					/* Initialize it */
    					memset(q, 0, sizeof(struct ast_call_queue));
    
    Mark Spencer's avatar
    Mark Spencer committed
    					strncpy(q->name, cat, sizeof(q->name));
    					new = 1;
    				} else new = 0;
    			} else
    					new = 0;
    			if (q) {
    				if (!new) 
    
    Mark Spencer's avatar
    Mark Spencer committed
    				/* Re-initialize the queue */
    				q->dead = 0;
    				q->retry = 0;
    				q->timeout = -1;
    				q->maxlen = 0;
    
    				q->announcefrequency = 0;
    				q->announceholdtime = 0;
    				q->holdtime = 0;
    				q->callscompleted = 0;
    				q->callsabandoned = 0;
    				q->callscompletedinsl = 0;
    				q->servicelevel = 0;
    
    				free_members(q, 0);
    
    Mark Spencer's avatar
    Mark Spencer committed
    				strcpy(q->moh, "");
    				strcpy(q->announce, "");
    				strcpy(q->context, "");
    
    				strcpy(q->monfmt, "");
    				strcpy(q->sound_next, "queue-youarenext");
    				strcpy(q->sound_thereare, "queue-thereare");
    				strcpy(q->sound_calls, "queue-callswaiting");
    				strcpy(q->sound_holdtime, "queue-holdtime");
    				strcpy(q->sound_minutes, "queue-minutes");
    				strcpy(q->sound_thanks, "queue-thankyou");
    
    				prev = q->members;
    				if (prev) {
    					/* find the end of any dynamic members */
    					while(prev->next)
    						prev = prev->next;
    				}
    
    Mark Spencer's avatar
    Mark Spencer committed
    				var = ast_variable_browse(cfg, cat);
    				while(var) {
    					if (!strcasecmp(var->name, "member")) {
    						/* Add a new member */
    						cur = malloc(sizeof(struct member));
    						if (cur) {
    							memset(cur, 0, sizeof(struct member));
    							strncpy(cur->tech, var->value, sizeof(cur->tech) - 1);
    
    							if ((tmp = strchr(cur->tech, ','))) {
    								*tmp = '\0';
    								tmp++;
    								cur->penalty = atoi(tmp);
    								if (cur->penalty < 0)
    									cur->penalty = 0;
    							}
    
    Mark Spencer's avatar
    Mark Spencer committed
    							if ((tmp = strchr(cur->tech, '/')))
    								*tmp = '\0';
    							if ((tmp = strchr(var->value, '/'))) {
    								tmp++;
    								strncpy(cur->loc, tmp, sizeof(cur->loc) - 1);
    
    								if ((tmp = strchr(cur->loc, ',')))
    									*tmp = '\0';
    
    Mark Spencer's avatar
    Mark Spencer committed
    							} else
    								ast_log(LOG_WARNING, "No location at line %d of queue.conf\n", var->lineno);
    							if (prev)
    								prev->next = cur;
    							else
    								q->members = cur;
    							prev = cur;
    						}
    					} else if (!strcasecmp(var->name, "music")) {
    						strncpy(q->moh, var->value, sizeof(q->moh) - 1);
    					} else if (!strcasecmp(var->name, "announce")) {
    						strncpy(q->announce, var->value, sizeof(q->announce) - 1);
    					} else if (!strcasecmp(var->name, "context")) {
    						strncpy(q->context, var->value, sizeof(q->context) - 1);
    					} else if (!strcasecmp(var->name, "timeout")) {
    						q->timeout = atoi(var->value);
    
    					} else if (!strcasecmp(var->name, "monitor-format")) {
    						strncpy(q->monfmt, var->value, sizeof(q->monfmt) - 1);
    					} else if (!strcasecmp(var->name, "queue-youarenext")) {
    						strncpy(q->sound_next, var->value, sizeof(q->sound_next) - 1);
    					} else if (!strcasecmp(var->name, "queue-thereare")) {
    						strncpy(q->sound_thereare, var->value, sizeof(q->sound_thereare) - 1);
    					} else if (!strcasecmp(var->name, "queue-callswaiting")) {
    						strncpy(q->sound_calls, var->value, sizeof(q->sound_calls) - 1);
    					} else if (!strcasecmp(var->name, "queue-holdtime")) {
    						strncpy(q->sound_holdtime, var->value, sizeof(q->sound_holdtime) - 1);
    					} else if (!strcasecmp(var->name, "queue-minutes")) {
    						strncpy(q->sound_minutes, var->value, sizeof(q->sound_minutes) - 1);
    					} else if (!strcasecmp(var->name, "queue-thankyou")) {
    						strncpy(q->sound_thanks, var->value, sizeof(q->sound_thanks) - 1);
    					} else if (!strcasecmp(var->name, "announce-frequency")) {
    						q->announcefrequency = atoi(var->value);
    					} else if (!strcasecmp(var->name, "announce-holdtime")) {
    						q->announceholdtime = (!strcasecmp(var->value,"once")) ? 1 : ast_true(var->value);
    
    Mark Spencer's avatar
    Mark Spencer committed
    					} else if (!strcasecmp(var->name, "retry")) {
    						q->retry = atoi(var->value);
    					} else if (!strcasecmp(var->name, "maxlen")) {
    						q->maxlen = atoi(var->value);
    
    					} else if (!strcasecmp(var->name, "servicelevel")) {
    						q->servicelevel= atoi(var->value);
    
    					} else if (!strcasecmp(var->name, "strategy")) {
    						q->strategy = strat2int(var->value);
    						if (q->strategy < 0) {
    							ast_log(LOG_WARNING, "'%s' isn't a valid strategy, using ringall instead\n", var->value);
    							q->strategy = 0;
    						}
    
    Mark Spencer's avatar
    Mark Spencer committed
    					} else {
    						ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s at line %d of queue.conf\n", cat, var->name, var->lineno);
    					}
    					var = var->next;
    				}
    				if (q->retry < 1)
    					q->retry = DEFAULT_RETRY;
    				if (q->timeout < 0)
    					q->timeout = DEFAULT_TIMEOUT;
    				if (q->maxlen < 0)
    					q->maxlen = 0;
    				if (!new) 
    
    Mark Spencer's avatar
    Mark Spencer committed
    				if (new) {
    					q->next = queues;
    					queues = q;
    				}
    			}
    		}
    		cat = ast_category_browse(cfg, cat);
    	}
    
    	ast_destroy(cfg);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	q = queues;
    	ql = NULL;
    	while(q) {
    		qn = q->next;
    		if (q->dead) {
    			if (ql)
    				ql->next = q->next;
    			else
    				queues = q->next;
    			if (!q->count) {
    				free(q);
    			} else
    				ast_log(LOG_WARNING, "XXX Leaking a litttle memory :( XXX\n");
    		} else
    			ql = q;
    		q = qn;
    	}
    
    static int __queues_show(int fd, int argc, char **argv, int queue_show)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    	struct ast_call_queue *q;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	struct queue_ent *qe;
    	struct member *mem;
    	int pos;
    	time_t now;
    	char max[80];
    
    Mark Spencer's avatar
    Mark Spencer committed
    	time(&now);
    
    	if ((!queue_show && argc != 2) || (queue_show && argc != 3))
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return RESULT_SHOWUSAGE;
    
    	ast_mutex_lock(&qlock);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	q = queues;
    	if (!q) {	
    
    		ast_mutex_unlock(&qlock);
    
    		if (queue_show)
    			ast_cli(fd, "No such queue: %s.\n",argv[2]);
    		else
    			ast_cli(fd, "No queues.\n");
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return RESULT_SUCCESS;
    	}
    	while(q) {
    
    		if (queue_show) {
    			if (strcasecmp(q->name, argv[2]) != 0) {
    				ast_mutex_unlock(&q->lock);
    
    				if (!q) {
    					ast_cli(fd, "No such queue: %s.\n",argv[2]);
    					break;
    				}
    				continue;
    			}
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (q->maxlen)
    			snprintf(max, sizeof(max), "%d", q->maxlen);
    		else
    			strcpy(max, "unlimited");
    
    		sl = 0;
    		if(q->callscompleted > 0)
    			sl = 100*((float)q->callscompletedinsl/(float)q->callscompleted);
    		ast_cli(fd, "%-12.12s has %d calls (max %s) in '%s' strategy (%ds holdtime), C:%d, A:%d, SL:%2.1f%% within %ds\n",
    			q->name, q->count, max, int2strat(q->strategy), q->holdtime, q->callscompleted, q->callsabandoned,sl,q->servicelevel);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (q->members) {
    			ast_cli(fd, "   Members: \n");
    
    			for (mem = q->members; mem; mem = mem->next) {
    				if (mem->penalty)
    
    Mark Spencer's avatar
    Mark Spencer committed
    					snprintf(max, sizeof(max) - 20, " with penalty %d", mem->penalty);
    
    Mark Spencer's avatar
    Mark Spencer committed
    				if (mem->dynamic)
    					strcat(max, " (dynamic)");
    
    				if (mem->calls) {
    					snprintf(calls, sizeof(calls), " has taken %d calls (last was %ld secs ago)",
    
    							mem->calls, (long)(time(NULL) - mem->lastcall));
    
    				} else
    					strcpy(calls, " has taken no calls yet");
    				ast_cli(fd, "      %s/%s%s%s\n", mem->tech, mem->loc, max, calls);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		} else
    			ast_cli(fd, "   No Members\n");
    		if (q->head) {
    			pos = 1;
    			ast_cli(fd, "   Callers: \n");
    			for (qe = q->head; qe; qe = qe->next) 
    
    				ast_cli(fd, "      %d. %s (wait: %ld:%2.2ld)\n", pos++, qe->chan->name,
    
    								(long)(now - qe->start) / 60, (long)(now - qe->start) % 60);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		} else
    			ast_cli(fd, "   No Callers\n");
    		ast_cli(fd, "\n");
    
    Mark Spencer's avatar
    Mark Spencer committed
    		q = q->next;
    
    		if (queue_show)
    			break;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    	ast_mutex_unlock(&qlock);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return RESULT_SUCCESS;
    }
    
    
    static int queues_show(int fd, int argc, char **argv)
    {
    	return __queues_show(fd, argc, argv, 0);
    }
    
    static int queue_show(int fd, int argc, char **argv)
    {
    	return __queues_show(fd, argc, argv, 1);
    }
    
    static char *complete_queue(char *line, char *word, int pos, int state)
    {
    	struct ast_call_queue *q;
    	int which=0;
    	
    	ast_mutex_lock(&qlock);
    	q = queues;
    	while(q) {
    		if (!strncasecmp(word, q->name, strlen(word))) {
    			if (++which > state)
    				break;
    		}
    		q = q->next;
    	}
    	ast_mutex_unlock(&qlock);
    	return q ? strdup(q->name) : NULL;
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    /* JDG: callback to display queues status in manager */
    static int manager_queues_show( struct mansession *s, struct message *m )
    {
    	char *a[] = { "show", "queues" };
    	return queues_show( s->fd, 2, a );
    } /* /JDG */
    
    
    
    /* Dump queue status */
    static int manager_queues_status( struct mansession *s, struct message *m )
    {
    	time_t now;
    	int pos;
    
    	char *id = astman_get_header(m,"ActionID");
    	char idText[256] = "";
    
    	struct ast_call_queue *q;
    	struct queue_ent *qe;
    
    	float sl = 0;
    	struct member *mem;
    
    	astman_send_ack(s, m, "Queue status will follow");
    
    	time(&now);
    
    	ast_mutex_lock(&qlock);
    
    	q = queues;
    
    	if (id && &id) {
    		snprintf(idText,256,"ActionID: %s\r\n",id);
    	}
    
    	while(q) {
    
    
    		/* List queue properties */
    		if(q->callscompleted > 0)
    			sl = 100*((float)q->callscompletedinsl/(float)q->callscompleted);
    
    		ast_cli(s->fd, "Event: QueueParams\r\n"
    					"Queue: %s\r\n"
    					"Max: %d\r\n"
    					"Calls: %d\r\n"
    
    					"Holdtime: %d\r\n"
    					"Completed: %d\r\n"
    					"Abandoned: %d\r\n"
    					"ServiceLevel: %d\r\n"
    					"ServicelevelPerf: %2.1f\r\n"
    
    					"\r\n",
    
    						q->name, q->maxlen, q->count, q->holdtime, q->callscompleted,
    						q->callsabandoned, q->servicelevel, sl, idText);
    
    		/* List Queue Members */
    
    		for (mem = q->members; mem; mem = mem->next) 
    
    			ast_cli(s->fd, "Event: QueueMember\r\n"
    				"Queue: %s\r\n"
    				"Location: %s/%s\r\n"
    				"Membership: %s\r\n"
    				"Penalty: %d\r\n"
    				"CallsTaken: %d\r\n"
    				"LastCall: %ld\r\n"
    				"%s"
    				"\r\n",
    					q->name, mem->tech, mem->loc, mem->dynamic ? "dynamic" : "static",
    					mem->penalty, mem->calls, mem->lastcall, idText);
    
    		/* List Queue Entries */
    
    
    		pos = 1;
    		for (qe = q->head; qe; qe = qe->next) 
    
    			ast_cli(s->fd, "Event: QueueEntry\r\n"
    
    				"Queue: %s\r\n"
    				"Position: %d\r\n"
    				"Channel: %s\r\n"
    				"CallerID: %s\r\n"
    				"Wait: %ld\r\n"
    
    				"\r\n", 
    
    					q->name, pos++, qe->chan->name, (qe->chan->callerid ? qe->chan->callerid : ""), (long)(now - qe->start), idText);
    
    		q = q->next;
    	}
    
    	ast_mutex_unlock(&qlock);
    
    	return RESULT_SUCCESS;
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    static char show_queues_usage[] = 
    "Usage: show queues\n"
    "       Provides summary information on call queues.\n";
    
    static struct ast_cli_entry cli_show_queues = {
    	{ "show", "queues", NULL }, queues_show, 
    	"Show status of queues", show_queues_usage, NULL };
    
    
    static char show_queue_usage[] = 
    "Usage: show queue\n"
    "       Provides summary information on a specified queue.\n";
    
    static struct ast_cli_entry cli_show_queue = {
    	{ "show", "queue", NULL }, queue_show, 
    	"Show status of a specified queue", show_queue_usage, complete_queue };
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    int unload_module(void)
    {
    	STANDARD_HANGUP_LOCALUSERS;
    
    	ast_cli_unregister(&cli_show_queue);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	ast_cli_unregister(&cli_show_queues);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	ast_manager_unregister( "Queues" );
    
    	ast_manager_unregister( "QueueStatus" );
    
    	ast_unregister_application(app_aqm);
    	ast_unregister_application(app_rqm);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return ast_unregister_application(app);
    }
    
    int load_module(void)
    {
    	int res;
    	res = ast_register_application(app, queue_exec, synopsis, descrip);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (!res) {
    
    		ast_cli_register(&cli_show_queue);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_cli_register(&cli_show_queues);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_manager_register( "Queues", 0, manager_queues_show, "Queues" );
    
    		ast_manager_register( "QueueStatus", 0, manager_queues_status, "Queue Status" );
    
    
    		// [PHM 06/26/03]
    		ast_register_application(app_aqm, aqm_exec, app_aqm_synopsis, app_aqm_descrip) ;
    		ast_register_application(app_rqm, rqm_exec, app_rqm_synopsis, app_rqm_descrip) ;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	reload_queues();
    	return res;
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    int reload(void)
    {
    	reload_queues();
    	return 0;
    }
    
    char *description(void)
    {
    	return tdesc;
    }
    
    int usecount(void)
    {
    	int res;
    	STANDARD_USECOUNT(res);
    	return res;
    }
    
    char *key()
    {
    	return ASTERISK_GPL_KEY;
    }