[asterisk-commits] branch oej/managerstuff r31354 - in /team/oej/managerstuff: ./ apps/ channels...

asterisk-commits at lists.digium.com asterisk-commits at lists.digium.com
Thu Jun 1 06:53:49 MST 2006


Author: oej
Date: Thu Jun  1 08:53:48 2006
New Revision: 31354

URL: http://svn.digium.com/view/asterisk?rev=31354&view=rev
Log:
Update

Modified:
    team/oej/managerstuff/   (props changed)
    team/oej/managerstuff/app.c
    team/oej/managerstuff/apps/app_dial.c
    team/oej/managerstuff/apps/app_macro.c
    team/oej/managerstuff/apps/app_meetme.c
    team/oej/managerstuff/apps/app_queue.c
    team/oej/managerstuff/apps/app_verbose.c
    team/oej/managerstuff/apps/app_voicemail.c
    team/oej/managerstuff/apps/app_while.c
    team/oej/managerstuff/asterisk.c
    team/oej/managerstuff/channels/chan_agent.c
    team/oej/managerstuff/channels/chan_h323.c
    team/oej/managerstuff/channels/chan_sip.c
    team/oej/managerstuff/channels/chan_zap.c
    team/oej/managerstuff/doc/README.variables
    team/oej/managerstuff/file.c
    team/oej/managerstuff/frame.c
    team/oej/managerstuff/funcs/func_logic.c
    team/oej/managerstuff/include/asterisk/app.h
    team/oej/managerstuff/redhat/asterisk.spec
    team/oej/managerstuff/res/res_features.c
    team/oej/managerstuff/rtp.c

Propchange: team/oej/managerstuff/
------------------------------------------------------------------------------
    automerge = http://edvina.net/training/

Propchange: team/oej/managerstuff/
------------------------------------------------------------------------------
--- svnmerge-integrated (original)
+++ svnmerge-integrated Thu Jun  1 08:53:48 2006
@@ -1,1 +1,1 @@
-/branches/1.2:1-7496,7498-24215
+/branches/1.2:1-7496,7498-27338

Modified: team/oej/managerstuff/app.c
URL: http://svn.digium.com/view/asterisk/team/oej/managerstuff/app.c?rev=31354&r1=31353&r2=31354&view=diff
==============================================================================
--- team/oej/managerstuff/app.c (original)
+++ team/oej/managerstuff/app.c Thu Jun  1 08:53:48 2006
@@ -538,7 +538,7 @@
 static int global_silence_threshold = 128;
 static int global_maxsilence = 0;
 
