/************************************************************************
 *   psybnc2.2.2, src/p_server.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_server.c, v 2.2.2 2001/03/25 02:02:02 psychoid Exp $";
#endif

#define P_SERVER

#include <p_global.h>

jmp_buf serveralarmret;

int serverinit(int usern,int msgn)
{
    FILE *userb;
    char fname[40];
    char sic[4096];
    char *zer;
    int uusern;
    pcontext;
    uusern=usern;
    if(user(usern)->parent!=0) uusern=user(usern)->parent;
    if (user(usern)->srinit == 0) return 0x0;
    ap_snprintf(fname,sizeof(fname),"motd/USER%d.MOTD",usern);
    if (msgn == 1) 
    {
       zer=strstr(ircbuf," 001 ");
       if(zer!=NULL)
       {
           zer=strchr(zer,'@');
	   if(zer!=NULL)
	   {
	       zer++;
	       *zer=0;
	       if(strlen(ircbuf)+strlen(user(uusern)->host)<sizeof(ircbuf))
	           strcat(ircbuf,user(uusern)->host);
	   }
       }
       user(usern)->welcome=1;
       if (user(usern)->channels!=NULL && user(usern)->leavequit==0) {
          rejoinchannels(usern);
       }
       strmncpy(user(usern)->server,ircfrom,sizeof(user(usern)->server));
#ifdef INTNET
       if(user(usern)->parent==0)
       {
           strmncpy(irccontent,ircto,sizeof(irccontent));
           strmncpy(sic,ircbuf,sizeof(sic));
           cmdintnick(usern,0);
           strmncpy(ircbuf,sic,sizeof(ircbuf));
           parse();
       }
#endif
       strmncpy(user(usern)->nick,ircto,sizeof(user(usern)->nick));
       strmncpy(user(usern)->firstnick,ircto,sizeof(user(usern)->firstnick));
       ssnprintf(user(usern)->outsock,"MODE %s +i\r\n",ircto);
       userb=fopen(fname,"w");
       user(usern)->gotop=0;
    } else {
       userb=fopen(fname,"a");
    }
    fprintf(userb,"%s",ircbuf);
    fclose(userb);
    if(user(usern)->parent!=0) ircbuf[0]=0;
    return 1;
}

int pong(int usern)
{
    char buf[400];
    struct uchannelt *chan;
    pcontext;
    if (user(usern)->outstate != STD_CONN) return 0x0;
    /* we try to regain nick on ping, antiidle, rejoin channels, reop us and save actual channel stats */
    if (strlen(irccontent)>0) { /* some servers need ponged content of ping */
	ap_snprintf(buf,sizeof(buf),"PONG :%s\r\n",irccontent);
    } else {
	ap_snprintf(buf,sizeof(buf),"PONG :%s\r\n",user(usern)->server);
    }
    writesock_URGENT(user(usern)->outsock,buf);
    user(usern)->autopongreply=0;
    if (user(usern)->instate != STD_CONN) {
       if (user(usern)->channels!=NULL && user(usern)->leavequit==0 && user(usern)->autorejoin==1) {
          rejoinchannels(usern);
       }
       if(user(usern)->antiidle==1)
           ssnprintf(user(usern)->outsock,"PRIVMSG %s :away\r\n",user(usern)->nick);
       if(*user(usern)->awaynick!=0)
           ssnprintf(user(usern)->outsock,"NICK %s\r\n",user(usern)->awaynick);
       else	
           ssnprintf(user(usern)->outsock,"NICK %s\r\n",user(usern)->wantnick);
       chan=user(usern)->channels;
       while(chan!=NULL)
       {
	    askforop(usern,chan->name);
	    chan=chan->next;
       }
    }
    user(usern)->kicks = 0;        
}

int nickused(int usern)
{
    char buf[400];
    char nnick[64];
    int cnt;
    pcontext;
    if (user(usern)->instate == STD_CONN && user(usern)->parent==0) {
	writesock(user(usern)->insock,ircbuf);
	return -1; /* if we are the master user and he is connected,
		      his client should care for nick problems. */
    }
    if (user(usern)->welcome == 1) {
	return -1; /* we fuck the jupe, it happened after we tried to change the nick */
    }
    cnt=1;
    strmncpy(nnick,randstring(9),sizeof(nnick));
    while((cnt<5) && (user(usern)->wantnick[cnt]))
    {
        nnick[cnt]=user(usern)->wantnick[cnt];
	cnt++;
    }
    cnt=cnt+4;
    if (cnt<9) nnick[cnt]=0x0;
    ssnprintf(user(usern)->outsock,"NICK %s\r\n",nnick);
    return 0x0;
}

int gotnick(int usern)
{
    char sic[4096];
    pcontext;
    if(strmcmp(ircnick,user(usern)->nick))
    {
	strmncpy(user(usern)->nick,ircto,sizeof(user(usern)->nick));
	ap_snprintf(sic,sizeof(sic),"USER%d",usern);
	writeini("USER","NICK",sic,ircto);
#ifdef INTNET
	if(user(usern)->parent==0)
	{
	    strmncpy(irccontent,ircto,sizeof(irccontent));
	    strmncpy(sic,ircbuf,sizeof(sic));
	    cmdintnick(usern,0);
	    strmncpy(ircbuf,sic,sizeof(ircbuf));
	}
#endif
    }
    nickchange(usern,ircnick,ircto);
    return 0x0;    
}

