/*
 * whois.c: Some tricky routines for querying the server for information
 * about a nickname using WHOIS.... all the time hiding this from the user.  
 *
 *
 * Written By Michael Sandrof
 *
 * Copyright(c) 1990 
 *
 * See the COPYRIGHT file, or do a HELP IRCII COPYRIGHT 
 */

#undef MONITOR_Q		/* this one is for monitoring of the 'whois queue' (debug) */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "irc.h"

#include "whois.h"
#include "hook.h"
#include "lastlog.h"
#include "vars.h"
#include "server.h"
#include "ignore.h"
#include "ircaux.h"
#include "notify.h"
#include "numbers.h"
#include "window.h"
#include "commands.h"
#include "output.h"
#include "parse.h"
#include "ctcp.h"
#include "misc.h"
#include "status.h"
#include "whowas.h"
#include "struct.h"
#include "fset.h"

/* current setting for BEEP_ON_MSG */
static int ignore_whois_crap = 0;
static int eat_away = 0;

/* WQ_head and WQ_tail point to the head and tail of the whois queue */
WhoisQueue *WQ_head = NULL;
WhoisQueue *WQ_tail = NULL;

static char show_away_flag = 0;

void typed_add_to_whois_queue(int, char *, void (*)(WhoisStuff *, char *, char *), char *, ...);
static char *whois_queue_head(int);
static int whois_type_head(int);
static void (*whois_func_head(int)) (WhoisStuff *, char *, char *);
static WhoisQueue *remove_from_whois_queue(int);

/*
 * whois_queue_head: returns the nickname at the head of the whois queue, or
 * NULL if the queue is empty.  It does not modify the queue in any way. 
 */
static char *whois_queue_head(int server_index)
{
    if ((WQ_head = (WhoisQueue *) get_server_qhead(server_index)) != NULL)
	return (WQ_head->nick);
    else
	return (NULL);
}

static int whois_type_head(int server_index)
{
    if ((WQ_head = (WhoisQueue *) get_server_qhead(server_index)) != NULL)
	return (WQ_head->type);
    else
	return -1;
}

static void (*whois_func_head(int server_index)) () {
    if ((WQ_head = (WhoisQueue *) get_server_qhead(server_index)) != NULL)
	return ((void *) WQ_head->func);
    else
	return NULL;
}

/*
 * remove_from_whois_queue: removes the top element of the whois queue and
 * returns the element as its function value.  This routine repairs handles
 * all queue stuff, but the returned element is mallocd and must be freed by
 * the calling routine 
 */
static WhoisQueue *remove_from_whois_queue(int server_index)
{
    WhoisQueue *new;

    new = (WhoisQueue *) get_server_qhead(server_index);
    set_server_qhead(server_index, new->next);
    if (new->next == NULL)
	set_server_qtail(server_index, NULL);
    return (new);
}

/*
 * clean_whois_queue: this empties out the whois queue.  This is used after
 * server reconnection to assure that no bogus entries are left in the whois
 * queue 
 */
void clean_whois_queue(void)
{
    WhoisQueue *thing;

    while (whois_queue_head(from_server)) {
	thing = remove_from_whois_queue(from_server);
	new_free(&thing->nick);
	new_free(&thing->text);
	new_free(&thing);
    }
    ignore_whois_crap = 0;
    eat_away = 0;
}

/* ison_returned: this is called when numeric 303 is received in
 * numbers.c. ISON must always be the property of the WHOIS queue.
 * Although we will check first that the top element expected is
 * actually an ISON.
 */
void ison_returned(char *from, char **ArgList)
{
    WhoisQueue *thing;

    if (whois_type_head(from_server) == WHOIS_ISON) {
	thing = remove_from_whois_queue(from_server);
	thing->func(thing->nick, ArgList[0] ? ArgList[0] : empty_str);
	new_free(&thing->nick);
	new_free(&thing->text);
	new_free(&thing);
    } else
	ison_now(NULL, ArgList[0] ? ArgList[0] : empty_str);
}

