/************************************************************************
 *   psybnc2.2.2, src/p_memory.c
 *   Copyright (C) 2001 the most psychoid  and
 *                      the cool lam3rz IRC Group, IRCnet
 *			http://www.psychoid.lam3rz.de
 *
 *   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.
 */

#ifndef lint
static char rcsid[] = "@(#)$Id: p_memory.c, v 2.2.2 2001/03/25 02:02:02 psychoid Exp $";
#endif

#define P_MEMORY

#include <p_global.h>

/* the usermemory has been made variable, this function returns the desired userstructure */

/* malloc-wrapper. No memory will log an error entry and kill the bouncer */

unsigned long *__pmalloc(unsigned long size,char *module,int line)
{
    unsigned long *rc;
    if (!(rc=(unsigned long *)malloc(size)))
    {
	log(LOG_ERROR,-1,"OUT OF MEMORY. Last context: %s Line %d - killing the bouncer !",module,line);
	exit(0x0);
    }	
    memset(rc,0x0,size);
    return rc;
}

/* struct wrappers. Those alloc, delete and return the needed structures */

/* user structure */

struct usert *_lastu=NULL;
int _lastuser=-1;

struct usert *user(int usern)
{
    static struct usernodes *th,*thold;
    if(usern>10000) usern-=10000;
    if(usern==_lastuser && U_CREATE!=1) return _lastu;
    _lastuser=usern;
    if (dummyuser==NULL) {
        dummyuser=(struct usert *)pmalloc(sizeof(struct usert));
    }
    if (nulluser==NULL) {
	nulluser=(struct usert *)pmalloc(sizeof(struct usert));
    }
    if (usernode==NULL) {
	usernode=(struct usernodes *)pmalloc(sizeof(struct usert));
    }
    th=usernode;
    if (usern==0 || usern >MAX_USER) {
       thisuser=NULL;
       _lastu=nulluser;	
       return nulluser;
    }
    while(th!=NULL)
    {
	if (th->uid==usern)
	{
	    if (th->user==NULL) {
		th->user=(struct usert *) pmalloc(sizeof(struct usert));
	    }
	    thisuser=th;
	    _lastu=th->user;
	    return th->user;		
	}
	thold=th;
	th=th->next;
    }
    if (U_CREATE==1)
    {
	U_CREATE=0; /* resetting this */
	thold->next=(struct usernodes *) pmalloc(sizeof(struct usernodes));
	th=thold->next;
	th->user=(struct usert *) pmalloc(sizeof(struct usert));
	th->uid=usern;
	th->next=NULL;
	thisuser=th;
	_lastu=th->user;
	return th->user;
    } else {
	thisuser=NULL;
	_lastu=dummyuser;
	return dummyuser; /* yes, sure. Get it */
    }        
}

struct newpeert *_npeer;
int _lastpeer=-1;

/* this function returns the desired peerstructure */

struct newpeert *newpeer(int usern)
{
    static struct peernodes *th,*thold;
    if(_lastpeer==usern && P_CREATE!=1) return _npeer;
    _lastpeer=usern;
    if (dummypeer==NULL) {
        dummypeer=(struct newpeert *)pmalloc(sizeof(struct newpeert));
    }
    th=peernode;
    while(th!=NULL)
    {
	if (th->uid==usern)
	{
	    if (th->peer==NULL) {
		th->peer=(struct newpeert *) pmalloc(sizeof(struct newpeert));
	    }
	    thispeer=th;
	    _npeer=th->peer;
	    return th->peer;		
	}
	thold=th;
	th=th->next;
    }
    if (P_CREATE==1)
    {
	P_CREATE=0; /* resetting this */
	thold->next=(struct peernodes *) pmalloc(sizeof(struct peernodes));
	th=thold->next;
	th->peer=(struct newpeert *) pmalloc(sizeof(struct newpeert));
	th->uid=usern;
	th->next=NULL;
	thispeer=th;
	_npeer=th->peer;
	return th->peer;
    } else {
	thispeer=NULL;
	_npeer=dummypeer;
	return dummypeer; /* yes, sure. Get it */
    }        
}

struct datalinkt *_nlink;
int _lastlink=-1;

/* this function returns the desired datalink-structure */