int rejoinchannels(int usern)
{
    struct uchannelt *chan;
    if(user(usern)->outstate!=STD_CONN) return 0x0; /* not connected */
    chan=user(usern)->channels;
    while(chan!=NULL)
    {
	if(chan->ison==0)
	{
	    if(strchr(chan->modes,'k')!=NULL)
		ssnprintf(user(usern)->outsock,"JOIN %s %s\n",chan->name,chan->key);
	    else
		ssnprintf(user(usern)->outsock,"JOIN %s\n",chan->name);
	}
	chan=chan->next;
    }
    return 0x0;
}

/* leaving all channels */

int partleavemsg(int usern)
{
    struct uchannelt *chan;
    chan=user(usern)->channels;
    while(chan!=NULL)
    {
	if(chan->ison==1)
	{
	    if(strmcmp(user(usern)->leavemsg,"$QUIT")==1)
		ssnprintf(user(usern)->outsock,"PART %s :%s\r\n",chan->name,irccontent);
	    else 
		ssnprintf(user(usern)->outsock,"PART %s :%s\r\n",chan->name,user(usern)->leavemsg);
	}
	chan=chan->next;
    }
    return 0x0;
}

/* sending a leavemsg to all channels we are on */

int sendleavemsg(int usern)
{
    struct uchannelt *chan;
    chan=user(usern)->channels;
    while(chan!=NULL)
    {
	if(chan->ison==1)
	{
	    if(strmcmp(user(usern)->leavemsg,"$QUIT")==1)
		ssnprintf(user(usern)->outsock,"PRIVMSG %s :%cACTION is away (%s)%c\r\n",chan->name,1,irccontent,1);
	    else
		ssnprintf(user(usern)->outsock,"PRIVMSG %s :%cACTION is away (%s)%c\r\n",chan->name,1,user(usern)->leavemsg,1);
	}
	chan=chan->next;
    }
    return 0x0;
}

int rejoinclient(int usern)
{
    struct uchannelt *chan;
    struct uchannelusert *chanuser;
    char netc[15];
    char mynick[64];
    char md[2];
    pcontext;
    md[1]=0;
    user(usern)->afterquit=0;
    chan=user(usern)->channels;
    if (user(usern)->parent != 0)
    {
	ap_snprintf(netc,sizeof(netc),"#%s~",user(usern)->network);
	strmncpy(mynick,user(user(usern)->parent)->nick,sizeof(mynick));
    } else {
	netc[0]=0;
	strmncpy(mynick,user(usern)->nick,sizeof(mynick));
    }
    while(chan!=NULL)
    {
	if(chan->ison==1)
	{
	    ssnprintf(user(usern)->insock,":%s!%s@%s JOIN :%s%s\n",mynick,user(usern)->login,user(usern)->host,netc,chan->name);
	    if(chan->topic[0]!=0)
		ssnprintf(user(usern)->insock,":%s 332 %s %s%s :%s\n",user(usern)->server,user(usern)->nick,netc,chan->name,chan->topic);
	    /* for bitchx and other clients - the names :) */
	    sendnames(usern,chan);
	    chanuser=getchannelnick(chan,user(usern)->nick);
	    if(chanuser!=NULL) /* should not happen */
	    {
		if (chanuser->mode[0]=='@') md[0]='o';
		if (chanuser->mode[0]=='+') md[0]='v';
		if (chanuser->mode[0]=='^') md[0]='a';
		if (chanuser->mode[0]=='%') md[0]='h';
		if (strchr("@+%^",chanuser->mode[0])!=NULL && chanuser->mode[0]!=0)
		    ssnprintf(user(usern)->insock,":irc.psychoid.net MODE %s%s +%c %s\r\n",netc,chan->name,md[0],mynick);
	    }
	}
	chan=chan->next;
    }
    user(usern)->instate = STD_CONN;
    return 0x0;
}

int checkbans(int usern)
{
    char bc=')';
    int userp;
    pcontext;
    if (user(usern)->parent!=0) userp=user(usern)->parent; else userp=usern;
    if (user(usern)->kicks >= 10) return 0x0;
    if (checkstrings(user(usern)->bans)) {
        if (*ehost==bc) return 0x0;
	ssnprintf(user(usern)->outsock,"MODE %s +b %s",ircto,ehost);
	ssnprintf(user(usern)->outsock,"KICK %s %s :banned: %s",ircto,ircnick,eparm);
	user(usern)->kicks++;
	if (user(usern)->instate == STD_CONN) {
   	   ssnprintf(user(usern)->insock,":-psyBNC!psyBNC@lam3rz.de NOTICE %s :User %s(%s) matches ban(%s), kickbanned on %s",user(userp)->nick,ircnick,ircfrom,ehost,ircto);
	}
    }
}