/* userhost_returned: this is called when numeric 302 is received in
 * numbers.c. USERHOST must also remain the property of the WHOIS
 * queue. Sending it without going via the WHOIS queue will cause
 * the queue to become corrupted.
 *
 * While USERHOST gives us similar information to WHOIS, this routine's
 * format is a little different to those that handle the WHOIS numerics.
 * A list of nicks can be supplied to USERHOST, and any of those
 * nicks are not currently signed on, it will just omit reporting it.
 * this means we must go through the list of nicks returned and check
 * them for missing entries. This means stepping through the requested
 * nicks one at a time.
 *
 * A side effect of this is that user initiated USERHOST requests get
 * reported as separate replies even though one request gets made and
 * only one reply is actually received. This should make it easier for
 * people wanting to use USERHOST for their own purposes.
 *
 * In this routine, cnick points to all the information in the next
 * entry from the returned data.
 */
void userhost_returned(char *from, char **ArgList)
{
    WhoisQueue *thing;
    WhoisStuff *whois_stuff = NULL;
    char *nick = NULL, *cnick = NULL, *tnick = NULL;
    char *user = NULL, *cuser = NULL;
    char *host = NULL, *chost = NULL;

    int ishere, cishere = 1;
    int isoper, cisoper = 0;
    int noton, isuser, parsed;

    char *queue_nicks = NULL;

    if (!ArgList[0] || *ArgList[0] == '\0')
	return;

    if (whois_type_head(from_server) == WHOIS_USERHOST) {
	isuser = ((void *) whois_func_head(from_server) == USERHOST_USERHOST);
	whois_stuff = get_server_whois_stuff(from_server);
	thing = remove_from_whois_queue(from_server);
	queue_nicks = thing->nick;
	if (*queue_nicks == ' ')
	    ++queue_nicks;
    } else {
	isuser = 1;
	thing = NULL;
	queue_nicks = NULL;
	whois_stuff = NULL;
    }
    parsed = 0;
    while ((parsed || (cnick = next_arg(ArgList[0], ArgList))) || queue_nicks) {
	if (queue_nicks) {
	    tnick = next_arg(queue_nicks, &queue_nicks);
	    if (!*queue_nicks)
		queue_nicks = NULL;
	} else
	    tnick = NULL;
	if (cnick && !parsed) {
	    if (!(cuser = strchr(cnick, '=')))
		break;
	    if (*(cuser - 1) == '*') {
		*(cuser - 1) = '\0';
		cisoper = 1;
	    } else
		cisoper = 0;
	    *cuser++ = '\0';
	    if (*cuser++ == '+')
		cishere = 1;
	    else
		cishere = 0;
	    if (!(chost = strchr(cuser, '@')))
		break;
	    *chost++ = '\0';
	    parsed = 1;
	}
	if (!cnick && thing) {
	    if (!tnick && !queue_nicks) {	/* ever happen? */
		say("tnick and queue_nick are both NULL, tell rex!");
		goto out_free;
	    }
	    if (tnick) {
		if (queue_nicks)
		    *--queue_nicks = ' ';
		queue_nicks = tnick;
		memcpy(thing->nick, queue_nicks, strlen(queue_nicks) + 1);
	    }

	    thing->next = get_server_qhead(from_server);
	    set_server_qhead(from_server, thing);
	    return;
	}
	if (tnick && my_stricmp(cnick, tnick)) {
	    if (tnick)
		nick = tnick;
	    else
		nick = cnick;
	    user = host = "<UNKNOWN>";
	    isoper = 0;
	    ishere = 1;
	    noton = 1;
	} else {
	    nick = cnick;
	    user = cuser;
	    host = chost;
	    isoper = cisoper;
	    ishere = cishere;
	    noton = parsed = 0;
	}
	if (!isuser) {
	    malloc_strcpy(&whois_stuff->nick, nick);
	    malloc_strcpy(&whois_stuff->user, user);
	    malloc_strcpy(&whois_stuff->host, host);
	    whois_stuff->oper = isoper;
	    whois_stuff->not_on = noton;
	    if (!ishere)
		malloc_strcpy(&whois_stuff->away, empty_str);
	    else
		new_free(&whois_stuff->away);
	    thing->func(whois_stuff, tnick, thing->text);
	    new_free(&whois_stuff->away);
	} else {
	    char *nsl = NULL;

	    if (get_int_var(AUTO_NSLOOKUP_VAR) && host && isdigit(*(host + strlen(host) - 1)))
		nsl = do_nslookup(host);

	    if (do_hook(current_numeric, "%s %s %s %s %s", nick, isoper ? "+" : "-", ishere ? "+" : "-", user, nsl ? nsl : host)) {
		put_it("%s %s is %s@%s%s%s", line_thing,
		       nick, user, nsl ? nsl : host, isoper ? " (Is an IRC operator)" : empty_str, ishere ? empty_str : " (away)");
	    }
	}
    }
    if (thing) {
      out_free:new_free(&thing->nick);
	new_free(&thing->text);
	new_free(&thing);
    }
}

