/*
 * parse.c: handles messages from the server.   Believe it or not.  I
 * certainly wouldn't if I were you. 
 *
 * Written By Michael Sandrof
 *
 * Copyright(c) 1990 
 *
 * See the COPYRIGHT file, or do a HELP IRCII COPYRIGHT 
 */

#if 0
static	char	rcsid[] = "$Id: parse.c,v 1.21 1994/02/03 09:02:51 mrgreen Exp $";
#endif

#include "irc.h"

#define AUTO_RECONNECT
#include "server.h"
#include "names.h"
#include "vars.h"
#include "ctcp.h"
#include "hook.h"
#include "edit.h"
#include "ignore.h"
#include "whois.h"
#include "lastlog.h"
#include "ircaux.h"
#include "funny.h"
#include "crypt.h"
#include "term.h"
#include "flood.h"
#include "window.h"
#include "screen.h"
#include "output.h"
#include "numbers.h"
#include "parse.h"
#include "notify.h"
#include "notice.h"

#define STRING_CHANNEL '+'
#define MULTI_CHANNEL '#'
#define LOCAL_CHANNEL '&'

#define space ' '	/* Taken from rfc 1453 */
#define	MAXPARA	15	/* Taken from rfc 1453 */

static  void    strip_modes _((char *, char *, char *));

/*
 * joined_nick: the nickname of the last person who joined the current
 * channel 
 */
	char	*joined_nick = (char *) 0;

/* public_nick: nick of the last person to send a message to your channel */
	char	*public_nick = (char *) 0;

/* User and host information from server 2.7 */
	char	*FromUserHost = empty_string;

/* doing a PRIVMSG */
	int	doing_privmsg = 0;

/* set if an encrypted message has been parsed */
extern  int     sed;
extern	int	in_e_nick;

/* fake: alerts the user. */
void fake _((void))
{
	yell("-- Fake Message receieved -- ");
	return;
}

/*
 * is_channel: determines if the argument is a channel.  If it's a number,
 * begins with MULTI_CHANNEL and has no '*', or STRING_CHANNEL, then its a
 * channel 
 */
#ifdef __STDC__
int is_channel(char *to)
#else
int is_channel(to)
char	*to;
#endif
{
	int	Version;

	Version = get_server_version(from_server);
	return ( (*to == MULTI_CHANNEL) 
		|| (
#ifdef COMPAT_27
		    Version > Server2_7 &&
#endif
		  			    *to == LOCAL_CHANNEL) 
		|| (Version == Server2_8 && *to == '0'));
}


#ifdef __STDC__
char	*PasteArgs (char **Args, int StartPoint)
#else
char	*PasteArgs(Args, StartPoint)
	char	**Args;
	int	StartPoint;
#endif
{
	int	i;

	for (; StartPoint; Args++, StartPoint--)
		if (!*Args)
			return NULL;
	for (i = 0; Args[i] && Args[i+1]; i++)
		Args[i][strlen(Args[i])] = ' ';
	Args[1] = (char *) 0;
	return Args[0];
}

/*
 * BreakArgs: breaks up the line from the server, in to where its from,
 * setting FromUserHost if it should be, and returns all the arguements
 * that are there.   Re-written by phone, dec 1992.
 *		     Re-written again by esl, april 1996.
 *
 * This doesnt strip out extraneous spaces any more.
 */
#ifdef __STDC__
void BreakArgs (char *Input, char **Sender, char **OutPut)
#else
void BreakArgs(Input, Sender, OutPut)
	char	*Input;
	char	**Sender;
	char	**OutPut;
#endif
{
	int	ArgCount = 0;

	/*
	 * The RFC describes it fully, but in a short form, a line looks like:
	 * [:sender[!user@host]] COMMAND ARGUMENT [[:]ARGUMENT]{0..14}
	 */

	/*
	 * Look to see if the optional :sender is present.
	 */
	if (*Input == ':')
	{
		*Sender = ++Input;
		while (*Input && *Input != space)
			Input++;
		if (*Input == space)
			*Input++ = 0;

		/*
		 * Look to see if the optional !user@host is present.
		 */
		FromUserHost = *Sender;
		while (*FromUserHost && *FromUserHost != '!')
			FromUserHost++;
		if (*FromUserHost == '!')
			*FromUserHost++ = 0;
	}
	/*
	 * No sender present.
	 */
	else
		*Sender = FromUserHost = empty_string;

	/*
	 * Now we go through the argument list...
	 */
	for (;;)
	{
		while (*Input && *Input == space)
			Input++;

		if (!*Input)
			break;

		if (*Input == ':')
		{
			OutPut[ArgCount++] = ++Input;
			break;
		}

		OutPut[ArgCount++] = Input;
		if (ArgCount >= MAXPARA)
			break;

		while (*Input && *Input != space)
			(void) *Input++;
		if (*Input == space)
			*Input++ = 0;
	}
	OutPut[ArgCount] = NULL;
}