int checkautoop(int usern)
{
    char bc=')';
    int userp;
    pcontext;
    if (user(usern)->parent!=0) userp=user(usern)->parent; else userp=usern;
    if (user(usern)->kicks >= 10) return 0x0;
    if (checkstrings(user(usern)->aops)) {
        if (*ehost==bc) return 0x0;
	ssnprintf(user(usern)->outsock,"MODE %s +o %s",ircto,ircnick);
	user(usern)->kicks++;
	if (user(usern)->instate == STD_CONN) {
   	   ssnprintf(user(usern)->insock,":-psyBNC!psyBNC@lam3rz.de NOTICE %s :User %s(%s) matches autoop(%s), opped on %s",user(userp)->nick,ircnick,ircfrom,ehost,ircto);
	}
    }
}

int checkop(int usern)
{
    char *pt;
    char *pt1;
    char bc='-';
    char *pt2;
    int userp;
    pcontext;
    if (user(usern)->parent!=0) userp=user(usern)->parent; else userp=usern;
    pt1 = irccontent +3;
    if (*pt1 == 0) return -1;
    pt = strchr(pt1,' '); /* the pass */
    pt2 = strchr(irccontent,'#'); /* the chan */
    if (pt == NULL) return -1;
    if (pt2 == NULL) return -1;
    *pt=0;pt=pt1;
    strmncpy(ircto,pt2,sizeof(ircto));
    if (checkstrings(user(usern)->ops)) {
        if (*ehost==bc) return 0x0;
	if (strmcmp(pt,eparm)) {
 	    ssnprintf(user(usern)->outsock,"MODE %s +o %s",pt2,ircnick);
	    if (user(usern)->instate == STD_CONN) {
   	       ssnprintf(user(usern)->insock,":-psyBNC!psyBNC@lam3rz.de NOTICE %s :User %s(%s) matches op (%s), opped on %s",user(userp)->nick,ircnick,ircfrom,ehost,pt2);
  	       return 0x0;
	    }
	}
    }
    return -1;
}

/* check a host if it will be asked */

int getops(int usern)
{
    struct datalinkt *dccbot;
    char buf[1024];
    char bc='-',bc1=')';
    int userp;
    pcontext;
    if (user(usern)->parent!=0) userp=user(usern)->parent; else userp=usern;
    if (strstr(irccontent,"@") == NULL) return 0x0;
    if (checkstrings(user(usern)->askops)) {
        if (*ehost==bc || *ehost==bc1) {
	    dccbot=checkdcc(usern,ehost+1);
	    if (dccbot !=NULL)
	    {
		if (dccbot->outstate==STD_CONN)
		{
		    ap_snprintf(buf,sizeof(buf),".op %s %s\r\n",user(usern)->nick,echan);
		    writesock_DELAY(dccbot->outsock,buf,3);
		    user(usern)->gotop=1; /* no multiple asks */
		}
	    }
	    return 0x0;
	}
	user(usern)->gotop=1; /* no multiple asks */
	ap_snprintf(buf,sizeof(buf),"PRIVMSG %s :op %s %s",ircnick,eparm,ircto);
	writesock_DELAY(user(usern)->outsock,buf,3);
	if (user(usern)->instate == STD_CONN) {
   	   ssnprintf(user(usern)->insock,":-psyBNC!psyBNC@lam3rz.de NOTICE %s :Asked %s(%s) for op on %s",user(userp)->nick,ircnick,ircfrom,ircto);
	}
    }
    return 0x0;
}

/* ask for op for a single channel */

int askforop(int usern, char *channel)
{
    struct uchannelt *chan;
    struct uchannelusert *usr;

    chan=getuserchannel(usern,channel);
    if(chan!=NULL)
    {
	if(chan->ison!=0)
	{
	    usr=getchannelnick(chan,user(usern)->nick);
	    if(usr!=NULL)
	    {
		if(strchr(usr->mode,'@')==NULL)
		{
		    user(usern)->gotop=0;
		    usr=chan->users;
		    while(user(usern)->gotop==0 && usr!=NULL)
		    {
			if(strchr(usr->mode,'@')!=NULL && strlen(usr->host)>1)
			{
			    ap_snprintf(ircfrom,sizeof(ircfrom),"%s!%s@%s",usr->nick,usr->ident,usr->host);
			    strmncpy(ircto,channel,sizeof(ircto));
			    strmncpy(ircnick,usr->nick,sizeof(ircnick));
			    strmncpy(irchost,usr->host,sizeof(irchost));
			    strmncpy(ircident,usr->ident,sizeof(ircident));
			    strmncpy(irccontent,usr->mode,sizeof(irccontent));
			    getops(usern);
			}   
			usr=usr->next;
		    }
		}
	    }
	}
    }
    return 0x0;
}

/* send who to the channel on join */

int sendwho(int usern)
{
    struct uchannelt *chan;
    pcontext;
    chan=getuserchannel(usern,ircto);
    if(chan!=NULL) removeallusersfromchannel(chan);
    ssnprintf(user(usern)->outsock,"WHO %s",ircto);
    return 0x0;
}

/* who entry - add to the channel structure */