/*
 * whois_name: routine that is called when numeric 311 is received in
 * numbers.c. This routine parses out the information in the numeric and
 * saves it until needed (see whois_server()).  If the whois queue is empty,
 * this routine simply displays the data in the normal fashion.  Why would
 * the queue ever be empty, you ask? If the user does a "WHOIS *" or any
 * other whois with a wildcard, you will get multiple returns from the
 * server.  So, instead of attempting to handle each of these, only the first
 * is handled, and the others fall through.  It is up to the programmer to
 * prevent wildcards from interfering with what they want done.  See
 * channel() in edit.c 
 */
void whois_name(char *from, char **ArgList)
{
    char *nick, *user, *host, *channel, *ptr, *name;

    WhoisStuff *whois_stuff;

    PasteArgs(ArgList, 4);
    nick = ArgList[0];
    user = ArgList[1];
    host = ArgList[2];
    channel = ArgList[3];
    name = ArgList[4];
    if (!nick || !user || !host || !channel || !name)
	return;

    whois_stuff = get_server_whois_stuff(from_server);
    if ((ptr = whois_queue_head(from_server)) && (whois_type_head(from_server) == WHOIS_WHOIS) && (my_stricmp(ptr, nick) == 0)) {
	malloc_strcpy(&whois_stuff->nick, nick);
	malloc_strcpy(&whois_stuff->user, user);
	malloc_strcpy(&whois_stuff->host, host);
	malloc_strcpy(&whois_stuff->name, name);
	malloc_strcpy(&whois_stuff->channel, channel);
	new_free(&whois_stuff->away);
	whois_stuff->oper = 0;
	whois_stuff->chop = 0;
	whois_stuff->not_on = 0;
	ignore_whois_crap = 1;
	eat_away = 1;
    } else {
	char *nsl = NULL;

	ignore_whois_crap = 0;
	eat_away = 0;

	if (host && isdigit(*(host + strlen(host) - 1)) && get_int_var(AUTO_NSLOOKUP_VAR))
	    nsl = do_nslookup(host);

	message_from(NULL, LOG_CRAP);
	if (do_hook(current_numeric, "%s %s %s %s %s %s", from, nick, user, nsl ? nsl : host, channel, name)) {
	    put_it("%s", convert_output_format(get_fset_var(FORMAT_WHOIS_HEADER_FSET), NULL));
	    put_it("%s",
		   convert_output_format(get_fset_var(FORMAT_WHOIS_NICK_FSET), "%s %s %s %s", nick, user, nsl ? nsl : host,
					 country(host)));
	    put_it("%s", convert_output_format(get_fset_var(FORMAT_WHOIS_NAME_FSET), "%s", name));
	}
    }
}

/*
 * whowas_name: same as whois_name() above but it is called with a numeric of
 * 314 when the user does a WHOWAS or when a WHOIS'd user is no longer on IRC 
 * and has set the AUTO_WHOWAS variable.
 */