/* in response to a TOPIC message from the server */
#ifdef __STDC__
static void p_topic (char *from, char **ArgList)
#else
static	void p_topic(from, ArgList)
	char	*from,
		**ArgList;
#endif
{
	if (!ArgList[1]) 
		/* Thanks to Bob Page for reminding me that you cant
		 * return a void function. ickers. */
		{fake();return;}

	if (check_ignore_channel(from, FromUserHost, ArgList[0], IGNORE_CRAP) != IGNORED)
	{
		message_from(ArgList[0], LOG_CRAP);
		if (do_hook(TOPIC_LIST, "%s %s %s", from, ArgList[0], ArgList[1]))
			say("%s has changed the topic on channel %s to %s",
				from, ArgList[0], ArgList[1]);
		message_from((char *) 0, LOG_CURRENT);
	}
}

#ifdef __STDC__
static void p_wallops (char *from, char **ArgList)
#else
static	void p_wallops(from, ArgList)
	char	*from,
		**ArgList;
#endif
{
	char	*line;
	int from_server = index(from, '.') ? 1 : 0;

	if (!(line = PasteArgs(ArgList, 0)))
		{fake();return;}

	/* wallops from a server */
	if (from_server || check_flooding(from, WALLOP_FLOOD, line))
	{
		int	level;
		char	*high;

		switch (check_ignore(from, FromUserHost, IGNORE_WALLOPS))
		{
			case (IGNORED):
				return;
			case (HIGHLIGHTED):
				high = highlight_char;
				break;
			default:
				high = empty_string;
				break;
		}
		message_from(from, LOG_WALLOP);
		level = set_lastlog_msg_level(LOG_WALLOP);
		if (do_hook(WALLOP_LIST, "%s %c %s", from, from_server ? 'S' : '*', line))
			put_it("%s!%s%s!%s %s", high, from, from_server ? empty_string : "*", high, line);
		if (beep_on_level & LOG_WALLOP)
			beep_em(1);
		set_lastlog_msg_level(level);
		message_from((char *) 0, LOG_CRAP);
	}
}

#ifdef __STDC__
void whoreply (char *from, char **ArgList)
#else
void whoreply(from, ArgList)
	char	**ArgList,
		*from;