-int ast_play_and_record(struct ast_channel *chan, const char *playfile, const char *recordfile, int maxtime, const char *fmt, int *duration, int silencethreshold, int maxsilence, const char *path)
+int ast_play_and_record_full(struct ast_channel *chan, const char *playfile, const char *recordfile, int maxtime, const char *fmt, int *duration, int silencethreshold, int maxsilence, const char *path, const char *acceptdtmf, const char *canceldtmf)
 {
 	int d;
 	char *fmts;
@@ -689,19 +689,18 @@
 				/* Write only once */
 				ast_writestream(others[0], f);
 			} else if (f->frametype == AST_FRAME_DTMF) {
-				if (f->subclass == '#') {
+				if (strchr(acceptdtmf, f->subclass)) {
 					if (option_verbose > 2)
-						ast_verbose( VERBOSE_PREFIX_3 "User ended message by pressing %c\n", f->subclass);
-					res = '#';
+						ast_verbose(VERBOSE_PREFIX_3 "User ended message by pressing %c\n", f->subclass);
+					res = f->subclass;
 					outmsg = 2;
 					ast_frfree(f);
 					break;
 				}
-				if (f->subclass == '0') {
-				/* Check for a '0' during message recording also, in case caller wants operator */
+				if (strchr(canceldtmf, f->subclass)) {
 					if (option_verbose > 2)
-						ast_verbose(VERBOSE_PREFIX_3 "User cancelled by pressing %c\n", f->subclass);
-					res = '0';
+						ast_verbose(VERBOSE_PREFIX_3 "User cancelled message by pressing %c\n", f->subclass);
+					res = f->subclass;
 					outmsg = 0;
 					ast_frfree(f);
 					break;
@@ -761,6 +760,14 @@
 	if (sildet)
 		ast_dsp_free(sildet);
 	return res;
+}
+
+static char default_acceptdtmf[] = "#";
+static char default_canceldtmf[] = "0";
+
+int ast_play_and_record(struct ast_channel *chan, const char *playfile, const char *recordfile, int maxtime, const char *fmt, int *duration, int silencethreshold, int maxsilence, const char *path)
+{
+	return ast_play_and_record_full(chan, playfile, recordfile, maxtime, fmt, duration, silencethreshold, maxsilence, path, default_acceptdtmf, default_canceldtmf);
 }
 
 int ast_play_and_prepend(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt, int *duration, int beep, int silencethreshold, int maxsilence)

Modified: team/oej/managerstuff/apps/app_dial.c
URL: http://svn.digium.com/view/asterisk/team/oej/managerstuff/apps/app_dial.c?rev=31354&r1=31353&r2=31354&view=diff
==============================================================================
--- team/oej/managerstuff/apps/app_dial.c (original)
+++ team/oej/managerstuff/apps/app_dial.c Thu Jun  1 08:53:48 2006
@@ -114,7 +114,8 @@
 "    G(context^exten^pri) - If the call is answered, transfer the calling party to\n"
 "           the specified priority and the called party to the specified priority+1.\n"
 "           Optionally, an extension, or extension and context may be specified. \n"
-"           Otherwise, the current extension is used.\n"
+"           Otherwise, the current extension is used. You cannot use any additional\n"
+"           action post answer options in conjunction with this option.\n" 
 "    h    - Allow the called party to hang up by sending the '*' DTMF digit.\n"
 "    H    - Allow the calling party to hang up by hitting the '*' DTMF digit.\n"
 "    j    - Jump to priority n+101 if all of the requested channels were busy.\n"
@@ -147,6 +148,8 @@
 "           * GOTO:<context>^<exten>^<priority> - Transfer the call to the\n"
 "                          specified priority. Optionally, an extension, or\n"
 "                          extension and priority can be specified.\n"
+"           You cannot use any additional action post answer options in conjunction\n"
+"           with this option.\n"
 "    n    - This option is a modifier for the screen/privacy mode. It specifies\n"
 "           that no introductions are to be saved in the priv-callerintros\n"
 "           directory.\n"

Modified: team/oej/managerstuff/apps/app_macro.c
URL: http://svn.digium.com/view/asterisk/team/oej/managerstuff/apps/app_macro.c?rev=31354&r1=31353&r2=31354&view=diff
==============================================================================
--- team/oej/managerstuff/apps/app_macro.c (original)
+++ team/oej/managerstuff/apps/app_macro.c Thu Jun  1 08:53:48 2006
@@ -335,7 +335,7 @@
 			*label_b = '\0';
 			label_b++;
 		}
-		if (ast_true(expr))
+		if (pbx_checkcondition(expr))
 			macro_exec(chan, label_a);
 		else if (label_b) 
 			macro_exec(chan, label_b);

Modified: team/oej/managerstuff/apps/app_meetme.c
URL: http://svn.digium.com/view/asterisk/team/oej/managerstuff/apps/app_meetme.c?rev=31354&r1=31353&r2=31354&view=diff
==============================================================================
--- team/oej/managerstuff/apps/app_meetme.c (original)
+++ team/oej/managerstuff/apps/app_meetme.c Thu Jun  1 08:53:48 2006
@@ -2084,9 +2084,12 @@
 								break;
 							} else {
 								/* Pin invalid */
-								res = ast_streamfile(chan, "conf-invalidpin", chan->language);
-								if (!res)
-									ast_waitstream(chan, AST_DIGIT_ANY);
+								if (!ast_streamfile(chan, "conf-invalidpin", chan->language))
+									res = ast_waitstream(chan, AST_DIGIT_ANY);
+								else {
+									ast_log(LOG_WARNING, "Couldn't play invalid pin msg!\n");
+									break;
+								}
 								if (res < 0)
 									break;
 								pin[0] = res;

Modified: team/oej/managerstuff/apps/app_queue.c
URL: http://svn.digium.com/view/asterisk/team/oej/managerstuff/apps/app_queue.c?rev=31354&r1=31353&r2=31354&view=diff
==============================================================================
--- team/oej/managerstuff/apps/app_queue.c (original)
+++ team/oej/managerstuff/apps/app_queue.c Thu Jun  1 08:53:48 2006
@@ -91,13 +91,16 @@
 #include "asterisk/causes.h"
 #include "asterisk/astdb.h"
 #include "asterisk/devicestate.h"
-
-#define QUEUE_STRATEGY_RINGALL		0
-#define QUEUE_STRATEGY_ROUNDROBIN	1
-#define QUEUE_STRATEGY_LEASTRECENT	2
-#define QUEUE_STRATEGY_FEWESTCALLS	3
-#define QUEUE_STRATEGY_RANDOM		4
-#define QUEUE_STRATEGY_RRMEMORY		5
+#include "asterisk/stringfields.h"
+
+enum {
+	QUEUE_STRATEGY_RINGALL = 0,
+	QUEUE_STRATEGY_ROUNDROBIN,
+	QUEUE_STRATEGY_LEASTRECENT,
+	QUEUE_STRATEGY_FEWESTCALLS,
+	QUEUE_STRATEGY_RANDOM,
+	QUEUE_STRATEGY_RRMEMORY
+};
 
 static struct strategy {
 	int strategy;
@@ -128,7 +131,7 @@
 static char *synopsis = "Queue a call for a call queue";
 
 static char *descrip =
-"  Queue(queuename[|options[|URL][|announceoverride][|timeout]]):\n"
+"  Queue(queuename[|options[|URL][|announceoverride][|timeout][|AGI]):\n"
 "Queues an incoming call in a particular call queue as defined in queues.conf.\n"
 "This application will return to the dialplan if the queue does not exist, or\n"
 "any of the join options cause the caller to not enter the queue.\n"
@@ -147,6 +150,8 @@
 "up by another user.\n"
 "  The optional URL will be sent to the called party if the channel supports\n"
 "it.\n"
+"  The optional AGI parameter will setup an AGI script to be executed on the \n"
+"calling party's channel once they are connected to a queue member.\n"
 "  The timeout will cause the queue to fail out after a specified number of\n"
 "seconds, checked between each queues.conf 'timeout' and 'retry' cycle.\n"
 "  This application sets the following channel variable upon completion:\n"
@@ -233,6 +238,12 @@
 /*! \brief queues.conf per-queue weight option */
 static int use_weight = 0;
 
+/*! \brief queues.conf [general] option */
+static int autofill_default = 0;
+
+/*! \brief queues.conf [general] option */
+static int montype_default = 0;
+
 enum queue_result {
 	QUEUE_UNKNOWN = 0,
 	QUEUE_TIMEOUT = 1,
@@ -260,7 +271,8 @@
    use it not only for keeping track of what is in use but
    also for keeping track of who we're dialing. */
 
-struct localuser {
+struct callattempt {
+	struct callattempt *q_next;
 	struct ast_channel *chan;
 	char interface[256];
 	int stillgoing;
@@ -268,11 +280,9 @@
 	int oldstatus;
 	time_t lastcall;
 	struct member *member;
-	struct localuser *next;
 };
 
 LOCAL_USER_DECL;
-
 
 struct queue_ent {
 	struct ast_call_queue *parent;	/*!< What queue is our parent */
@@ -303,9 +313,17 @@
 	int status;			/*!< Status of queue member */
 	int paused;			/*!< Are we paused (not accepting calls)? */
 	time_t lastcall;		/*!< When last successful call was hungup */
-	int dead;			/*!< Used to detect members deleted in realtime */
+	unsigned int dead:1;			/*!< Used to detect members deleted in realtime */
+	unsigned int delme:1;		/*!< Flag to delete entry on reload */
 	struct member *next;		/*!< Next member */
 };
+
+struct ast_member_interfaces {
+	char interface[80];
+	AST_LIST_ENTRY(ast_member_interfaces) list;    /*!< Next call queue */
+};
+
+static AST_LIST_HEAD_STATIC(interfaces, ast_member_interfaces);
 
 /* values used in multi-bit flags in ast_call_queue */
 #define QUEUE_EMPTY_NORMAL 1
@@ -324,6 +342,8 @@
 	unsigned int joinempty:2;
 	unsigned int eventwhencalled:1;
 	unsigned int leavewhenempty:2;
+	unsigned int ringinuse:1;
+	unsigned int setinterfacevar:1;
 	unsigned int reportholdtime:1;
 	unsigned int wrapped:1;
 	unsigned int timeoutrestart:1;
@@ -340,6 +360,7 @@
 	int servicelevel;               /*!< seconds setting for servicelevel*/
 	int callscompletedinsl;         /*!< Number of calls answered with servicelevel*/
 	char monfmt[8];                 /*!< Format to use when recording calls */
+	int montype;			/*!< Monitor type  Monitor vs. MixMonitor */
 	char sound_next[80];            /*!< Sound file: "Your call is now first in line" (def. queue-youarenext) */
 	char sound_thereare[80];        /*!< Sound file: "There are currently" (def. queue-thereare) */
 	char sound_calls[80];           /*!< Sound file: "calls waiting to speak to a representative." (def. queue-callswaiting)*/
@@ -374,6 +395,16 @@
 
 static int set_member_paused(char *queuename, char *interface, int paused);
 
+static void rr_dep_warning(void)
+{
+	static unsigned int warned = 0;
+
+	if (!warned) {
+		ast_log(LOG_NOTICE, "The 'roundrobin' queue strategy is deprecated. Please use the 'rrmemory' strategy instead.\n");
+		warned = 1;
+	}
+}
+
 static void set_queue_result(struct ast_channel *chan, enum queue_result res)
 {
 	int i;
@@ -440,6 +471,8 @@
 	for (member = q->members; member; member = member->next) {
 		if (max_penalty && (member->penalty > max_penalty))
 			continue;
+
+		if (member->paused) continue;
 
 		switch (member->status) {
 		case AST_DEVICE_INVALID:
@@ -466,6 +499,7 @@
 	struct ast_call_queue *q;
 	struct statechange *sc = data;
 	struct member *cur;
+	struct ast_member_interfaces *curint;
 	char *loc;
 	char *technology;
 
@@ -478,36 +512,50 @@
 		free(sc);
 		return NULL;
 	}
-	if (option_debug)
-		ast_log(LOG_DEBUG, "Device '%s/%s' changed to state '%d' (%s)\n", technology, loc, sc->state, devstate2str(sc->state));
-	AST_LIST_LOCK(&queues);
-	AST_LIST_TRAVERSE(&queues, q, list) {
-		ast_mutex_lock(&q->lock);
-		cur = q->members;
-		while(cur) {
-			if (!strcasecmp(sc->dev, cur->interface)) {
-				if (cur->status != sc->state) {
-					cur->status = sc->state;
-					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: %d\r\n"
-							"Status: %d\r\n"
-							"Paused: %d\r\n",
-						q->name, cur->interface, cur->dynamic ? "dynamic" : "static",
-						cur->penalty, cur->calls, (int)cur->lastcall, cur->status, cur->paused);
+
+	AST_LIST_LOCK(&interfaces);
+	AST_LIST_TRAVERSE(&interfaces, curint, list) {
+		if (!strcasecmp(curint->interface, sc->dev))
+			break; 
+	}
+	AST_LIST_UNLOCK(&interfaces);
+
+	if (curint) {
+
+		if (option_debug)
+			ast_log(LOG_DEBUG, "Device '%s/%s' changed to state '%d' (%s)\n", technology, loc, sc->state, devstate2str(sc->state));
+		AST_LIST_LOCK(&queues);
+		AST_LIST_TRAVERSE(&queues, q, list) {
+			ast_mutex_lock(&q->lock);
+			cur = q->members;
+			while(cur) {
+				if (!strcasecmp(sc->dev, cur->interface)) {
+					if (cur->status != sc->state) {
+						cur->status = sc->state;
+						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: %d\r\n"
+								"Status: %d\r\n"
+								"Paused: %d\r\n",
+							q->name, cur->interface, cur->dynamic ? "dynamic" : "static",
+							cur->penalty, cur->calls, (int)cur->lastcall, cur->status, cur->paused);
+						}
 					}
 				}
-			}
-			cur = cur->next;
-		}
-		ast_mutex_unlock(&q->lock);
-	}
-	AST_LIST_UNLOCK(&queues);
+				cur = cur->next;
+			}
+			ast_mutex_unlock(&q->lock);
+		}
+		AST_LIST_UNLOCK(&queues);
+	} else {
+		if (option_debug)
+			ast_log(LOG_DEBUG, "Device '%s/%s' changed to state '%d' (%s) but we don't care because they're not a member of any queue.\n", technology, loc, sc->state, devstate2str(sc->state));
+	}
 	free(sc);
 	return NULL;
 }
@@ -573,6 +621,10 @@
 	q->announceholdtime = 0;
 	q->roundingseconds = 0; /* Default - don't announce seconds */
 	q->servicelevel = 0;
+	q->ringinuse = 1;
+	q->setinterfacevar = 0;
+	q->autofill = autofill_default;
+	q->montype = montype_default;
 	q->moh[0] = '\0';
 	q->announce[0] = '\0';
 	q->context[0] = '\0';
@@ -602,6 +654,87 @@
 	q->wrapuptime = 0;
 }
 
+static int add_to_interfaces(char *interface) 
+{
+	struct ast_member_interfaces *curint, *newint;
+
+	AST_LIST_LOCK(&interfaces);
+	AST_LIST_TRAVERSE(&interfaces, curint, list) {
+		if (!strcasecmp(curint->interface, interface))
+			break; 
+	}
+
+	if (!curint) {
+		if (option_debug)
+			ast_log(LOG_DEBUG, "Adding %s to the list of interfaces that make up all of our queue members.\n", interface);
+
+	        if ((newint = ast_calloc(1, sizeof(*newint)))) {
+			ast_copy_string(newint->interface, interface, sizeof(newint->interface));
+			AST_LIST_INSERT_HEAD(&interfaces, newint, list);
+		}
+	}
+	AST_LIST_UNLOCK(&interfaces);
+
+ return 0;
+}
+
+static int interface_exists_global(char *interface)
+{
+	struct ast_call_queue *q;
+	struct member *mem;
+	int ret = 0;
+
+	AST_LIST_LOCK(&queues);
+	AST_LIST_TRAVERSE(&queues, q, list) {
+		ast_mutex_lock(&q->lock);
+		for (mem = q->members; mem; mem = mem->next)
+			if (!strcasecmp(interface, mem->interface)) {
+				ast_mutex_unlock(&q->lock);
+				ret = 1;
+				break;
+			}
+		ast_mutex_unlock(&q->lock);
+	}
+	AST_LIST_UNLOCK(&queues);
+
+	return ret;
+}
+
+
+static int remove_from_interfaces(char *interface)
+{
+	struct ast_member_interfaces *curint;
+
+	AST_LIST_LOCK(&interfaces);
+	AST_LIST_TRAVERSE_SAFE_BEGIN(&interfaces, curint, list) {
+		if (!strcasecmp(curint->interface, interface) && !interface_exists_global(interface)) {
+			if (option_debug)
+				ast_log(LOG_DEBUG, "Removing %s from the list of interfaces that make up all of our queue members.\n", interface);
+			AST_LIST_REMOVE_CURRENT(&interfaces, list);
+			free(curint);
+		}
+	}
+	AST_LIST_TRAVERSE_SAFE_END;
+	AST_LIST_UNLOCK(&interfaces);
+
+ 	return 0;
+}
+
+static void clear_and_free_interfaces(void)
+{
+	struct ast_member_interfaces *curint;
+
+	AST_LIST_LOCK(&interfaces);
+	AST_LIST_TRAVERSE_SAFE_BEGIN(&interfaces, curint, list) {
+		AST_LIST_REMOVE_CURRENT(&interfaces, list);
+		free(curint);
+	}
+	AST_LIST_TRAVERSE_SAFE_END;
+	AST_LIST_UNLOCK(&interfaces);
+
+ 	return;
+}
+
 /*! \brief Configure a queue parameter.
 \par
    For error reporting, line number is passed for .conf static configuration.
@@ -624,6 +757,10 @@
 		q->timeout = atoi(val);
 		if (q->timeout < 0)
 			q->timeout = DEFAULT_TIMEOUT;
+	} else if (!strcasecmp(param, "ringinuse")) {
+		q->ringinuse = ast_true(val);
+	} else if (!strcasecmp(param, "setinterfacevar")) {
+		q->setinterfacevar = ast_true(val);
 	} else if (!strcasecmp(param, "monitor-join")) {
 		q->monjoin = ast_true(val);
 	} else if (!strcasecmp(param, "monitor-format")) {
@@ -696,6 +833,9 @@
 		q->wrapuptime = atoi(val);
 	} else if (!strcasecmp(param, "autofill")) {
 		q->autofill = ast_true(val);
+	} else if (!strcasecmp(param, "monitor-type")) {
+		if (!strcasecmp(val, "mixmonitor"))
+			q->montype = 1;
 	} else if (!strcasecmp(param, "autopause")) {
 		q->autopause = ast_true(val);
 	} else if (!strcasecmp(param, "maxlen")) {
@@ -709,7 +849,7 @@
 		if (q->strategy < 0) {
 			ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
 				val, q->name);
-			q->strategy = 0;
+			q->strategy = QUEUE_STRATEGY_RINGALL;
 		}
 	} else if (!strcasecmp(param, "joinempty")) {
 		if (!strcasecmp(val, "strict"))
@@ -775,6 +915,7 @@
 		m = create_queue_member(interface, penalty, 0);
 		if (m) {
 			m->dead = 0;
+			add_to_interfaces(interface);
 			if (prev_m) {
 				prev_m->next = m;
 			} else {
@@ -799,6 +940,7 @@
 				prev->next = next;
 			else
 				q->members = next;
+			remove_from_interfaces(curm->interface);
 			free(curm);
 		} else 
 			prev = curm;
@@ -896,6 +1038,9 @@
 		v = v->next;
 	}
 
+	if (q->strategy == QUEUE_STRATEGY_ROUNDROBIN)
+		rr_dep_warning();
+
 	/* Temporarily set non-dynamic members dead so we can detect deleted ones. */
 	m = q->members;
 	while (m) {
@@ -921,6 +1066,7 @@
 			} else {
 				q->members = next_m;
 			}
+			remove_from_interfaces(m->interface);
 			free(m);
 		} else {
 			prev_m = m;
@@ -1031,15 +1177,15 @@
 		ast_copy_string(qe->context, q->context, sizeof(qe->context));
 		q->count++;
 		res = 0;
+		/* XXX missing CalledIDnum ? */
 		manager_event(EVENT_FLAG_CALL, "Join", 
-			      "Channel: %s\r\nCallerID: %s\r\nCallerIDName: %s\r\nQueue: %s\r\nPosition: %d\r\nCount: %d\r\n",
+			      "Channel: %s\r\nCallerID: %s\r\nCallerIDName: %s\r\nQueue: %s\r\nPosition: %d\r\nCount: %d\r\nUniqueid: %s\r\n",
 			      qe->chan->name, 
-			      qe->chan->cid.cid_num ? qe->chan->cid.cid_num : "unknown",
-			      qe->chan->cid.cid_name ? qe->chan->cid.cid_name : "unknown",
-			      q->name, qe->pos, q->count );
-#if 0
-ast_log(LOG_NOTICE, "Queue '%s' Join, Channel '%s', Position '%d'\n", q->name, qe->chan->name, qe->pos );
-#endif
+			      S_OR(qe->chan->cid.cid_num, "unknown"), /* XXX somewhere else it is <unknown> */
+			      S_OR(qe->chan->cid.cid_name, "unknown"),
+			      q->name, qe->pos, q->count, qe->chan->uniqueid );
+		if (option_debug)
+			ast_log(LOG_DEBUG, "Queue '%s' Join, Channel '%s', Position '%d'\n", q->name, qe->chan->name, qe->pos );
 	}
 	ast_mutex_unlock(&q->lock);
 	AST_LIST_UNLOCK(&queues);
@@ -1185,12 +1331,17 @@
 		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);