void whowas_name(char *from, char **ArgList)
{
    char *nick, *user, *host, *channel, *ptr, *name;
    WhoisStuff *whois_stuff;
    int lastlog_level;

    PasteArgs(ArgList, 4);
    nick = ArgList[0];
    user = ArgList[1];
    host = ArgList[2];
    channel = ArgList[3];
    name = ArgList[4];
    if (!nick || !user || !host || !channel || !name)
	return;

    lastlog_level = set_lastlog_msg_level(LOG_CRAP);
    whois_stuff = get_server_whois_stuff(from_server);
    if ((ptr = whois_queue_head(from_server)) && (whois_type_head(from_server) & WHOIS_WHOIS) && (my_stricmp(ptr, nick) == 0)) {
	malloc_strcpy(&whois_stuff->nick, nick);
	malloc_strcpy(&whois_stuff->user, user);
	malloc_strcpy(&whois_stuff->host, host);
	malloc_strcpy(&whois_stuff->name, name);
	malloc_strcpy(&whois_stuff->channel, channel);
	new_free(&whois_stuff->away);
	whois_stuff->oper = 0;
	whois_stuff->chop = 0;
	whois_stuff->not_on = 1;
	ignore_whois_crap = 1;
    } else {
	ignore_whois_crap = 0;
	message_from(NULL, LOG_CRAP);
	if (do_hook(current_numeric, "%s %s %s %s %s %s", from, nick, user, host, channel, name)) {
	    put_it("%s", convert_output_format(get_fset_var(FORMAT_WHOWAS_HEADER_FSET), NULL));
	    put_it("%s", convert_output_format(get_fset_var(FORMAT_WHOWAS_NICK_FSET), "%s %s %s", nick, user, host));
	    put_it("%s", convert_output_format(get_fset_var(FORMAT_WHOIS_NAME_FSET), "%s", name));
	}
    }
    set_lastlog_msg_level(lastlog_level);
}

void whois_channels(char *from, char **ArgList)
{
    char *ptr;
    char *line;
    WhoisStuff *whois_stuff;

    PasteArgs(ArgList, 1);
    line = ArgList[1];
    whois_stuff = get_server_whois_stuff(from_server);
    if ((ptr = whois_queue_head(from_server)) &&
	(whois_type_head(from_server) & WHOIS_WHOIS) && whois_stuff->nick && (my_stricmp(ptr, whois_stuff->nick) == 0)) {
	if (whois_stuff->channels == NULL)
	    malloc_strcpy(&whois_stuff->channels, line);
	else
	    malloc_strcat(&whois_stuff->channels, line);
    } else {
	message_from(NULL, LOG_CRAP);
	if (do_hook(current_numeric, "%s %s", from, line))
	    put_it("%s", convert_output_format(get_fset_var(FORMAT_WHOIS_CHANNELS_FSET), "%s", line));
    }
}

/*
 * whois_server: Called in numbers.c when a numeric of 312 is received.  If
 * all went well, this routine collects the needed information, pops the top
 * element off the queue and calls the function as described above in
 * WhoisQueue.  It then releases all the mallocd data.  If the queue is empty
 * (same case as described in whois_name() above), the information is simply
 * displayed in the normal fashion. Added a check to see if whois_stuff->nick
 * is NULL. This can happen if something is added to an empty whois queue
 * between the whois name being received and the server.
 */
void whois_server(char *from, char **ArgList)
{
    char *server, *ptr;
    char *line;
    WhoisStuff *whois_stuff;

    show_away_flag = 1;
    if (!ArgList[0] || !ArgList[1])
	return;
    if (ArgList[2]) {
	server = ArgList[1];
	line = ArgList[2];
    } else {
	server = ArgList[0];
	line = ArgList[1];
    }
    whois_stuff = get_server_whois_stuff(from_server);
    if ((ptr = whois_queue_head(from_server)) && (whois_type_head(from_server) & WHOIS_WHOIS) && whois_stuff->nick &&	/* This is
															 * *weird* */
	(!my_stricmp(ptr, whois_stuff->nick))) {
	malloc_strcpy(&whois_stuff->server, server);
	malloc_strcpy(&whois_stuff->server_stuff, line);
    } else {
	message_from(NULL, LOG_CRAP);
	if (do_hook(current_numeric, "%s %s %s", from, server, line))
	    put_it("%s", convert_output_format(get_fset_var(FORMAT_WHOIS_SERVER_FSET), "%s %s", server, line));
    }
}

/*
 * whois_oper: This displays the operator status of a user, as returned by
 * numeric 313 from the server.  If the ignore_whois_crap flag is set,
 * nothing is dispayed. 
 */
void whois_oper(char *from, char **ArgList)
{
    WhoisStuff *whois_stuff;

    whois_stuff = get_server_whois_stuff(from_server);
    PasteArgs(ArgList, 1);
    if (ignore_whois_crap)
	whois_stuff->oper = 1;
    else {
	char *nick;

	if ((nick = ArgList[0]) != NULL) {
	    message_from(NULL, LOG_CRAP);
	    if (do_hook(current_numeric, "%s %s %s", from, nick, ArgList[1]))
		put_it("%s",
		       convert_output_format(get_fset_var(FORMAT_WHOIS_OPER_FSET), "%s %s", nick, " (is \002NOT\002 an IRC warrior)"));
	}

    }
}