struct datalinkt *datalink(int usern)
{
    static struct linknodes *th,*thold;
    if(_lastlink==usern && D_CREATE!=1) return _nlink;
    _lastlink=usern;
    if (dummylink==NULL) {
        dummylink=(struct datalinkt *) pmalloc(sizeof(struct datalinkt));
        memset(dummylink,0x0,sizeof(struct datalinkt));
    }
    th=linknode;
    while(th!=NULL)
    {
	if (th->uid==usern)
	{
	    if (th->link==NULL)
		th->link=(struct datalinkt *) pmalloc(sizeof(struct datalinkt));
	    thislink=th;
	    _nlink=th->link;
	    return th->link;		
	}
	thold=th;
	th=th->next;
    }
    if (D_CREATE==1)
    {
	D_CREATE=0; /* resetting this */
	thold->next=(struct linknodes *) pmalloc(sizeof(struct linknodes));
	th=thold->next;
	th->link=(struct datalinkt *) pmalloc(sizeof(struct datalinkt));
	th->uid=usern;
	th->next=NULL;
	thislink=th;
	_nlink=th->link;
	return th->link;
    } else {
	thislink=NULL;
	_nlink=dummylink;
	return dummylink; /* yes, sure. Get it */
    }        
}

/* clearing structs */

int clearuser(int usern)
{
    struct linknodes *nextdcc;
    struct linknodes *thisdcc;
    _lastuser=-1;
    while (user(usern)->bans!=NULL)
        user(usern)->bans=removestring(0,user(usern)->bans);
    while (user(usern)->ops!=NULL)
        user(usern)->ops=removestring(0,user(usern)->ops);
    while (user(usern)->aops!=NULL)
        user(usern)->aops=removestring(0,user(usern)->aops);
    while (user(usern)->askops!=NULL)
        user(usern)->askops=removestring(0,user(usern)->askops);
    while (user(usern)->logs!=NULL)
        user(usern)->logs=removestring(0,user(usern)->logs);
    while (user(usern)->keys!=NULL)
        user(usern)->keys=removestring(0,user(usern)->keys);
#ifdef CRYPT
    while (user(usern)->encrypt!=NULL)
        user(usern)->encrypt=removestring(0,user(usern)->encrypt);
#endif
#ifdef TRANSLATE
    while (user(usern)->translates!=NULL)
        user(usern)->translates=removestring(0,user(usern)->translates);
#endif
#ifdef SCRIPTING
    clearuserscript(usern);
#endif
    removeallchannelsfromuser(usern);
    nextdcc=user(usern)->dcc;
    if (nextdcc !=NULL)
    {	
	thisdcc=nextdcc;
	if (thisdcc->link !=NULL)
	{
	    if (thisdcc->link->outstate==STD_CONN)
		killsocket(thisdcc->link->outsock);
	    if (thisdcc->link!=NULL)
		free(thisdcc->link);
	}
	nextdcc=nextdcc->next;
	free(thisdcc);
    }
#ifdef TRAFFICLOG
    if(user(usern)->trafficlog!=NULL) fclose(user(usern)->trafficlog);
#endif
    memset(user(usern),0x0,sizeof(struct usert));
    if (thisuser!=NULL)
    {
	/* collecting the garbage */
	if (thisuser->user != NULL)
	{
	    free(thisuser->user);
	    thisuser->user=NULL;
	}
    }
}

int clearpeer(int peern)
{
    memset(newpeer(peern),0x0,sizeof(struct newpeert));
    _lastpeer=-1;
    if (thispeer!=NULL)
    {
	if (thispeer->peer != NULL)
	{
	    free(thispeer->peer);
	    thispeer->peer=NULL;
	}
    }
}

int clearlink(int peern)
{
    memset(datalink(peern),0x0,sizeof(struct datalinkt));
    _lastlink=-1;
    if (thislink!=NULL)
    {
	if (thislink->link != NULL)
	{
	    free(thislink->link);
	    thislink->link=NULL;
	}
    }
}

/* logging last context */

int p_debug()
{
    log(LOG_ERROR,-1,"Program Context : %s Line %d",ctxt,cline);
    return 0x0;
}

/* error handling */

void bus_error(int r)
{
    p_debug();
    log(LOG_ERROR,-1,"BUS Error - Crashing");
    exit(0x0);
}

void segv_error(int r)
{
    p_debug();
    log(LOG_ERROR,-1,"SEGMENT VIOLATION - Crashing");
    exit(0x0);
}

void fpe_error(int r)
{
    p_debug();
    log(LOG_ERROR,-1,"Floating Point Error - Crashing");
    exit(0x0);
}

void term_error(int r)
{
    struct usernodes *th;
    p_debug();
#ifdef SIGDIE
    log(LOG_ERROR,-1,"Terminated - quitting all Users");
    th=usernode;
    while(th!=NULL)
    {
	if(th->user!=NULL)
	{
	    if(th->user->outstate==STD_CONN)
	    {
		writesock_URGENT(th->user->outsock,"QUIT :terminated\n");
		usleep(10);
		killsocket(th->user->outsock);
	    }
	    if(th->user->instate==STD_CONN)
	    {
		writesock_URGENT(th->user->insock,"ERROR :terminated\n");
		usleep(10);
		killsocket(th->user->outsock);
	    }
	}
	th=th->next;
    }
    exit(0x0);
#else
    return;
#endif
}