+	if (res && !valid_exit(qe, res))
+		res = 0;
 
  playout:
 	/* Set our last_pos indicators */
  	qe->last_pos = now;
 	qe->last_pos_said = qe->pos;
-	ast_moh_start(qe->chan, qe->moh);
+
+	/* Don't restart music on hold if we're about to exit the caller from the queue */
+	if (!res)
+		ast_moh_start(qe->chan, qe->moh);
 
 	return res;
 }
@@ -1233,11 +1384,10 @@
 
 			/* 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
+				"Channel: %s\r\nQueue: %s\r\nCount: %d\r\nUniqueid: %s\r\n",
+				qe->chan->name, q->name,  q->count, qe->chan->uniqueid);
+			if (option_debug)
+				ast_log(LOG_DEBUG, "Queue '%s' Leave, Channel '%s'\n", q->name, qe->chan->name );
 			/* Take us out of the queue */
 			if (prev)
 				prev->next = cur->next;
@@ -1261,16 +1411,16 @@
 }
 
 /* Hang up a list of outgoing calls */
-static void hangupcalls(struct localuser *outgoing, struct ast_channel *exception)
-{
-	struct localuser *oo;
+static void hangupcalls(struct callattempt *outgoing, struct ast_channel *exception)
+{
+	struct callattempt *oo;
 
 	while(outgoing) {
 		/* Hangup any existing lines we have open */
 		if (outgoing->chan && (outgoing->chan != exception))
 			ast_hangup(outgoing->chan);
 		oo = outgoing;
-		outgoing=outgoing->next;
+		outgoing=outgoing->q_next;
 		free(oo);
 	}
 }
@@ -1282,8 +1432,7 @@
 	/* 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) {
+	for (cur = q->members; cur; cur = cur->next) {
 		if (member == cur) {
 			cur->status = status;
 			if (!q->maskmemberstatus) {
@@ -1301,7 +1450,6 @@
 			}
 			break;
 		}
-		cur = cur->next;
 	}
 	ast_mutex_unlock(&q->lock);
 	return 0;
@@ -1353,13 +1501,22 @@
 	return found;
 }
 
-static int ring_entry(struct queue_ent *qe, struct localuser *tmp, int *busies)
+/*! \brief common hangup actions */
+static void do_hang(struct callattempt *o)
+{
+	o->stillgoing = 0;
+	ast_hangup(o->chan);
+	o->chan = NULL;
+}
+
+static int ring_entry(struct queue_ent *qe, struct callattempt *tmp, int *busies)
 {
 	int res;
 	int status;
 	char tech[256];
 	char *location;
 
+	/* on entry here, we know that tmp->chan == NULL */
 	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);
@@ -1369,7 +1526,16 @@
 		(*busies)++;
 		return 0;
 	}
