#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
#include <netdb.h>
#include <errno.h>
#include <time.h>
#include <string.h>
#include <pthread.h>

#include "language.h"
#include "globals.h"
#include "functions.h"

int sockfd,bancount,allowcount,badwordcount,port;
struct User **users;
struct User nulluser;
unsigned char channels[MAXCHAN+2][20];
int channelop[MAXCHAN+2];
int cflags[MAXCHAN+2];
int international,debug,censor;
unsigned int uptime;
unsigned char sysoppw[15];
unsigned char serverflags;
unsigned char bans[BANS_BUFFER_SIZE];
unsigned char allows[ALLOWS_BUFFER_SIZE];
unsigned char badword[BAD_WORD_BUFFER_SIZE];
unsigned char welcome[2048];
unsigned char logfile[256];
unsigned char passwd[256];
char configdir[256];
int welcomelen;
int logging;
int maxconn;
int minconn;
int gag_size;
int max_idle_time=0;
FILE *logfp;

int init(int socketid, struct sockaddr_in *cli_addr, int clilen)
{
int k,r,t,ID;
unsigned char tempString[200];
struct hostent *my_hostent=NULL;
unsigned char *address=0;
unsigned char *fullmessage={ SORRY_FULL };
unsigned char *toomanyconnections={ TOO_MANY_CONNECTIONS };
unsigned char *allowptr;
unsigned char *banptr;

  if ((serverflags&2)==2)
  { my_hostent=gethostbyaddr((char *)&(cli_addr->sin_addr.s_addr),4,AF_INET); }

  for (r=0; r<maxconn; r++)
  { 
    if (users[r]->inuse==0) 
    { break; } 
  }

  if (r>=maxconn)
  {
#ifdef DEBUG
    if (debug==1) printf("Chat server full\n");
#endif
    write(socketid,fullmessage,strlen(fullmessage)); 
    close(socketid);
    return -1;
  }

  if (users[r]->idletime==-1)
  { 
#ifdef DEBUG
    if (debug==1) printf("Allocing new user %d\n",r);
#endif
    users[r]=malloc(sizeof(struct User)); 
    users[r]->gags=malloc(gag_size); 
  }

#ifdef DEBUG
  if (debug==1)
  {
    printf("searched for open User: found %d\n",r);
    fflush(stdout);
  }
#endif

  if (my_hostent!=NULL && (serverflags&2)==2)
  { safestrncpy(users[r]->location,my_hostent->h_name,61); }
    else
  { 
    address=(char*)&cli_addr->sin_addr.s_addr;
    sprintf(users[r]->location,"%d.%d.%d.%d",address[0],address[1],address[2],address[3]);
  }

#ifdef DEBUG
  if (debug==1)
  {
    printf("Connected from location: %s\n",users[r]->location);
    fflush(stdout);
  }
#endif

  if ((serverflags&8)==8)
  {
    for (t=0; t<maxconn; t++)
    { 
      if (users[t]->inuse!=0) 
      {
        if (t==r) continue;
        if (strcmp(users[t]->location,users[r]->location)==0)
        {
#ifdef DEBUG
          if (debug==1) printf("Too many connections from this IP\n");
#endif
          write(socketid,toomanyconnections,strlen(toomanyconnections)); 
          close(socketid);
          return -1;
        }
      } 
    }
  }

  banptr=bans;
  for (t=0; t<bancount; t++)
  { 
    if (strcasestr_m(users[r]->location,banptr)!=NULL)
    {
      write(socketid,BANNED,strlen(BANNED));
#ifdef DEBUG
      if (debug==1)
      { 
        printf("This site was banned(%d):\n",close(socketid)); 
        fflush(stdout); 
      }
#endif

      close(socketid);
      return -1;
    }

    banptr=banptr+strlen(banptr)+1;
  }

  if (allowcount!=0 && t==bancount)
  {
    allowptr=allows;
    for (t=0; t<allowcount; t++)
    {
      if (strcasestr_m(users[r]->location,allowptr)!=NULL) break;
      allowptr=allowptr+strlen(allowptr)+1;
    }

    if (t==allowcount) 
    {
      write(socketid,REFUSED,strlen(REFUSED));
#ifdef DEBUG
      if (debug==1)
      { 
        printf("This site was banned(%d):\n",close(socketid)); 
        fflush(stdout); 
      }
#endif

      close(socketid);
      return -1;
    }
  }

  k=0;

  if ((serverflags&8)==8)
  {
    for (k=0; k<maxconn; k++)
    {
      if (users[k]->inuse!=1) continue;
      if (strcmp(users[r]->location,users[k]->location)==0)
      { k=-1; break; }
    }
  }

  users[r]->ID=r;
  users[r]->socketid=socketid;
  ID=r;

  users[ID]->spam=0;
  users[ID]->channel=0;
  users[ID]->user_level=0;
  users[ID]->uflags=16;
  users[ID]->channel=0;
  users[ID]->stamptime=0;
  users[ID]->lastp=-1;
  users[ID]->ptr=0;
  users[ID]->lptr=0;
  users[ID]->lr=0;
  users[ID]->emote_char=EMOTE_CHAR;
  users[ID]->logontime=time(NULL);
  users[ID]->idletime=time(NULL);
  safestrncpy(users[ID]->username,users[ID]->location,MAX_USER_NAME_LENGTH);
  users[ID]->socketfd=fdopen(socketid,"rb+");

  if (users[ID]->socketfd==0) printf("Problem opening file desc.\n\n");

  fcntl(users[ID]->socketid, F_SETFL, O_NONBLOCK);

/* hmmm 

  if (setsockopt(sockfd,SOL_SOCKET,SO_RCVLOWAT,&sopt,sizeof(sopt)))
  { printf("socket options SO_RCVLOWAT can't be set.\n"); }
  if (setsockopt(sockfd,SOL_SOCKET,SO_SNDLOWAT,&sopt,sizeof(sopt)))
  { printf("socket options SO_SNDLOWAT can't be set.\n"); }
*/
 
  t=0;
  while((users[ID]->username[t]!=0 && users[ID]->username[t]!='.') && t<16) t++;
  users[ID]->username[t]=0; 

  if ((serverflags&128)==128)
  {
    if (checkname_dup(users[ID]->username)==1)
    { strcpy(users[ID]->username,"unnamed"); }
  }

  if (passwd[0]!=0)
  {
    if (checkname_reg(users[ID]->username,-1)==1)
    { strcpy(users[ID]->username,"unnamed"); }
  }

  for (r=0; r<gag_size; r++)
  { users[ID]->gags[r]=0; }

  write(users[ID]->socketid,welcome,welcomelen);

  if ((serverflags&64)==64)
  {
/*
    sprintf(tempString,NEED_TO_LOGIN,ID,users[ID]->location);
    message(ID,tempString,0);
*/

    users[ID]->inuse=100;
  }
    else
  {
    users[ID]->inuse=1;

    sprintf(tempString,YOU_LOGIN,ID,users[ID]->location);
    message(ID,tempString,0);

    if ((serverflags&4)!=0)
    { sprintf(tempString,SOMEONE_LOGIN1,ID,users[ID]->location); }
      else
    { sprintf(tempString,SOMEONE_LOGIN2,ID); }

    sendallbut(ID,tempString);
    update_list(ID,1);
  }

/*
  sprintf(tempString,"+[%d]%s\n\r",ID,users[ID]->username);
  for (r=0; r<maxconn; r++)
  {
    if (users[r]->inuse==1 && (users[r]->uflags&256)!=0)
    { message(r,tempString,0); }
  }
*/

  return 0;
}