int performwho(int usern)
{
    char hnick[64];
    char hident[64];
    char hhost[1024];
    char hchan[1024];
    char hbuf[8192];
    char flags[10];
    char *p1;
    char *p2;
    struct uchannelt *chan;
    struct uchannelusert *chanuser;
    pcontext;
    strmncpy(hbuf,ircbuf,sizeof(hbuf));
    p1 = strchr(hbuf,' ');
    if (p1 == NULL) return 0x0;
    p1++;
    p1 = strchr(p1,' ');
    if (p1 == NULL) return 0x0;
    p1++;
    p1 = strchr(p1,' ');
    if (p1 == NULL) return 0x0;
    p1++;
    p2=p1;
    p1 = strchr(p1,' ');
    if (p1 == NULL) return 0x0;
    *p1=0;
    strmncpy(hchan,p2,sizeof(hchan));
    p2=p1;
    p2++;
    p1 = strchr(p2,' ');
    if (p1 == NULL) return 0x0;
    *p1=0;
    strmncpy(hident,p2,sizeof(hident));
    p1++;
    p2 = strchr(p1,' ');
    if (p2 == NULL) return 0x0;
    *p2=0;
    strmncpy(hhost,p1,sizeof(hhost));
    p2++;
    p1 = strchr(p2,' ');
    if (p1 == NULL) return 0x0;
    p1++;
    p2 = strchr(p1,' ');
    if (p2 == NULL) return 0x0;
    *p2 = 0;
    strmncpy(hnick,p1,sizeof(hnick));
    p2++;
    p1 = strchr(p2,' ');
    if (p1 == NULL) return 0x0;
    *p1 = 0;
    strmncpy(flags,p2,sizeof(flags));
    chan=getuserchannel(usern,hchan);
    if(chan!=NULL)
    {
	chanuser=addnicktochannel(chan,hnick,hident,hhost);
	if(chanuser!=NULL)
	{
	    if(strchr(flags,'@'))
		chanuser->mode[0]='@';
	    else if(strchr(flags,'+'))
		chanuser->mode[0]='+';
	    else if(strchr(flags,'^'))
		chanuser->mode[0]='^';
	    else if(strchr(flags,'%'))
		chanuser->mode[0]='%';
	    chanuser->mode[1]=0;
	}
    }
    return 0x0;
}

/* someone joined a channel */

int gotjoin(int usern)
{
    struct uchannelt *chan;
    pcontext;
    if (strmcmp(user(usern)->nick,ircnick)==1)
    {
	    chan=getuserchannel(usern,ircto);
	    if(chan==NULL)
	    {
		chan=addchanneltouser(usern,ircto,0);
	    }
	    if(chan!=NULL)
	    {
		addnicktochannel(chan,ircnick,ircident,irchost);
		chan->ison=1;
	    }
	    sendwho(usern);
	    return 0x0;
    }
    chan=getuserchannel(usern,ircto);
    if(chan!=NULL)
	addnicktochannel(chan,ircnick,ircident,irchost);
    checkbans(usern);
    checkautoop(usern);
    return 0x0;
}

/* someone parted from a channel */

int gotpart(int usern)
{
    struct uchannelt *chan;
    pcontext;
    if (strmcmp(user(usern)->nick,ircnick)==1)
    {
	    if(user(usern)->instate==STD_CONN)
	    {
	        removechannelfromuser(usern,ircto);
	    }
	    else
	    {
		chan=getuserchannel(usern,ircto);
		if(chan==NULL) return 0x0;
		removeallusersfromchannel(chan);
		chan->topic[0]=0;
		chan->ison=0;
	    }
	    return 0x0;
    }
    chan=getuserchannel(usern,ircto);
    if(chan!=NULL)
	removenickfromchannel(chan,ircnick);
    return 0x0;
}

/* someone was kicked */

int gotkick(int usern)
{
    struct uchannelt *chan;
    char *pt,*pt1,*pt2;
    char knick[64],kchan[200];
    pcontext;
    pt=strstr(ircbuf," KICK ");
    if(pt!=NULL)
    {
	pt+=6;
	pt2=strchr(pt,' ');
	if(pt2!=NULL)
	{
	    *pt2=0;
	    strmncpy(kchan,pt,sizeof(kchan));
	    *pt2=' ';
	    pt2++;
	    pt=strchr(pt2,' ');
	    if(pt!=NULL)
	    {
		*pt=0;
		strmncpy(knick,pt2,sizeof(knick));
		*pt=' ';
		chan=getuserchannel(usern,kchan);
pcontext;
		if(chan!=NULL)
		    removenickfromchannel(chan,knick);
		if(strmcmp(knick,user(usern)->nick)==1)
		{
		    if(chan!=NULL)
		    {
			removeallusersfromchannel(chan);
			chan->topic[0]=0;
			chan->ison=0;
			if(user(usern)->instate!=STD_CONN)
			{
			    ap_snprintf(ircbuf,sizeof(ircbuf),"[KICKED] by %s!%s@%s from %s",ircnick,ircident,irchost,chan->name);
			    privatelog(usern);
			}
		    }
		}
	    }
	}
    }
    return 0x0;
}

/* someone quitted */

int gotquit(int usern)
{
    struct uchannelt *chan;
    pcontext;
    
    if(strmcmp(ircnick,user(usern)->nick)==1)
    {
	resetallchannels(usern);
    } else {
	removenickfromallchannels(usern,ircnick);
    }
    return 0x0;
}

/* someone set a mode  */