#endif
{
	static	char	format[40];
	static	int	last_width = -1;
	int	ok = 1;
	char	*channel,
		*user,
		*host,
		*server,
		*nick,
		*stat,
		*name;

	FILE	*fip;
	char	buf_data[BUFSIZ];

	if (!ArgList[5])
		{fake();return;}

	if (last_width != get_int_var(CHANNEL_NAME_WIDTH_VAR))
	{
		if ((last_width = get_int_var(CHANNEL_NAME_WIDTH_VAR)) != 0)
		    sprintf(format, "%%-%u.%us %%-9s %%-3s %%s@%%s (%%s)",
					(unsigned char) last_width,
					(unsigned char) last_width);
		else
		    strcpy(format, "%s\t%-9s %-3s %s@%s (%s)");
	}
	channel =  ArgList[0];
	user = ArgList[1];
	host = ArgList[2];
	server = ArgList[3];
	nick = ArgList[4];
	stat = ArgList[5];
	PasteArgs(ArgList, 6);

	if (*stat == 'S')	/* this only true for the header WHOREPLY */
	{
		channel = "Channel";
		if (((who_mask & WHO_FILE) == 0) || (fopen (who_file, "r")))
		{
			if (do_hook(WHO_LIST, "%s %s %s %s %s %s %s", channel,
					nick, stat, user, host, server, ArgList[6]))
				put_it(format, channel, nick, stat, user,
					host, ArgList[6]);
			return;
		}
	}

	name = ArgList[6];

#if 0
	if (who_on_channel_join)
	{
		register_userhost(ArgList[0], ArgList[1], ArgList[2],
			ArgList[3], ArgList[4], ArgList[5], ArgList[6]);
		ok = 0;
	}
	else
#endif
	if (who_mask)
	{
		if (who_mask & WHO_HERE)
			ok = ok && (*stat == 'H');
		if (who_mask & WHO_AWAY)
			ok = ok && (*stat == 'G');
		if (who_mask & WHO_OPS)
			ok = ok && (*(stat + 1) == '*');
		if (who_mask & WHO_LUSERS)
			ok = ok && (*(stat + 1) != '*');
		if (who_mask & WHO_CHOPS)
			ok = ok && ((*(stat + 1) == '@') ||
			(*(stat + 2) == '@'));
		if (who_mask & WHO_NAME)
			ok = ok && wild_match(who_name, user);
		if (who_mask & WHO_NICK)
			ok = ok && wild_match(who_nick, nick);
		if (who_mask & WHO_HOST)
			ok = ok && wild_match(who_host, host);
		if (who_mask & WHO_REAL)
			ok = ok && wild_match(who_real, name);
		if (who_mask & WHO_SERVER)
			ok = ok && wild_match(who_server, server);
		if (who_mask & WHO_FILE)
		{
			ok = 0;
			cannot_open = (char *) 0;
			if ((fip = fopen (who_file, "r")) != (FILE *) 0)
			{
				while (fgets (buf_data, BUFSIZ, fip) != (char *) 0)
				{
					buf_data[strlen(buf_data)-1] = '\0';
					ok = ok || wild_match(buf_data, nick);
				}
				fclose (fip);
			} else
				cannot_open = who_file;
		}
	}
	if (ok)
	{
		if (do_hook(WHO_LIST, "%s %s %s %s %s %s %s", channel, nick,
				stat, user, host, server, name))
		{
			if (get_int_var(SHOW_WHO_HOPCOUNT_VAR))
				put_it(format, channel, nick, stat, user, host, name);
			else
			{
				char	*tmp;

				if ((tmp = (char *) index(name, ' ')) != (char *) 0)
					tmp++;
				else
					tmp = name;
				put_it(format, channel, nick, stat, user, host, tmp);
			}
		}
	}
}

#ifdef __STDC__
static void p_privmsg (char *from, char **Args)
#else
static	void p_privmsg(from, Args)
	char	*from,
		**Args;