void kill_user(int ID)
{
int r,curr;
unsigned char spa[80];

#ifdef DEBUG
  if (debug==1)
  {
    printf("Getting ready to close socket.\n");
    if (users[ID]->inuse!=4)
      printf("Socket Close Return value: %d:\n",close(users[ID]->socketid));
  }
    else
#endif
  { if (users[ID]->inuse!=4) close(users[ID]->socketid); }

  curr=users[ID]->channel;
  if (curr!=0)
  {
    if (channelop[curr]==ID) channelop[curr]=-1;
    for (r=0; r<maxconn; r++)
      if (users[r]->inuse==1)
        if (users[r]->channel==curr && r!=ID) r=maxconn+6;

    if (r<maxconn+2)
    { channels[curr][0]=0; channelop[curr]=-1; cflags[curr]=0; }
  }

  for (r=0; r<maxconn; r++) if (users[r]->inuse==1) users[r]->gags[ID/8]&=(unsigned char)((1<<(ID%8))^255);
  for (r=0; r<maxconn; r++) if (users[r]->inuse==1 && users[r]->lastp==ID) users[r]->lastp=-1;

  for (r=0; r<maxconn; r++) 
  { if (channelop[r]==ID) channelop[r]=-1; }

#ifdef DEBUG
  if (debug==1) printf("Connection dead: %d\n",ID);
#endif

  if (users[ID]->inuse!=100)
  {
    users[ID]->inuse=0;
    sprintf(spa,JUST_LEFT,ID,users[ID]->username);

    if ((serverflags&1)==0)
    { sendallbut(ID,spa); }
      else
    { sendchanbut(ID,users[ID]->channel,spa); }

    update_list(ID,0);
  }
    else
  { users[ID]->inuse=0; }

  users[ID]->idletime=time(NULL);
}

int buffered_read(int ID)
{
/* char telnet_echo[]={ 255, 251, 34, 255, 250, 34, 1, 1, 240 }; */
/* char telnet_echo[]={ 255, 252, 1 }; */
int r,t=0; 

  while(1)
  {
    errno=0;
    if (users[ID]->lr>=users[ID]->lptr)
    {
      users[ID]->lptr=recv(users[ID]->socketid,users[ID]->myString,512-users[ID]->ptr,0);
      users[ID]->lr=0;
      if (users[ID]->lptr>=0) users[ID]->myString[users[ID]->lptr]=0;

      if (users[ID]->lptr==0) return -1;
      if (users[ID]->lptr<0)
      {
/*
        if (users[ID]->inuse==1)
         if (errno==EINTR || errno==EAGAIN) 
         { errno=0; return 0; }
 */ 
#ifdef DEBUG
        if (debug==1)
        { printf("Lost Connection (NULL): %d\n",ID); fflush(stdout); }
#endif

        users[ID]->ptr=0; 
        users[ID]->lptr=0;
        return -1;
      }
    }
  
    for (r=users[ID]->lr; r<users[ID]->lptr; r++)
    { 
      if ((users[ID]->myString[r]=='\n' || users[ID]->myString[r]=='\r') || users[ID]->myString[r]==0)
      { 
/*
        if (t!=0 || users[ID]->myString[r]==0)
        { t=-1; break; }
*/
        t=-1;
        break;
      }

      if (users[ID]->myString[r]==127 || users[ID]->myString[r]==8) 
      { if (users[ID]->ptr>0) users[ID]->ptr--; }
        else
      if (international==0)
      { if (users[ID]->myString[r]>=32 && users[ID]->myString[r]<127) users[ID]->outString[users[ID]->ptr++]=users[ID]->myString[r]; }
        else
      { if (users[ID]->myString[r]>=32 && users[ID]->myString[r]<255) users[ID]->outString[users[ID]->ptr++]=users[ID]->myString[r]; }

      t=1;
    }

    users[ID]->lr=r+1;

    if ((t==-1 && users[ID]->ptr>0) || users[ID]->ptr>512) 
    { 
      users[ID]->outString[users[ID]->ptr]=0;
      users[ID]->ptr=0;
      if (users[ID]->myString[users[ID]->lr]=='\n' || 
          users[ID]->myString[users[ID]->lr]=='\r')
      { users[ID]->lr++; }

      return 1; 
    }
      else
    { return 0; }
  }
}