void whois_lastcom(char *from, char **ArgList)
{
    if (!ignore_whois_crap) {
	char *nick, *idle_str;

	PasteArgs(ArgList, 2);
	message_from(NULL, LOG_CRAP);
	if ((nick = ArgList[0]) && (idle_str = ArgList[1]) &&
	    do_hook(current_numeric, "%s %s %s %s", from, nick, idle_str, ArgList[2])) {
	    int seconds = 0;
	    int hours = 0;
	    int minutes = 0;
	    int secs = my_atol(idle_str);

	    hours = secs / 3600;
	    minutes = (secs - (hours * 3600)) / 60;
	    seconds = secs % 60;
	    put_it("%s",
		   convert_output_format(get_fset_var(FORMAT_WHOIS_IDLE_FSET), "%d %d %d %s", hours, minutes, seconds,
					 ArgList[2] ? ArgList[2] : empty_str));
	}
    }
}

void end_of_whois(char *from, char **ArgList)
{
    char *nick;
    char *ptr;
    WhoisStuff *whois_stuff;

    whois_stuff = get_server_whois_stuff(from_server);

    show_away_flag = 0;
    if ((nick = ArgList[0]) != NULL) {
	ptr = whois_queue_head(from_server);
	if (ptr && (whois_type_head(from_server) & WHOIS_WHOIS) && (!my_stricmp(ptr, nick))) {
	    WhoisQueue *thing;

	    thing = remove_from_whois_queue(from_server);
	    whois_stuff->not_on = 0;
	    thing->func(whois_stuff, thing->nick, thing->text);
	    new_free(&whois_stuff->channels);
	    new_free(&thing->nick);
	    new_free(&thing->text);
	    new_free(&thing);
	    ignore_whois_crap = 0;
	    return;
	}
	PasteArgs(ArgList, 0);
	message_from(NULL, LOG_CRAP);
	if (do_hook(current_numeric, "%s %s", from, ArgList[0]) && get_fset_var(FORMAT_WHOIS_FOOTER_FSET))
	    put_it("%s", convert_output_format(get_fset_var(FORMAT_WHOIS_FOOTER_FSET), NULL));
    }
}

/*
 * no_such_nickname: Handler for numeric 401, the no such nickname error. If
 * the nickname given is at the head of the queue, then this routine pops the
 * top element from the queue, sets the whois_stuff->flag to indicate that the
 * user is no longer on irc, then calls the func() as normal.  It is up to
 * that function to set the ignore_whois_crap variable which will determine
 * if any other information is displayed or not. 
 *
 * that is, it used to.  now it does bugger all, seeing all the functions that
 * used to use it, now use no such command.  -phone, april 1993.
 */