#endif
{
	int		level,
			list_type,
			flood_type,
			log_type;
	unsigned char	ignore_type;
	char		*ptr,
			*to;
	char		*high;
	int		no_flood;

	PasteArgs(Args, 1);
	to = Args[0];
	ptr = Args[1];
	if (!to || !ptr)
		{fake();return;}

	doing_privmsg = 1;

	/* ooops. cant just do is_channel(to) because of # walls... */
	if (is_channel(to) && im_on_channel(to))
	{
		message_from(to, LOG_MSG);
		malloc_strcpy(&public_nick, from);
		flood_type = PUBLIC_FLOOD;
		log_type = LOG_PUBLIC;
		ignore_type = IGNORE_PUBLIC;

		if (!is_on_channel(to, from))
			list_type = PUBLIC_MSG_LIST;
		else
		{
			if (is_current_channel(to, 0))
				list_type = PUBLIC_LIST;
			else
				list_type = PUBLIC_OTHER_LIST;
		}
	}
	else
	{
		message_from(from, LOG_MSG);
		flood_type = MSG_FLOOD;
		if (my_stricmp(to, get_server_nickname(from_server)))
		{
			log_type = LOG_WALL;
			ignore_type = IGNORE_WALLS;
			list_type = MSG_GROUP_LIST;
		}
		else
		{
			log_type = LOG_MSG;
			ignore_type = IGNORE_MSGS;
			list_type = MSG_LIST;
		}
	}
	ptr = do_ctcp(from, to, ptr);

	switch (check_ignore_channel(from, FromUserHost, to, ignore_type))
	{
		case IGNORED:
		{
			/* 
			 * I really hate this -- flooding someone who you are ignoring
			 * doesnt do a lick of good except run you off for flooding 
			 *
			 * XXXX - this needs to change to use send_text().
			 */
			if ((list_type == MSG_LIST) && get_int_var(SEND_IGNORE_MSG_VAR))
				send_to_server("NOTICE %s :%s is ignoring you", from, get_server_nickname(from_server));
			doing_privmsg = 0;
			return;
		}
		case HIGHLIGHTED:
			high = highlight_char;
			break;
		default:
			high = empty_string;
			break;
	}
	if (!*ptr)
	{
		doing_privmsg = 0;
		return;
	}

	level = set_lastlog_msg_level(log_type);
	no_flood = check_flooding(from, flood_type, ptr);
	if ((sed == 1) && (!do_hook(ENCRYPTED_PRIVMSG_LIST,"%s %s %s",from, to, ptr)))
		 sed = 0; 
	else 
	{ 
		switch (list_type)
		{
		case PUBLIC_MSG_LIST:
			if (no_flood && do_hook(list_type, "%s %s %s", from, to, ptr))
			    put_it("%s(%s/%s)%s %s", high, from, to, high, ptr);
			break;
		case MSG_GROUP_LIST:
			if (no_flood && do_hook(list_type, "%s %s %s", from, to, ptr))
			    put_it("%s-%s:%s-%s %s", high, from, to, high, ptr);
			break;
		case MSG_LIST:
		{
			if (!no_flood)
				break;
			malloc_strcpy(&recv_nick, from);
			if (away_set)
				beep_em(get_int_var(BEEP_WHEN_AWAY_VAR));

			if (do_hook(list_type, "%s %s", from, ptr))
			{
			    if (away_set)
			    {
				time_t blah = time(NULL);
				put_it("%s*%s*%s %s <%.16s>", high, from, high, ptr, ctime(&blah));
			    }
			    else
				put_it("%s*%s*%s %s", high, from, high, ptr);
			}
			break;
		}
		case PUBLIC_LIST:
		{
			if (no_flood && do_hook(list_type, "%s %s %s", from, to, ptr))
				put_it("%s<%s>%s %s", high, from, high, ptr);
			break;
		}
		case PUBLIC_OTHER_LIST:
		{
			if (no_flood && do_hook(list_type, "%s %s %s", from, to, ptr))
				put_it("%s<%s:%s>%s %s", high, from, to, high, ptr);
			break;
		}
		}
		if (beep_on_level & log_type)
			beep_em(1);
	}
	set_lastlog_msg_level(level);
	message_from((char *) 0, LOG_CURRENT);
	doing_privmsg = 0;
}

#ifdef __STDC__
static void p_quit (char *from, char **ArgList)
#else
static void p_quit(from, ArgList)
	char	*from,
		**ArgList;
#endif
{
	int	one_prints = 0;
	char	*chan;
	char	*Reason;

	if (check_ignore(from, FromUserHost, IGNORE_CRAP) != IGNORED)
	{
		PasteArgs(ArgList, 0);
		Reason = ArgList[0] ? ArgList[0] : "?";
		for (chan = walk_channels(1, from); chan; chan = walk_channels(0, from))
		{
			message_from(chan, LOG_CRAP);
			if (do_hook(CHANNEL_SIGNOFF_LIST, "%s %s %s", chan, from, Reason))
				one_prints = 1;
		}
		if (one_prints)
		{
			message_from(what_channel(from), LOG_CRAP);
			if (do_hook(SIGNOFF_LIST, "%s %s", from, Reason))
				say("Signoff: %s (%s)", from, Reason);
		}
	}
	notify_mark(from, 0, 0);
	remove_from_channel((char *) 0, from, from_server);
	message_from((char *) 0, LOG_CURRENT);


	/*
	 * If we ever see our own quit, something is very amiss.
	 * Apparantly, this can happen when you change servers,
	 * and you change nicks because your old nick is lagged
	 * behind.  If the client somehow doesnt get wind of your
	 * new nick change before the old nick gets knocked off,
	 * the client could possibly see your old nick quit before
	 * it knows about your new nick.  (I personally dont believe
	 * this, but thats the story ive been getting.)
	 */
	if (!my_stricmp(from, get_server_nickname(from_server)))
	{
		yell("Internal inconsistency: Quit message for myself:");
		yell("Pertinent information: (%s[%s]) (%s!%s (%s))",get_server_nickname(from_server), get_server_name(from_server), from, FromUserHost, ArgList[0]);
	}
}