int gotmode(int usern)
{
    struct uchannelt *chan;
    char *param;
    char nbuf[2];
    pcontext;
    nbuf[0]=0;
    chan=getuserchannel(usern,ircto);
    if(chan!=NULL)
    {
	param=strchr(irccontent,' ');
	if(param==NULL)
	    param=nbuf;
	else
	{
	    *param=0;
	    param++;
	}
	addchannelmode(chan,irccontent,param);
    }
    return 0x0;
}

/* got topic change */

int gottopic(int usern)
{
    struct uchannelt *chan;
    chan=getuserchannel(usern,ircto);
    if(chan!=NULL)
	setchanneltopic(chan,irccontent);	
    return 0x0;
}

/* server sent an error to us */

int msgERROR(int usern)
{
    char fi[40];
    struct socketnodes *lkm;
    pcontext;
    log(LOG_ERROR,-1,"User %s (%s) got disconnected (from %s) Reason: %s",user(usern)->login,user(usern)->network,user(usern)->server,irccontent);
    lkm=getpsocketbysock(user(usern)->outsock);
    if(lkm!=NULL)
	lkm->sock->destructor=NULL;
    killsocket(user(usern)->outsock);
    memset(user(usern)->server,0x0,sizeof(user(usern)->server));
    user(usern)->outstate=STD_NOCON;
    user(usern)->outsock = 0;
    resetallchannels(usern);
    ap_snprintf(fi,sizeof(fi),"motd/USER%d.MOTD",usern);
    oldfile(fi);
    return -1;
}

int msg001(int usern)
{
    pcontext;
    user(usern)->srinit = 1;
    serverinit(usern,1);
    return -1;
}

int msg002to005(int usern)
{
    pcontext;
    if (serverinit(usern,2)) return 0;
    return 1;
}

int msg251to255(int usern)
{
    pcontext;
    if (serverinit(usern,3)) return 0;
    return 1;
}

int msg372to375(int usern)
{
    pcontext;
    if (serverinit(usern,4)) return 0;
    return 1;
}

int msg376(int usern)
{
    pcontext;
    serverinit(usern,4);
    user(usern)->srinit = 0;
    return -1;
}

int msg352(int usern)
{
    pcontext;
    if (user(usern)->triggered == 0) 
    {
	performwho(usern);
	return 0;
    }
    return 1;
}

int msg315(int usern)
{
    char *pt,*ept;
    struct uchannelt *chan;
    pcontext;
    user(usern)->gotop=0;
    if (user(usern)->triggered == 0) {
	pt=strstr(ircbuf," 315 ");
	if(pt!=NULL)
	{
	    pt+=5;
	    pt=strchr(pt,' ');
	    if(pt!=NULL)
	    {
		pt++;
		ept=strchr(pt,' ');
		if(ept!=NULL)
		{
		    *ept=0;
		    chan=getuserchannel(usern,pt);
		    if(chan!=NULL)
			askforop(usern,pt);
		    
		    *ept=' ';
		}
	    }
	}
	return 0;
    }
    user(usern)->triggered--;
    return 1;
}

int msg432to437(int usern)
{
    pcontext;
    if (nickused(usern)==0) return 0;
    return 1;
}

/* channelmode */

int msg324(int usern)
{
    struct uchannelt *channel;
    char *pt,*pt2,*pt3;
    char chan[200],mode[40],param[200];
    char ebuf[2048];
    strmncpy(ebuf,ircbuf,sizeof(ebuf));
    pt=strchr(ebuf,'\r');
    if(pt==NULL) pt=strchr(ebuf,'\n');
    if(pt!=NULL) *pt=0;
    param[0]=0;
    pt=strstr(ebuf," 324 ");
    if(pt!=NULL)
    {
	pt+=5;
	pt=strchr(pt,' ');
	if(pt!=NULL)
	{
	    pt++;
	    pt2=strchr(pt,' ');
	    if(pt2!=NULL)
	    {
		*pt2=0;
		strmncpy(chan,pt,sizeof(chan));
		*pt2=' ';
		pt2++;
		if(*pt2=='+') pt2++;
		pt3=strchr(pt2,' ');
		if(pt3!=NULL)
		{
		    *pt3=0;
		    strmncpy(mode,pt2,sizeof(mode));
		    *pt3=' ';
		    pt3++;
		    strmncpy(param,pt3,sizeof(param));
		} else {
		    strmncpy(mode,pt2,sizeof(mode));
		}
		channel=getuserchannel(usern,chan);
		if(channel!=NULL)
		{
		    setchannelmode(channel,mode,param);
		}
	    }
	}
    }
}

/* a names line */

int msg332(int usern)
{
    struct uchannelt *channel;
    char *pt,*pt2,*pt3;
    char chan[200],topic[300];
    topic[0]=0;
    pt=strstr(ircbuf," 332 ");
    if(pt!=NULL)
    {
	pt+=5;
	pt=strchr(pt,' ');
	if(pt!=NULL)
	{
	    pt++;
	    pt2=strchr(pt,' ');
	    if(pt2!=NULL)
	    {
		*pt2=0;
		strmncpy(chan,pt,sizeof(chan));
		*pt2=' ';
		pt2++;
		if(*pt2=='+') pt2++;
		pt3=strchr(pt2,':');
		if(pt3!=NULL)
		{
		    pt3++;
		    strmncpy(topic,pt3,sizeof(topic));
		    pt=strchr(topic,'\r');
		    if(pt==NULL) pt=strchr(topic,'\n');
		    if(pt!=NULL) *pt=0;
		}
		channel=getuserchannel(usern,chan);
		if(channel!=NULL)
		{
		    setchanneltopic(channel,topic);
		}
	    }
	}
    }
}