void no_such_nickname(char *from, char **ArgList)
{
    char *nick;
    char *ptr, *bar;
    WhoisStuff *whois_stuff;
    int foo;

    whois_stuff = get_server_whois_stuff(from_server);
    ptr = whois_queue_head(from_server);

    if (ptr && *ptr == ' ') {
	++ptr;
    }
    PasteArgs(ArgList, 1);
    nick = ArgList[0];
    if (*nick == '!') {
	char *name = nick + 1;

	if (ptr && (whois_type_head(from_server) & WHOIS_WHOIS) && !strcmp(ptr, name)) {
	    WhoisQueue *thing;

	    /* There's a query in the WhoisQueue : assume it's completed and remove it from the queue -Sol */

	    thing = remove_from_whois_queue(from_server);
	    whois_stuff->not_on = 0;
	    thing->func(whois_stuff, thing->nick, thing->text);
	    new_free(&whois_stuff->channels);
	    new_free(&thing->nick);
	    new_free(&thing->text);
	    new_free(&thing);
	    ignore_whois_crap = 0;
	    return;
	}
	return;
    }
    notify_mark(nick, NULL, NULL, 0);
    if (ptr && (whois_type_head(from_server) & WHOIS_USERHOST) && (bar = strstr(ptr, nick))) {
	WhoisQueue *thing;
	int i;

	/* Remove query from queue. Don't display anything. -Sol */

	i = strlen(nick);

	if (*(bar + i + 1) == '\0' && (bar != ptr)) {
	    *bar = '\0';
	}
	if ((*(bar + i) == ' ') && (*(bar + i + 1) != '\0')) {
	    memcpy(bar, bar + i + 1, strlen(bar) - i);
	} else {
	    /* take it off all together */
	    thing = remove_from_whois_queue(from_server);
	    new_free(&whois_stuff->channels);
	    new_free(&thing->nick);
	    new_free(&thing->text);
	    new_free(&thing);
	    ignore_whois_crap = 0;
	}
	return;
    }
    message_from(NULL, LOG_CRAP);
    if ((foo = get_int_var(AUTO_WHOWAS_VAR))) {
	if (foo > -1)
	    send_to_server(SERVER(from_server), "WHOWAS %s %d", nick, foo);
	else
	    send_to_server(SERVER(from_server), "WHOWAS %s", nick);
    } else if (do_hook(current_numeric, "%s %s %s", from, nick, ArgList[1]))
	put_it("%s",
	       convert_output_format(get_fset_var(FORMAT_NONICK_FSET), "%s %s %s %s", update_clock(GET_TIME), nick, from, ArgList[1]));
}

/*
 * user_is_away: called when a 301 numeric is received.  Nothing is displayed
 * by this routine if the ignore_whois_crap flag is set 
 */
void user_is_away(char *from, char **ArgList)
{
    char *message, *who;
    WhoisStuff *whois_stuff;

    if (!from)
	return;

    PasteArgs(ArgList, 1);
    whois_stuff = get_server_whois_stuff(from_server);

    if ((who = ArgList[0]) && (message = ArgList[1])) {
	if (whois_stuff->nick && (!strcmp(who, whois_stuff->nick)) && eat_away)
	    malloc_strcpy(&whois_stuff->away, message);
	else {
	    message_from(NULL, LOG_CRAP);
	    if (do_hook(current_numeric, "%s %s", who, message))
		put_it("%s", convert_output_format(get_fset_var(FORMAT_WHOIS_AWAY_FSET), "%s %s", who, message));
	}
    }
}

/*
 * The stuff below this point are all routines suitable for use in the
 * add_to_whois_queue() call as the func parameter 
 */

/*
 * whois_ignore_msgs: This is used when you are ignoring MSGs using the
 * user@hostname format 
 */
void whois_ignore_msgs(WhoisStuff * stuff, char *nick, char *text)
{
    char *ptr;
    int level;

    if (stuff) {
	ptr = new_malloc(strlen(stuff->user) + strlen(stuff->host) + 2);
	strcpy(ptr, stuff->user);
	strcat(ptr, "@");
	strcat(ptr, stuff->host);
	if (is_ignored(ptr, IGNORE_MSGS) != IGNORED) {
	    level = set_lastlog_msg_level(LOG_MSG);
	    message_from(stuff->nick, LOG_MSG);
	    if (do_hook(MSG_LIST, "%s %s", stuff->nick, text)) {
		if (away_set) {
		    time_t t;
		    char *msg = NULL;

		    t = time(NULL);
		    put_it("%s",
			   convert_output_format(get_fset_var(FORMAT_IGNORE_MSG_AWAY_FSET), "%s %s %s", update_clock(GET_TIME),
						 stuff->nick, msg));
		    beep_em(get_int_var(BEEP_WHEN_AWAY_VAR));
		} else {
		    put_it("%s",
			   convert_output_format(get_fset_var(FORMAT_IGNORE_MSG_FSET), "%s %s %s", update_clock(GET_TIME), stuff->nick,
						 text));
		    beep_em(get_int_var(BEEP_ON_MSG_VAR));
		}
	    }
	    if (beep_on_level & LOG_MSG)
		beep_em(1);
	    set_lastlog_msg_level(level);
	    notify_mark(nick, stuff->user, stuff->host, 1);
	} else if (get_int_var(SEND_IGNORE_MSG_VAR))
	    send_to_server(SERVER(from_server), "NOTICE %s :%s is ignoring you.", nick, get_server_nickname(from_server));
	new_free(&ptr);
    }
}