void hup_error(int r)
{
    p_debug();
    log(LOG_WARNING,-1,"Received HangUp - rehashing");
    if(*user(1)->login!=0)
	cmdrehash(1);
    return;
}

void quit_error(int r)
{
    p_debug();
    log(LOG_WARNING,-1,"Received QUIT Signal - ignoring");
    return;
}

void usr1_error(int r)
{
    int i;
    struct socketnodes *lkm,*pre;
    char buf[800];
    char *types[3];
    char *flags[5];
    char *enc[5];
    int noadv;
    types[0]="CONNECT";
    types[1]="LISTEN ";
    flags[0]="NOUSE  ";
    flags[1]="SYN    ";
    flags[2]="CONN   ";
    flags[3]="ERROR  ";
    enc[0]  ="NOENC  ";
    enc[1]  ="ENC    ";
    enc[2]  ="REFUSED";
    if(r!=31337)
	log(LOG_INFO,-1,"Received USER1 - Debugging sockets");
    lkm=socketnode;
    pre=lkm;
    while(lkm!=NULL)
    {
	noadv=0;
	
	if(lkm->sock!=NULL)
	{
	    log(LOG_INFO,-1,"SOCK %-3d : TYPE - %s, SRC - %-41s:%-5d, DST- %-41s:%-5d, STATE - %s, BYTESIN - %-16d, BYTESOUT - %-16d, ENC - %s, SINCE - %s",
	             lkm->sock->syssock,types[lkm->sock->type],lkm->sock->source,lkm->sock->sport,lkm->sock->dest,lkm->sock->dport,
		     flags[lkm->sock->flag],lkm->sock->bytesin,lkm->sock->bytesout,enc[lkm->sock->encryption],lkm->sock->since);
	    if(lkm->sock->type==SOC_CONN || lkm->sock->flag==SOC_SYN)
	    {
		if(fcntl(lkm->sock->syssock, F_GETFD,0) <0) {
		    log(LOG_WARNING,-1,"SOCK: %-3d expired, closing",lkm->sock->syssock);
		    killsocket(lkm->sock->syssock);
		    lkm=pre->next;
		    noadv=1;
		}
	    }
	}
	if (noadv==0) {pre=lkm; lkm=lkm->next;}
    }
    if(r!=31337)
	log(LOG_INFO,-1,"Done");
    return;
}

void usr2_error(int r)
{
    p_debug();
    log(LOG_INFO,-1,"Received USR2 - Reloading Configuration.");
    resetconfig();
    readconfig();
    loadusers();
    return;
}

void int_error(int r)
{
    p_debug();
    log(LOG_ERROR,-1,"Interrupted - stopping application.");
    exit(0x0);
}

void ill_error(int r)
{
    p_debug();
    log(LOG_WARNING,-1,"Got Ill signal - continuing.");
    return;
}

void kill_error(int r)
{
    p_debug();
    log(LOG_ERROR,-1,"Killed - stopping application.");
    exit(0x0);
}

void alrm_error(int r)
{
    return;
}

/* setting the handlers */

int errorhandling()
{
  struct sigaction sv;
  sigemptyset(&sv.sa_mask);
  sv.sa_flags=0;  
  sv.sa_handler = killed;
  sv.sa_handler=bus_error;
  sigaction( SIGBUS, &sv, NULL);
  sv.sa_handler=segv_error;
  sigaction( SIGSEGV, &sv, NULL);
  sv.sa_handler=fpe_error;
  sigaction( SIGFPE, &sv, NULL);
  sv.sa_handler=term_error;
  sigaction( SIGTERM, &sv, NULL);
  sv.sa_handler=hup_error;
  sigaction( SIGHUP, &sv, NULL);
  sv.sa_handler=quit_error;
  sigaction( SIGQUIT, &sv, NULL);
  sv.sa_handler=SIG_IGN; /* broken pipes ignoring */
  sigaction( SIGPIPE, &sv, NULL);
  sv.sa_handler=usr1_error;
  sigaction( SIGUSR1, &sv, NULL);
  sv.sa_handler=usr2_error;
  sigaction( SIGUSR2, &sv, NULL);
  sv.sa_handler=int_error;
  sigaction( SIGINT, &sv, NULL);
  sv.sa_handler=ill_error;
  sigaction( SIGILL, &sv, NULL);
  sv.sa_handler=kill_error;
  sigaction( SIGKILL, &sv, NULL);
  sv.sa_handler=alrm_error;
  sigaction( SIGALRM, &sv, NULL);
  umask( ~S_IRUSR & ~S_IWUSR );
  srand( time( NULL) );
  return 0x0;
}