/* a single names line, needs to be saved */

int msg353(int usern)
{
    struct uchannelt *chan;
    struct uchannelusert *chanuser;
    char *pt,*pt2;
    char channel[300];
    static char m3[2048];
    char mm[2];
    mm[1]=0;
    mm[0]=0;
    strmncpy(m3,ircbuf,sizeof(m3));
    pt=strchr(m3,'\r');
    if(pt==NULL) pt=strchr(m3,'\n');
    if(pt!=NULL) *pt=0;
    pt=strstr(m3," 353 ");
    if(pt!=NULL)
    {
	pt+=5;
	pt2=strstr(pt," = ");
	if(pt2==NULL) pt2=strstr(pt," @ ");
	if(pt2==NULL) pt2=strstr(pt," * ");
	if(pt2!=NULL)
	{
	    pt2+=3;
	    pt=pt2;
	    pt2=strchr(pt2,' ');
	    if(pt2!=NULL)
	    {
		*pt2=0;
		strmncpy(channel,pt,sizeof(channel));
		if(user(usern)->innames==0)
		{
		    strmncpy(user(usern)->inchan,channel,sizeof(user(usern)->inchan));
		    user(usern)->innames=1;
		}
		*pt2=' ';
		pt=strchr(pt2,':');
		if(pt!=NULL)
		{
		    chan=getuserchannel(usern,channel);
		    if(chan==NULL) return 0x0;
		    pt++;
		    while(pt!=NULL)
		    {
			mm[0]=0;
			pt2=strchr(pt,' ');
			if(pt2!=NULL)
			{
			    *pt2=0;
			}
			while(strchr("@+% ",*pt)!=NULL && *pt!=0) 
			{ 
			      /* handeling ^ isnt possible on names, valid nick character */
			      if(*pt!=' ') mm[0]=*pt;
			      pt++;
			}
			if(strlen(pt)>0 && *pt!=' ')
			{
			    chanuser=addnicktochannel(chan,pt,"","");
			    if(chanuser!=NULL)
				strmncpy(chanuser->mode,mm,sizeof(chanuser->mode));
			}
			if(pt2!=NULL)
			{
			    *pt2=' ';
			    pt2++;
			}
			pt=pt2;
		    }
		}
	    }
	}
    } 
    return 0x0;
}

int msg366(int usern)
{
    struct uchannelt *chan;
    if(user(usern)->innames==2)
    {
	chan=getuserchannel(usern,rtrim(user(usern)->inchan));
	if(chan!=NULL && user(usern)->instate==STD_CONN)
	    sendnames(usern,chan);
    }
    if(user(usern)->innames!=0)
    {
	user(usern)->innames=0;
	askforop(usern,user(usern)->inchan);
	user(usern)->inchan[0]=0;
    }
    return 0x0;
}

/* privmsg */

int msgprivmsg(int usern)
{
    char buf[400];
    char *po;
    pcontext;
    if(parsectcps(usern)==1) return 0x0;
#ifdef CRYPT
    checkcrypt(usern);
#endif
#ifdef TRANSLATE
    checktranslate(usern);
#endif
    strcpy(buf,"op ");
    po = strstr(irccontent,buf);
    if (po == irccontent) {
	if (checkop(usern) == 0x0) return 0;
    }
    if (strmcmp(ircto,user(usern)->nick) && user(usern)->instate!=STD_CONN) {
	if (strstr(ircto,user(usern)->nick)) {
	    privatelog(usern);
	}
    }
    return 1;
}