#ifdef __STDC__
static void p_pong (char *from, char **ArgList)
#else
static	void p_pong(from, ArgList)
	char	*from,
		**ArgList;
#endif
{
	if (check_ignore(from, FromUserHost, IGNORE_CRAP) != IGNORED)
		if (do_hook(PONG_LIST, "%s %s", from, ArgList[0]))
			if (ArgList[0] && index(ArgList[0], '.'))
				say("%s: PONG received from %s", ArgList[0], from);
}

#ifdef __STDC__
static void p_error (char *from, char **ArgList)
#else
static void p_error(from, ArgList)
	char	*from,
		**ArgList;
#endif
{
	PasteArgs(ArgList, 0);
	if (!ArgList[0])
		{fake();return;}
	say("%s", ArgList[0]);
}

#ifdef __STDC__
static void p_channel (char *from, char **ArgList)
#else
static void p_channel(from, ArgList)
	char	*from;
	char	**ArgList;
#endif
{
	char	*c, *channel = NULL;
	int op = 0, vo = 0;

	if (!strcmp(ArgList[0], "0"))
		{fake();return;}

	channel = ArgList[0];
	message_from(channel, LOG_CRAP);
	malloc_strcpy(&joined_nick, from);
	notify_mark(from, 1, 0);

	if (!my_stricmp(from, get_server_nickname(from_server)))
	{
		add_channel(channel, from_server);
		send_to_server("MODE %s", channel);
/*		send_to_server("WHO %s", channel);	*/
		funny_set_ignore_channel(channel);
	}
	else
	{
		/*
		 * Workaround for gratuitous protocol change in ef2.9
		 */
		if ((c = index(channel, '\007')))
		{
			for (*c++ = 0; *c; c++)
			{
				     if (*c == 'o') op = 1;
				else if (*c == 'v') vo = 1;
			}
		}

		add_to_channel(channel, from, from_server, op, vo);
	}

#ifdef I_DONT_TRUST_MY_USERS
	if (!get_channel_oper(channel, from_server))
		in_on_who = 1
#endif

	if (check_ignore_channel(from, FromUserHost, channel, IGNORE_CRAP) != IGNORED)
	{
		message_from(channel, LOG_CRAP);
		if (do_hook(JOIN_LIST, "%s %s %s", from, channel, FromUserHost))
			say("%s (%s) has joined channel %s", from, FromUserHost, channel);
		message_from((char *) 0, LOG_CURRENT);
	}

#ifdef I_DONT_TRUST_MY_USERS
	in_on_who = 0;
#endif
}

#ifdef __STDC__
static void 	p_invite (char *from, char **ArgList)
#else
static	void	p_invite(from, ArgList)
	char	*from,
		**ArgList;
#endif
{
	char	*high;

	switch (check_ignore_channel(from, FromUserHost, ArgList[1], IGNORE_INVITES))
	{
		case IGNORED:
			/* crud, i sure do hate this */
			if (get_int_var(SEND_IGNORE_MSG_VAR))
				send_to_server("NOTICE %s :%s is ignoring you",
					from, get_server_nickname(from_server));
			return;
		case HIGHLIGHTED:
			high = highlight_char;
			break;
		default:
			high = empty_string;
			break;
	}
	if (ArgList[0] && ArgList[1])
	{
		message_from(from, LOG_CRAP);
		if (do_hook(INVITE_LIST, "%s %s %s", from, ArgList[1],FromUserHost))
			say("%s%s(%s)%s invites you to channel %s", high,
				from, FromUserHost, high, ArgList[1]);
		malloc_strcpy(&invite_channel, ArgList[1]);
		malloc_strcpy(&recv_nick, from);
	}
}

/* Reconnect has been put back in the "jon2 comprimise" 
 *
 * Unlimited autoreconnect for server kills
 * Limited autoreconnect for forground procis on oper kills
 *	(/set auto_reconnect ON)
 * No autoreconnect on oper kills for background procis
 *
 * /on disconnect is still always hooked no matter what.
 *
 * You may configure this any way you please.. I really dont give
 * a darn what autoconnect you use... but this is a fair default.
 *
 * NOTE: If you want to change it, you *will* have to do it yourself.
 * Im quite serious.  Im not going to change this.
 */
#ifdef __STDC__
static void p_kill (char *from, char **ArgList)
#else
static	void	p_kill(from, ArgList)
char	*from,
	**ArgList;
