summary refs log tree commit diff stats
path: root/src/common/modes.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/common/modes.c')
-rw-r--r--src/common/modes.c836
1 files changed, 836 insertions, 0 deletions
diff --git a/src/common/modes.c b/src/common/modes.c
new file mode 100644
index 00000000..1acf7f54
--- /dev/null
+++ b/src/common/modes.c
@@ -0,0 +1,836 @@
+/* X-Chat
+ * Copyright (C) 1998 Peter Zelezny.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <glib.h>
+#include <glib/gprintf.h>
+
+#include "xchat.h"
+#include "xchatc.h"
+#include "modes.h"
+#include "server.h"
+#include "text.h"
+#include "fe.h"
+#include "util.h"
+#include "inbound.h"
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif
+
+typedef struct
+{
+	server *serv;
+	char *op;
+	char *deop;
+	char *voice;
+	char *devoice;
+} mode_run;
+
+static int is_prefix_char (server * serv, char c);
+static void record_chan_mode (session *sess, char sign, char mode, char *arg);
+static char *mode_cat (char *str, char *addition);
+static void handle_single_mode (mode_run *mr, char sign, char mode, char *nick, char *chan, char *arg, int quiet, int is_324);
+static int mode_has_arg (server *serv, char sign, char mode);
+static void mode_print_grouped (session *sess, char *nick, mode_run *mr);
+static int mode_chanmode_type (server * serv, char mode);
+
+
+/* word[] - list of nicks.
+   wpos   - index into word[]. Where nicks really start.
+   end    - index into word[]. Last entry plus one.
+   sign   - a char, e.g. '+' or '-'
+   mode   - a mode, e.g. 'o' or 'v'	*/
+void
+send_channel_modes (session *sess, char *tbuf, char *word[], int wpos,
+						  int end, char sign, char mode, int modes_per_line)
+{
+	int usable_modes, orig_len, len, wlen, i, max;
+	server *serv = sess->server;
+
+	/* sanity check. IRC RFC says three per line. */
+	if (serv->modes_per_line < 3)
+		serv->modes_per_line = 3;
+	if (modes_per_line < 1)
+		modes_per_line = serv->modes_per_line;
+
+	/* RFC max, minus length of "MODE %s " and "\r\n" and 1 +/- sign */
+	/* 512 - 6 - 2 - 1 - strlen(chan) */
+	max = 503 - strlen (sess->channel);
+
+	while (wpos < end)
+	{
+		tbuf[0] = '\0';
+		orig_len = len = 0;
+
+		/* we'll need this many modechars too */
+		len += modes_per_line;
+
+		/* how many can we fit? */
+		for (i = 0; i < modes_per_line; i++)
+		{
+			/* no more nicks left? */
+			if (wpos + i >= end)
+				break;
+			wlen = strlen (word[wpos + i]) + 1;
+			if (wlen + len > max)
+				break;
+			len += wlen; /* length of our whole string so far */
+		}
+		if (i < 1)
+			return;
+		usable_modes = i;	/* this is how many we'll send on this line */
+
+		/* add the +/-modemodemodemode */
+		len = orig_len;
+		tbuf[len] = sign;
+		len++;
+		for (i = 0; i < usable_modes; i++)
+		{
+			tbuf[len] = mode;
+			len++;
+		}
+		tbuf[len] = 0;	/* null terminate for the strcat() to work */
+
+		/* add all the nicknames */
+		for (i = 0; i < usable_modes; i++)
+		{
+			strcat (tbuf, " ");
+			strcat (tbuf, word[wpos + i]);
+		}
+		serv->p_mode (serv, sess->channel, tbuf);
+
+		wpos += usable_modes;
+	}
+}
+
+/* does 'chan' have a valid prefix? e.g. # or & */
+
+int
+is_channel (server * serv, char *chan)
+{
+	if (strchr (serv->chantypes, chan[0]))
+		return 1;
+	return 0;
+}
+
+/* is the given char a valid nick mode char? e.g. @ or + */
+
+static int
+is_prefix_char (server * serv, char c)
+{
+	int pos = 0;
+	char *np = serv->nick_prefixes;
+
+	while (np[0])
+	{
+		if (np[0] == c)
+			return pos;
+		pos++;
+		np++;
+	}
+
+	if (serv->bad_prefix)
+	{
+		if (strchr (serv->bad_nick_prefixes, c))
+		/* valid prefix char, but mode unknown */
+			return -2;
+	}
+
+	return -1;
+}
+
+/* returns '@' for ops etc... */
+
+char
+get_nick_prefix (server * serv, unsigned int access)
+{
+	int pos;
+	char c;
+
+	for (pos = 0; pos < USERACCESS_SIZE; pos++)
+	{
+		c = serv->nick_prefixes[pos];
+		if (c == 0)
+			break;
+		if (access & (1 << pos))
+			return c;
+	}
+
+	return 0;
+}
+
+/* returns the access bitfield for a nickname. E.g.
+	@nick would return 000010 in binary
+	%nick would return 000100 in binary
+	+nick would return 001000 in binary */
+
+unsigned int
+nick_access (server * serv, char *nick, int *modechars)
+{
+	int i;
+	unsigned int access = 0;
+	char *orig = nick;
+
+	while (*nick)
+	{
+		i = is_prefix_char (serv, *nick);
+		if (i == -1)
+			break;
+
+		/* -2 == valid prefix char, but mode unknown */
+		if (i != -2)
+			access |= (1 << i);
+
+		nick++;
+	}
+
+	*modechars = nick - orig;
+
+	return access;
+}
+
+/* returns the access number for a particular mode. e.g.
+	mode 'a' returns 0
+	mode 'o' returns 1
+	mode 'h' returns 2
+	mode 'v' returns 3
+	Also puts the nick-prefix-char in 'prefix' */
+
+int
+mode_access (server * serv, char mode, char *prefix)
+{
+	int pos = 0;
+
+	while (serv->nick_modes[pos])
+	{
+		if (serv->nick_modes[pos] == mode)
+		{
+			*prefix = serv->nick_prefixes[pos];
+			return pos;
+		}
+		pos++;
+	}
+
+	*prefix = 0;
+
+	return -1;
+}
+
+static void
+record_chan_mode (session *sess, char sign, char mode, char *arg)
+{
+	/* Somebody needed to acutally update sess->current_modes, needed to
+		play nice with bouncers, and less mode calls. Also keeps modes up
+		to date for scripts */
+	server *serv = sess->server;
+	GString *current = g_string_new(sess->current_modes);
+	gint mode_pos = -1;
+	gchar *current_char = current->str;
+	gint modes_length;
+	gint argument_num = 0;
+	gint argument_offset = 0;
+	gint argument_length = 0;
+	int i = 0;
+	gchar *arguments_start;
+
+	/* find out if the mode currently exists */
+	arguments_start = g_strstr_len(current->str	, -1, " ");
+	if (arguments_start) {
+		modes_length = arguments_start - current->str;
+	}
+	else {
+		modes_length = current->len;
+		/* set this to the end of the modes */
+		arguments_start = current->str + current->len;
+	}
+
+	while (mode_pos == -1 && i < modes_length)
+	{
+		if (*current_char == mode)
+		{
+			mode_pos = i;
+		}
+		else
+		{
+			i++;
+			current_char++;
+		}
+	}
+
+	/* if the mode currently exists and has an arg, need to know where
+	 * (including leading space) */
+	if (mode_pos != -1 && mode_has_arg(serv, '+', mode))
+	{
+		current_char = current->str;
+
+		i = 0;
+		while (i <= mode_pos)
+		{
+			if (mode_has_arg(serv, '+', *current_char))
+				argument_num++;
+			current_char++;
+			i++;
+		}
+
+		/* check through arguments for where to start */
+		current_char = arguments_start;
+		i = 0;
+		while (i < argument_num && *current_char != '\0')
+		{
+			if (*current_char == ' ')
+				i++;
+			if (i != argument_num)
+				current_char++;
+		}
+		argument_offset = current_char - current->str;
+
+		/* how long the existing argument is for this key
+		 * important for malloc and strncpy */
+		if (i == argument_num)
+		{
+			argument_length++;
+			current_char++;
+			while (*current_char != '\0' && *current_char != ' ')
+			{
+				argument_length++;
+				current_char++;
+			}
+		}
+	}
+
+	/* two cases, adding and removing a mode, handled differently */
+	if (sign == '+')
+	{
+		if (mode_pos != -1)
+		{
+			/* if it already exists, only need to do something (change)
+			 * if there should be a param */
+			if (mode_has_arg(serv, sign, mode))
+			{
+				/* leave the old space there */
+				current = g_string_erase(current, argument_offset+1, argument_length-1);
+				current = g_string_insert(current, argument_offset+1, arg);
+
+				free(sess->current_modes);
+				sess->current_modes = g_string_free(current, FALSE);
+			}
+		}
+		/* mode wasn't there before */
+		else
+		{
+			/* insert the new mode character */
+			current = g_string_insert_c(current, modes_length, mode);
+
+			/* add the argument, with space if there is one */
+			if (mode_has_arg(serv, sign, mode))
+			{
+				current = g_string_append_c(current, ' ');
+				current = g_string_append(current, arg);
+			}
+
+			free(sess->current_modes);
+			sess->current_modes = g_string_free(current, FALSE);
+		}
+	}
+	else if (sign == '-' && mode_pos != -1)
+	{
+		/* remove the argument first if it has one*/
+		if (mode_has_arg(serv, '+', mode))
+			current = g_string_erase(current, argument_offset, argument_length);
+
+		/* remove the mode character */
+		current = g_string_erase(current, mode_pos, 1);
+
+		free(sess->current_modes);
+		sess->current_modes = g_string_free(current, FALSE);
+	}
+}
+
+static char *
+mode_cat (char *str, char *addition)
+{
+	int len;
+
+	if (str)
+	{
+		len = strlen (str) + strlen (addition) + 2;
+		str = realloc (str, len);
+		strcat (str, " ");
+		strcat (str, addition);
+	} else
+	{
+		str = strdup (addition);
+	}
+
+	return str;
+}
+
+/* handle one mode, e.g.
+   handle_single_mode (mr,'+','b',"elite","#warez","banneduser",) */
+
+static void
+handle_single_mode (mode_run *mr, char sign, char mode, char *nick,
+						  char *chan, char *arg, int quiet, int is_324)
+{
+	session *sess;
+	server *serv = mr->serv;
+	char outbuf[4];
+
+	outbuf[0] = sign;
+	outbuf[1] = 0;
+	outbuf[2] = mode;
+	outbuf[3] = 0;
+
+	sess = find_channel (serv, chan);
+	if (!sess || !is_channel (serv, chan))
+	{
+		/* got modes for a chan we're not in! probably nickmode +isw etc */
+		sess = serv->front_session;
+		goto genmode;
+	}
+
+	/* is this a nick mode? */
+	if (strchr (serv->nick_modes, mode))
+	{
+		/* update the user in the userlist */
+		userlist_update_mode (sess, /*nickname */ arg, mode, sign);
+	} else
+	{
+		if (!is_324 && !sess->ignore_mode && mode_chanmode_type(serv, mode) >= 1)
+			record_chan_mode (sess, sign, mode, arg);
+	}
+
+	switch (sign)
+	{
+	case '+':
+		switch (mode)
+		{
+		case 'k':
+			safe_strcpy (sess->channelkey, arg, sizeof (sess->channelkey));
+			fe_update_channel_key (sess);
+			fe_update_mode_buttons (sess, mode, sign);
+			if (!quiet)
+				EMIT_SIGNAL (XP_TE_CHANSETKEY, sess, nick, arg, NULL, NULL, 0);
+			return;
+		case 'l':
+			sess->limit = atoi (arg);
+			fe_update_channel_limit (sess);
+			fe_update_mode_buttons (sess, mode, sign);
+			if (!quiet)
+				EMIT_SIGNAL (XP_TE_CHANSETLIMIT, sess, nick, arg, NULL, NULL, 0);
+			return;
+		case 'o':
+			if (!quiet)
+				mr->op = mode_cat (mr->op, arg);
+			return;
+		case 'h':
+			if (!quiet)
+				EMIT_SIGNAL (XP_TE_CHANHOP, sess, nick, arg, NULL, NULL, 0);
+			return;
+		case 'v':
+			if (!quiet)
+				mr->voice = mode_cat (mr->voice, arg);
+			return;
+		case 'b':
+			if (!quiet)
+				EMIT_SIGNAL (XP_TE_CHANBAN, sess, nick, arg, NULL, NULL, 0);
+			return;
+		case 'e':
+			if (!quiet)
+				EMIT_SIGNAL (XP_TE_CHANEXEMPT, sess, nick, arg, NULL, NULL, 0);
+			return;
+		case 'I':
+			if (!quiet)
+				EMIT_SIGNAL (XP_TE_CHANINVITE, sess, nick, arg, NULL, NULL, 0);
+			return;
+		}
+		break;
+	case '-':
+		switch (mode)
+		{
+		case 'k':
+			sess->channelkey[0] = 0;
+			fe_update_channel_key (sess);
+			fe_update_mode_buttons (sess, mode, sign);
+			if (!quiet)
+				EMIT_SIGNAL (XP_TE_CHANRMKEY, sess, nick, NULL, NULL, NULL, 0);
+			return;
+		case 'l':
+			sess->limit = 0;
+			fe_update_channel_limit (sess);
+			fe_update_mode_buttons (sess, mode, sign);
+			if (!quiet)
+				EMIT_SIGNAL (XP_TE_CHANRMLIMIT, sess, nick, NULL, NULL, NULL, 0);
+			return;
+		case 'o':
+			if (!quiet)
+				mr->deop = mode_cat (mr->deop, arg);
+			return;
+		case 'h':
+			if (!quiet)
+				EMIT_SIGNAL (XP_TE_CHANDEHOP, sess, nick, arg, NULL, NULL, 0);
+			return;
+		case 'v':
+			if (!quiet)
+				mr->devoice = mode_cat (mr->devoice, arg);
+			return;
+		case 'b':
+			if (!quiet)
+				EMIT_SIGNAL (XP_TE_CHANUNBAN, sess, nick, arg, NULL, NULL, 0);
+			return;
+		case 'e':
+			if (!quiet)
+				EMIT_SIGNAL (XP_TE_CHANRMEXEMPT, sess, nick, arg, NULL, NULL, 0);
+			return;
+		case 'I':
+			if (!quiet)
+				EMIT_SIGNAL (XP_TE_CHANRMINVITE, sess, nick, arg, NULL, NULL, 0);
+			return;
+		}
+	}
+
+	fe_update_mode_buttons (sess, mode, sign);
+
+ genmode:
+	/* Received umode +e. If we're waiting to send JOIN then send now! */
+	if (mode == 'e' && sign == '+' && !serv->p_cmp (chan, serv->nick))
+		inbound_identified (serv);
+
+	if (!quiet)
+	{
+		if (*arg)
+		{
+			char *buf = malloc (strlen (chan) + strlen (arg) + 2);
+			sprintf (buf, "%s %s", chan, arg);
+			EMIT_SIGNAL (XP_TE_CHANMODEGEN, sess, nick, outbuf, outbuf + 2, buf, 0);
+			free (buf);
+		} else
+			EMIT_SIGNAL (XP_TE_CHANMODEGEN, sess, nick, outbuf, outbuf + 2, chan, 0);
+	}
+}
+
+/* does this mode have an arg? like +b +l +o */
+
+static int
+mode_has_arg (server * serv, char sign, char mode)
+{
+	int type;
+
+	/* if it's a nickmode, it must have an arg */
+	if (strchr (serv->nick_modes, mode))
+		return 1;
+
+	type = mode_chanmode_type (serv, mode);
+	switch (type)
+	{
+	case 0:					  /* type A */
+	case 1:					  /* type B */
+		return 1;
+	case 2:					  /* type C */
+		if (sign == '+')
+			return 1;
+	case 3:					  /* type D */
+		return 0;
+	default:
+		return 0;
+	}
+
+}
+
+/* what type of chanmode is it? -1 for not in chanmode */
+static int
+mode_chanmode_type (server * serv, char mode)
+{
+	/* see what numeric 005 CHANMODES=xxx said */
+	char *cm = serv->chanmodes;
+	int type = 0;
+	int found = 0;
+
+	while (*cm && !found)
+	{
+		if (*cm == ',')
+		{
+			type++;
+		} else if (*cm == mode)
+		{
+			found = 1;
+		}
+		cm++;
+	}
+	if (found)
+		return type;
+	/* not found? -1 */
+	else
+		return -1;
+}
+
+static void
+mode_print_grouped (session *sess, char *nick, mode_run *mr)
+{
+	/* print all the grouped Op/Deops */
+	if (mr->op)
+	{
+		EMIT_SIGNAL (XP_TE_CHANOP, sess, nick, mr->op, NULL, NULL, 0);
+		free (mr->op);
+		mr->op = NULL;
+	}
+
+	if (mr->deop)
+	{
+		EMIT_SIGNAL (XP_TE_CHANDEOP, sess, nick, mr->deop, NULL, NULL, 0);
+		free (mr->deop);
+		mr->deop = NULL;
+	}
+
+	if (mr->voice)
+	{
+		EMIT_SIGNAL (XP_TE_CHANVOICE, sess, nick, mr->voice, NULL, NULL, 0);
+		free (mr->voice);
+		mr->voice = NULL;
+	}
+
+	if (mr->devoice)
+	{
+		EMIT_SIGNAL (XP_TE_CHANDEVOICE, sess, nick, mr->devoice, NULL, NULL, 0);
+		free (mr->devoice);
+		mr->devoice = NULL;
+	}
+}
+
+
+/* handle a MODE or numeric 324 from server */
+
+void
+handle_mode (server * serv, char *word[], char *word_eol[],
+				 char *nick, int numeric_324)
+{
+	session *sess;
+	char *chan;
+	char *modes;
+	char *argstr;
+	char sign;
+	int len;
+	int arg;
+	int i, num_args;
+	int num_modes;
+	int offset = 3;
+	int all_modes_have_args = FALSE;
+	int using_front_tab = FALSE;
+	mode_run mr;
+
+	mr.serv = serv;
+	mr.op = mr.deop = mr.voice = mr.devoice = NULL;
+
+	/* numeric 324 has everything 1 word later (as opposed to MODE) */
+	if (numeric_324)
+		offset++;
+
+	chan = word[offset];
+	modes = word[offset + 1];
+	if (*modes == ':')
+		modes++;
+
+	if (*modes == 0)
+		return;	/* beyondirc's blank modes */
+
+	sess = find_channel (serv, chan);
+	if (!sess)
+	{
+		sess = serv->front_session;
+		using_front_tab = TRUE;
+	}
+	/* remove trailing space */
+	len = strlen (word_eol[offset]) - 1;
+	if (word_eol[offset][len] == ' ')
+		word_eol[offset][len] = 0;
+
+	if (prefs.raw_modes && !numeric_324)
+		EMIT_SIGNAL (XP_TE_RAWMODES, sess, nick, word_eol[offset], 0, 0, 0);
+
+	if (numeric_324 && !using_front_tab)
+	{
+		if (sess->current_modes)
+			free (sess->current_modes);
+		sess->current_modes = strdup (word_eol[offset+1]);
+	}
+
+	sign = *modes;
+	modes++;
+	arg = 1;
+
+	/* count the number of arguments (e.g. after the -o+v) */
+	num_args = 0;
+	i = 1;
+	while ((i + offset + 1) < PDIWORDS)
+	{
+		i++;
+		if (!(*word[i + offset]))
+			break;
+		num_args++;
+	}
+
+	/* count the number of modes (without the -/+ chars */
+	num_modes = 0;
+	i = 0;
+	while (i < strlen (modes))
+	{
+		if (modes[i] != '+' && modes[i] != '-')
+			num_modes++;
+		i++;
+	}
+
+	if (num_args == num_modes)
+		all_modes_have_args = TRUE;
+
+	while (*modes)
+	{
+		switch (*modes)
+		{
+		case '-':
+		case '+':
+			/* print all the grouped Op/Deops */
+			mode_print_grouped (sess, nick, &mr);
+			sign = *modes;
+			break;
+		default:
+			argstr = "";
+			if ((all_modes_have_args || mode_has_arg (serv, sign, *modes)) && arg < (num_args+1))
+			{
+				arg++;
+				argstr = word[arg + offset];
+			}
+			handle_single_mode (&mr, sign, *modes, nick, chan,
+									  argstr, numeric_324 || prefs.raw_modes,
+									  numeric_324);
+		}
+
+		modes++;
+	}
+
+	/* update the title at the end, now that the mode update is internal now */
+	if (!using_front_tab)
+		fe_set_title (sess);
+
+	/* print all the grouped Op/Deops */
+	mode_print_grouped (sess, nick, &mr);
+}
+
+/* handle the 005 numeric */
+
+void
+inbound_005 (server * serv, char *word[])
+{
+	int w;
+	char *pre;
+
+	w = 4;							  /* start at the 4th word */
+	while (w < PDIWORDS && *word[w])
+	{
+		if (strncmp (word[w], "MODES=", 6) == 0)
+		{
+			serv->modes_per_line = atoi (word[w] + 6);
+		} else if (strncmp (word[w], "CHANTYPES=", 10) == 0)
+		{
+			free (serv->chantypes);
+			serv->chantypes = strdup (word[w] + 10);
+		} else if (strncmp (word[w], "CHANMODES=", 10) == 0)
+		{
+			free (serv->chanmodes);
+			serv->chanmodes = strdup (word[w] + 10);
+		} else if (strncmp (word[w], "PREFIX=", 7) == 0)
+		{
+			pre = strchr (word[w] + 7, ')');
+			if (pre)
+			{
+				pre[0] = 0;			  /* NULL out the ')' */
+				free (serv->nick_prefixes);
+				free (serv->nick_modes);
+				serv->nick_prefixes = strdup (pre + 1);
+				serv->nick_modes = strdup (word[w] + 8);
+			} else
+			{
+				/* bad! some ircds don't give us the modes. */
+				/* in this case, we use it only to strip /NAMES */
+				serv->bad_prefix = TRUE;
+				if (serv->bad_nick_prefixes)
+					free (serv->bad_nick_prefixes);
+				serv->bad_nick_prefixes = strdup (word[w] + 7);
+			}
+		} else if (strncmp (word[w], "WATCH=", 6) == 0)
+		{
+			serv->supports_watch = TRUE;
+		} else if (strncmp (word[w], "NETWORK=", 8) == 0)
+		{
+/*			if (serv->networkname)
+				free (serv->networkname);
+			serv->networkname = strdup (word[w] + 8);*/
+
+			if (serv->server_session->type == SESS_SERVER)
+			{
+				safe_strcpy (serv->server_session->channel, word[w] + 8, CHANLEN);
+				fe_set_channel (serv->server_session);
+			}
+
+			/* use /NICKSERV */
+			if (strcasecmp (word[w] + 8, "RusNet") == 0)
+				serv->nickservtype = 1;
+			else if (strcasecmp (word[w] + 8, "UniBG") == 0)
+				serv->nickservtype = 3;
+			else if (strcasecmp (word[w] + 8, "QuakeNet") == 0)
+				serv->nickservtype = 4;
+
+		} else if (strncmp (word[w], "CASEMAPPING=", 12) == 0)
+		{
+			if (strcmp (word[w] + 12, "ascii") == 0)	/* bahamut */
+				serv->p_cmp = (void *)strcasecmp;
+		} else if (strncmp (word[w], "CHARSET=", 8) == 0)
+		{
+			if (strcasecmp (word[w] + 8, "UTF-8") == 0)
+			{
+				server_set_encoding (serv, "UTF-8");
+			}
+		} else if (strcmp (word[w], "NAMESX") == 0)
+		{
+									/* 12345678901234567 */
+			tcp_send_len (serv, "PROTOCTL NAMESX\r\n", 17);
+		} else if (strcmp (word[w], "WHOX") == 0)
+		{
+			serv->have_whox = TRUE;
+		} else if (strcmp (word[w], "CAPAB") == 0)
+		{
+			serv->have_capab = TRUE;
+									/* 12345678901234567890 */
+			tcp_send_len (serv, "CAPAB IDENTIFY-MSG\r\n", 20);
+			/* now wait for numeric 290 */	
+		} else if (strcmp (word[w], "EXCEPTS") == 0)
+		{
+#ifndef WIN32
+			serv->have_except = TRUE;
+#endif
+		} else if (strncmp (word[w], "ELIST=", 6) == 0)
+		{
+			/* supports LIST >< min/max user counts? */
+			if (strchr (word[w] + 6, 'U') || strchr (word[w] + 6, 'u'))
+				serv->use_listargs = TRUE;
+		}
+
+		w++;
+	}
+}