-	
+
+	if (!qe->parent->ringinuse && (tmp->member->status == AST_DEVICE_INUSE)) {
+		if (option_debug)
+			ast_log(LOG_DEBUG, "%s in use, can't receive call\n", tmp->interface);
+		if (qe->chan->cdr)
+			ast_cdr_busy(qe->chan->cdr);
+		tmp->stillgoing = 0;
+		return 0;
+	}
+
 	if (tmp->member->paused) {
 		if (option_debug)
 			ast_log(LOG_DEBUG, "%s paused, can't receive call\n", tmp->interface);
@@ -1413,19 +1579,13 @@
 	tmp->chan->whentohangup = 0;
 	if (tmp->chan->cid.cid_num)
 		free(tmp->chan->cid.cid_num);
-	tmp->chan->cid.cid_num = NULL;
+	tmp->chan->cid.cid_num = ast_strdup(qe->chan->cid.cid_num);
 	if (tmp->chan->cid.cid_name)
 		free(tmp->chan->cid.cid_name);
-	tmp->chan->cid.cid_name = NULL;
+	tmp->chan->cid.cid_name = ast_strdup(qe->chan->cid.cid_name);
 	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);
+	tmp->chan->cid.cid_ani = ast_strdup(qe->chan->cid.cid_ani);
 
 	/* Inherit specially named variables from parent channel */
 	ast_channel_inherit_variables(qe->chan, tmp->chan);