#endif
{
        char sc[4];

	close_server(from_server,empty_string);
	clean_whois_queue();
	window_check_servers();
	sprintf(sc, "+%i", from_server);

	if (index(from, '.'))
        {
		say("Server [%s] has rejected you (probably due to a nick collision)", from);
        	server (NULL, sc, empty_string);
	}
	else
	{
		int foo;
		foo = do_hook(DISCONNECT_LIST,"Killed by %s (%s)",from,
			ArgList[1] ? ArgList[1] : "(No Reason Given)");

		if (foo)
			say("You have been killed by that fascist [%s] %s", from,
				ArgList[1] ? ArgList[1] : "(No Reason Given)");

/* If we are a background, and /on disconnect didnt hook, then we arent
   going anywhere.  We might as well quit. */ 
#ifndef QUIT_ON_OPERATOR_KILL
		if (background && !foo)
#endif
		{
			say("Too bad, you lose.");
			irc_exit(1, NULL);
		}

		if (!background && get_int_var(AUTO_RECONNECT_VAR))
			server (NULL, sc, empty_string);
	}
}

#ifdef __STDC__
static void p_ping (char **ArgList)
#else
static	void	p_ping(ArgList)
char	**ArgList;
#endif
{
        PasteArgs(ArgList, 0);
	send_to_server("PONG %s", ArgList[0]);
}

#ifdef __STDC__
static void p_silence (char *from, char **ArgList)
#else
static	void	p_silence (from, ArgList)
char	*from,
	**ArgList;
#endif
{
	char *target = ArgList[0];
	char mag = *target++;

	if (do_hook(SILENCE_LIST, "%c %s", mag, target))
	{
		if (mag == '+')
			say ("You will no longer recieve msgs from %s", target);
		else if (mag == '-')
			say ("You will now recieve msgs from %s", target);
		else
			say ("Unrecognized silence argument: %s", target);
	}
}


#ifdef __STDC__
static void p_nick (char *from, char **ArgList)
#else
static	void	p_nick(from, ArgList)
char	*from,
	**ArgList;
#endif
{
	int	one_prints = 0,
		its_me = 0;
	char	*chan;
	char	*line;

	line = ArgList[0];
	if (!my_stricmp(from, get_server_nickname(from_server)))
	{
		if (from_server == primary_server)
			strmcpy(nickname, line, 
#ifdef ALLOW_LONG_NICKNAMES
						LONG_NICKNAME_LEN
#else
						NICKNAME_LEN
#endif
								);
		set_server_nickname(from_server, line);
		its_me = 1;
		in_e_nick = 0;
	}
	if (check_ignore(from, FromUserHost, IGNORE_CRAP) != IGNORED)
	{
		for (chan = walk_channels(1, from); chan; chan = walk_channels(0, from))
		{
			message_from(chan, LOG_CRAP);
			if (do_hook(CHANNEL_NICK_LIST, "%s %s %s", chan, from, line))
				one_prints = 1;
		}
		if (one_prints)
		{
			if (its_me)
				message_from((char *) 0, LOG_CRAP);
			else
				message_from(what_channel(from), LOG_CRAP);
			if (do_hook(NICKNAME_LIST, "%s %s", from, line))
				say("%s is now known as %s", from, line);
		}
	}
	rename_nick(from, line, from_server);
}

#ifdef __STDC__
static void p_mode (char *from, char **ArgList)
#else
static void p_mode(from, ArgList)
	char	*from,
		**ArgList;
#endif
{
	char	*channel;
	char	*line;
	int	flag;

	PasteArgs(ArgList, 1);
	channel = ArgList[0];
	line = ArgList[1];
	flag = check_ignore_channel(from, FromUserHost, channel, IGNORE_CRAP);
	message_from(channel, LOG_CRAP);
	if (channel && line)
	{
                if (get_int_var(MODE_STRIPPER_VAR))
                        strip_modes(from, channel, line);
		if (is_channel(channel))
		{
			if (flag != IGNORED && do_hook(MODE_LIST, "%s %s %s",
					from, channel, line))
				say("Mode change \"%s\" on channel %s by %s",
						line, channel, from);
			update_channel_mode(channel, line);
		}
		else
		{
			if (flag != IGNORED && do_hook(MODE_LIST, "%s %s %s",
					from, channel, line))
				say("Mode change \"%s\" for user %s by %s",
						line, channel, from);
			update_user_mode(line);
		}
		update_all_status();
	}
	message_from(NULL, LOG_CURRENT);
}