int userconnected(int nuser)
{
    struct hostent *he;
    int nlink;
    int proto;
    char buf[400];
    pcontext;
    nlink=user(nuser)->vlink;
    if(user(nuser)->pport!=0)
    {
	nlink=0;
	if(user(nuser)->pport==1080) /* socks 4 */
	{
	    proto=getprotocol(user(nuser)->server);
#ifdef IPV6
	    /* for this we need an INET6 supporting SOCKS4 demon.
	     * If anyone got one, tell me :) */
	    if(proto==AF_INET6)
	    {
		if(!setjmp(serveralarmret))
		{
		    alarm(10);
#ifdef SUNOS
		    he=getipnodebyname(user(nuser)->server,AF_INET6,AI_DEFAULT,&error_num);
#else
		    he=gethostbyname2(user(nuser)->server,AF_INET6);
#endif
		    alarm(0);
		} else
		    he=NULL;
		if(!he)
		{
		    killsocket(user(nuser)->outsock);
		    return 0x0;
		}
		if(he->h_length>=16)
		{
		    ap_snprintf(buf,sizeof(buf),"\x04\x01%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c",
			(user(nuser)->port>>8) & 0xff,user(nuser)->port & 0xff,
			(char) he->h_addr[0],
			(char) he->h_addr[1],
			(char) he->h_addr[2],
			(char) he->h_addr[3],
			(char) he->h_addr[4],
			(char) he->h_addr[5],
			(char) he->h_addr[6],
			(char) he->h_addr[7],
			(char) he->h_addr[8],
			(char) he->h_addr[9],
			(char) he->h_addr[10],
			(char) he->h_addr[11],
			(char) he->h_addr[12],
			(char) he->h_addr[13],
			(char) he->h_addr[14],
			(char) he->h_addr[15]);
		    send(user(nuser)->outsock,buf,8+1,0);
		}
		currentsocket->sock->nowfds=1;
	    } else
#endif	    
	    {
		if(!setjmp(serveralarmret)) 
		{
		    alarm(10);
		    he=gethostbyname(user(nuser)->server);
		    alarm(0);
		} else
		    he=NULL;
		if(!he)
		{
		    killsocket(user(nuser)->outsock);
		    return 0x0;
		}
		ap_snprintf(buf,sizeof(buf),"\x04\x01%c%c%c%c%c%c",
		    (user(nuser)->port>>8) & 0xff,user(nuser)->port & 0xff,
		    (char) he->h_addr[0],
		    (char) he->h_addr[1],
		    (char) he->h_addr[2],
		    (char) he->h_addr[3]);
		send(user(nuser)->outsock,buf,8+1,0);
		currentsocket->sock->nowfds=1;
	    }
	    writesock_DELAY(user(nuser)->outsock,"\n",3);
	} else {
	    if(user(nuser)->pport==23) /* wingate */
	    {
		ssnprintf(user(nuser)->outsock,"%s %d\r\n",user(nuser)->server,user(nuser)->port);
		currentsocket->sock->nowfds=1;
		writesock_DELAY(user(nuser)->outsock,"\n",3);
	    } else { /* any other port, we assume a web proxy */
    		ap_snprintf(ircbuf,sizeof(ircbuf),"CONNECT %s:%d HTTP/1.0\n\n",user(nuser)->server,user(nuser)->port);
		send(user(nuser)->outsock,ircbuf,strlen(ircbuf)+1,0);
		currentsocket->sock->nowfds=1;
		writesock_DELAY(user(nuser)->outsock,"\n",3);
	    }
	}
    } 
    if(user(nuser)->pport==0)
    {
	if (nlink!=0)
	{
    	    if (datalink(nlink)->type !=1 && datalink(nlink)->type!=2)
    		nlink=0;
    	    if (datalink(nlink)->outstate!=STD_CONN && datalink(nlink)->instate!=STD_CONN)
    		nlink=0;
	}
    }
    if (nlink!=0)
    {
        ssnprintf(user(nuser)->outsock,"RELAY %s :%d\r\n",datalink(nlink)->name,socketnode->sock->sport);
        ssnprintf(user(nuser)->outsock,"VHOST :%s\r\n",user(nuser)->vhost);
        ssnprintf(user(nuser)->outsock,"CONNECT %s :%d\r\n",user(nuser)->server,user(nuser)->port);
    }
    if(*user(nuser)->spass!=0)
    {
	ssnprintf(user(nuser)->outsock,"PASS %s\r\n",user(nuser)->spass);
    }
    ssnprintf(user(nuser)->outsock,"USER %s %s 127.0.0.1 :%s\r\n",user(nuser)->login,user(nuser)->login,user(nuser)->user);
    if(user(nuser)->instate!=STD_CONN && *user(nuser)->awaynick!=0)
        ssnprintf(user(nuser)->outsock,"NICK %s\r\n",user(nuser)->awaynick);
    else
        ssnprintf(user(nuser)->outsock,"NICK %s\r\n",user(nuser)->wantnick);
    if (nlink!=0)
    {
        ssnprintf(user(nuser)->outsock,"PASS %s\r\n",datalink(nlink)->pass);
    }
    log(LOG_INFO,nuser,"User %s (%s) connected to %s:%d (%s)",user(nuser)->login,user(nuser)->network,user(nuser)->server,user(nuser)->port,user(nuser)->vhost);
    user(nuser)->autopongreply=500;
    return 0x0;
}

int usererror(int nuser,int errn)
{
    struct socketnodes *th;
    char buf[400];
    pcontext;
    if(*user(nuser)->server)
	log(LOG_ERROR,nuser,"User %s: cant connect to %s port %d.",user(nuser)->login,user(nuser)->server,user(nuser)->port);
    th=getpsocketbysock(user(nuser)->outsock);
    if(th!=NULL)
	th->sock->destructor=NULL;
    killsocket(user(nuser)->outsock);
    user(nuser)->outstate=STD_NOCON;
    ap_snprintf(buf,sizeof(buf),"motd/USER%d.MOTD",nuser);
    resetallchannels(nuser);
    oldfile(buf);
    memset(user(nuser)->server,0x0,sizeof(user(nuser)->server));
    return -1;
}