void whois_nickname(WhoisStuff * stuff, char *nick, char *text)
{
    if (stuff) {
	if (!(my_stricmp(stuff->user, username)) && (!my_stricmp(stuff->host, local_host_name ? local_host_name : def_hostname)))
	    set_server_nickname(from_server, nick);
    }
}

/*
 * whois_ignore_notices: This is used when you are ignoring NOTICEs using the
 * user@hostname format 
 */
void whois_ignore_notices(WhoisStuff * stuff, char *nick, char *text)
{
    char *ptr;
    int level;

    if (stuff) {
	ptr = new_malloc(strlen(stuff->user) + strlen(stuff->host) + 2);
	strcpy(ptr, stuff->user);
	strcat(ptr, "@");
	strcat(ptr, stuff->host);
	if (is_ignored(ptr, IGNORE_NOTICES) != IGNORED) {
	    level = set_lastlog_msg_level(LOG_NOTICE);
	    message_from(stuff->nick, LOG_NOTICE);
	    if (do_hook(NOTICE_LIST, "%s %s", stuff->nick, text))
		put_it("%s",
		       convert_output_format(get_fset_var(FORMAT_IGNORE_NOTICE_FSET), "%s %s %s", update_clock(GET_TIME), stuff->nick,
					     text));
	    set_lastlog_msg_level(level);
	}
	new_free(&ptr);
    }
}

/*
 * whois_ignore_invites: This is used when you are ignoring INVITES using the
 * user@hostname format 
 */
void whois_ignore_invites(WhoisStuff * stuff, char *nick, char *text)
{
    char *ptr;

    if (stuff) {
	ptr = new_malloc(strlen(stuff->user) + strlen(stuff->host) + 2);
	strcpy(ptr, stuff->user);
	strcat(ptr, "@");
	strcat(ptr, stuff->host);
	if (is_ignored(ptr, IGNORE_INVITES) != IGNORED) {
	    struct whowas_chan_list *w_chan = NULL;
	    struct channel *chan = NULL;

	    if (do_hook(INVITE_LIST, "%s %s", stuff->nick, text))
		put_it("%s", convert_output_format(get_fset_var(FORMAT_IGNORE_INVITE_FSET), "%s %s", stuff->nick, text));
	    malloc_strcpy(&invite_channel, text);
	    if (!(chan = lookup_channel(invite_channel, from_server, 0))) {
		if ((w_chan = check_whowas_chan_buffer(invite_channel, 0)))
		    chan = w_chan->channellist;
	    }
	    if (chan && get_int_var(AUTO_REJOIN_VAR))
		send_to_server(SERVER(from_server), "JOIN %s%s%s", invite_channel, chan->key ? " " : empty_str,
			       chan->key ? chan->key : empty_str);
	} else if (get_int_var(SEND_IGNORE_MSG_VAR))
	    send_to_server(SERVER(from_server), "NOTICE %s :%s is ignoring you.", nick, get_server_nickname(from_server));
	new_free(&ptr);
    }
}

/*
 * whois_ignore_walls: This is used when you are ignoring WALLS using the
 * user@hostname format 
 */
void whois_ignore_walls(WhoisStuff * stuff, char *nick, char *text)
{
    char *ptr;
    int level;

    level = set_lastlog_msg_level(LOG_WALL);
    message_from(stuff->nick, LOG_WALL);
    if (stuff) {
	ptr = new_malloc(strlen(stuff->user) + strlen(stuff->host) + 2);
	strcpy(ptr, stuff->user);
	strcat(ptr, "@");
	strcat(ptr, stuff->host);
	if (is_ignored(ptr, IGNORE_WALLS) != IGNORED) {
	    if (do_hook(WALL_LIST, "%s %s", stuff->nick, text))
		put_it("%s",
		       convert_output_format(get_fset_var(FORMAT_IGNORE_WALL_FSET), "%s %s %s", update_clock(GET_TIME), stuff->nick,
					     text));
	    if (beep_on_level & LOG_WALL)
		beep_em(1);
	}
	new_free(&ptr);
    }
    set_lastlog_msg_level(level);
}