#ifdef __STDC__
static void strip_modes (char *from, char *channel, char *line)
#else
static  void    strip_modes (from, channel, line)
char    *from;
char    *channel;
char    *line;
#endif
{
	char	*mode;
	char 	*pointer;
	char	mag = '+'; /* XXXX Bogus */
        char    *copy = (char *) 0;
        char    *free_copy;
	
        malloc_strcpy(&copy, line);
        free_copy = copy;
	mode = next_arg(copy, &copy);
	if (is_channel(channel))
	{
		for (pointer = mode;*pointer;pointer++)
		{
			char	c = *pointer;
			switch (c) {
				case '+' :
				case '-' : mag = c; break;
				case 'l' : if (mag == '+')
						do_hook(MODE_STRIPPED_LIST,"%s %s %c%c %s",
						  from,channel,mag,c,next_arg(copy,&copy));
					   else
						do_hook(MODE_STRIPPED_LIST,"%s %s %c%c",
						  from,channel,mag,c);
					   break;
				case 'a' :
				case 'i' :
				case 'm' :
				case 'n' :
				case 'p' :
				case 's' :
				case 't' : do_hook(MODE_STRIPPED_LIST,"%s %s %c%c",from,
						channel,mag,c);
					   break;
				case 'b' :
				case 'k' :
				case 'o' :
				case 'v' : do_hook(MODE_STRIPPED_LIST,"%s %s %c%c %s",from,
						channel,mag,c,next_arg(copy,&copy));
					   break;
				}
		}
	}
	else /* User mode */
	{
		for (pointer = mode;*pointer;pointer++)
		{
			char	c = *pointer;
			switch (c) {
				case '+' :
				case '-' : mag = c; break;
				default  : do_hook(MODE_STRIPPED_LIST,"%s %s %c%c",from, channel, mag, c);
					   break;
			}
		}
	}
        new_free(&free_copy);
}

#ifdef __STDC__
static void p_kick (char *from, char **ArgList)
#else
static	void p_kick(from, ArgList)
	char	*from,
		**ArgList;
#endif
{
	char	*channel,
		*who,
		*comment;

	channel = ArgList[0];
	who = ArgList[1];
	comment = ArgList[2] ? ArgList[2] : "(no comment)" ;

	message_from(channel, LOG_CRAP);
	if (channel && who)
	{
		if (my_stricmp(who, get_server_nickname(from_server)) == 0)
		{
			char *booya = m_strdup(channel_key(channel));
			Window *window = lookup_channel(channel, from_server, 0)->window;
			Window *old_window = curr_scr_win;
			int server = from_server;

			curr_scr_win = window;
			remove_channel(channel, from_server);
			update_all_status();

			if (do_hook(KICK_LIST, "%s %s %s %s", who, from, channel, comment))
				say("You have been kicked off channel %s by %s (%s)", channel, from, comment);

			if (get_int_var(AUTO_REJOIN_VAR))
			{
				from_server = window->server;

				if (booya && *booya)
					send_to_server("JOIN %s %s", channel, booya);
				else
					send_to_server("JOIN %s", channel);

				malloc_strcpy(&window->waiting_channel, channel);
				from_server = server;
			}
			new_free(&booya);
			curr_scr_win = old_window;
		}
		else
		{
			remove_from_channel(channel, who, from_server);
			if ((check_ignore_channel(from, FromUserHost, channel, IGNORE_CRAP) != IGNORED) && 
			     do_hook(KICK_LIST, "%s %s %s %s", who, from, channel, comment))
				say("%s has been kicked off channel %s by %s (%s)", who, channel, from, comment);
		}
	}
	message_from(NULL, LOG_CURRENT);
}

#ifdef __STDC__
static void p_part (char *from, char **ArgList)
#else
static	void p_part(from, ArgList)
	char	*from,
		**ArgList;
#endif
{
	char	*channel;

	channel = ArgList[0];
	message_from(channel, LOG_CRAP);
	in_on_who = 1;

	if ((check_ignore_channel(from, FromUserHost, channel, IGNORE_CRAP) != IGNORED) && 
	    do_hook(LEAVE_LIST, "%s %s %s", from, channel, FromUserHost))
		say("%s has left channel %s", from, channel);

	if (my_stricmp(from, get_server_nickname(from_server)) == 0)
	{
		remove_channel(channel, from_server);
		remove_from_mode_list(channel);
	}
	else
		remove_from_channel(channel, from, from_server);

	message_from(NULL, LOG_CURRENT);
	in_on_who = 0;
}