int userclosed(int nuser)
{
    struct socketnodes *th;
    char buf[400];
    pcontext;
    log(LOG_WARNING,nuser,"User %s got disconnected from server.",user(nuser)->login);
    th=getpsocketbysock(user(nuser)->outsock);
    if(th!=NULL)
    {
	th->sock->destructor=NULL;
    }
    killsocket(user(nuser)->outsock);
    user(nuser)->outstate=STD_NOCON;
    ap_snprintf(buf,sizeof(buf),"motd/USER%d.MOTD",nuser);
    resetallchannels(nuser);
    oldfile(buf);
    memset(user(nuser)->server,0x0,sizeof(user(nuser)->server));
    return -1;
}
/* check the clients for server connections */

int checkclients()
{
    struct usernodes *th;
    int usern;
    th=usernode;
    while (th!=NULL) {
	usern=th->uid;
        if (connectuser(usern)==1) return 0x0;
	th=th->next;
    }
    return 0x0;
}


/* connect User #nuser */

int connectuser(int nuser)
{
   char buf[400];
   char *ep;
   struct socketnodes *lkm;
#ifdef OIDENTD
   mode_t oldum;
   FILE *oid;
#endif
   int proto;
   int nlink=0;    
   if (user(nuser)->outstate == STD_NOCON)
   {
	if (user(nuser)->quitted ==1) return 0x0;
	if (user(nuser)->delayed) {
	    user(nuser)->delayed-=delayinc;
	    return 0x0;
	}
	if (getnextserver(nuser) != 0) {
	    user(nuser)->outstate = STD_NOCON;
	    log(LOG_INFO,nuser,"User %s (%s) has no server added",user(nuser)->login,user(nuser)->network);
	    memset(user(nuser)->server,0x0,sizeof(user(nuser)->server));
	    user(nuser)->delayed = 5;
	    return 0x0;
	}
	if (user(nuser)->port == 0) {
	    user(nuser)->outstate = STD_NOCON;
	    log(LOG_INFO,nuser,"User %s (%s) has no server added",user(nuser)->login,user(nuser)->network);
	    memset(user(nuser)->server,0x0,sizeof(user(nuser)->server));
	    user(nuser)->delayed = 5;
	    return 0x0;
	}
	user(nuser)->delayed = 0;
	nlink=user(nuser)->vlink;
	if (nlink!=0)
	{
	    if (datalink(nlink)->type !=1 && datalink(nlink)->type!=2)
	    {
		nlink=0;
	    }
	    if (datalink(nlink)->outstate!=STD_CONN && datalink(nlink)->instate!=STD_CONN)
		nlink=0;
	}
	user(nuser)->triggered=0;
	if (user(nuser)->pport!=0)
	    proto=getprotocol(user(nuser)->proxy);
	else if (nlink!=0)
	    proto=getprotocol(datalink(nlink)->host);
	else
	    proto=getprotocol(user(nuser)->server);
#ifdef OIDENTD
	ep=getenv("HOME");
	if(ep!=NULL)
	{
	    ap_snprintf(buf,sizeof(buf),"%s/.ispoof",ep);
	    oldum=umask(0000);
	    umask(022);
	    oid=fopen(buf,"w");
	    if(oid!=NULL)
	    {
		fprintf(oid,"%s\n",user(nuser)->login);
		fclose(oid);
	    }
	    umask(oldum);
	}
#endif
	user(nuser)->outsock=createsocket(0,ST_CONNECT,nuser,NULL,userconnected,usererror,useroutbound,userclosed,proto);
	lkm=getpsocketbysock(user(nuser)->outsock);
	if (user(nuser)->pport!=0)
	{
	   log(LOG_INFO,nuser,"User %s (%s) trying proxy %s port %d to server %s port %d",user(nuser)->login,user(nuser)->network,user(nuser)->proxy,user(nuser)->pport,user(nuser)->server,user(nuser)->port);
	   user(nuser)->outsock=connectto(user(nuser)->outsock,user(nuser)->proxy,user(nuser)->pport,NULL);
	} else
	if (nlink!=0)
	{
  	   log(LOG_INFO,nuser,"User %s (%s) trying %s port %d on link %s (%s:%d) (%s).",user(nuser)->login,user(nuser)->network,user(nuser)->server,user(nuser)->port,datalink(nlink)->name,datalink(nlink)->host,datalink(nlink)->port,user(nuser)->vhost);
	   user(nuser)->outsock=connectto(user(nuser)->outsock,datalink(nlink)->host,datalink(nlink)->port,NULL);
	} else {
#ifdef CRYPT
	   if(lkm!=NULL)
	       lkm->sock->encryption=SE_REFUSE;
#endif
  	   log(LOG_INFO,nuser,"User %s (%s) trying %s port %d (%s).",user(nuser)->login,user(nuser)->network,user(nuser)->server,user(nuser)->port,user(nuser)->vhost);
	   user(nuser)->outsock=connectto(user(nuser)->outsock,user(nuser)->server,user(nuser)->port,user(nuser)->vhost);
	}
	if (lkm!=NULL)
	    lkm->sock->serversocket=1;
	if (user(nuser)->outsock == 0) 
	{
	    if(*user(nuser)->server)
		log(LOG_ERROR,nuser,"User %s: cant connect to %s port %d.",user(nuser)->login,user(nuser)->server,user(nuser)->port);
	    return 0x0;
	}
        user(nuser)->outstate=STD_CONN;
        user(nuser)->welcome=0;
	return 0x1;
    }
    return 0x0;
}