/*
 * add_to_whois_queue: This routine is called whenever you want to do a WHOIS
 * or WHOWAS.  What happens is this... each time this function is called it
 * adds a new element to the whois queue using the nick and func as in
 * WhoisQueue, and creating the text element using the format and args.  It
 * then issues the WHOIS or WHOWAS.  
 */
void add_to_whois_queue(char *nick, void (*func) (WhoisStuff *, char *, char *), char *format, ...)
{
    int Type;

    if (func == USERHOST_USERHOST || func == userhost_cmd_returned)
	Type = WHOIS_USERHOST;
    else
	Type = WHOIS_WHOIS;

    if (format) {
	char blah[BIG_BUFFER_SIZE + 1];
	va_list vlist;

	va_start(vlist, format);
	vsnprintf(blah, BIG_BUFFER_SIZE, format, vlist);
	va_end(vlist);
	typed_add_to_whois_queue(Type, nick, func, "%s", blah);
    }
}

void add_to_userhost_queue(char *nick, void (*func) (WhoisStuff *, char *, char *), const char *format, ...)
{
    if (format) {
	char blah[BIG_BUFFER_SIZE + 1];
	va_list vlist;

	va_start(vlist, format);
	vsnprintf(blah, BIG_BUFFER_SIZE, format, vlist);
	va_end(vlist);

	typed_add_to_whois_queue(WHOIS_USERHOST, nick, func, "%s", blah);
    }
}

void add_ison_to_whois(char *nick, void (*func) ())
{
    typed_add_to_whois_queue(WHOIS_ISON, nick, func, NULL);
}

void typed_add_to_whois_queue(int type, char *nick, void (*func) (WhoisStuff *, char *, char *), char *format, ...)
{
    char buffer[BIG_BUFFER_SIZE + 1];
    WhoisQueue *new = NULL;
    char *p = nick;

    if ((nick == NULL) || !is_server_connected(from_server))
	return;

    for (; *p == ' ' || *p == '\t'; p++) ;
    if (!*p)
	return;			/* nick should always be a non-blank string, but I'd rather check because of a "ISON not enough
				 * parameters" coming from the server -Sol */

    if (strchr(nick, '*') == NULL) {
	new = new_malloc(sizeof(WhoisQueue));
	memset(new, 0, sizeof(WhoisQueue));
	new->func = func;
	new->type = type;
	new->nick = m_strdup(nick);

	if (format) {
	    va_list args;

	    va_start(args, format);
	    vsnprintf(buffer, BIG_BUFFER_SIZE, format, args);
	    va_end(args);
	    new->text = m_strdup(buffer);
	}
	if ((void *) get_server_qhead(from_server) == NULL)
	    set_server_qhead(from_server, new);
	if (get_server_qtail(from_server))
	    ((WhoisQueue *) get_server_qtail(from_server))->next = new;
	set_server_qtail(from_server, new);
	switch (type) {
	case WHOIS_ISON:
#ifdef MONITOR_Q
	    put_it("+++ ISON %s", nick);
#endif
	    send_to_server(SERVER(from_server), "ISON %s", nick);
	    break;
	case WHOIS_USERHOST:
#ifdef MONITOR_Q
	    put_it("+++ USERHOST %s", nick);
#endif
	    send_to_server(SERVER(from_server), "USERHOST %s", nick);
	    break;
	case WHOIS_WHOIS:
#ifdef MONITOR_Q
	    put_it("+++ WHOIS %s", nick);
#endif
	    send_to_server(SERVER(from_server), "WHOIS %s", nick);
	    break;
	case WHOIS_WHOWAS:
#ifdef MONITOR_Q
	    put_it("+++ WHOWAS %s", nick);
#endif
	    send_to_server(SERVER(from_server), "WHOWAS %s", nick);
	    break;
	}
    }
}

void userhost_cmd_returned(WhoisStuff * stuff, char *nick, char *text)
{
    char args[BIG_BUFFER_SIZE + 1];

    strcpy(args, stuff->nick ? stuff->nick : empty_str);
    strcat(args, stuff->oper ? " + " : " - ");
    strcat(args, stuff->away ? "+ " : "- ");
    strcat(args, stuff->user ? stuff->user : empty_str);
    strcat(args, space_str);
    strcat(args, stuff->host ? stuff->host : empty_str);
    parse_line("USERHOST", text, args, 0, 0);
}