/*
 * parse_server: parses messages from the server, doing what should be done
 * with them 
 */
#ifdef __STDC__
void 	parse_server (char *line)
#else
void	parse_server(line)
char	*line;
#endif
{
	char	*from,
		*comm,
		*end;
/*	char	*copy[BIG_BUFFER_SIZE + 1]; */
	int	numeric;
	char	**ArgList;
	char	*TrueArgs[MAXPARA + 1];

	if (!line || !*line)
		return;		/* empty line from server -- bye bye */

	end = strlen(line) + line;
	if (*--end == '\n')
		*end-- = '\0';
	if (*end == '\r')
		*end-- = '\0';

	/* 
	 * automatically print stuff coming from ircserv
	 *
	 * :P Wildy -- before you even THINK of using this as
	 * a way to spoof messages into EPIC, make sure you read
	 * the code in ircserv.c where it weeds out incoming lines
	 * that start with '!' :^P~~~~~~~
	 */
	if (using_server_process && *line == '!')
	{
		if (do_hook(IRCSERV_LIST, "%s", line+1))
			say("Message from IRCSERV: %s", line+1);
		return;
	}

	if (*line == ':')
	{
		if (!do_hook(RAW_IRC_LIST, "%s", line + 1))
			return;
	}
	else if (!do_hook(RAW_IRC_LIST, "%s %s", "*", line))
		return;

/*	strcpy(copy, line); */
	ArgList = TrueArgs;
	BreakArgs(line, &from, ArgList);

	/* XXXX - i dont think 'from' can be null here.  */
	if ((!(comm = *ArgList++)) || !from || !*ArgList)
		return;		/* Serious protocol violation -- ByeBye */

	/* 
	 * I reformatted these in may '96 by using the output of /stats m
	 * from a few busy servers.  They are arranged so that the most 
	 * common types are high on the list (to save the average number
	 * of compares.)  I will be doing more testing in the future on
	 * a live client to see if this is a reasonable order.
	 */
	if ((numeric = atoi(comm)))
		numbered_command(from, numeric, ArgList);

	/* There are the core msgs for most non-numeric traffic. */
	else if (!strcmp(comm, "PRIVMSG")) 	p_privmsg(from, ArgList);
	else if (!strcmp(comm, "JOIN")) 	p_channel(from, ArgList);
	else if (!strcmp(comm, "NICK")) 	p_nick(from, ArgList);
	else if (!strcmp(comm, "PART")) 	p_part(from, ArgList);
	else if (!strcmp(comm, "MODE")) 	p_mode(from, ArgList);
	else if (!strcmp(comm, "QUIT")) 	p_quit(from, ArgList);
	else if (!strcmp(comm, "NOTICE")) 	parse_notice(from, ArgList);
	else if (!strcmp(comm, "TOPIC")) 	p_topic(from, ArgList);
	else if (!strcmp(comm, "KICK")) 	p_kick(from, ArgList);
	else if (!strcmp(comm, "INVITE")) 	p_invite(from, ArgList);

	/* These are used, but not nearly as much as ones above */
	else if (!strcmp(comm, "WALLOPS")) 	p_wallops(from, ArgList);
	else if (!strcmp(comm, "ERROR")) 	p_error(from, ArgList);
	else if (!strcmp(comm, "ERROR:")) 	p_error(from, ArgList);
	else if (!strcmp(comm, "SILENCE")) 	p_silence(from, ArgList);
	else if (!strcmp(comm, "KILL")) 	p_kill(from, ArgList);
	else if (!strcmp(comm, "PONG")) 	p_pong(from, ArgList);
	else if (!strcmp(comm, "PING")) 	p_ping(ArgList);

	/* Some kind of unrecognized/unsupported command */
	else
	{
		PasteArgs(ArgList, 0);
		if (from)
			say("Odd server stuff: \"%s %s\" (%s)", comm, ArgList[0], from);
		else
			say("Odd server stuff: \"%s %s\"", comm, ArgList[0]);
	}

	from_server = -1;
}