void chat(int *params)
{
int t=0,r,trim,x,y;
unsigned int tid;
unsigned char tempString[1500];
unsigned char ch,ch1;
unsigned char *bad;
unsigned char *badwordptr;
unsigned char *trimString;
time_t myTime;
int ID=0;
int msock=0;
fd_set readset;
struct timeval tv;
char *outString;
unsigned char *s;
int gc_time,thread_num;
int dirty_buffer;

  thread_num=params[0];
  gc_time=time(NULL);

  while(1)
  {
    if (time(NULL)-gc_time>GC_TIME)
    { 
      for (r=minconn+thread_num; r<MAXCONN; r=r+MAX_USER_THREADS)
      {
        if (users[r]->inuse==0 && users[r]->idletime!=-1)
        {
#ifdef DEBUG
          if (debug==1) printf("Deallocating line %d\n",r);
#endif
          if (time(NULL)-users[r]->idletime>GC_TIME)
          free(users[r]->gags);
          free(users[r]);
          users[r]=&nulluser;
        }
      }

      if (max_idle_time>0)
      {
        for (r=thread_num; r<MAXCONN; r=r+MAX_USER_THREADS)
        {
          if ((users[r]->inuse!=0 && users[r]->user_level<5) && time(NULL)-users[r]->idletime>max_idle_time)
          {
	    message(r,KICKED_FOR_IDLING,0);
	    users[r]->inuse=2;
            kill_user(r);
          }
        }
      }

      gc_time=time(NULL);
    }

/* printf("HERE AGAIN\n"); */

    FD_ZERO(&readset);
/*
    FD_SET(sockfd,&readset);
    msock=sockfd;
*/
    msock=0;
    dirty_buffer=0;

    for (r=thread_num; r<maxconn; r=r+MAX_USER_THREADS)
    {
      if (users[r]->inuse==1 || users[r]->inuse==100)
      {
        FD_SET(users[r]->socketid,&readset);
        if (msock<users[r]->socketid) msock=users[r]->socketid;
        if (users[r]->lr<users[r]->lptr) dirty_buffer=1;
      }
    }

    if (dirty_buffer==0)
    {    
      tv.tv_sec=1;
      tv.tv_usec=0;
    }
      else
    {
      tv.tv_sec=0;
      tv.tv_usec=1;
    }

    /* if (((t=select(msock+1,&readset,NULL,NULL,&tv))==-1) && (errno!=EINTR)) */

    if ((t=select(msock+1,&readset,NULL,NULL,&tv))==-1)
    {
      if (errno!=EINTR)
      { 
#ifdef DEBUG
        if (debug==1) printf("not EINTR\n");
#endif
      }
         else
      { 
#ifdef DEBUG
        if (debug==1) printf("Problem with select\n"); 
#endif
      }
    }

/*
printf("Heartbeat %d  --- %d\n",thread_num,t);
*/

  for (ID=thread_num; ID<maxconn; ID=ID+MAX_USER_THREADS)
  { 
    errno=0;
    if (users[ID]==0) continue;
    if (users[ID]->inuse!=1 && users[ID]->inuse!=100) continue;

/*
printf("Checking line %d     %d %d %d\n",ID,users[ID]->lr,users[ID]->lptr,users[ID]->ptr);
*/
    if (users[ID]->inuse==100 && time(NULL)-users[ID]->logontime>60)
    { kill_user(ID); }

    if (users[ID]->lr<users[ID]->lptr)
    {
      /* printf("dirty\n"); */
    }
      else
    if (FD_ISSET(users[ID]->socketid,&readset)) 
    {  } 
      else 
    continue;

    r=buffered_read(ID);

    if (r<0) kill_user(ID);
    if (r<1) continue;

/*
printf("FILE: %d\n",ID);
fflush(stdout);

printf("READ: %s\n\n",users[ID]->outString);
fflush(stdout);
*/

    outString=users[ID]->outString;

    trim=0;

    while(outString[trim]==' ') trim++;

    if (censor>0 && outString[trim+1]!='P')
    {
      badwordptr=badword;
      for (r=0; r<badwordcount; r++)
      {
        if ((bad=strcasestr_m(&outString[trim],badwordptr))!=0)
	{
	  if (censor==1)
	  {
            while (bad[0]!=0 && bad[0]!=' ') { bad[0]='*'; bad++; }
          }
	    else
	  if (censor==2)
	  {
	    message(ID,PLEASE_CENSOR,0);
	    outString[trim]=0;
	    r=999999;
	  }
	    else
	  if (censor==3)
	  {
	    users[ID]->warnings++;
	    if (users[ID]->warnings<4)
	    { message(ID,PLEASE_CENSOR,0); }
	      else
	    {
	      message(ID,WARNED,0);
	      users[ID]->inuse=2;
              kill_user(ID);
	    }
	    
	    outString[trim]=0;
	    r=999999;
	  }
	    else
	  if (censor==4)
	  {
	    message(ID,PLEASE_CENSOR,0);
	    users[ID]->inuse=2;
            kill_user(ID);
	    r=999999;
	  }

	  r=r-1;
          continue;
	}

        badwordptr=badwordptr+strlen(badwordptr)+1;
      }
    }

    if (r==999999) continue;

    if (time(NULL)-users[ID]->idletime<2)
    { if ((outString[trim]!='\n' && outString[trim]!='\r') && outString[trim]!=0) users[ID]->spam++; }
      else
    { users[ID]->spam=0; }

    if (users[ID]->spam==8)
    {
      message(ID,SPAM,0);
      kill_user(ID);
      continue;
    }

    myTime=time(NULL);
    users[ID]->idletime=myTime;

#ifdef DEBUG
    if (debug==1)
    {
      printf("Read in: %d bytes on thread %d.\n",strlen(outString),thread_num);
      printf("%s typed: %s\n",users[ID]->username,outString);
      fflush(stdout); 
    }
#endif

    ch=outString[0];

    if (users[ID]->inuse==100)
    {
      if ((ch!='.' && ch!='/') || outString[1]!='n')
      {
        message(ID,NAME_YOURSELF,0);
        continue;
      }
    }

    if (ch=='.' || ch=='/')
    {
      ch=outString[1];
      ch1=outString[2];

      trim=2;
      while(outString[trim]==' ' && outString[trim]!=0) trim++;

      trimString=outString+trim+1;
      r=strlen(trimString)-1;
      while((trimString[r]==' ' || trimString[r]=='\r') || trimString[r]=='\n') trimString[r--]=0;

      if (ch=='n')
      {
        if (outString[trim]!=0)
	{ 
          if (passwd[0]!=0)
          {
            r=checkname_reg(&outString[trim],ID);
            if (r==1)
            {
	      message(ID,NAME_IS_REGISTERED,0);
              continue;
            }
              else
            if (r==2 && (serverflags&32)==32)
            {
	      message(ID,MUST_REGISTER,0);
              continue;
            }
          }

          if ((serverflags&128)==128)
          {
            if (checkname_dup(&outString[trim])==1)
            {
	      message(ID,NAME_IN_USE,0);
              continue;
            }
          }

          update_list(ID,0);

	  safestrncpy(users[ID]->username,outString+trim,MAX_USER_NAME_LENGTH);
	  sprintf(outString,YOUR_NAME,users[ID]->username);
	  message(ID,outString,0);

          update_list(ID,1);
          if (users[ID]->inuse==100)
          {
            users[ID]->inuse=1;

            if ((serverflags&4)!=0)
            { sprintf(tempString,NAME_LOGIN1,ID,users[ID]->username,users[ID]->location); }
              else
            { sprintf(tempString,NAME_LOGIN2,ID,users[ID]->username); }

            if ((serverflags&1)==0)
            { sendall(ID,tempString); }
              else
            { sendchan(ID,users[ID]->channel,tempString); }
          }
	}
	  else
	{ message(ID,ILLEGAL_NAME,0); }
      }
	else
      if (ch=='Z')
      {
	clientshow(ID);
        users[ID]->uflags^=256;
      }
	else
      if (ch=='w')
      {
	who(ID,outString+trim);
      }
	else
      if (ch=='q')
      {
  	message(ID, BYE_BYE,0);
        kill_user(ID);
      }
	else
      if (ch=='f') { who_short(ID);  }
	else
      if (ch=='e')
      {
        if (outString[trim]!=0)
        {
          users[ID]->emote_char=outString[trim];
          sprintf(tempString,NEW_EMOTE_CHAR,users[ID]->emote_char);
          message(ID,tempString,0);
        }
          else
        {
	  if ((users[ID]->uflags&16)==16)
	  { message(ID,ECHOS_OFF,0); }
	    else
	  { message(ID,ECHOS_ON,0); }
	  users[ID]->uflags=users[ID]->uflags^16;
        }
      }
	else
      if (ch=='y' && ((serverflags&1)==0 || users[ID]->user_level>=8))
      {
	if ((users[ID]->uflags&8)!=0)
	{ message(ID,CANT_YELL,0); }
	  else
	if ((users[ID]->uflags&4)!=0)
	{ message(ID,CANT_YELL,0); }
	  else
	{
	  if (outString[trim]==users[ID]->emote_char)
	  {
	    trim++;
	    while(outString[trim]==' ' && outString[trim]!=0) trim++;
	    sprintf(tempString,"#%d#%s %s\n\r",ID,users[ID]->username,outString+trim);
	    yell(ID,tempString);
	  }
	    else
	 {
	   sprintf(tempString,"#%d#%s: %s\n\r",ID,users[ID]->username,outString+trim);
	   yell(ID,tempString);
	 }
	}
      }
	else
      if (ch=='b')
      {
	if ((users[ID]->uflags&1)==1)
	{ message(ID,BEEPS_OFF,0); }
	  else
	{ message(ID,BEEPS_ON,0); }
	users[ID]->uflags=users[ID]->uflags^1;
      }
	else
      if (ch=='h' && ch1=='i')
      {
	if ((users[ID]->uflags&2)==2)
	{ message(ID,HILITE_OFF,0); }
	  else
	{ message(ID,HILITE_ON,0); }
	users[ID]->uflags=users[ID]->uflags^2;
      }
	else
      if (ch=='h' && ch1=='e') { help(ID); }
	else
      if (ch=='m')
      {
	users[ID]->uflags=users[ID]->uflags^64;
	if ((users[ID]->uflags&64)==64)
	{ message(ID,SYSMES_OFF,0); }
	  else
	{ message(ID,SYSMES_ON,0); }
      }
	else
      if (ch=='d')
      {
	users[ID]->uflags=users[ID]->uflags^32;
	if ((users[ID]->uflags&32)==32)
	{ message(ID,TIMESTAMP_ON,0); }
	  else
	{ message(ID,TIMESTAMP_OFF,0); }
      }
	else
      if (ch=='h' && ch1=='u')
      {
	if ((users[ID]->uflags&8)==8)
	{ message(ID,YELLING_ON,0); }
	  else
	{ message(ID,YELLING_OFF,0); }
	users[ID]->uflags=users[ID]->uflags^8;
      }
	else
      if (ch=='a' && ((serverflags&1)==0 || users[ID]->user_level>=8))
      { channelinfo(ID); }
	else
      if (ch=='l')
      {
	if ((channelop[users[ID]->channel]!=ID || users[ID]->channel==0) && users[ID]->user_level<10)
	{ message(ID,CANNOT_LOCK,0); }
	  else
        if (users[ID]->channel==0)
	{ message(ID,CANT_LOCK_MAIN,0); }
	  else
	{
	  cflags[users[ID]->channel]=cflags[users[ID]->channel]^1;
	  if ((cflags[users[ID]->channel]&1)==1)
	  { sendchan(ID,users[ID]->channel,CHANNEL_LOCKED); }
	    else
	  { sendchan(ID,users[ID]->channel,CHANNEL_UNLOCKED); }
	}
      }
        else
      if (ch=='o')
      {  
        r=getNum(outString+trim,ID);

        if (r==-1)
        { }
          else
        if ((channelop[users[ID]->channel]!=ID || users[ID]->channel==0) && users[ID]->user_level<10)
        { message(ID,NOT_OWNER,0); }
          else
        if (users[ID]->channel==0)
        { message(ID,NO_MAIN_OWNER,0); }
	  else
        if (r==ID)
        { message(ID,ALREADY_OWN,0); }
          else
        if (users[r]->channel!=users[ID]->channel)
        { message(ID,NOT_IN_CHAN,0); }
          else
        {
          channelop[users[ID]->channel]=r;
          sprintf(tempString,NEW_OWNER,r,users[r]->username);
          sendchan(-1,users[ID]->channel,tempString);
        }
      }
	else
      if (ch=='s')  
      {
        if (channelop[users[ID]->channel]==ID && users[ID]->channel!=0)
        {
          r=getNum(outString+trim,ID);

          if (r==-1)
          { }
            else  
          {
            if (users[r]->channel==users[ID]->channel)
            {
              if ((users[r]->uflags&4)==4)
              {   
                message(r,YOU_UNSQUELCH,0);
                sprintf(tempString,YOU_UNSQUELCHED,r,users[r]->username);
                message(ID,tempString,0);
              }     
                else
              {
                message(r,YOU_SQUELCH,0);
                sprintf(tempString,YOU_SQUELCHED,r,users[r]->username);
                message(ID,tempString,0);
              }
              users[r]->uflags=users[r]->uflags^4;
            }     
              else
            { message(ID,NOT_IN_CHAN,0); }
          }
        }
          else 
        { message(ID,CANT_SQUELCH,0); }
      }
        else
      if (ch=='c' && ((serverflags&1)==0 || users[ID]->user_level>=8))
      {
        if (strlen(outString+trim)>0)
        {
          outString[14+trim]=0;
	  r=0;
	  while(outString[r]!='\n' && outString[r]!='\r' && outString[r]!=0) r++;
	  outString[r]=0;
	  if (r==trim)
	  {
	    sprintf(tempString,YOU_ARE_IN,channels[users[ID]->channel]);
	    message(ID,tempString,0);
	    continue;
	  }

          t=0;
          r=channel(ID,outString+trim);
          if (r==-2)
          { 
	    sprintf(tempString,YOU_ALREADY_IN,outString+trim);
	    message(ID,tempString,0); 
	  }
            else  
          if (r==-1)
          { message(ID,CHAN_LOCKED,0); }
            else
          if (r==-3)
          { message(ID,CHAN_FULL,0); }
            else
          {
            if (channelop[users[ID]->channel]==ID && users[ID]->channel!=r)
            { channelop[users[ID]->channel]=-1; }

#ifdef HIDDEN_CAVES
            if ((t=ConvNum(outString+trim))>=1000 && t<2000)
            { 
	      sprintf(tempString,WENT_TO_CAVE,ID,users[ID]->username);
	      sendchanbut(ID,users[ID]->channel,tempString); 
 	    }
              else
#endif
            { 
	      sprintf(tempString,WENT_TO_CHAN,ID,users[ID]->username,channels[r]);
	      sendchanbut(ID,users[ID]->channel,tempString); 
	    }

#ifdef USER_LIST_CHAN
            update_list(ID,0);
#endif
            users[ID]->channel=r;
            users[ID]->uflags=users[ID]->uflags&65531;
	    sprintf(tempString,HAS_JOINED,ID,users[ID]->username);
	    sendchan(ID,users[ID]->channel,tempString);
#ifdef USER_LIST_CHAN
            update_list(ID,1);
#endif
            if ((users[ID]->uflags&256)!=0) clientshow(ID);
          }
        }
          else
        {
	  sprintf(tempString,YOU_ARE_IN,channels[users[ID]->channel]);
	  message(ID,tempString,0);
	}
      }
	else
      if (ch=='u')
      {
        timediff(time(NULL),uptime,outString);
        sprintf(tempString,UPTIME,outString);
        message(ID,tempString,0);
      }
        else
      if (ch=='t')
      {
        users[ID]->stamptime=time(NULL);
        sprintf(tempString,THE_TIME,ctime((time_t*)&users[ID]->idletime));
        message(ID,tempString,0);
      }
        else
      if (ch=='v')
      {
        sprintf(tempString,">> %s\n\r",VERSION);
	message(ID,tempString,0);
      }
        else
      if (ch=='g')
      {
        r=getNum(outString+trim,ID);

        if (r==-1)
        { }
          else
        {
          if (r==ID)
          { message(ID,CANT_GAG,0); }
            else
          {
            x=r/8;
            y=r%8;
            users[ID]->gags[x]=(unsigned char)((int)users[ID]->gags[x]^(1<<y));
            if ((users[ID]->gags[x]&(1<<y))!=0)
            { sprintf(tempString,YOU_GAGGED,r,users[r]->username); }
              else
            { sprintf(tempString,YOU_UNGAGGED,r,users[r]->username); }
            message(ID,tempString,0);
          }
	}
      }
	else
      if (ch=='P')
      {
        if (strcmp(outString+trim,sysoppw)==0)
        {
          /* users[ID]->sysop=users[ID]->sysop^1; */
          if (users[ID]->user_level==10)
          { users[ID]->user_level=0; }
            else
          { users[ID]->user_level=10; }

          if (users[ID]->user_level==0) { message(ID,SUPER_OFF,0); }
            else
          { message(ID,SUPER_ON,0); }
        }
          else  
        if (users[ID]->user_level==10)
        {
          r=getNum(outString+trim,ID);

          if (r==-1)
          { }
            else
          {
            /* users[r]->sysop=users[r]->sysop^1; */
            if (users[r]->user_level==10)
            { users[r]->user_level=0; }
              else
            { users[r]->user_level=10; }

            if (users[r]->user_level==10)
            { 
              message(r,GRANT_SUPER,0);
              sprintf(tempString,NOW_SUPER,r,users[r]->username); 
            }
              else
            { 
              message(r,REVOKE_SUPER,0);
              sprintf(tempString,NOT_SUPER,r,users[r]->username); 
            }
            message(ID,tempString,0);
          }
        }
          else
        { message(ID,UNDERSTAND_MESSAGE,0); }
      }
        else
      if (ch=='k')
      {
        if ((channelop[users[ID]->channel]==ID && users[ID]->channel!=0) || (users[ID]->user_level==10 && users[ID]->channel!=0))
        {
          r=getNum(outString+trim,ID);

          if (r==-1)
          { }
            else
          {
            if (users[r]->channel==users[ID]->channel || users[ID]->user_level==10)
            {
              t=channel(r,"0");
              sprintf(tempString,WAS_KICKED,r,users[r]->username);
              sendchanbut(r,users[ID]->channel,tempString);
              sprintf(tempString,KICKED_YOU,ID,users[ID]->username);
              message(r,tempString,0);
             
              users[r]->channel=t;
              users[r]->uflags=users[r]->uflags&65531;
              sprintf(tempString,HAS_JOINED,r,users[r]->username);
              sendchan(r,0,tempString);
            }
              else
            { message(ID,NOT_IN_CHAN,0); }
          }
        }
          else
        { message(ID,CANNOT_KICK,0); }
      }
	else
      if (ch=='i')
      {
        r=getNum(outString+trim,-1);
        if (r==-1)
        { r=ID; }

        if ((r<0 || r>maxconn) || (users[r]->inuse!=1 
             || ((users[r]->uflags&128)==128 && users[ID]->user_level<7)))
	{ message(ID,NOT_HERE,0); }
	  else
	{
          if ((serverflags&4)!=0 || users[ID]->user_level>=7)
          { sprintf(tempString,INFO_LOC,r,users[r]->username,users[r]->location); }
            else
          { sprintf(tempString,">> [%d]%s\n\r",r,users[r]->username); }

	  strcat(tempString,TIME_ONLINE);
          timediff(time(NULL),users[r]->logontime,outString);
	  strcat(tempString,outString);
	  strcat(tempString,"\n\r");
	  strcat(tempString,IDLE_TIME);
          timediff(time(NULL),users[r]->idletime,outString);
	  strcat(tempString,outString);
	  strcat(tempString,"\n\r");
          message(ID,tempString,0);
	}
      }
        else
      if (ch=='p')
      {
	r=getNum(outString+trim,-1);

        if (r==-1) 
        { r=users[ID]->lastp; }
          else
        { trim=trim+strip(outString+trim); }

	if (r>=maxconn)
	{ message(ID,NOT_HERE,0); }
	  else
	if (r!=-1)
	{
	  if (strlen(outString+trim)==0) strcpy(outString," ");
	  if (users[r]->inuse!=1)
	  { message(ID,NOT_HERE,0); }
	    else
	  if ((users[r]->gags[ID/8]&(1<<(ID%8)))!=0)
	  {  message(ID,GAGGED_YOU,0); }
            else
	  if ((serverflags&1)==1 && users[ID]->channel!=users[r]->channel && users[ID]->user_level<8)
	  { message(ID,NOT_HERE,0); }
	    else
	  if (outString[trim]==users[ID]->emote_char)
	  {
	    trim++;
	    while(outString[trim]==' ' && outString[trim]!=0)
	    { trim++; }
	    sprintf(tempString,PRIVATE1,ID,users[ID]->username,outString+trim);
	    sendpriv(r,ID,tempString);
            users[ID]->lastp=r;
	  }
	    else
	  {
	    sprintf(tempString,PRIVATE2,ID,users[ID]->username,outString+trim);
	    sendpriv(r,ID,tempString);
            users[ID]->lastp=r;
	  }
	}
	  else
	{ message(ID,UNDERSTAND_MESSAGE,0); }
      }
        else
      if (users[ID]->user_level==10)
      {
        if (ch=='R')
        { 
          message(ID,RE_READ,0);
          read_config();
        }
	  else
        if (ch=='U')
        {
          r=getNum(outString+trim,ID);
          trim=trim+strip(outString+trim);
          sprintf(tempString,"*%s\n\r",outString+trim);

          if (r==-1)
          { sendchanbut(ID,users[ID]->channel,tempString); }
            else
          { message(r,tempString,0); }

          message(ID,URL_SENT,0);
        }
          else
        if (ch=='G')
        {
          if ((outString+trim)[0]==0)
          {
            r=-1; tid=-1;
            for (t=0; t<maxconn; t++)
            {
              if ((users[t]->inuse==1 && users[t]->logontime<tid) &&
                 (users[t]->user_level<10 && users[t]->channel==0))
              {
                r=t;
                tid=users[t]->logontime;
              }
            }

            if (r==-1)
            { message(ID,NO_ONE_TO_GRAB,0); }
          }
            else
          { r=getNum(outString+trim,ID); }
  
          if (r==-1)
          { }
            else
          {
            tid=r;
            t=0;
            r=channel(tid,channels[users[ID]->channel]);

            if (r==-2)
            { message(ID,ALREADY_IN,0); }
              else  
            if (r==-3)
            { message(ID,NO_FREE_CHAN,0); }
              else  
            { 
              r=users[ID]->channel;
              if (channelop[users[tid]->channel]==tid && users[tid]->channel!=r)
              { channelop[users[tid]->channel]=-1; }

#ifdef HIDDEN_CAVES
              if ((t=ConvNum(outString+trim))>=1000 && t<2000)
              { 
	        sprintf(tempString,WENT_TO_CAVE,tid,users[tid]->username);
                sendchanbut(tid,users[tid]->channel,tempString); 
 	      }
                else
#endif
              { 
	        sprintf(tempString,WENT_TO_CHAN,tid,users[tid]->username,channels[r]);
	        sendchanbut(tid,users[tid]->channel,tempString); 
	      }

#ifdef USER_LIST_CHAN
              update_list(tid,0);
#endif
              users[tid]->channel=r;
              users[tid]->uflags=users[tid]->uflags&65531;
	      sprintf(tempString,HAS_JOINED,tid,users[tid]->username);
	      sendchan(tid,users[tid]->channel,tempString);
              /* clientshow(ID); */
#ifdef USER_LIST_CHAN
              update_list(tid,1);
#endif
	    }
          }
        }
          else
        if (ch=='L')
        {
          safestrncpy(users[ID]->location,outString+trim,32);
          sprintf(tempString,YOUR_LOCATION,users[ID]->location);
	  message(ID,tempString,0);
        }
	  else
        if (ch=='N')
        {
          r=getNum(outString+trim,ID);
          trim=trim+strip(outString+trim);

          if (r==-1)
          { }
            else
          if (outString[trim]!=0)
	  { 
            update_list(r,0);
	    safestrncpy(users[r]->username,outString+trim,MAX_USER_NAME_LENGTH);
	    sprintf(tempString,SYS_CHANGE_NAME,r,users[r]->username);
	    message(ID,tempString,0);
            update_list(r,1);
	  }
	    else
	  { message(ID,ILLEGAL_NAME,0); }
        }
          else
        if (ch=='S')
        {  
          r=getNum(outString+trim,ID);

          if (r==-1)
          { }
            else
          if ((users[r]->uflags&4)==4)
          {   
            message(r,YOU_UNSQUELCH,0);
            sprintf(tempString,YOU_UNSQUELCHED,r,users[r]->username);
            message(ID,tempString,0);
          }     
            else
          {
            message(r,YOU_SQUELCH,0);
            sprintf(tempString,YOU_SQUELCHED,r,users[r]->username);
            message(ID,tempString,0);
          }

          users[r]->uflags=users[r]->uflags^4;
        }
          else
#ifdef LEET
        if (ch=='3')
        {
          if ((serverflags&16)==0)
          { message(ID,">> 3l1t3 writing turned on.\n\r",0); }
            else
          { message(ID,">> 3l1t3 writing turned off.\n\r",0); }

          serverflags^=16;
        }
          else
#endif
        if (ch=='W')
        {
          if (logfile[0]==0)
          { message(ID,CHAT_LOG_OFF,0); }
            else
          if (logging==1)
          {
            message(ID,CHAT_LOG_OFF,0);
            logging=0;
            fclose(logfp);
          }
            else
          {
            message(ID,CHAT_LOG_ON,0);
            logfile_on(ID);
          }
        }
	  else
        if (ch=='X')
        {
          r=getNum(outString+trim,ID);

	  if (r!=-1)
	  {
	    ban_address(users[r]->location);
	    sprintf(tempString,TEMP_BANNED,users[r]->location);
/*
	    safestrncpy(bans[bancount++],users[r]->location,29);
	    sprintf(tempString,TEMP_BANNED,bans[bancount-1]);
*/
	    message(ID,tempString,0);

            kill_user(r);
	  }
	    else
	  { message(ID,UNDERSTAND_MESSAGE,0); }
        }
          else
        if (ch=='B')
        {
          if (is_number(outString+trim)==0)
          {
            r=trim;
            while((outString[r]!=' ' && outString[r]!='\t') &&
               ((outString[r]!='\n' && outString[r]!='\r'))
               && outString[r]!=0) r++;
            outString[r]=0;

            if (r==trim)
            { message(ID,TO_BAN,0); }
              else
            { 
              if (ban_address(&outString[trim])==1)
              { message(ID,TOO_MANY_BANS,0); }
                else
              {
                sprintf(tempString,TEMP_BANNED,&outString[trim]);
                message(ID,tempString,0);
              }
	    }
          }
            else
          {
            r=getNum(outString+trim,-1);
            if ((s=unban_number(r))==0)
            {
              message(ID,NO_SITE_BANNED,0);
            }
              else
            {
              sprintf(tempString,UN_BANNED,s);
              message(ID,tempString,0);
            }
          }
        }
          else
        if (ch=='C')
        {
          r=getNum(outString+trim,-1);

          if (r==-1)
          { message(ID,UNDERSTAND_MESSAGE,0); }
            else
          if (r>4)
          { message(ID,CENSOR_LEVEL,0); }
            else
          {
            censor=r;
            sprintf(tempString,CENSOR_LEVEL_AT,censor);
            message(ID,tempString,0);
          }
        }
          else
        if (ch=='K')
        {
          r=getNum(outString+trim,ID);

          if (r==-1)
          { }
            else
          { kill_user(r); }
        }
          else
        if (ch=='V')
        { viewbans(ID); }
          else
        if (ch=='D')
        {
          if ((serverflags&8)!=0)
          { message(ID,IP_NODUPOFF,0); }
            else
          { message(ID,IP_NODUPON,0); }

          serverflags=serverflags^8; 
        }
          else
        if (ch=='H')
        {
          if ((serverflags&4)!=0)
          { message(ID,IP_ADDRESS_HIDE,0); }
            else
          { message(ID,IP_ADDRESS_SHOW,0); }

          serverflags=serverflags^4; 
        }
          else
        if (ch=='Y')
        {
          if ((users[ID]->uflags&128)==0)
          { message(ID,INVIS_SYSOP_ON,0); }
            else
          { message(ID,INVIS_SYSOP_OFF,0); }

          users[ID]->uflags=users[ID]->uflags^128;
        }
          else
        if (ch=='I')
        {
          if (international==1)
          {
            international=0; 
            message(ID,INTERNATIONAL_OFF,0);
          }
            else
          {
            international=1;
            message(ID,INTERNATIONAL_ON,0);
          }
        }
	  else
        if (ch=='F')
        {
          r=getNum(outString+trim,-1);

          if (r==-1)
          {
            show_levels(ID);
          }
            else
          {
            trim=trim+strip(outString+trim);
            t=getNum(outString+trim,-1);

            if (t==-1)
            {
              sprintf(tempString,USER_LEVEL_IS,r,users[r]->username,users[r]->user_level);
	      message(ID,tempString,0);
            }
              else
            {
              if (t>10) t=10;
              users[r]->user_level=t;

              sprintf(tempString,USER_LEVEL_CHANGE,r,users[r]->username,t);
	      message(ID,tempString,0);
              sprintf(tempString,YOUR_LEVEL_CHANGE,t);
	      message(r,tempString,0);
            }
          }
        }
          else
        { message(ID,UNDERSTAND_MESSAGE,0); }
      }
	else
      { message(ID,UNDERSTAND_MESSAGE,0); }
    }
      else 
    {
      /* if (outString[trim]!='\n') */
      if (outString[trim]!=0)
      {
	if ((users[ID]->uflags&4)!=4)
	{
	  if (outString[trim]==users[ID]->emote_char)
	  {  
	    trim++;
	    while(outString[trim]==' ' && outString[trim]!=0) trim++;

#ifdef ENGLISH
            if (outString[trim]=='\'' && outString[trim+1]=='s')
            {
	      sprintf(tempString,"[%d]%s%s\n\r",ID,users[ID]->username,outString+trim);
            }
              else
#endif
            {
	      sprintf(tempString,"[%d]%s %s\n\r",ID,users[ID]->username,outString+trim);
            }
	  }
	    else
	  {
	    sprintf(tempString,"[%d]%s: %s\n\r",ID,users[ID]->username,outString+trim);
	  }

          sendchan(ID,users[ID]->channel,tempString);
        }
	  else
	{ message(ID,SQUELCHED,0); }
      }
    }
  }
  }
}