@@ -1441,9 +1601,7 @@
 			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;
+		do_hang(tmp);
 		(*busies)++;
 		return 0;
 	} else {
@@ -1457,8 +1615,8 @@
 						"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",
+						S_OR(tmp->chan->cid.cid_num, "unknown"),
+						S_OR(tmp->chan->cid.cid_name, "unknown"),
 						qe->chan->context, qe->chan->exten, qe->chan->priority);
 		}
 		if (option_verbose > 2)
@@ -1467,69 +1625,58 @@
 	return 1;
 }
 
-static int ring_one(struct queue_ent *qe, struct localuser *outgoing, int *busies)
-{
-	struct localuser *cur;
-	struct localuser *best;
-	int bestmetric=0;
-
-	do {
-		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) {
-			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);
-						ring_entry(qe, cur, busies);
-					}
-					cur = cur->next;
-				}
-			} else {
-				/* Ring just the best channel */
-				if (option_debug)
-					ast_log(LOG_DEBUG, "Trying '%s' with metric %d\n", best->interface, best->metric);
-				ring_entry(qe, best, busies);
-			}
-		}
-	} while (best && !best->chan);
-	if (!best) {
-		if (option_debug)
-			ast_log(LOG_DEBUG, "Nobody left to try ringing in queue\n");
-		return 0;
-	}
-	return 1;
-}
-
-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) {
+/*! \brief find the entry with the best metric, or NULL */
+static struct callattempt *find_best(struct callattempt *outgoing)
+{
+	struct callattempt *best = NULL, *cur;
+
+	for (cur = outgoing; cur; cur = cur->q_next) {
 		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->metric < best->metric)) {	/* We haven't found one yet, or it's better */
 				best = cur;
 		}
-		cur = cur->next;
-	}
+	}
+	return best;
+}
+
+static int ring_one(struct queue_ent *qe, struct callattempt *outgoing, int *busies)
+{
+	int ret = 0;
+
+	while (ret == 0) {
+		struct callattempt *best = find_best(outgoing);
+		if (!best) {
+			if (option_debug)
+				ast_log(LOG_DEBUG, "Nobody left to try ringing in queue\n");
+			break;
+		}
+		if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) {
+			struct callattempt *cur;
+			/* Ring everyone who shares this best metric (for ringall) */
+			for (cur = outgoing; cur; cur = cur->q_next) {
+				if (cur->stillgoing && !cur->chan && cur->metric <= best->metric) {
+					if (option_debug)
+						ast_log(LOG_DEBUG, "(Parallel) Trying '%s' with metric %d\n", cur->interface, cur->metric);
+					ring_entry(qe, cur, busies);
+				}
+			}
+		} else {
+			/* Ring just the best channel */
+			if (option_debug)
+				ast_log(LOG_DEBUG, "Trying '%s' with metric %d\n", best->interface, best->metric);
+			ring_entry(qe, best, busies);
+		}
+		if (best->chan) /* break out with result = 1 */
+			ret = 1;
+	}
+	return ret;
+}
+
+static int store_next(struct queue_ent *qe, struct callattempt *outgoing)
+{
+	struct callattempt *best = find_best(outgoing);
+
 	if (best) {
 		/* Ring just the best channel */
 		if (option_debug)
@@ -1559,7 +1706,7 @@
 	if (!res) {
 		/* Wait for a keypress */
 		res = ast_waitstream(chan, AST_DIGIT_ANY);
-		if (res <= 0 || !valid_exit(qe, res))
+		if (res < 0 || !valid_exit(qe, res))
 			res = 0;
 
 		/* Stop playback */
@@ -1602,8 +1749,9 @@
 	/* play the announcement */
 	res = background_file(qe, qe->chan, qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound]);
 
-	/* Resume Music on Hold */
-	ast_moh_start(qe->chan, qe->moh);
+	/* Resume Music on Hold if the caller is going to stay in the queue */
+	if (!res)
+		ast_moh_start(qe->chan, qe->moh);
 
 	/* update last_periodic_announce_time */
 	qe->last_periodic_announce_time = now;
@@ -1617,40 +1765,43 @@
 static void record_abandoned(struct queue_ent *qe)
 {
 	ast_mutex_lock(&qe->parent->lock);
+	manager_event(EVENT_FLAG_AGENT, "QueueCallerAbandon",
+	              "Queue: %s\r\n"
+	              "Uniqueid: %s\r\n"
+	              "Position: %d\r\n"
+	              "OriginalPosition: %d\r\n"
+	              "HoldTime: %d\r\n",
+	              qe->parent->name, qe->chan->uniqueid, qe->pos, qe->opos, (int)(time(NULL) - qe->start));
+
 	qe->parent->callsabandoned++;
 	ast_mutex_unlock(&qe->parent->lock);
 }
 
+/*! \brief RNA == Ring No Answer. Common code that is executed when we try a queue member and they don't answer. */
+static void rna(int rnatime, struct queue_ent *qe, char *membername)
+{
+
+	if (option_verbose > 2)
+		ast_verbose( VERBOSE_PREFIX_3 "Nobody picked up in %d ms\n", rnatime);
+	ast_queue_log(qe->parent->name, qe->chan->uniqueid, membername, "RINGNOANSWER", "%d", rnatime);
+	if (qe->parent->autopause) {
+		if (!set_member_paused(qe->parent->name, membername, 1)) {
+			if (option_verbose > 2)
+				ast_verbose( VERBOSE_PREFIX_3 "Auto-Pausing Queue Member %s in queue %s since they failed to answer.\n", membername, qe->parent->name);
+		} else {
+			if (option_verbose > 2)
+				ast_verbose( VERBOSE_PREFIX_3 "Failed to pause Queue Member %s in queue %s!\n", membername, qe->parent->name);
+		}
+	}
+ return;
+} 
 
 #define AST_MAX_WATCHERS 256
 
-#define BUILD_WATCHERS do { \
-		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)
+static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callattempt *outgoing, int *to, char *digit, int prebusies, int caller_disconnect)
 {
 	char *queue = qe->parent->name;
-	char on[256] = "";
-	struct localuser *o;
-	int found;
-	int numlines;
+	struct callattempt *o;
 	int status;
 	int sentringing = 0;
 	int numbusies = prebusies;
@@ -1658,21 +1809,39 @@
 	int stillgoing = 0;
 	int orig = *to;
 	struct ast_frame *f;
-	struct localuser *peer = NULL;
-	struct ast_channel *watchers[AST_MAX_WATCHERS];
-	int pos;
+	struct callattempt *peer = NULL;
 	struct ast_channel *winner;
 	struct ast_channel *in = qe->chan;
+	char on[256] = "";
+	long starttime = 0;
+	long endtime = 0;	
+
+	starttime = (long)time(NULL);
 	
 	while(*to && !peer) {
-		BUILD_WATCHERS;
-		if ((found < 0) && stillgoing && !qe->parent->strategy) {
+		int numlines, retry, pos = 1;
+		struct ast_channel *watchers[AST_MAX_WATCHERS];
+		watchers[0] = in;
+
+		for (retry = 0; retry < 2; retry++) {
+			numlines = 0;
+			for (o = outgoing; o; o = o->q_next) { /* Keep track of important channels */
+				if (o->stillgoing) {	/* Keep track of important channels */
+					stillgoing = 1;
+					if (o->chan)
+						watchers[pos++] = o->chan;
+				}
+				numlines++;
+			}
+			if (pos > 1 /* found */ || !stillgoing /* nobody listening */ ||
+					 (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) /* ring would not be delivered */)
+				break;
 			/* 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);
-			BUILD_WATCHERS;
-		}
-		if (found < 0) {
+			/* and retry... */
+		}
+		if (pos == 1 /* not found */) {
 			if (numlines == (numbusies + numnochan)) {
 				ast_log(LOG_DEBUG, "Everyone is busy at this time\n");
 			} else {
@@ -1682,8 +1851,7 @@
 			return NULL;
 		}
 		winner = ast_waitfor_n(watchers, pos, to);
-		o = outgoing;
-		while(o) {
+		for (o = outgoing; o; o = o->q_next) {
 			if (o->stillgoing && (o->chan) &&  (o->chan->_state == AST_STATE_UP)) {
 				if (!peer) {
 					if (option_verbose > 2)
@@ -1698,8 +1866,7 @@
 					char *tech;
 					ast_copy_string(tmpchan, o->chan->call_forward, sizeof(tmpchan));
 					if ((stuff = strchr(tmpchan, '/'))) {
-						*stuff = '\0';
-						stuff++;
+						*stuff++ = '\0';
 						tech = tmpchan;
 					} else {
 						snprintf(tmpchan, sizeof(tmpchan), "%s@%s", o->chan->call_forward, o->chan->context);
@@ -1720,22 +1887,13 @@
 					} else {
 						if (o->chan->cid.cid_num)
 							free(o->chan->cid.cid_num);
-						o->chan->cid.cid_num = NULL;
+						o->chan->cid.cid_num = ast_strdup(in->cid.cid_num);
+
 						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->cid.cid_name = ast_strdup(in->cid.cid_name);
+
+						ast_string_field_set(o->chan, accountcode, in->accountcode);
 						o->chan->cdrflags = in->cdrflags;
 
 						if (in->cid.cid_ani) {
@@ -1745,15 +1903,10 @@
 						}
 						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);
+						o->chan->cid.cid_rdnis = ast_strdup(S_OR(in->macroexten, 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;
+							do_hang(o);
 							numnochan++;
 						}
 					}
@@ -1776,12 +1929,13 @@
 						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);
-							ast_hangup(o->chan);
-							o->chan = NULL;
-							if (qe->parent->strategy) {
+							do_hang(o);
+							endtime = (long)time(NULL);
+							endtime -= starttime;
+							rna(endtime*1000, qe, on);
+							if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
 								if (qe->parent->timeoutrestart)
 									*to = orig;
 								ring_one(qe, outgoing, &numbusies);
@@ -1791,12 +1945,13 @@
 						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);
-							ast_hangup(o->chan);
-							o->chan = NULL;
-							if (qe->parent->strategy) {
+							endtime = (long)time(NULL);
+							endtime -= starttime;
+							rna(endtime*1000, qe, on);
+							do_hang(o);
+							if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
 								if (qe->parent->timeoutrestart)
 									*to = orig;
 								ring_one(qe, outgoing, &numbusies);
@@ -1822,17 +1977,17 @@
 					}
 					ast_frfree(f);
 				} else {
-					o->stillgoing = 0;
-					ast_hangup(o->chan);
-					o->chan = NULL;
-					if (qe->parent->strategy) {
+					endtime = (long)time(NULL);
+					endtime -= starttime;
+					rna(endtime*1000, qe, on);
+					do_hang(o);
+					if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
 						if (qe->parent->timeoutrestart)
 							*to = orig;
 						ring_one(qe, outgoing, &numbusies);
 					}
 				}
 			}
-			o = o->next;
 		}
 		if (winner == in) {
 			f = ast_read(in);
@@ -1866,19 +2021,8 @@
 			}
 			ast_frfree(f);
 		}
-		if (!*to) {
-			if (option_verbose > 2)
-				ast_verbose( VERBOSE_PREFIX_3 "Nobody picked up in %d ms\n", orig);
-			if (qe->parent->autopause) {
-				if (!set_member_paused(qe->parent->name, on, 1)) {
-					if (option_verbose > 2)
-						ast_verbose( VERBOSE_PREFIX_3 "Auto-Pausing Queue Member %s in queue %s since they failed to answer.\n", on, qe->parent->name);
-				} else {
-					if (option_verbose > 2)
-						ast_verbose( VERBOSE_PREFIX_3 "Failed to pause Queue Member %s in queue %s!\n", on, qe->parent->name);
-				}
-			}
-		}
+		if (!*to) 
+			rna(orig, qe, on);
 	}
 
 	return peer;
@@ -1888,20 +2032,68 @@
 static int is_our_turn(struct queue_ent *qe)
 {
 	struct queue_ent *ch;
+	struct member *cur;
+	int avl = 0;
+	int idx = 0;
 	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) || (qe->parent->autofill)) {
+	if (!qe->parent->autofill) {
+		/* 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;
+		}	
+
+	} else {
+
+		/* This needs a lock. How many members are available to be served? */
+	
+		ast_mutex_lock(&qe->parent->lock);
+			
+		ch = qe->parent->head;
+		cur = qe->parent->members;
+	
+		while (cur) {
+			if (cur->status == 1) 
+				avl++;
+			cur = cur->next;
+		}
+
 		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;
-	}
+			ast_log(LOG_DEBUG, "There are %d available members.\n", avl);
+	
+		if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) {
+			if (option_debug)
+				ast_log(LOG_DEBUG, "Even though there are %d available members, the strategy is ringall so only the head call is allowed in!\n", avl);
+			avl = 1;
+		}
+	
+		while ((idx < avl) && (ch) && (ch != qe)) {
+			idx++;
+			ch = ch->next;			
+		}
+	
+		/* If the queue entry is within avl [the number of available members] calls from the top ... */
+		if (ch && idx < avl) {
+			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;
+		}
+		

[... 16913 lines stripped ...]


More information about the asterisk-commits mailing list