/************************************************************************
*   IRC - Internet Relay Chat, src/whowas.c
*   Copyright (C) 1990 Markku Savela
*
*   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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

/* $Id: whowas.c,v 1.4 2005/07/09 01:22:50 sheik Exp $ */

#include "struct.h"
#include "common.h"
#include "sys.h"
#include "numeric.h"
#include "h.h"
#include "memcount.h"

/* externally defined functions */
unsigned int hash_whowas_name(char *);	/* defined in hash.c */
/* internally defined function */
static void add_whowas_to_clist(aWhowas **, aWhowas *);
static void del_whowas_from_clist(aWhowas **, aWhowas *);
static void add_whowas_to_list(aWhowas **, aWhowas *);
static void del_whowas_from_list(aWhowas **, aWhowas *);

aWhowas     WHOWAS[NICKNAMEHISTORYLENGTH];
aWhowas    *WHOWASHASH[WW_MAX];

int         whowas_next = 0;

void add_history(aClient *cptr, int online)
{
    aWhowas    *new;
    
    new = &WHOWAS[whowas_next];

    if (new->hashv != -1) 
    {
	if (new->online)
	    del_whowas_from_clist(&(new->online->whowas), new);
	del_whowas_from_list(&WHOWASHASH[new->hashv], new);
    }
    new->hashv = hash_whowas_name(cptr->name);
    new->logoff = NOW;
    strncpyzt(new->name, cptr->name, NICKLEN + 1);
    strncpyzt(new->username, cptr->user->username, USERLEN + 1);
    strncpyzt(new->hostname, cptr->user->host, HOSTLEN);
    strncpyzt(new->realname, cptr->info, REALLEN);

    if (MyClient(cptr))
        strncpyzt(new->realhost, cptr->sockhost, HOSTLEN);
    else
        strncpyzt(new->realhost, cptr->user->realhost ?
            cptr->user->realhost : cptr->user->host, HOSTLEN);
    /*
     * Its not string copied, a pointer to the scache hash is copied
     * -Dianora
     */
    new->servername = cptr->user->server;
    new->umode = cptr->umode;

    if (online) 
    {
	new->online = cptr;
	add_whowas_to_clist(&(cptr->whowas), new);
    }
    else
	new->online = NULL;
    add_whowas_to_list(&WHOWASHASH[new->hashv], new);
    whowas_next++;
    if (whowas_next == NICKNAMEHISTORYLENGTH)
	whowas_next = 0;
}

void off_history(aClient *cptr)
{
    aWhowas    *temp, *next;
    
    for (temp = cptr->whowas; temp; temp = next) 
    {
	next = temp->cnext;
	temp->online = NULL;
	del_whowas_from_clist(&(cptr->whowas), temp);
    }
}

aClient *get_history(char *nick, time_t timelimit)
{
    aWhowas    *temp;
    int         blah;

    timelimit = NOW - timelimit;
    blah = hash_whowas_name(nick);
    temp = WHOWASHASH[blah];
    for (; temp; temp = temp->next)
    {
	if (mycmp(nick, temp->name))
	    continue;
	if (temp->logoff < timelimit)
	    continue;
	return temp->online;
    }
    return NULL;
}

/*
 * m_whowas 
 * parv[0] = sender prefix
 * parv[1] = nickname queried
 */
int m_whowas(aClient *cptr, aClient *sptr, int parc, char *parv[])
{
    aWhowas *temp;
    int cur = 0;
    int         max = -1, found = 0;
    char       *p, *nick, *s;

    if (parc < 2) 
    {
	sendto_one(sptr, err_str(ERR_NONICKNAMEGIVEN),
		   me.name, parv[0]);
	return 0;
    }
    if (parc > 2)
	max = atoi(parv[2]);
    if (parc > 3)
	if (hunt_server(cptr, sptr, ":%s WHOWAS %s %s :%s", 3, parc, parv))
	    return 0;

    parv[1] = canonize(parv[1]);
    if (!MyConnect(sptr) && (max > 20))
	max = 20;
    for (s = parv[1]; (nick = strtoken(&p, s, ",")); s = NULL) 
    {
	temp = WHOWASHASH[hash_whowas_name(nick)];
	found = 0;
	for (; temp; temp = temp->next) 
	{
	    if (!mycmp(nick, temp->name)) 
	    {
		sendto_one(sptr, rpl_str(RPL_WHOWASUSER),
			   me.name, parv[0], temp->name,
			   temp->username,
			   IsAnOper(sptr) ? temp->realhost : temp->hostname,
			   temp->realname);
		if((temp->umode & UMODE_W) && !IsAnOper(sptr))
		    sendto_one(sptr, rpl_str(RPL_WHOISSERVER),
			       me.name, parv[0], temp->name,
			      HIDDEN_SERVER_NAME, myctime(temp->logoff));
		else
		    sendto_one(sptr, rpl_str(RPL_WHOISSERVER),
			       me.name, parv[0], temp->name,
			       temp->servername, myctime(temp->logoff));
		cur++;
		found++;
	    }
	    if (max > 0 && cur >= max)
		break;
	}
	if (!found)
	    sendto_one(sptr, err_str(ERR_WASNOSUCHNICK),
		       me.name, parv[0], nick);
	if (p)
	    p[-1] = ',';
    }
    sendto_one(sptr, rpl_str(RPL_ENDOFWHOWAS), me.name, parv[0], parv[1]);
    return 0;
}

void initwhowas()
{
    int i;

    for (i = 0; i < NICKNAMEHISTORYLENGTH; i++)
    {
	memset((char *) &WHOWAS[i], '\0', sizeof(aWhowas));
	WHOWAS[i].hashv = -1;
    }
    for (i = 0; i < WW_MAX; i++)
	WHOWASHASH[i] = NULL;
}

static void add_whowas_to_clist(aWhowas ** bucket, aWhowas * whowas)
{
    whowas->cprev = NULL;
    if ((whowas->cnext = *bucket) != NULL)
	whowas->cnext->cprev = whowas;
    *bucket = whowas;
}

static void del_whowas_from_clist(aWhowas ** bucket, aWhowas * whowas)
{
    if (whowas->cprev)
	whowas->cprev->cnext = whowas->cnext;
    else
	*bucket = whowas->cnext;
    if (whowas->cnext)
	whowas->cnext->cprev = whowas->cprev;
}

static void add_whowas_to_list(aWhowas ** bucket, aWhowas * whowas)
{
    whowas->prev = NULL;
    if ((whowas->next = *bucket) != NULL)
	whowas->next->prev = whowas;
    *bucket = whowas;
}

static void del_whowas_from_list(aWhowas ** bucket, aWhowas * whowas)
{
    if (whowas->prev)
	whowas->prev->next = whowas->next;
    else
	*bucket = whowas->next;
    if (whowas->next)
	whowas->next->prev = whowas->prev;
}

u_long
memcount_whowas(MCwhowas *mc)
{
    mc->file = __FILE__;

    mc->s_whowas.c = sizeof(WHOWAS)/sizeof(WHOWAS[0]);
    mc->s_whowas.m = sizeof(WHOWAS);
    mc->s_hash.c = sizeof(WHOWASHASH)/sizeof(WHOWASHASH[0]);
    mc->s_hash.m = sizeof(WHOWASHASH);

    return 0;
}


syntax highlighted by Code2HTML, v. 0.9.1