int main(int argc,char *argv[])
{
struct sockaddr_in serv_addr;
int r,sopt=1;
struct linger mylinger;
int newsockfd,clilen;
struct sockaddr_in cli_addr;
pthread_t pid;
int threads[MAX_USER_THREADS];

  maxconn=MAXCONN;
  minconn=10;
  max_idle_time=0;
  setup(argc,argv);

  if (debug==0)
  {
    r=fork();

    if (r==-1)
    { perror("Error: Could not fork\n"); exit(3); }
      else
    if (r==0)
    {
      close(STDIN_FILENO);
      close(STDERR_FILENO);

      if (setsid()==-1)
      { exit(4); }
    }
      else
    { return 0; }
  }

#ifdef DEBUG
  if (debug==1)
  {
    printf("minconn: %d\n",minconn);
    printf("maxconn: %d\n",maxconn);
  }
#endif

  if ((sockfd=socket(AF_INET,SOCK_STREAM,0))<0)
  { printf("Can't open socket.\n"); exit(1); }

  bzero((char*)&serv_addr,sizeof(serv_addr));
  serv_addr.sin_family=AF_INET;
  serv_addr.sin_addr.s_addr=htonl(INADDR_ANY);
  serv_addr.sin_port=htons(port);

  if (setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&sopt,sizeof(sopt)))
  { printf("socket options REUSERADDR can't be set.\n"); exit(1); }

  mylinger.l_onoff=0;
  mylinger.l_linger=1;
  if (setsockopt(sockfd,SOL_SOCKET,SO_LINGER,&mylinger,sizeof(struct linger)))
  { printf("socket options LINGER can't be set.\n"); exit(1); }

  if(bind(sockfd,(struct sockaddr *)&serv_addr,sizeof(serv_addr))<0)
  { printf("server can't bind.\n"); exit(1); }

  listen(sockfd,5);
  setsignals();

  /* YOU MAY NOT EDIT THE NEXT LINE OF CODE */

  printf("\nNaken Chat 2.00 (c) 1998-2003 - Michael Kohn\n");
  printf("Started on port: %d\n",port);
  printf(VERSION"\n\n");
  fflush(stdout);

  if (debug==0) close(STDOUT_FILENO);

  nulluser.inuse=0;
  nulluser.idletime=-1;
  users=malloc(sizeof(char *)*maxconn);

  for (r=0; r<MAXCONN; r++)
  {
    users[r]=&nulluser;
  }

  gag_size=MAXCONN/8;
  if ((MAXCONN%8)!=0) gag_size++;

  for (r=0; r<minconn; r++)
  {
    users[r]=malloc(sizeof(struct User));
    users[r]->gags=malloc(gag_size); 
    users[r]->inuse=0;
  }

  for (r=0; r<MAX_USER_THREADS; r++)
  {
    threads[r]=r;
    pthread_create(&pid,NULL,(void*)chat,&threads[r]);
  }

  /* chat(); */

  clilen=sizeof(cli_addr);

  while(1)
  {
    newsockfd=accept(sockfd,(struct sockaddr *)&cli_addr,&clilen);

    if (newsockfd<0)
    {
#ifdef DEBUG
      if (debug==1) { printf("server accept error.\n"); }
#endif
    }
      else
    { 
#ifdef DEBUG
      if (debug==1)
      {
        printf("New socket accepted\n");
        fflush(stdout);
      }
#endif
      init(newsockfd,&cli_addr,clilen);
    }
  }

  return 0;
}



syntax highlighted by Code2HTML, v. 0.9.1