
/* 
   TetriNET Server for Linux                                     V1.13.XX
*************************************************************************
    
*/
   
/*
	This source code is with qirc-1.40 addons+patch
	qirc-1.40	date: Apr 23 2001
	Any questions maybe directed to drslum@tetrinet.org or brg@cheerful.com
	Do not complain about how brutal this coding is. :)
*/
   
 
   

/*undef TETDEBUG*/		/* Every time I find I need a printf, I'll encase it as a debug */

#include "config.h"			/* Generally about the only file */
                                        /* that the user can safely change */
#include <signal.h>
#include <unistd.h>
#include <ctype.h>
#include "main.h"
#include "dns.h"
#include "net.h"
#include "utils.h"
#include "crack.h"
#include "game.h"

#include "dns.c"
#include "utils.c"
#include "net.c"
#include "crack.c"
#include "game.c"


/* remnet (channel, n) - Removes n from the channel list */
void remnet(struct channel_t *chan, struct net_t *n)
  {
    struct net_t *nsock,*osock;
    
    nsock=chan->net;
    osock=NULL;
    while( (nsock!=NULL) && (nsock!= n))
      {
        osock=nsock;
        nsock=nsock->next;
      }
    if (nsock!=NULL)
      {
        if (osock==NULL)
          chan->net=nsock->next;
        else
          osock->next=nsock->next;
        nsock->channel=NULL;
      }
  }

/* addnet (channel, n) - Adds n to the channel list */
void addnet(struct channel_t *chan, struct net_t *n)
  {
    n->next=chan->net;
    chan->net=n;
    n->channel=chan;
  }

void add_rnet(struct net_t *n, int id) {
	struct res_t *r;
	r = malloc(sizeof(struct res_t));
	r->net = n;
	r->res_id = id;
	r->next = NULL;
	if (rnet == NULL) rnet = r;
	else {
		r->next = rnet;
		rnet = r;
	}
}

void rem_rnet(struct net_t *n) {
	struct res_t *r, *prev_r;
	prev_r = rnet;
	for (r = rnet; r; r = r->next) {
		if (r->net == n) {
			if (r == rnet) {
				rnet = r->next;
				free(r);
				return;
			}
			else {
				prev_r->next = r->next;
				free(r);
				return;
			}
		}
		prev_r = r;
	}
	lvprintf(0, "Fatal error: net not found on resolver list\n");
}

struct net_t *find_rnet(int id) {
	struct res_t *r;
	for (r = rnet; r; r = r->next) {
		if (r->res_id == id) return(r->net);
	}
	return NULL;
}
			

/* writepid() - Writes the current pid to game.pidfile */
void writepid(void)
  {
    FILE *file_out;
    
    file_out = fopen(game.pidfile,"w");
    if (file_out == NULL)
      {
        // lvprintf(0,"ERROR: Could not write to PID file %s\n", game.pidfile);
      }
    else
      {
        fprintf(file_out,"%d", (int)getpid());
        fclose(file_out);
      }
  }

/* delpid() - Delete the pid file */
void delpid(void)
  {
    remove(game.pidfile);
  }

/* int numallplayers(chan) - Returns ANY player that is connected in a channel */
int numallplayers(struct channel_t *chan)
  {
    int count;
    struct net_t *n;
    
    count=0;
    n=chan->net;
    while (n!=NULL)
      {
        count++;
        n=n->next;
      }
    return(count);
  }
/* int numplayers(chan)  - Returns number of CONNECTED Players in a channel */
int numplayers(struct channel_t *chan)
  {
    int count;
    struct net_t *n;
    
    count=0;
    n=chan->net;
    while (n!=NULL)
      {
        if( ((n->type==NET_CONNECTED) || (n->type==NET_WAITINGFORTEAM))) count++;
        n=n->next;
      }
    return(count);
  }

/* int numchannels() - Returns number of channels  */
int numchannels(void)
  {
    int count;
    struct channel_t *chan;
    
    chan=chanlist;
    count=0;
    while (chan!= NULL)
      {
        count++;
        chan=chan->next;
      }
    return(count);
    
  }

int is_valid_channelname(char *p)
{
	char *r;
	int valid=1;
	if (!p) return 0;
	for (r = p; *r; r++) {
		if (  (*r >= '0' && *r <= '9') ||
              (*r >= 'A' && *r <= 'Z')  ||
              (*r >= 'a' && *r <= 'z') ||
              (*r == '_' || *r == '-') ||
              (*r == '[' || *r ==']')  )
			continue;
		valid = 0;
		break;
	}
	return(valid);
}

char is_explicit_banned(struct net_t *n)
  { /* I should use regex, but I've not used it before, and it was late. Easier to write a quick one of my own */
    char ip_str[UHOSTLEN+1], host[UHOSTLEN+1];
    char n1[4], n2[4], n3[4], n4[4];
    int i, j; int found;
   
    sprintf(n1,"%lu", (unsigned long)(n->addr&0xff000000)/(unsigned long)0x1000000);
    sprintf(n2,"%lu", (unsigned long)(n->addr&0x00ff0000)/(unsigned long)0x10000);
    sprintf(n3,"%lu", (unsigned long)(n->addr&0x0000ff00)/(unsigned long)0x100);
    sprintf(n4,"%lu", (unsigned long)n->addr&0x000000ff);
    sprintf(ip_str, "%s.%s.%s.%s", n1, n2, n3, n4);

    found = 0;
    i = 0;
    for (j=0; n->host[j] != '\0'; j++)
	host[j] = tolower(n->host[j]);
    host[j] = '\0';
    while (i < MAXBAN && !found && banlist[i].addr[0] != 0) {
        if (!fnmatch(banlist[i].addr, host, 0) ||
            !fnmatch(banlist[i].addr, ip_str, 0)) {
                found = 1;
                break;
		}
		i++;
    }
    return(found);
  }

int is_banned(struct net_t *n)
  { /* I should use regex, but I've not used it before, and it was late. Easier to write a quick one of my own */
    char ip_str[UHOSTLEN+1], host[UHOSTLEN+1];
    char n1[4], n2[4], n3[4], n4[4];
    int i, j; int found, allow;
   
    allow = 0;
    i = 0;
    while (i < MAXALLOW && allowlist[i].nick[0] != 0) {
	if (!strcasecmp(n->nick, allowlist[i].nick) &&
	    !strcmp(n->team, allowlist[i].pass)) {
		allow =1;
		n->team[0] = '\0';
		break;
	}
        i++;
    }
    if (allow)
	return(0);
		
	
    sprintf(n1,"%lu", (unsigned long)(n->addr&0xff000000)/(unsigned long)0x1000000);
    sprintf(n2,"%lu", (unsigned long)(n->addr&0x00ff0000)/(unsigned long)0x10000);
    sprintf(n3,"%lu", (unsigned long)(n->addr&0x0000ff00)/(unsigned long)0x100);
    sprintf(n4,"%lu", (unsigned long)n->addr&0x000000ff);
    sprintf(ip_str, "%s.%s.%s.%s", n1, n2, n3, n4);

    found = 0;
    i = 0;
    for (j=0; n->host[j] != '\0'; j++)
	host[j] = tolower(n->host[j]);
    host[j] = '\0';
    while (!found && i < MAXBAN && combanlist[i].addr[0] != 0) {
        if (!fnmatch(combanlist[i].addr, ip_str, 0) ||
            !fnmatch(combanlist[i].addr, host, 0)) {
                found = 1;
				break;
		}
	    i++;
    }
    return(found);
  }

/* is_op( net ) - Returns 1 if this player is the lowest gameslot, thus the chanop */
char is_op(struct net_t *n)
  {
    int found;
    struct net_t *nt;
    found = 0;

    nt=n->channel->net;
    while( nt!=NULL)
      {
        if ( ( (nt->type == NET_CONNECTED) || (nt->type == NET_WAITINGFORTEAM) ) && (nt->gameslot < n->gameslot))
          found = 1;
        nt=nt->next;
      }
    return(!found);
  }

/* passed_level(net, level required to pass) - Returns 1 if the player's security */
/*            level is equal to or higher than the level required to pass */
/*            Passing is_op gives player a security level of at least 2*/
char passed_level(struct net_t *n, int passlevel)
  {
    int currlevel;

    currlevel=LEVEL_NORMAL; /* Normal Player */

    if (is_op(n)) currlevel=LEVEL_OP;  /* Op by position */
    
    /* Player might already have a higher level... say they did a /op */
    if (n->securitylevel > currlevel) 
      currlevel=n->securitylevel;
    
    return( currlevel >= passlevel);
  }

/* kick(net from, player to be kicked) - Disconnects player, and informs all others that they were kicked */
void kick(struct net_t *n_from, int kick_gameslot)
  {
    struct net_t *n;
    struct net_t *n_to;
    
    n=n_from->channel->net;
    n_to=NULL;
    while (n!=NULL)
      {
        if ( (n->gameslot == kick_gameslot) && ((n->type == NET_CONNECTED) || (n->type == NET_WAITINGFORTEAM))) 
          n_to = n;
        n=n->next;
      }
    if ((n_to!=NULL) && (kick_gameslot>=1) && (kick_gameslot<=6))
      {
	net_query_parser("kick %s %d %s #%s", n_to->nick, n_to->gameslot, n_to->host, n_to->channel->name); 
        n=n_from->channel->net;
        net_playback_send(n_to->channel->name, "kick %d\xff", kick_gameslot);
        while (n!=NULL)
          {
            if ( ((n->type == NET_CONNECTED) || (n->type == NET_WAITINGFORTEAM))) 
              {
                tprintf(n->sock,"kick %d\xff", kick_gameslot);
              }
            n=n->next;
          }
        killsock(n_to->sock); lostnet(n_to);
      }
  }

/* tet_checkversion( client version ) - Returns 0 if the server supports this */
/*   client version. At the moment, ONLY support 1.13 client*/ 
int tet_checkversion(char *buf)
  { /* Returns -1 if versioncheck fails */
    if (!strcmp(TETVERSION,buf))
      return(0);
    else
      return(-1);
  }

/* lvprintf( priority, same as printf ) - Logs to the log IF priority is smaller */
/*                                        or equal to game.verbose */
void lvprintf(int level, char *format, ...)
{
  static char LBUF[1024];
  if (format) {
        va_list args;
        va_start (args, format);
        vsnprintf(LBUF, 1023, format, args);
        va_end(args);
        if (level <= game.verbose)
                lprintf("%s", LBUF);
  }
}

/* lprintf( same as printf ) - Logs to the log. */
void lprintf(char *format, ...)
{
  static char LBUF[1024];
  FILE *file_out;
  char *mytime;
  char *P;
  time_t cur_time;

  if (format) {
        va_list args;
        va_start (args, format);
        vsnprintf(LBUF, 1023, format, args);
        va_end(args);
        file_out = fopen(FILE_LOG,"a");
        if (file_out != NULL) {
                cur_time = time(NULL);
                mytime=ctime(&cur_time);
                P=mytime;
                P+=4;
                mytime[strlen(mytime)-6]=0;
                fprintf(file_out,"%s %s", P, LBUF);
                fclose(file_out);
        }
  }
}

/* Open a Listening Socket on the TetriNET port */
void init_telnet_port()
{
  int i,j;
  struct net_t *n;
  
  /* find old entry if it exists */
  n=gnet;
  while ( (n!=NULL) && (n->type!=NET_TELNET))
    n=n->next;
    
  if (n==NULL) {
    /* no existing entry */
    n=gnet;
    gnet=malloc(sizeof(struct net_t));
    gnet->next=NULL;
    n=gnet;
    n->addr=getmyip();
    n->type=NET_TELNET;
    n->channel = malloc(sizeof(struct channel_t));
    n->channel->name[0] = '\0';
    strcpy(n->nick,"(telnet)");
    getmyhostname(n->host);
  }
  else {
    /* already an entry */
    killsock(n->sock);
  }
  j=TELNET_PORT;
  i=open_listen_socket(&j,game.bindip);

  if (i>=0) {
    n->port=j;
    n->sock=i;

    lvprintf(3,"Listening at telnet port %d, on socket %d, bound to %s\n", j,i,game.bindip);
    return;
  }
  printf("Couldn't find telnet port %d. (TetriNET already running?)\n",j);
  lvprintf(0,"Couldn't find telnet port %d.\n", j);
  exit(1);
}

/* Open a Listening Socket on the TetriNET query port */
void init_query_port()
{
  int i,j;
  struct net_t *n;
  
  /* find old entry if it exists */
  n = gnet;
  if (!n) {
	lvprintf(0, "Fatal error: gnet not found!! \n");
	exit(0);
  }

  while (n->next && (n->type!=NET_QUERY))
    n=n->next;
    
  if (n->next) 
    killsock(n->sock);

  /* no existing entry */
  n->next = malloc(sizeof(struct net_t));
  n = n->next;
  n->addr=getmyip();
  n->type=NET_QUERY;
  n->next=NULL;
  n->channel = malloc(sizeof(struct channel_t));
  n->channel->name[0] = '\0';
  strcpy(n->nick,"(telnet)");
  getmyhostname(n->host);
  j=QUERY_PORT;
  i=open_listen_socket(&j,game.bindip);

  if (i>=0) {
    n->port=j;
    n->sock=i;

    lvprintf(3,"Listening at query port %d, on socket %d, bound to %s\n", j,i,game.bindip);
    return;
  }
  printf("Couldn't find telnet port %d. (TetriNET already running?)\n",j);
  lvprintf(0,"Couldn't find telnet port %d.\n", j);
  exit(1);
}

/* Open a Listening Socket on the TetriNET query port */
void init_playback_port()
{
  int i,j;
  struct net_t *n;

  /* find old entry if it exists */
  n = gnet;
  if (!n) {
        lvprintf(0, "Fatal error: gnet not found!! \n");
        exit(0);
  }

  while (n->next && (n->type!=NET_PLAYBACK))
    n=n->next;

  if (n->next)
    killsock(n->sock);

  /* no existing entry */
  n->next = malloc(sizeof(struct net_t));
  n = n->next;
  n->addr=getmyip();
  n->type=NET_PLAYBACK;
  n->next=NULL;
  n->channel = malloc(sizeof(struct channel_t));
  n->channel->name[0] = '\0';
  strcpy(n->nick,"(telnet)");
  getmyhostname(n->host);
  j=PLAYBACK_PORT;
  i=open_listen_socket(&j,game.bindip);

  if (i>=0) {
    n->port=j;
    n->sock=i;

    lvprintf(3,"Listening at playback port %d, on socket %d, bound to %s\n", j,i,game.bindip);
    return;
  }
  printf("Couldn't find telnet port %d. (TetriNET already running?)\n",j);
  lvprintf(0,"Couldn't find telnet port %d.\n", j);
  exit(1);
}

/* Main loop calls here when activity found on a net socket */
void net_activity(int z, char *buf, int len)
  {
    struct net_t *n;
    struct channel_t *chan;

 
    chan=chanlist;
    n = NULL;
    while ( (chan!=NULL) && (n==NULL) )
      {
        n=chan->net;
        while( (n!=NULL) && (n->sock!=z) )
          n=n->next;
        chan=chan->next;
      }

    if (n==NULL) 
      { /* Try global list */
        n=gnet;
        while( (n!=NULL) && (n->sock!=z) )
          n=n->next;
      }
    
      
    if (n==NULL) return;
    
    /* Reset timeout on socket */
    if (n->status == STAT_PLAYING)
      n->timeout_ingame = game.timeout_ingame;
    else
      n->timeout_outgame = game.timeout_outgame;
    
    switch (n->type)
      {
        case NET_TELNET: 		/* Recieved connection */
          {
            net_telnet(n,buf);
            break;
          }
        case NET_QUERY:			/* Recieved connection */
          {
            net_query(n,buf);
            break;
          }
	case NET_PLAYBACK:		/* Received playback connection */
	  {
	    net_playback(n,buf);
	    break;
	  }
        case NET_TELNET_INIT:		/* Received clients init sequence */
          {
            net_telnet_init(n,buf);
            break;
          }
        case NET_QUERY_INIT:		/* Received clients init sequence */
          {
            net_query_init(n,buf);
            break;
          }
	case NET_PLAYBACK_INIT:
	  {
	    net_playback_init(n,buf);
	    break;
	  }
        case NET_WAITINGFORTEAM:	/* Waiting for inital team */
          {
            net_waitingforteam(n,buf);
            break;
          }
        case NET_CONNECTED:             /* Recieved command from client */
          {
            net_connected(n,buf);
            break;
          }
        case NET_QUERY_CONNECTED:
          {
            net_query_connected(n,buf);
            break;
          }
        case NET_PLAYBACK_CONNECTED:
          {
            net_playback_connected(n,buf);
            break;
          }
		case NET_WAITINGFORDNS:
		  {
			lvprintf(0, "Internal Error - Untrapped DNS event.\n");
			break;
		  }
        default:
          {
            /* lvprintf(0,"Internal Error - Untrapped Network activity.\n"); */
          }
      }
  }


/* String length (minus colours) */
int strclen(char *S)
  {
    int i;int count;
    if (S==NULL)
      return 0;
      
    i=0;count=0;
    while(S[i]!=0)
      {
        if(              (S[i] != BOLD)
                      && (S[i] != CYAN)
                      && (S[i] != BLACK)
                      && (S[i] != BLUE)
                      && (S[i] != DARKGRAY)
                      && (S[i] != MAGENTA)
                      && (S[i] != GREEN)
                      && (S[i] != NEON)
                      && (S[i] != SILVER)
                      && (S[i] != BROWN)
                      && (S[i] != NAVY)
                      && (S[i] != VIOLET)
                      && (S[i] != RED)
                      && (S[i] != ITALIC)
                      && (S[i] != TEAL)
                      && (S[i] != WHITE)
                      && (S[i] != YELLOW)
                      && (S[i] != UNDERLINE)
		      && (S[i] != 11) )
                count++;
        i++;  
      }
    return(i);
  }

/* Connected, recieving commands */
void net_connected(struct net_t *n, char *buf)
  {
    char COMMAND[101]; char PARAM[601]; char MSG[601];
    char STRG[513], STRG2[513];
    int num; int num1; int num2; int num3; int num4; int s; int i,j,k,l,x,y;
    int valid_param;
    int byt;
    char *P=NULL;
    struct channel_t *chan,*ochan,*c,*oc;
    struct net_t *nsock=NULL;
    struct net_t *ns1,*ns2;
    
    if (!buf) return;
    /* sometimes this is really happen!!! zero length buf!!! */
    byt = strlen(buf);
    if (byt <= 2 || byt > 512) return;
    
    COMMAND[0]=0;PARAM[0]=0;MSG[0]=0;

    /* Ensure command is proper before passing it to other players */
    sscanf(buf, "%100s %600[^\n\r]", COMMAND, PARAM);
    valid_param = 0; /* 0 = invalid    */
                     /* 1 = valid      */
                     /* 2 = valid command, but failed some test */


    /* Party Line Message - pline <playernumber> <message> */
    if ( !strcasecmp(COMMAND, "pline") )
      {
        valid_param=2;
        s = sscanf(PARAM, "%d %600[^\n\r]", &num, MSG);
	n->timeout_outgame = game.timeout_outgame;
        if ( ((s >= 2) && (num==n->gameslot)) || (n->type == NET_PLAYBACK_CONNECTED))
          {

            if (MSG[0]=='/')
              {
                valid_param=1;
                /* First parse to see if it's a server command */
                if ( !strncasecmp(MSG, "/kick", 5) && (game.command_kick > 0))
                  {
					int self_kick = 0;
                    valid_param=2;
                    if ( passed_level(n,game.command_kick) )
                      {
                        if (strlen(MSG) < 7) {
                           tprintf(n->sock,"pline 0 %cFormat: %c/kick %c<slot number>\xff", NAVY,RED,BLUE);
                           return;
                        }
                        P=MSG+6;
                        while( (((*P)-'0') >= 1) && (((*P)-'0') <= 6) )
                          {
						    if (((*P)-'0') == n->gameslot)
								self_kick = 1;
						    else
                            	kick(n, (*P)-'0');
                            P++;
                          }
						if (self_kick)
							kick(n, n->gameslot);
                      }
                    else
                      tprintf(n->sock,"pline 0 %cYou do NOT have access to that command!\xff",RED);
                   
                  }

                /* Who is online */
                if ( !strncasecmp(MSG, "/who", 4) && (game.command_who>0))
                  {
                    valid_param=2;
                    if (  n->type == NET_PLAYBACK_CONNECTED || passed_level(n,game.command_who) )
                      {
                        if (n->securitylevel == LEVEL_AUTHOP)
                          tprintf(n->sock,"pline 0 %cNickname\tTeam            \tChannel\tIP\xff", NAVY);
                        else
                          tprintf(n->sock,"pline 0 %cNickname\tTeam            \tChannel\xff", NAVY);
                        l=1;
                        chan=chanlist;
                        while(chan!=NULL)
                          {
                            nsock=chan->net;
                            while (nsock!=NULL)
                              {
                                if (nsock->type==NET_CONNECTED)
                                  {
                                    STRG[0]=0;
                                    STRG2[0]=0;
                                    strcpy(STRG,nsock->nick);
                                    j=strclen(STRG);
                                    k=strlen(STRG);
                                    while(j<15)
                                      {
                                        STRG[k]=' ';
                                        k++;
                                        j++;
                                      }
                                    STRG[k]=0;
                              
                                    strcpy(STRG2,nsock->team);
                                    j=strclen(STRG2);
                                    k=strlen(STRG2);
                                    while(j<17)
                                      {
                                        STRG2[k]=' ';
                                        k++;
                                        j++;
                                      }
                                    STRG2[k]=0;
                            
                                    if (nsock==n)
                                      j=RED;
                                    else
                                      j=DARKGRAY;
                                    
                                    if (n->securitylevel == LEVEL_AUTHOP)
                                      tprintf(n->sock,"pline 0 %c(%c%d%c) %c%s\t%c%s\t%c#%s\t%s\xff", NAVY,j,l,NAVY, j, STRG,TEAL,STRG2,BLUE,nsock->channel->name,nsock->host);
                                    else
                                      tprintf(n->sock,"pline 0 %c(%c%d%c) %c%s\t%c%s\t%c#%s\xff", NAVY,j,l,NAVY, j, STRG,TEAL,STRG2,BLUE,nsock->channel->name);
                                    l++;
                                  }
                                nsock=nsock->next;
                              }
                            chan=chan->next;
                          }
                      } 
                    else
                      tprintf(n->sock,"pline 0 %cYou do NOT have access to that command!\xff",RED);
                  }
              
                /* List Channels */
                if ( !strncasecmp(MSG, "/list", 5) && (game.command_list>0))
                  {
                    valid_param=2;
                    if ( n->type == NET_PLAYBACK_CONNECTED || passed_level(n,game.command_list) )
                      {
                        if (chanlist != NULL)
                          {
                            tprintf(n->sock,"pline 0 %cTetriNET Channel Lister - (Type %c/join %c#channelname%c)\xff",NAVY,RED,BLUE,NAVY);
                            chan=chanlist;
                            i=1;
                            while (chan != NULL)
                              {
                                if ((!strcasecmp(n->channel->name, chan->name)))
                                  j=RED;
                                else
                                  j=BLUE;
                              
                                if (chan->status==STATE_INGAME)
                                  {
                                    sprintf(STRG,"%c{INGAME}%c", DARKGRAY,NAVY);
                                  }
                                else
                                  {
                                    sprintf(STRG,"                ");
                                  }
                                if (numplayers(chan) >= chan->maxplayers)
                                  tprintf(n->sock,"pline 0 %c(%c%d%c) %c#%-6s\t%c[%cFULL%c] %s       (%d)   %c%s\xff", NAVY, j, i, NAVY, BLUE, chan->name, NAVY,RED,NAVY,STRG, chan->priority,BLACK,chan->description);
                                else
                                  {
                                    tprintf(n->sock,"pline 0 %c(%c%d%c) %c#%-6s\t%c[%cOPEN%c-%d/%d%c] %s (%d)   %c%s\xff", NAVY, j, i, NAVY, BLUE, chan->name, NAVY,TEAL,BLUE,numplayers(chan),chan->maxplayers,NAVY,STRG, chan->priority,BLACK,chan->description);
                                  }
                                i++;
                                chan=chan->next;
                              }
                          }
                      }
                    else
                      tprintf(n->sock,"pline 0 %cYou do NOT have access to that command!\xff",RED);
                  }
              
                /* Join Channel */
                if ( !strncasecmp(MSG, "/j", 2) && (game.command_join>0))
                  {
                    valid_param=2;
                    if ( passed_level(n,game.command_join) || (game.command_join==4))
                      {
                        if ( !strncasecmp(MSG,"/join ", 6) )
                          P=MSG+6;
                        else if ( !strncasecmp(MSG,"/j ", 3) )
                          P=MSG+3;                        else {
                           tprintf(n->sock,"pline 0 %cFormat: %c/join %c<#channel|channel number>\xff", NAVY,RED,BLUE);
                           return;
                        }
                        STRG[0]=0;
                        k = sscanf(P,"%512s", STRG);
                        if ((k != 0) )
                          {
                            /* First, is it a #channel or channel number? */
                            j=atoi(STRG);
                            chan=NULL;
                            ochan=NULL;
                            if (j>0)
                              { /* Position */
                                chan=chanlist;
                                j--;
                                while( (j>0) && (chan!=NULL) ) 
                                  {
                                    chan=chan->next;
                                    j--;
                                  }
                              }
                            else if (STRG[0]=='#')
                              { /* Name */
								net_query_TrimStr(STRG);
								net_query_StripCtrlStr(STRG);
                                sscanf(P,"#%512s", STRG);
			    				if (!is_valid_channelname(STRG)) {
                    				tprintf(n->sock, "pline 0 %cInvalid channel name!\xff", RED);
                    				return;
                				}

                                if (!strcasecmp(STRG, DEFAULTSPECCHANNEL)) {
                                        tprintf(n->sock, "pline 0 %cThe channel you request is restricted!\xff", RED);
                                        return;
                                }

                            
                                chan = chanlist;
                                while ( (chan != NULL) && (strcasecmp(STRG,chan->name)) )
                                  chan=chan->next;
                                j=0;
                              }
                            else
                              {
                                j=-1;
                                tprintf(n->sock,"pline 0 %cFormat: %c/join %c<#channel|channel number>\xff", NAVY,RED,BLUE);
                              }
                            ochan=n->channel;
                            if ( (chan!=NULL) && (numplayers(chan)>=chan->maxplayers))
                              { /* A Full channel */
                                tprintf(n->sock,"pline 0 %cThat channel is %cFULL%c!\xff", NAVY, RED,BLACK); 
                              }
                            else if ( (chan==NULL) && (j>=0) && (numchannels() >= game.maxchannels))
                              { /* Too many channels */
                                tprintf(n->sock,"pline 0 %cCannot create any more channels!\xff",RED);
                              }
                            else if ( (chan==NULL) && (j>=0) && (game.command_join==4) && (n->securitylevel!=LEVEL_AUTHOP))
                              { /* Can't create a NEW channel */
                                tprintf(n->sock,"pline 0 %cCannot create new channel\xff",RED);
                              }
                            else if (j>=0)
                              {
                                if (chan == NULL)
                                  { /* New channel */
                                    chan=chanlist;
                                    while ((chan!= NULL) && (chan->next!=NULL)) chan=chan->next;
                                
                                    if (chan==NULL)
                                      {
                                        chanlist=malloc(sizeof(struct channel_t));
                                        chan=chanlist;
                                      }
                                    else
                                      {
                                        chan->next=malloc(sizeof(struct channel_t));
                                        chan=chan->next;
                                      }
                                
                                    chan->next=NULL;
                                    chan->net=NULL;
                                    strncpy(chan->name, STRG, CHANLEN-1); chan->name[CHANLEN-1]=0;
                                    chan->maxplayers=DEFAULTMAXPLAYERS;
                                    chan->status=STATE_ONLINE;
                                    chan->description[0]=0;
                                    chan->priority=DEFAULTPRIORITY;
                                    chan->sd_mode=SD_NONE;
                                    chan->persistant=0;
                                
                                    /* Copy default settings */
                                    chan->starting_level=game.starting_level;
                                    chan->lines_per_level=game.lines_per_level;
                                    chan->level_increase=game.level_increase;
                                    chan->lines_per_special=game.lines_per_special;
                                    chan->special_added=game.special_added;
                                    chan->special_capacity=game.special_capacity;
                                    chan->classic_rules=game.classic_rules;
                                    chan->average_levels=game.average_levels;
                                    chan->sd_timeout=game.sd_timeout;
                                    chan->sd_lines_per_add=game.sd_lines_per_add;
                                    chan->sd_secs_between_lines=game.sd_secs_between_lines;
                                    strcpy(chan->sd_message,game.sd_message);
                                    chan->block_leftl=game.block_leftl;
                                    chan->block_leftz=game.block_leftz;
                                    chan->block_square=game.block_square;
                                    chan->block_rightl=game.block_rightl;
                                    chan->block_rightz=game.block_rightz;
                                    chan->block_halfcross=game.block_halfcross;
                                    chan->block_line=game.block_line;
                                    chan->special_addline=game.special_addline;
                                    chan->special_clearline=game.special_clearline;
                                    chan->special_nukefield=game.special_nukefield;
                                    chan->special_randomclear=game.special_randomclear;
                                    chan->special_switchfield=game.special_switchfield;
                                    chan->special_clearspecial=game.special_clearspecial;
                                    chan->special_gravity=game.special_gravity;
                                    chan->special_quakefield=game.special_quakefield;
                                    chan->special_blockbomb=game.special_blockbomb;
                                    chan->stripcolour=game.stripcolour;
                                    chan->serverannounce=game.serverannounce;
                                    chan->pingintercept=game.pingintercept;
				    chan->winlist_file=game.winlist_file;
				    strncpy(chan->game_type, game.game_type, GAMETYPELEN-1);
				    chan->game_type[GAMETYPELEN-1] = 0;
                                
                                    /* Remove ourselves from old channel list */
                                    n->channel=chan;
                                
			    	    net_query_parser("newchan %s %d %s #%s", n->nick, n->gameslot, n->host, n->channel->name);
                                    tprintf(n->sock,"pline 0 %cCreated new Channel - %c#%s%c\xff", GREEN, BLUE, chan->name, BLACK);
                                  }
                        
                                else
                                  { /* An already existing channel */
                                    n->channel = chan;
/*
				    net_query_parser("playerjoin %s %d %s #%s", n->nick, n->gameslot, n->host, chan->name);
*/
 
                                    tprintf(n->sock,"pline 0 %cJoined existing Channel - %c#%s%c\xff", GREEN, BLUE, chan->name, BLACK);
                                  }
                                remnet(ochan,n);
                                addnet(chan,n);
                            
                                /* Send to old channel, this player join message */
                                nsock=ochan->net;
                                net_playback_send(ochan->name, "pline 0 %c%s%c %chas joined channel #%s\xff", DARKGRAY,n->nick,BLACK,DARKGRAY,n->channel->name);
                                while (nsock!=NULL)
                                  {
                                    if ( (nsock->type==NET_CONNECTED) )
                                      {
                                        /* Tell them we've joined this channel */
                                        tprintf(nsock->sock,"pline 0 %c%s%c %chas joined channel #%s\xff", DARKGRAY,n->nick,BLACK,DARKGRAY,n->channel->name);
                                      }
                                    nsock=nsock->next;
                                  }
                            
                                if ((ochan->status == STATE_INGAME) || (ochan->status ==STATE_PAUSED))
                                  {
                                    n->status = STAT_NOTPLAYING;
                                    tprintf(n->sock,"endgame\xff");
                                  }
                                  
                                /* Cleartheir Field */
                                for(y=0;y<FIELD_MAXY;y++)
                                  for(x=0;x<FIELD_MAXX;x++)
                                    n->field[x][y]=0; /* Nothing */
                        
                                /* Work out gameslot */
                                num1=n->gameslot;
                                num2=0; num3=1;
                                while ( (num2 < n->channel->maxplayers) && (num3) )
                                  {
                                    num2++;
                                    num3=0;
                                    nsock=n->channel->net;
                                    while (nsock!=NULL)
                                      {
                                        if( (nsock!=n) && ( (nsock->type==NET_CONNECTED) || (nsock->type==NET_WAITINGFORTEAM) )&&(nsock->gameslot==num2) ) num3=1;
                                        nsock=nsock->next;
                                      }
                                  }
                                if (num3==1)
                                  {
                                    // lvprintf(0,"#%s-%s Ran out of places in channel (FATAL)\n",n->channel->name,n->nick);
                                    killsock(n->sock);lostnet(n);
                                    return;
                                  }
                              
                                /* Clear our spot */
                                tprintf(n->sock,"playerleave %d\xff",n->gameslot);
                                net_playback_send(ochan->name, "playerleave %d\xff", n->gameslot);

                                n->gameslot=num2;
                            
                                /* Now, send playerleft to all on old channel AND to player */
                                num3=0;
                                num4=0;
                                STRG2[0]=0;
                                nsock=ochan->net;
				net_query_parser("playerleave %s %d %s #%s", n->nick, n->gameslot, n->host, ochan->name);
                                while (nsock!=NULL)
                                  {
                                    if ( (nsock!=n) && (nsock->type == NET_CONNECTED))
                                      {
                                        tprintf(nsock->sock,"playerleave %d\xff", num1);
                                        tprintf(n->sock,"playerleave %d\xff", nsock->gameslot);
                                        if (strcasecmp(STRG2,nsock->team))
                                          {/* Different team, so add player */
                                            num3++;
                                            if (nsock->status==STAT_PLAYING) num4++;
                                            strcpy(STRG2,nsock->team);
                                          }
                                        else if (STRG2[0]==0) 
                                          {
                                            num3++;
                                            if (nsock->status==STAT_PLAYING) num4++;
                                          }
                                      }
                                    nsock=nsock->next;
                                  }
                            
                                /* Now send to new channel, this player joined, AND to player also reset playerstatus */
				n->status = STAT_NOTPLAYING;
		
				sendwinlist(n);
                                net_playback_send(n->channel->name, "playerjoin %d %s\xffteam %d %s\xff", n->gameslot, n->nick, n->gameslot, n->team);
				net_query_parser("playerjoin %s %d %s #%s", n->nick, n->gameslot, n->host, n->channel->name);
                                nsock=n->channel->net;
                                while (nsock!=NULL)
                                  {
                                    if ( (nsock!=n) && (nsock->type==NET_CONNECTED))
                                      {
                                        /* Send each other player and their team anme */
                                        tprintf(n->sock, "playerjoin %d %s\xffteam %d %s\xff",nsock->gameslot, nsock->nick, nsock->gameslot, nsock->team);
                                        /* Send to the other player, this player and their team */
                                        tprintf(nsock->sock, "playerjoin %d %s\xffteam %d %s\xff", n->gameslot, n->nick, n->gameslot, n->team);
                                        /* Tell them we've joined this channel */
                                        tprintf(nsock->sock,"pline 0 %c%s%c %chas joined channel #%s\xff", GREEN,n->nick,BLACK,GREEN,n->channel->name);
                                      }
                                    nsock=nsock->next;
                                  }
                        
                                /* Set our playernumber */
                                tprintf(n->sock, "playernum %d\xff",n->gameslot); 
                      
                                /* If game is in progress, send all other players fields */
                                if( (n->channel->status == STATE_INGAME) || (n->channel->status == STATE_PAUSED) )  
                                  {
                                    nsock=n->channel->net;
                                    while (nsock!=NULL)
                                      {
                                        if( (nsock->type==NET_CONNECTED) &&(nsock!=n))  
                                          {
                                            /* Tell this player that the newjoined player has lost */
				            tprintf(nsock->sock,"playerlost %d\xff", n->gameslot);
                                            /* Each player who has lost, send player_lost */
                                            if (nsock->status != STAT_PLAYING)
                                              {
                                                tprintf(n->sock,"playerlost %d\xff", nsock->gameslot);
                                              }
                                            sendfield(n, nsock); /* Send to player, this players field */
                                          }
                                        nsock=nsock->next;
                                      }
                                  }
                          
                                /* If we are ingame, then tell this person that we are! */
                                if ( (n->channel->status == STATE_INGAME) || (n->channel->status == STATE_PAUSED) )
                                  tprintf(n->sock,"ingame\xff");
                          
                                /* If we are currently paused, kindly let them know that fact to */ 
                                if ( n->channel->status == STATE_PAUSED )
                                  tprintf(n->sock,"pause 1\xff");
                                                                               
                                /* If 1 or less players/teams now are playing, AND the player that quit WAS playing, STOPIT */
                                if ( (num4 <= 1) && (ochan->status == STATE_INGAME) && (n->status == STAT_PLAYING) )
                                  {
                                    nsock=ochan->net;
                                    while (nsock!=NULL)
                                      {
                                        if ( (nsock!=n) && (nsock->type == NET_CONNECTED))
                                          {
                                            tprintf(nsock->sock,"endgame\xff");
                                            nsock->status=STAT_NOTPLAYING;
                                          }
                                        nsock=nsock->next;
                                      }

                                    net_playback_send(ochan->name, "endgame\xff");
                                    ochan->status = STATE_ONLINE;
                                  }
          
                                /* If no players, then we delete the channel IF it's not persistant*/
                                if ( (numallplayers(ochan) == 0) && (ochan!=n->channel) && (!ochan->persistant) )
                                  {
                                    c=chanlist;
                                    oc=NULL;
                                    while ( (c != ochan) && (c != NULL) ) 
                                      {
                                        oc=c;
                                        c=c->next;
                                      }
                                    if (c != NULL)
                                      {
                                        if (oc != NULL)
                                          oc->next=c->next;
                                        else
                                          chanlist=c->next;
                                        free(c); 
                                      } 
                                  }
                        
                                if (n->channel->status == STATE_ONLINE)
                                  {
                                    n->status=STAT_NOTPLAYING;
                                  }
                                else
                                  {
                                    n->status=STAT_LOST;
                                  }
                              }
                          }
                      }
                    else
                      tprintf(n->sock,"pline 0 %cYou do NOT have access to that command!\xff",RED);
                  }
              
                /* Save Config */
                if ( !strncasecmp(MSG, "/save", 5) && (game.command_save>0))
                  {
                    valid_param=2;
                    if ( passed_level(n,game.command_save) )
                      {
                        gamewrite();
                        tprintf(n->sock,"pline 0 %cGame configuration and Persistant Channel info saved\xff",NAVY);
                      }
                    else
                      tprintf(n->sock,"pline 0 %cYou do NOT have access to that command!\xff",RED);
                  }
            
                /* Private msg to a person - Suggestion by crazor */
                if ( !strncasecmp(MSG, "/msg", 4) && (game.command_msg>0))
                  {
                    valid_param=2;
                    if ( passed_level(n,game.command_msg) )
                      {
                        if (strlen(MSG) < 8) {
                           tprintf(n->sock, "pline 0 %c/msg %c<playernumber(s)> <message>\xff", RED, BLUE);
                           return;
                        }

                        P=MSG+5;
                        i = sscanf(P,"%512s %512[^\n\r]", STRG, STRG2);
			j = strlen (STRG);
			if (i != 2 || j > 6) {
				tprintf(n->sock, "pline 0 %c/msg %c<playernumber(s)> <message>\xff", RED, BLUE);
				return;
			}

			for (i = 0; i < j; i++) {
				if (STRG[i] < '0' || STRG[i] > '6') break;
				for (k = i+1; k < j; k++)
					if (STRG[i] == STRG[k]) break;
				if (k != j) break;
			}
			if (i != j) {
				tprintf(n->sock, "pline 0 %cInvalid playernumber %c(%s)\xff", RED, BLUE, STRG);
				return;
			}
			
                        P=STRG;
                
                        while( *P )
                          {
                            j=(*P)-'0';
                            nsock=n->channel->net;
                            while (nsock!=NULL)
                              {
                                if ( (nsock->type == NET_CONNECTED) && (nsock->gameslot == j))
                                  {
                                    tprintf(nsock->sock,"pline %d %c(msg)%c %s\xff", n->gameslot, NAVY, BLACK, STRG2); 
                                  }
                                nsock=nsock->next;
                              }
                            P++;
                          }
                      }  
                    else
                      tprintf(n->sock,"pline 0 %cYou do NOT have access to that command!\xff",RED);
                  }
              
                /* Move a player to another gamespot */
                if ( !strncasecmp(MSG, "/move", 5) && (game.command_move>0))
                  {
                    valid_param=2;
                    if ( passed_level(n,game.command_move) && (n->channel->status == STATE_ONLINE) )
                      {
                        if (strlen(MSG) < 9) {
                           tprintf(n->sock,"pline 0 %c/move %c<playernumber> <newplayernumber>\xff", RED, BLUE);
                           return;
                        }

                        P=MSG+6;
                        k=0;l=0;
                        sscanf(P," %d %d", &k, &l);
                    
                        if ( (k>0) && (k<=6) && (l>0) && (l<=6) && (k!=l) )
                          {
                            /* Ok, this is NOT supposed to work, but it does ;) */
                     
                            /* Give player l, player k's number */
                            /* Give player k, player l's number */
                            /* Send to all others, as if they've rejoined */
                        
                            /* Find sock_index... for k */
                            ns1=n->channel->net;
                            while ( (ns1!=NULL) && !( ((ns1->type == NET_CONNECTED) && (ns1->gameslot==k) )))
                              ns1=ns1->next;
                        
                            ns2=n->channel->net;
                            while( (ns2!=NULL) && !((ns2->type == NET_CONNECTED) && (ns2->gameslot==l) ))
                              ns2=ns2->next;
                        
                            if ( (ns1!=NULL) )
                              {
                                tprintf(ns1->sock,"playernum %d\xff", l);
                                ns1->gameslot = l;
                            
                                if (ns2!=NULL)
                                  { /* This player existed... */
                                    tprintf(ns2->sock,"playernum %d\xff", k);
                                    ns2->gameslot=k;
                                    tprintf(ns1->sock,"playerjoin %d %s\xff", ns2->gameslot, ns2->nick);
				    tprintf(ns1->sock,"team %d %s\xff", ns2->gameslot, ns2->team);
                                    tprintf(ns2->sock,"playerjoin %d %s\xff", ns1->gameslot, ns1->nick);
                                    tprintf(ns2->sock,"team %d %s\xff", ns1->gameslot, ns1->team);
				    net_query_parser("move %s(%d) %s(%d) #%s", ns1->nick, k, ns2->nick, l, n->channel->name);
                                  } 
                                else
                                  {
                                    tprintf(ns1->sock,"playerleave %d\xff", k);
                                  }

                                net_playback_send(n->channel->name, "playerjoin %d %s\xff", ns1->gameslot, ns1->nick);
                                net_playback_send(n->channel->name, "team %d %s\xff", ns1->gameslot, ns1->team);
                                if (ns2 != NULL) {
                                        net_playback_send(n->channel->name, "playerjoin %d %s\xff", ns2->gameslot, ns2->nick);
                                        net_playback_send(n->channel->name, "team %d %s\xff", ns2->gameslot, ns2->team);                                }
                                else
                                        net_playback_send(n->channel->name, "playerleave %d \xff", k);
                    
                                /* Now tell everyone else */
                                nsock=n->channel->net;
                                while (nsock!=NULL)
                                  {
                                    if ( (nsock->type == NET_CONNECTED) && (nsock!=ns1) && (nsock!=ns2) )
                                      {
                                        tprintf(nsock->sock,"playerjoin %d %s\xff", ns1->gameslot, ns1->nick);
					tprintf(nsock->sock,"team %d %s\xff", ns1->gameslot, ns1->team);
                                        if (ns2!=NULL)
                                          { /* Player existed */
                                            tprintf(nsock->sock,"playerjoin %d %s\xff", ns2->gameslot,ns2->nick);
                                            tprintf(nsock->sock,"team %d %s\xff", ns2->gameslot, ns2->team);

                                          }
                                        else
                                          {
                                            tprintf(nsock->sock,"playerleave %d\xff", k);

                                          }  
                                      }
                                    nsock=nsock->next;
                                  }
                              }
                          }
                        else 
			  if (n->type != NET_QUERY_CONNECTED && n->type != NET_PLAYBACK_CONNECTED)
                          tprintf(n->sock,"pline 0 %c/move %c<playernumber> <newplayernumber>\xff", RED, BLUE);
                      }  
                    else
                      {
                        if (n->channel->status != STATE_ONLINE)
                          tprintf(n->sock,"pline 0 %cCommand unavailable while a game is in progress. Stop it first.\xff",RED);
                        else
                          tprintf(n->sock,"pline 0 %cYou do NOT have access to that command!\xff",RED);
                      }
                  }
                
            
                /* Take "ops" - Suggestion by (jawfx@hotmail.com 21/9/98) */
                if ( !strncasecmp(MSG, "/op", 3) && (game.command_op>0))
                  {
                    valid_param=2;
                    if (strlen(MSG) < 5) {
                           return;
                    }

                    P=MSG+4;
                    if (securityread() < 0)
                      securitywrite();
                        
                    if ( (strlen(security.op_password) > 0) && !strcmp(P, security.op_password) )
                      { /* Passed, this player is OP */
                        n->securitylevel =LEVEL_AUTHOP;
                        tprintf(n->sock,"pline 0 %cYour security level is now: %cAUTHENTICATED OP\xff", GREEN, RED);
			net_query_parser("op %s %d %s #%s %s", n->nick, n->gameslot, n->host, n->channel->name, n->nick);
                      }
                    else
                      {
                        tprintf(n->sock,"pline 0 Invalid Password! (Attempt logged)\xff");
                        lvprintf(1,"#%s-%s Failed attempt to gain OP status\n",  n->channel->name, n->nick);
                      }
                  }
            
                /* Winlist. Display the top X people - Suggestion by crazor */
                if ( !strncasecmp(MSG, "/winlist", 8) && (game.command_winlist>0) )
                  {
		    struct winlist_t *w;
                    valid_param=2;
                    if (passed_level(n,game.command_winlist))
                      {
			w = find_winlist(n);
                        P=MSG+9;
                        if (strlen(MSG) == 8)
                          j=10;
                        else {
			  if (*P == '#') {
				w = find_winlist_from_chan(P);
				j = 10;
			  }
			  else 
                                j=atoi(P);
			}
			if (j > 40) j = 40;
			if (!w) {
				tprintf(n->sock, "pline 0 This channel is set to no scoring\xff");
				return;
			}
                        if ( (j >= 1) && (j <= MAXWINLIST) )
                          {
                            i=0;
                            tprintf(n->sock,"pline 0 %cTop %d Winlist\xff", BLUE, j);
                            while ( (i < j) && (w[i].inuse) )
                              {
                                if (w[i].status == 't')
                                  {
                                    if (!strcmp(n->team, w[i].name))
                                      k = RED;
                                    else
                                      k = BLACK;
                                    tprintf(n->sock,"pline 0 %c%d. %4d - Team %s\xff", k, i+1, w[i].score, w[i].name); 
                                  }
                                else
                                  {
                                    if (!strcmp(n->nick, w[i].name))
                                      k = RED;
                                    else
                                      k = BLACK;
                                    tprintf(n->sock,"pline 0 %c%d. %4d - Player %s\xff", k, i+1, w[i].score, w[i].name); 
                                  }
                                i++;
                              }
                          }
                      }
                    else
                      tprintf(n->sock,"pline 0 %cYou do NOT have access to that command!\xff",RED);
                  }
                if ( !strncasecmp(MSG, "/--version", 9))
                  {
                    valid_param = 2;
                    tprintf(n->sock,"pline 0 %c%s.%s\xff", WHITE, TETVERSION, SERVERBUILD);
                  }
                if ( !strncasecmp(MSG, "/help", 5) && (game.command_help>0) )
                  {
                    valid_param=2;
                    if (passed_level(n,game.command_help))
                      {
                        tprintf(n->sock,"pline 0 - Built in server commands %c(*) %crequires 'op', %c(!) %crequires '/op'\xff", TEAL, BLACK, RED, BLACK);
                        if (game.command_join)
                          {
                            STRG[0]=0;
                            switch(game.command_join)
                              {
                                case 2: /* Requires OP */
                                  {
                                    sprintf(STRG,"%c(*)", TEAL);
                                    break;
                                  }
                                case 3: /* Requires /OP */
                                  {
                                    sprintf(STRG,"%c(!)", RED);
                                    break;
                                  }
                              }
                            tprintf(n->sock,"pline 0   %c/join %c<#channel|channel number>\xff", RED, BLUE);
                            tprintf(n->sock,"pline 0       %-4s %cJoins or creates a virtual tetrinet channel\xff", STRG, BLACK);
                          }
                        if (game.command_kick)
                          {
                            STRG[0]=0;
                            switch(game.command_kick)
                              {
                                case 2: /* Requires OP */
                                  {
                                    sprintf(STRG,"%c(*)", TEAL);
                                    break;
                                  }
                                case 3: /* Requires /OP */
                                  {
                                    sprintf(STRG,"%c(!)", RED);
                                    break;
                                  }
                              }
                            tprintf(n->sock,"pline 0   %c/kick %c<playernumber(s)>\xff", RED, BLUE);
                            tprintf(n->sock,"pline 0       %-4s %cKicks player(s) from the server\xff", STRG, BLACK);
                          }
                        if (game.command_list)
                          {
                            STRG[0]=0;
                            switch(game.command_list)
                              {
                                case 2: /* Requires OP */
                                  {
                                    sprintf(STRG,"%c(*)", TEAL);
                                    break;
                                  }
                                case 3: /* Requires /OP */
                                  {
                                    sprintf(STRG,"%c(!)", RED);
                                    break;
                                  }
                              }
                            tprintf(n->sock,"pline 0   %c/list\xff", RED);
                            tprintf(n->sock,"pline 0       %-4s %cLists available virtual TetriNET channels\xff", STRG, BLACK);
                          }
                        tprintf(n->sock,"pline 0   %c/me %c<action>\xff", RED, BLUE);
                        tprintf(n->sock,"pline 0            Performs an action\xff");
                        if (game.command_move)
                          {
                            STRG[0]=0;
                            switch(game.command_move)
                              {
                                case 2: /* Requires OP */
                                  {
                                    sprintf(STRG,"%c(*)", TEAL);
                                    break;
                                  }
                                case 3: /* Requires /OP */
                                  {
                                    sprintf(STRG,"%c(!)", RED);
                                    break;
                                  }
                              }
                            tprintf(n->sock,"pline 0   %c/move %c<playernumber> <new playernumber>\xff", RED, BLUE);
                            tprintf(n->sock,"pline 0       %-4s %cMoves a player to a new playernumber\xff", STRG,BLACK);
                          }
                    
                        if (game.command_msg)
                          {
                            STRG[0]=0;
                            switch(game.command_msg)
                              {
                                case 2: /* Requires OP */
                                  {
                                    sprintf(STRG,"%c(*)", TEAL);
                                    break;
                                  }
                                case 3: /* Requires /OP */
                                  {
                                    sprintf(STRG,"%c(!)", RED);
                                    break;
                                  }
                              }
                            tprintf(n->sock,"pline 0   %c/msg %c<playernumber(s)> <msg>\xff", RED, BLUE);
                            tprintf(n->sock,"pline 0       %-4s %cPrivately messages player(s)\xff", STRG,BLACK);
                          }
                        if (game.command_op)
                          {
                            tprintf(n->sock,"pline 0   %c/op %c<op_password>\xff", RED, BLUE);
                            tprintf(n->sock,"pline 0            Gain AUTHENTICATED OP status\xff");
                          }
                        if (game.command_who)
                          {
                            STRG[0]=0;
                            switch(game.command_who)
                              {
                                case 2: /* Requires OP */
                                  {
                                    sprintf(STRG,"%c(*)", TEAL);
                                    break;
                                  }
                                case 3: /* Requires /OP */
                                  {
                                    sprintf(STRG,"%c(!)", RED);
                                    break;
                                  }
                              }
                            tprintf(n->sock,"pline 0   %c/who\xff", RED);
                            tprintf(n->sock,"pline 0       %-4s %cLists all logged in players, and what channel they're on\xff", STRG, BLACK);
                          }
                    
                        if (game.command_winlist)
                          {
                            STRG[0]=0;
                            switch(game.command_winlist)
                              {
                                case 2: /* Requires OP */
                                  {
                                    sprintf(STRG,"%c(*)", TEAL);
                                    break;
                                  }
                                case 3: /* Requires /OP */
                                  {
                                    sprintf(STRG,"%c(!)", RED);
                                    break;
                                  }
                              }
                            tprintf(n->sock,"pline 0   %c/winlist %c[n]\xff", RED, BLUE);
                            tprintf(n->sock,"pline 0       %-4s %cDisplays the top n players\xff",STRG,BLACK);
                          }
                      }
                    else
                      tprintf(n->sock,"pline 0 %cYou do NOT have access to that command!\xff",RED);
                  }
                if (valid_param==1)
                  {
                    tprintf(n->sock,"pline 0 %cInvalid /COMMAND!\xff",RED);
                  }
              }
            else
              {
	        net_query_parser("msg %s %d %s #%s %s", n->nick, n->gameslot, n->host, n->channel->name, MSG);
                net_playback_send(n->channel->name, "pline %d %c%s\xff", n->gameslot, BLACK, MSG);

                nsock=n->channel->net;
                while (nsock!=NULL)
                  {
                    if ( (nsock!=n) && (nsock->type == NET_CONNECTED) )
                      {  /* Write line, after first resetting it to black (incase colour still exists from nick) */
                        tprintf(nsock->sock,"pline %d %c%s\xff", n->gameslot, BLACK, MSG); 
                      }
                    nsock=nsock->next;
                  }
              }
            valid_param=1;
          }
      }
      
    /* Party Line Act - plineact <playernumber> <message> */
    if ( !strcasecmp(COMMAND, "plineact") )
      {
        valid_param=2;
        s = sscanf(PARAM, "%d %600[^\n\r]", &num, MSG);
        if ( (s >= 2) && (num==n->gameslot))
          {
            valid_param=1;
            net_query_parser("plineact %s %d %s #%s %s", n->nick, n->gameslot, n->host, n->channel->name, MSG);
            net_playback_send(n->channel->name, "plineact %d %s\xff", n->gameslot, MSG);
            nsock=n->channel->net;
            while (nsock!=NULL)
              {
                if ( (nsock!=n) && (nsock->type == NET_CONNECTED))
                  {  /* Write out action, after first resetting colors */
                    tprintf(nsock->sock,"plineact %d %s\xff", n->gameslot, MSG); 
                  }
                nsock=nsock->next;
              }
          }
      }
      
    
    /* Change Team - team <playernumber> <teamname> */
    if ( !strcasecmp(COMMAND, "team") )
      {
        valid_param=2;
        s = sscanf(PARAM, "%d %600[^\n\r]", &num, MSG);
        if ( (s >= 1) && (num==n->gameslot) && (n->status == STAT_NOTPLAYING))
          {
            valid_param=1;
            /* If it is the same ignore this message */
            if (!strncmp(n->team, MSG, TEAMLEN)) return;
            strncpy(n->team, MSG, TEAMLEN); n->team[TEAMLEN]=0;
            net_query_parser("team %s %d %s #%s %s", n->nick, n->gameslot, n->host, n->channel->name, n->team);
            net_playback_send(n->channel->name, "team %d %s\xff", n->gameslot, n->team);

            nsock=n->channel->net;
            while (nsock!=NULL)
              {
                if ( (n!=nsock) && (nsock->type == NET_CONNECTED) )
                  tprintf(nsock->sock,"team %d %s\xff", n->gameslot, MSG); 
                nsock=nsock->next;
              }
          }
      }
      
    /* Pause Game - pause <0|1> <playernumber> */
    if ( !strcasecmp(COMMAND, "pause") )
      {
        valid_param=2;
        s = sscanf(PARAM, "%d %d", &num, &num2);
        if ( (s >= 2) && is_op(n) && (num2==n->gameslot) && (n->channel->status == STATE_PAUSED || n->channel->status == STATE_INGAME) && ( (num==0)||(num==1)) )
          {
            if (num==1) {
              net_query_parser("pause %s %d %s #%s", n->nick, n->gameslot, n->host, n->channel->name);
	    }
            else {
              net_query_parser("unpause %s %d %s #%s", n->nick, n->gameslot, n->host, n->channel->name);
            }

            net_playback_send(n->channel->name, "pause %d\xff", num);

            valid_param=1;
            nsock=n->channel->net;
            while(nsock!=NULL)
              {
                if (nsock->type == NET_CONNECTED)
                  tprintf(nsock->sock,"pause %d\xff", num);
                nsock=nsock->next;
              }
            if (num==1)
              n->channel->status=STATE_PAUSED;
            else
              n->channel->status=STATE_INGAME;
          }
      }
      
    /* Game message - gmsg <message> */
    if ( !strcasecmp(COMMAND, "gmsg") )
      {
        valid_param=1;
		n->timeout_outgame = game.timeout_outgame;
        /* If it's just "t", then reply PONG to player only*/
        sprintf(MSG,"<%s> t", n->nick);
        if ( !strcmp(MSG, PARAM) && n->channel->pingintercept)
          {
            tprintf(n->sock,"gmsg * PONG\xff");
          }
        else
          {
            /* FIRST, get the NICK, and strip out color codes. Else it looks horrible */
            if (strlen(PARAM) < 5) return;
            if (PARAM[0] == '*') {
                P = strchr(PARAM + 2, ' ');
                sprintf(MSG, "* %s", n->nick);
            }
            else {
                P = strchr(PARAM, ' ');
                sprintf(MSG, "<%s>", n->nick);
            }
            if (!P) return;
            sprintf(STRG, "%s%s", MSG, P);
            strcpy(PARAM, STRG);
            j = strlen(PARAM);
            MSG[0]=0;
            P=MSG;
            for(i=0;i<j;i++)
            {
                    if ( n->channel->stripcolour
                      && (PARAM[i] != BOLD)
                      && (PARAM[i] != CYAN)
                      && (PARAM[i] != BLACK)
                      && (PARAM[i] != BLUE)
                      && (PARAM[i] != DARKGRAY)
                      && (PARAM[i] != MAGENTA)
                      && (PARAM[i] != GREEN)
                      && (PARAM[i] != NEON)
                      && (PARAM[i] != SILVER)
                      && (PARAM[i] != BROWN)
                      && (PARAM[i] != NAVY)
                      && (PARAM[i] != VIOLET)
                      && (PARAM[i] != RED)
                      && (PARAM[i] != ITALIC)
                      && (PARAM[i] != TEAL)
                      && (PARAM[i] != WHITE)
                      && (PARAM[i] != YELLOW)
                      && (PARAM[i] != UNDERLINE)
                      && (PARAM[i] != 11)
                      && (PARAM[i] != 127) )
                      {
                        *P = PARAM[i];
                        P++;
                      }
              }
            *P='\0';
            net_query_parser("gmsg %s %d %s #%s %s", n->nick, n->gameslot, n->host, n->channel->name, MSG);
            net_playback_send(n->channel->name, "gmsg %s\xff", PARAM);
            nsock=n->channel->net;
            while (nsock!=NULL)
              {
                if ( (nsock->type == NET_CONNECTED) )
                  {
                      tprintf(nsock->sock,"gmsg %s\xff", MSG);
                  }
                nsock=nsock->next;
              }
          }

      }
      
    /* Player Lost - playerlost <playernumber> */
    if ( !strcasecmp(COMMAND, "playerlost") )
      {
        valid_param=2;
        s = sscanf(PARAM, "%d %600[^\n\r]", &num, MSG);
        if ( (s >= 1) && ( (n->channel->status == STATE_INGAME) || (n->channel->status == STATE_PAUSED) ) && (num==n->gameslot) )
          {
            valid_param=1;
            /* Now, is this player actually playing? If not, we just ignore them */
            if (n->status == STAT_PLAYING)
              {
                /* Set player to be "Lost" */
                n->status = STAT_LOST;
                
                num2=0;    /* Assume no-one left playing */
                num3=0;	/* Player who is still in */
                MSG[0]=0;  /* Store playing team name here */
                /* Check if game is finished, and tell each person this player has lost */
                net_playback_send(n->channel->name, "playerlost %d\xff", num);
                nsock=n->channel->net;
                ns1=NULL;
                while (nsock!=NULL)
                  {
                    if ( (nsock!=n) && (nsock->type == NET_CONNECTED) && (nsock->status == STAT_PLAYING))
                      {
                        tprintf(nsock->sock,"playerlost %d\xff",num);
                        if (strcasecmp(MSG,nsock->team))
                          { /* Different team, so add player */
                            num2++;
                            ns1=nsock;
                            strcpy(MSG,nsock->team);
                          }
                        else if (MSG[0]==0) 
                          {
                            num2++;
                            ns1=nsock;
                          }
			num3++;
                      }
                    nsock=nsock->next;
                  }
		if (ns1 == NULL) {
		    /* This happens when we play alone */
		    n->channel->status=STATE_ONLINE;
		    tprintf(n->sock, "endgame\xff");
                    net_playback_send(n->channel->name, "endgame\xff");
                    n->status=STAT_NOTPLAYING;
		    return;
		}
		   
                if ( (num2 <= 1) && (ns1!=NULL) )
                  { /* 1 or less different teams playing. Stop the game, and take score */
                    n->channel->status=STATE_ONLINE;
                    nsock=n->channel->net;

                    while (nsock!=NULL)
                      {
                        if ( (nsock->type == NET_CONNECTED))
                          {
                            tprintf(nsock->sock,"endgame\xff");
                            tprintf(nsock->sock,"playerwon %d\xff", ns1->gameslot);
                            nsock->status=STAT_NOTPLAYING;
                            /* Send every playing field */
                            
                            ns2=n->channel->net;
                            while (ns2!=NULL)
                              {
                                if ( (ns2!=nsock) && (ns2->type == NET_CONNECTED))
                                  sendfield(nsock,ns2);
                                ns2=ns2->next;
                              }
                        
                          }
                        nsock=nsock->next;
                      }

                    net_playback_send(n->channel->name, "endgame\xff");
                    net_playback_send(n->channel->name, "playerwon %d\xff", ns1->gameslot);
                    if (ns1->type == NET_CONNECTED)
                      {
			char wteam[TEAMLEN+1], w2team[TEAMLEN+1];
			char *p;
			strncpy(wteam, ns1->team, TEAMLEN);
			wteam[TEAMLEN] = '\0';
			strncpy(w2team, n->team, TEAMLEN);
			w2team[TEAMLEN] = '\0';
			p = strstr(wteam, "::");
			if (p) *p = '\0';
			p = strstr(w2team, "::");
			if (p) *p = '\0';
			if (!strcasecmp(n->channel->game_type , "nickonly")) {
				wteam[0] = '\0'; 
				w2team[0] = '\0'; 
			}
                        if (strlen(wteam) > 0)
                          { /* Team won, so add score to team */
                            if (n->channel->serverannounce)
				for(nsock=n->channel->net; nsock; nsock=nsock->next)
				if (nsock->type == NET_CONNECTED)
                                    tprintf(nsock->sock,"pline 0 ---- Team %s%c WON ----\xff", ns1->team, BLACK);

                            net_playback_send(n->channel->name, "pline 0 ----  Team %s%c WON ----\xff", ns1->team, BLACK);

			    if (n->channel->winlist_file != -1 ) {
			    if (n->flood_players > 2) 
                            updatewinlist(wteam,'t',3,ns1);
			    else
                            updatewinlist(wteam,'t',2,ns1);
			    net_query_parser("teamwon %s #%s", wteam, n->channel->name);
		    }
                          }
                        else
                          { /* Player won, so add score to player name */
                            if (n->channel->serverannounce && !ns1->teamup)
				for(nsock=n->channel->net; nsock; nsock=nsock->next)
				if (nsock->type == NET_CONNECTED)
                                    tprintf(nsock->sock,"pline 0 ---- Player %s%c WON ----\xff", ns1->nick, BLACK);

                            net_playback_send(n->channel->name, "pline 0 ----  Player %s%c WON ----\xff", ns1->nick, BLACK);

			    if (n->channel->winlist_file != -1 && !ns1->teamup) { 
			    if (ns1->flood_players > 2) 
                            updatewinlist(ns1->nick,'p',3,ns1);
			    else
                            updatewinlist(ns1->nick,'p',2,ns1);
			    net_query_parser("playerwon %s #%s", ns1->nick, n->channel->name);
			    }
                          }
			if (n->flood_players > 4 && n->channel->winlist_file != -1) 
			  {
			    if (strlen(w2team) > 0)
			    	updatewinlist(w2team, 't', 1, n);
			    else 
				if (!n->teamup)
					updatewinlist(n->nick, 'p', 1, n);
			  }
                      }
                    writewinlist(n);
                    sendwinlist_to_all(n);	/* Send to all */
                
                  }
              
              } 
            
          }
      }
      
    /* Field Update - f <field codes> */
    if ( !strcasecmp(COMMAND, "f") )
      {
        valid_param=2;
        s = sscanf(PARAM, "%d %600[^\n\r]", &num, MSG);
        if ( (s >= 1) && (num==n->gameslot))
          {

            net_playback_send(n->channel->name, "f %d %s\xff", n->gameslot, MSG);
            valid_param=1;
            /* First, transmit these changes as is to all other players */
            nsock=n->channel->net;
	    n->field_changes++;
	    n->max_pieces_left++;
	
            /* Now parse it ourselves, and update our internal knowledge of players field */
            if (parsefield(n, MSG) == -1) {
                tprintf(n->sock, "pline 0 %cBroken client detected.  Attempt logged...\xff", RED);
		lvprintf(4, "Broken client from %s (%s)\n", n->nick, n->host); 
                killsock(n->sock); lostnet(n);
                return;
            }

            while (nsock!=NULL)
              {
                if ( (nsock!=n) && (nsock->type == NET_CONNECTED))
                  tprintf(nsock->sock,"f %d %s\xff", n->gameslot, MSG); 
                nsock=nsock->next;
              }
          }
      }
      
    /* Level - lvl <playernumber> <current level> */
    if ( !strcasecmp(COMMAND, "lvl") )
      { 
        int mt;
        if (16 % n->channel->lines_per_level)
                mt = 16 / n->channel->lines_per_level + 1;
        else
                mt = 16 / n->channel->lines_per_level;
        valid_param=2;
        s = sscanf(PARAM, "%d %d", &num, &num2);
        if (num2 < 0 || (num2 - n->level < 0) || num2 > n->channel->starting_level + 150 || (num2 - n->level > mt * n->channel->level_increase)) {
                lvprintf(3, "(Possible Cheating) Impossible level update from %s (%s) | req msg: lvl %s | Channel: %s\n", n->nick, n->host, PARAM, n->channel->name);
                killsock(n->sock); lostnet(n);
                return;
        }

        if ( (n->channel->status==STATE_INGAME) && (num==n->gameslot) && (n->status == STAT_PLAYING) )
          {
            valid_param=1;
	    n->level = num2;

            net_playback_send(n->channel->name, "lvl %d %d\xff", n->gameslot, num2);
            nsock=n->channel->net;
            while (nsock!=NULL)
              {
                if ( (nsock->type == NET_CONNECTED))
                  tprintf(nsock->sock,"lvl %d %d\xff", n->gameslot,num2);
                nsock=nsock->next;
              }
          }
      }
    
    /* Special Block Use - sb <to use on,0=all> <special block> <playernum> */
    if ( !strcasecmp(COMMAND, "sb") )
      {
        int k = 0, l = 0;
        valid_param=2;
        s = sscanf(PARAM, "%d %s %d", &num, MSG, &num2);
	if (MSG[0] == 'c' && MSG[1] == 's') {
		l = atoi(&MSG[2]);
		n->lines_add_to_all += l;
		if (l == 1)  k = 1;
		else if (l == 2)  k = 2;
		else if (l == 4)  k = 3;
		else if (n->channel->special_added == 0) {
	                lvprintf(3, "(Probable Cheating) Impossible add_line_to_all from %s (%s) | req msg: %s | Channel: %s | Pieces: %d\n", n->nick, n->host,  PARAM, n->channel->name, n->field_changes);
                        killsock(n->sock); lostnet(n);
                        return;
		}
		else
			return;
		n->max_pieces_left -= ((k+1) * 3);

		if (l == 4) n->tetris_made++;
	}
	else {
		/* someone is doing special on PURE */
		if (n->channel->special_added == 0) {
			lvprintf(3, "(Obvious Cheating) Special block used on PURE from %s (%s) | req msg: %s | channel: %s\n", n->nick, n->host, PARAM, n->channel->name);
			killsock(n->sock); lostnet(n);
			return;
		}
	}

         
        if ( (s >= 3) && (n->channel->status == STATE_INGAME) && (n->status == STAT_PLAYING))
          {
            valid_param=1;
	    if (num2 != n->gameslot) {
            	lvprintf(3, "(Obvious Cheating) Spoof special block message from %s (%s) | Slot: %d | req msg: %s | Channel: %s\n", n->nick, n->host, n->gameslot, PARAM, n->channel->name);
                killsock(n->sock); lostnet(n);
                return;
	    }

            net_playback_send(n->channel->name, "sb %d %s %d\xff", num, MSG, n->gameslot);

            nsock=n->channel->net;
            while (nsock!=NULL)
              {
                if ( (nsock!=n) && (nsock->type == NET_CONNECTED) )
                  {
                    tprintf(nsock->sock,"sb %d %s %d\xff", num, MSG, n->gameslot); 
                  }
                nsock=nsock->next;
              }
          }
      }
      
    /* Start/Stop Game - startgame <0/1, 0=stopgame> <playernumber> */
    if (!strcasecmp(COMMAND, "startgame"))
      {
        int flood_players = 0;
	int flood_channels = 0;
	int top_players = 0;
	struct net_t *nsock2;
	char newgamestr[2048];
	int newgameprinted = 0;
        valid_param=2;
        s = sscanf(PARAM, "%d %d %600[^\n\r]", &num, &num2, MSG);
        if ( (s >= 2) && is_op(n) && ( ((num==1) && (n->channel->status == STATE_ONLINE)) || ((num==0) && (n->channel->status == STATE_INGAME)) ||((num==0) && (n->channel->status == STATE_PAUSED))) && (num2==n->gameslot) && ( (num == 1) || (num == 0)) )
          {
            valid_param=1;
            if (num==1)
              {
                n->channel->status = STATE_INGAME;
		net_query_parser("gamestart %s %d %s #%s", n->nick, n->gameslot, n->host, n->channel->name);
              }
            else
              {
                n->channel->status = STATE_ONLINE;
                n->channel->sd_mode=SD_NONE;
		net_query_parser("gamestop %s %d %s #%s", n->nick, n->gameslot, n->host, n->channel->name);
                net_playback_send(n->channel->name, "endgame\xff");
              }
              
            /* Read in Game config and winlist, in case they were changed */
/*            gameread();*/
            /* readwinlist(); */
            
            /* Set the SuddenDeath timeout, if any */
            if (n->channel->sd_timeout>0)
              {
                n->channel->sd_mode=SD_INIT;
                n->channel->sd_timeleft=n->channel->sd_timeout;
              }
            nsock=n->channel->net;
	    flood_players = numplayers(nsock->channel);
	    for (chan = chanlist; chan; chan = chan->next)
			if (chan->status == STATE_INGAME || chan->status == STATE_PAUSED)
				flood_channels++;
	    for (nsock = n->channel->net; nsock; nsock=nsock->next)
		nsock->teamup = 0;
	    for (nsock = n->channel->net; nsock; nsock=nsock->next) {
		if (nsock->top_player == 1) top_players++;
		if (strlen(nsock->team) != 0)
		for (nsock2 = nsock->next; nsock2; nsock2=nsock2->next) {
			if (nsock!=nsock2 && !strcasecmp(nsock->team, nsock2->team)) {
				nsock->teamup = 1;
				nsock2->teamup = 1;
			}
		}
	    }
		
	    nsock=n->channel->net;
	    while (nsock!=NULL)
              {/* Send to every player the game parameters */
                if ( (nsock->type == NET_CONNECTED))
                  {
                    if (num == 1) /* Start game */
                      {
                        sprintf(newgamestr,"newgame 0 %d %d %d %d %d %d ", 
                                  n->channel->starting_level, n->channel->lines_per_level,
                                  n->channel->level_increase, n->channel->lines_per_special,
                                  n->channel->special_added, n->channel->special_capacity);
                        for(j=0;j<n->channel->block_leftl;j++) strcat(newgamestr,"3");
                        for(j=0;j<n->channel->block_leftz;j++) strcat(newgamestr,"5");
                        for(j=0;j<n->channel->block_square;j++) strcat(newgamestr, "2");
                        for(j=0;j<n->channel->block_rightl;j++) strcat(newgamestr, "4");
                        for(j=0;j<n->channel->block_rightz;j++) strcat(newgamestr, "6");
                        for(j=0;j<n->channel->block_halfcross;j++) strcat(newgamestr, "7");
                        for(j=0;j<n->channel->block_line;j++)  strcat(newgamestr, "1");
                        strcat(newgamestr, " ");
                        for(j=0;j<n->channel->special_addline;j++) strcat(newgamestr, "1");
                        for(j=0;j<n->channel->special_clearline;j++) strcat(newgamestr, "2");
                        for(j=0;j<n->channel->special_nukefield;j++) strcat(newgamestr, "3");
                        for(j=0;j<n->channel->special_randomclear;j++) strcat(newgamestr, "4");
                        for(j=0;j<n->channel->special_switchfield;j++) strcat(newgamestr,"5");
                        for(j=0;j<n->channel->special_clearspecial;j++) strcat(newgamestr,"6");
                        for(j=0;j<n->channel->special_gravity;j++) strcat(newgamestr, "7");
                        for(j=0;j<n->channel->special_quakefield;j++) strcat(newgamestr,"8");
                        for(j=0;j<n->channel->special_blockbomb;j++) strcat(newgamestr,"9");
                        sprintf(newgamestr ,"%s %d %d\xff", newgamestr, n->channel->average_levels, n->channel->classic_rules);
			tprintf(nsock->sock, "%s", newgamestr);

                        if (!newgameprinted) {
                                newgameprinted = 1;
                                net_playback_send(n->channel->name,"%s", newgamestr);
                        }
                        
                        /* And set them to be playing */
                        nsock->status = STAT_PLAYING;
                        
			/* Update flood parameters */
			nsock->flood_players = flood_players;
			nsock->flood_channels = flood_channels;
			nsock->lines_add_to_all = 0;
			nsock->tetris_made = 0;
			nsock->field_changes = 0;
			nsock->max_pieces_left = 0;
			nsock->level = nsock->channel->starting_level;
                        
                        /* BLANK *ALL* the fields and send them to each player */
                        /* Clear their Field */
                        for(y=0;y<FIELD_MAXY;y++)
                          for(x=0;x<FIELD_MAXX;x++)
                            nsock->field[x][y]=0; /* Nothing */
                                
                        /* Send to every other player */
/*
                        ns1=n->channel->net;
                        while (ns1!=NULL)
                          {
                            if ( (ns1!=nsock) && (ns1->type==NET_CONNECTED) )
                              {
                                sendfield(ns1,nsock);
                              }
                            ns1=ns1->next;
                          }
*/

                      }
                    else  /* End Game */
                      {
                        tprintf(nsock->sock,"endgame\xff");

                        /* And set them to not be playing */
                        nsock->status = STAT_NOTPLAYING;
                      }
                  }
                nsock=nsock->next;
              }
          }
      }
    
      
      
    /* Log invalid params */
    if (valid_param==0)
      lvprintf(1,"#%s-%s - Invalid Command - %s\n", n->channel->name,n->nick, buf);
  }

/* The player has sent their inital team name, well they should have anyway */
void net_waitingforteam(struct net_t *n, char *buf)
  {
    FILE *file_in;
    char strg[1024];
    char *P;
    struct net_t *nsock;
    
    sprintf(strg,"team %d ",n->gameslot);
    if (strncmp(buf,strg,strlen(strg)))
      { /* Incorrect, Kill this player, they never existed ;) */
        lvprintf(1,"Incorrect TEAM statement - %s!\n",buf);
        killsock(n->sock); lostnet(n);
        return;
      }

    
    P=buf+strlen(strg);
    n->status=STAT_NOTPLAYING;
    n->flood_players=0;
    n->flood_channels=0;
    n->wlist_cache = -1;
    n->authenticate = 0;
    n->field_changes = 0;
    n->max_pieces_left = 0;
    n->timeout_outgame = game.timeout_outgame;
    n->timeout_ingame = game.timeout_ingame;
    strncpy(n->team, P, TEAMLEN); n->team[TEAMLEN]='\0';
    if (n->team[0] != 0 && !net_query_isvalidnick(n->team)) {
	lvprintf(4,"Invalid team name %s-%s\n",n->team,n->host);
	killsock(n->sock); lostnet(n);
	return;
    }

    if (is_banned(n))
      { 
        tprintf(n->sock,"noconnecting Your host is banned from this server.\xff");
        killsock(n->sock);
	lostnet(n);
        return;
      }

    n->type=NET_CONNECTED;
    nsock=n->channel->net;
    net_query_parser("playerjoin %s %d %s #%s", n->nick, n->gameslot, n->host, n->channel->name);
    net_playback_send(n->channel->name, "playerjoin %d %s\xffteam %d %s\xff", n->gameslot, n->nick, n->gameslot, n->team);

    while (nsock!=NULL)
      {
       /* if ( (nsock != n) && (nsock->type==NET_CONNECTED)) */
       if ((nsock->type==NET_CONNECTED)) 
          { 
            /* Send each other player and their team to this player */
	    if (n != nsock)
            tprintf(n->sock, "playerjoin %d %s\xffteam %d %s\xff", nsock->gameslot, nsock->nick, nsock->gameslot, nsock->team);
          
            /* Send to the other player, this player and their team */
            tprintf(nsock->sock, "playerjoin %d %s\xffteam %d %s\xff", n->gameslot, n->nick, n->gameslot, n->team);
          }
        nsock=nsock->next;
      }
    
    tprintf(n->sock, "pline 0 %c%c%c%c%c%c%c%c\xff",
        BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK);
    /* Now for the game.motd if it exists. */
    file_in = fopen(FILE_MOTD,"r");
    if (file_in != NULL)
      {/* Exists, so send it to the player */
        while (fgets(strg, 256, file_in) != NULL)
          { 
            net_query_TrimStr(strg);
            tprintf(n->sock,"pline 0 %s\xff", strg);
          }
        fclose(file_in);
      }
      
    /* If game is in progress, send all other players fields */
    /* Tell each other player that we are lost... no really :P */
    if( (n->channel->status == STATE_INGAME) || (n->channel->status == STATE_PAUSED) )
      {
        nsock=n->channel->net;
        while (nsock!=NULL)
          {
            if( (nsock->type==NET_CONNECTED) &&(nsock!=n) )
              {
                /* Each player who has lost, send player_lost */
                if (nsock->status != STAT_PLAYING)
                  {
                    tprintf(n->sock,"playerlost %d\xff", nsock->gameslot);
                  }
                sendfield(n, nsock); /* Send to player, this players field */

                /* And tell this player that the newjoined player has lost */
                tprintf(nsock->sock,"playerlost %d\xff", n->gameslot);
              }
            nsock=nsock->next;
          }
      }
      
    /* If we are ingame, then tell this person that we are! */
    if ( (n->channel->status == STATE_INGAME) || (n->channel->status == STATE_PAUSED) )
      tprintf(n->sock,"ingame\xff");
      
    /* If we are currently paused, kindly let them know that fact to */
    if ( n->channel->status == STATE_PAUSED )
      tprintf(n->sock,"pause 1\xff");  
    
    /* And tell them their channel */
    tprintf(n->sock,"pline 0 %c%s%c %chas joined channel #%s\xff", GREEN,n->nick,BLACK,GREEN,n->channel->name);
    
    lvprintf(3,"%s(%s)(%s)\n", n->nick,n->ip,n->host);
  }

/*------------------------- query section ------------------------------ */

int net_query_lostnet(struct net_t *n)
{
	struct net_t *nn;
        if (n->type == NET_PLAYBACK_CONNECTED) {

/* Playback */
    nn = gnet;
    for (nn = gnet; nn; nn = nn->next) {
        if (nn->type == NET_PLAYBACK_CONNECTED &&
	    !strcasecmp(n->channel->name, nn->channel->name) && nn != n) {
                tprintf(nn->sock, "specleave %s\xff", n->nick);
        }
    }   

/* Query clients */
    if (n->type == NET_PLAYBACK_CONNECTED)
    	net_query_parser("specleave %s %d %s #%s", n->nick, 0, n->host, n->channel->name);  

        }

        else if (n->type == NET_QUERY_CONNECTED) {
                nn = gnet;
                for (nn = gnet; nn; nn = nn->next) {
                        if (nn->type == NET_QUERY_CONNECTED && nn != n) {
				tprintf(nn->sock, ":%s NOTICE %s :*** Signoff: %s (query@%s)\n", myhostname, nn->nick, n->nick, n->host);
                        }
                }
        }

	nn = gnet;
	while (nn && nn->next != n)
		nn = nn->next;
	if (nn && (nn->next == n))
		nn->next = n->next;
	free(n);
        return 1;
}

int net_query_playerquery(struct net_t *n, char *buf)
{
	struct channel_t *chan;
	struct net_t *nsock;
	int total_user;

        lvprintf(9,"%s: Playerquery request\n",n->host);
	total_user = 0;
	chan = chanlist;
	while (chan) {
		nsock = chan->net;
		while (nsock) {
                	if (nsock->gameslot >= 1 && nsock->gameslot <= 6)
				total_user++;
			nsock = nsock->next;
		}
	    chan = chan->next;
	}
        tprintf(n->sock, "[Server_id: %d-%d] [Max_channels: %d] [Total_users: %d] [Curchan_users: %d]\n", getpid(), getppid(), game.maxchannels, total_user, numplayers(n->channel));
	killsock(n->sock); net_query_lostnet(n);
        return 1;
}

int net_query_IPconvert(struct net_t *n, char *host)
{
	unsigned char x1, x2, x3, x4;
	IP ip;
	ip = n->addr;

	x1 = (ip >> 24);
	x2 = (ip >> 16) & 0xff;
        x3 = (ip >> 8)  & 0xff;
        x4 = (ip)       & 0xff;
	if (n->host[0] == 0 || strlen(n->host) >= UHOSTLEN)
		sprintf(host, "%d.%d.%d.%d",x1,x2,x3,x4);
	else
		strcpy(host, n->host);
	return 1;
}

/* Trim weird useless chars at the end */
int net_query_TrimStr(char *p)
{
	int len;
	if (!p) return 0;     /* P is NULL */
	if (p[0] == '\0')
		return 1;
	for (len = strlen(p)-1; len >= 0; len--) {
		if (p[len] == '\n' || p[len] == 13 || p[len] == ' ') 
			p[len] = '\0';
		else 
			break;
	}
	return 1;
}

/* Strip Control characters (warning: direct change to orig str) */
int net_query_StripCtrlStr2(char *p)
{
        char *q, *r;
        if (!p) return 0;
	r = p;
        for (q = r; *r != '\0'; r++)
                if (*r >= 32) *(q++) = *r;
        *q = '\0';
	if (p[0] == '\0') strcpy(p, "unknown");
	return 1;
}

int net_query_StripCtrlStr(char *p)
{
        char *q, *r;
        if (!p) return 0;
        r = p;
        for (q = r; *r != '\0'; r++)
                if ((*r >= 'A' && *r <= '}') ||
                    (*r == '_') ||
                    (*r == '-') ||
                    (*r == '~') ||
                    (*r >= '0' && *r <= '9')) *(q++) = *r;
        *q = '\0';
        if (p[0] == '\0') strcpy(p, "unknown");
        return 1;
}


int net_query_isvalidnick(char *p)
{
        char *r;
	int valid=0;
        if (!p) return 1;
	if (*p == '\0') return 0;
        for (r = p; *r != '\0'; r++) {
                if (*r == ' ' || *r == '\xff' || *r == 10 || *r == 13) {
			valid = 0;
			break;
		}
                if (*r > 32 || *r < 0) valid = 1;
	}
	return(valid);
}


int net_query_ChanStatStr(int status, char *statstr) { 

	if (status == STATE_OFFLINE)
                strcpy(statstr, "Offline");
        else if (status == STATE_ONLINE)
                strcpy(statstr, "Not in game");
        else if (status == STATE_INGAME)
                strcpy(statstr, "In game");
        else if (status == STATE_PAUSED)
                strcpy(statstr, "Paused");
        else
                strcpy(statstr, "N/A");
	return 1;
}

/* USER COMMAND */
int net_query_user(struct net_t *n, char *buf) {
	tprintf(n->sock, "NOTICE AUTH :To Log in, type \"/QUOTE pass server_password\"\n");
	return 1;
}

int net_query_nickfound(char *nick, struct net_t *n) {
	struct net_t *nn;
	struct channel_t *chan;

	if (!nick) return 0;
        for (chan = chanlist; chan; chan = chan->next)
                for (nn = chan->net; nn; nn = nn->next)
                        if (nn != n && (nn->type == NET_CONNECTED ||
                            nn->type == NET_WAITINGFORTEAM) &&
                             !strcasecmp(nick, nn->nick)) return 1;
        for (nn = gnet; nn; nn = nn->next)
                if (nn != n && (nn->type == NET_QUERY_CONNECTED ||
                     nn->type == NET_PLAYBACK_CONNECTED ||
                     nn->type == NET_PLAYBACK_INIT ||
                     nn->type == NET_QUERY_INIT) &&
                     !strcasecmp(nick, nn->nick)) return 1;
	return 0;
}

/* NICK COMMAND */
int net_query_nick(struct net_t *n, char *buf)
{
	char *p;
	p = strtok(buf, " ");
	p = strtok(NULL, " ");
	net_query_TrimStr(p);
	if (p && (p[0] != '\0')) {
		if (p[0] == ':') p++;
		if (!net_query_nickfound(p, n)) {
			strncpy(n->nick, p, NICKLEN);
			n->nick[NICKLEN] = 0;
		}
		else
			tprintf(n->sock, ":%s 433 * %s :Nickname is already in use.\n", myhostname, p);
	}
	else
		n->nick[0] = '\0';
	return 1;
}

/* This looks scary mmm */
/* MOTD */
int net_query_cmotd(struct net_t *n, char *buf) {
	FILE *file_in;
	char strg[1024];
        file_in = fopen(FILE_MOTD,"r");
        if (file_in != NULL) {
        	while (fgets(strg, 512, file_in) != NULL)
          	{ 
            	net_query_TrimStr(strg);
	    		tprintf(n->sock,":%s 372 %s :%s\n", myhostname, n->nick, strg);
          	}
			tprintf(n->sock,":%s 376 %s :End of /MOTD command\n", myhostname, n->nick);
          	fclose(file_in);
	    }
		return 1;
}

/* STATS w(winlist)  p(operlist)  k(banlist) */
int net_query_cstats(struct net_t *n, char *buf)
{
	/* int i; */
    	char *p;
	struct net_t *nn;

	p = strtok (buf, " ");
	p = strtok (NULL, " ");
	if (p) {
		if (p[0] == 'w' || p[0] == 'W') {
    			/* Winlist - Returns Winlist */
/*
    			tprintf(n->sock,"NOTICE AUTH :Nickname          Type      Score\n");
    			for(i=0;i<MAXWINLIST;i++) {
				if(winlist[i].inuse)
                			tprintf(n->sock,"NOTICE AUTH :%-17s  %c          %lu\n", winlist[i].name,winlist[i].status,winlist[i].score);
    			}
    			tprintf(n->sock,"NOTICE AUTH :End of WinList.\n");
*/
		}
		else if (p[0] == 'o' || p[0] == 'O' || p[0] == 'p') {
			/* Oper list */
			tprintf(n->sock, "NOTICE AUTH :Current server operators: ");
			for (nn = gnet; nn; nn = nn->next)
				if (nn->type == NET_QUERY_CONNECTED)
					tprintf(n->sock, "%s ", nn->nick);
			tprintf(n->sock, "\n");
		}
		else if (p[0] == 'k' || p[0] == 'K' || p[0] == 'b') {
			/* Ban list */
			FILE *file_in;
			char cbuf[256];
    			file_in = fopen(FILE_BAN,"r");
    			if (file_in) {
				while (fgets(cbuf, sizeof(cbuf)-1, file_in) !=  NULL)
					tprintf(n->sock, "NOTICE AUTH :%s", cbuf);
				fclose(file_in);
			}
			tprintf(n->sock, "NOTICE AUTH :End of ban list\n");
		}
		else
			tprintf(n->sock, "NOTICE AUTH :Invalid Stats request\n");
	}
		
	return 1;
}

/* Check /quote pass password print welcome messages */
int net_query_authenticate(struct net_t *n, char *buf)
{
	char *p; 
	char strg[10];
	time_t tm;
	struct net_t *nn;

        p = strtok(buf, " ");
        p = strtok(NULL, " ");
	if (!net_query_TrimStr(p)) {
                killsock(n->sock); net_query_lostnet(n);
                return 1;
	}

	if (n->nick[0] == '\0')
		strcpy(n->nick, "unknown");
        if (securityread() < 0)
                securitywrite();

        if ((strlen(security.query_password) <= 0) ||
             strcmp(p, security.query_password)) {

		tprintf(n->sock, "NOTICE AUTH :authentication failed... disconnecting\n", myhostname, n->nick);
		tprintf(n->sock, "NOTICE AUTH :Type \"/server serverhost %d\"  to reconnect\n", QUERY_PORT);
                killsock(n->sock); net_query_lostnet(n);
                return 1;
        }
        n->type = NET_QUERY_CONNECTED;
	n->securitylevel = LEVEL_AUTHOP;
	time(&tm);
	tprintf(n->sock, ":%s 001 %s :Welcome to Tetrinet Relay Network %s\n", myhostname, n->nick, n->nick);
	tprintf(n->sock, ":%s 002 %s :Your host is %s, running version 2.8\n", myhostname, n->nick, myhostname);
	tprintf(n->sock, ":%s 003 %s :Server time is %s\n", myhostname, n->nick, ctime(&tm));
	tprintf(n->sock, ":%s 004 %s %s bundaberg tetrinet server w/ slummy irc connection\n", myhostname, n->nick, myhostname);

        /* Now for the game.motd if it exists. */
	strcpy(strg, "motd");
	net_query_cmotd(n, strg);

	tprintf(n->sock,"NOTICE AUTH :For HELP type \"/QUOTE help\"\n");
	for (nn = gnet; nn; nn = nn->next)
		if (nn->type == NET_QUERY_CONNECTED)
			tprintf(nn->sock, ":%s NOTICE %s :*** Notice -- %s (query@%s) is now a server operator(O)\n", myhostname, nn->nick, n->nick, n->host);

	tprintf(n->sock, ":%s MODE %s :+owszyd\n", n->nick, n->nick);
	tprintf(n->sock, ":%s 381 %s :You have entered... the Twilight Zone!.\n", myhostname, n->nick);

	strcpy(strg, "stats p");
	net_query_cstats(n, strg);
	return 1;
}

/* PONG */
int net_query_cping(struct net_t *n, char *buf) {
/*
	char *p;
	p = strtok(buf, " ");
	p = strtok(NULL , "");
	if (p)
		tprintf(n->sock, ":%s PONG %s %s\n", myhostname, n->host, p);
*/
	return 1;
}

/* LIST */
int net_query_clist(struct net_t *n, char *buf) {
	struct channel_t *chan;
	char chan_status[40];
	int nusers, nchans;

	nusers = 0; nchans = 0;
	tprintf(n->sock, "NOTICE AUTH :#Channel        Users   Pri  Status       Topic\n");
        for (chan = chanlist; chan; chan = chan->next) {
		net_query_ChanStatStr(chan->status, chan_status);
                tprintf(n->sock, "NOTICE AUTH :#%-15s  %-2d    %-3d  %-12s %s\n", chan->name, numplayers(chan), chan->priority, chan_status, chan->description);
		nusers += numplayers(chan);
		nchans ++;
        }
        tprintf(n->sock, "NOTICE AUTH :Total: %d users in %d channels\n", nusers, nchans);
        tprintf(n->sock,":%s 323 %s :End of /LIST\n", myhostname, n->nick);
        return 1;
}

int net_query_cslist(struct net_t *n, char *buf) {
	struct net_t *nn;
	int i;

        tprintf(n->sock, "NOTICE AUTH :Spectator List\n");
	i = 0;
	for (nn=gnet; nn; nn = nn->next)
		if (nn->type == NET_PLAYBACK_CONNECTED)
			tprintf(n->sock, "NOTICE AUTH :#%d: #%-20s  %s\n", ++i, nn->channel->name, nn->nick);
        tprintf(n->sock,":%s 323 %s :End of /SLIST\n", myhostname, n->nick);
        return 1;
}


/* WHOIS */
int net_query_cwhois(struct net_t *n, char *buf) {
	struct channel_t *chan;
	struct net_t *nsock;
        int idle, argc, found;
	char nick[NICKLEN+1], host[UHOSTLEN+1], team[TEAMLEN+1];
	char *p;

	argc = 0;
	found = 0;
        p = strtok(buf, " ");
        p = strtok(NULL, " ");
	net_query_TrimStr(p);
	if (!p || (p && p[0] =='\0')) {
		tprintf(n->sock, ":%s 431 %s :No nickname given\n", myhostname, n->nick);
		return -1;
	}
	/* strcpy(nick, p); */
        chan = chanlist;
        for (chan = chanlist; chan; chan = chan->next) {
                for (nsock = chan->net; nsock; nsock = nsock->next) {
                	strcpy(nick, nsock->nick);
                	net_query_StripCtrlStr(nick);
			if (nsock->type == NET_CONNECTED ||
			    nsock->type == NET_WAITINGFORTEAM) {
				if (nsock->gameslot < 1 || nsock->gameslot > 6 || strcasecmp(nick, p)) 
					continue;
				else {
					net_query_IPconvert(nsock, host);
    					if (nsock->status == STAT_PLAYING)
					idle = game.timeout_ingame - nsock->timeout_ingame;
					else
					idle = game.timeout_outgame - nsock->timeout_outgame;
                                        if (strlen (nsock->team) == 0)
                                                strcpy(team, "No team");
                                        else
                                                strcpy(team, nsock->team);

                                	tprintf(n->sock, ":%s 311 %s %s slot%d %s * :%s\n", myhostname, n->nick, nick, nsock->gameslot , host, team);
					if (nsock->securitylevel == LEVEL_AUTHOP)
                                	tprintf(n->sock, ":%s 319 %s %s :@#%s\n", myhostname, n->nick, nick, nsock->channel->name);
					else
                                	tprintf(n->sock, ":%s 319 %s %s :#%s\n", myhostname, n->nick, nick, nsock->channel->name);
                                	tprintf(n->sock, ":%s 312 %s %s %s :Tetrinet Server\n", myhostname, n->nick, nick, myhostname);
                                	tprintf(n->sock, ":%s 317 %s %s %d :seconds idle\n", myhostname, n->nick, nick, idle);
                                	tprintf(n->sock, ":%s 318 %s %s :End of /WHOIS list.\n", myhostname, n->nick, nick);
					found = 1;
					break;
				}
                       }
		}
	}
	/* Check for query clients */
	for (nsock = gnet; nsock; nsock = nsock->next) {
		if ((nsock->type == NET_QUERY_CONNECTED || nsock->type == NET_PLAYBACK_CONNECTED) && !strcasecmp(nsock->nick, p))  {
			net_query_IPconvert(nsock, host);
			if (nsock->type == NET_QUERY_CONNECTED)
                		tprintf(n->sock, ":%s 311 %s %s query %s * :query client\n", myhostname, n->nick, nsock->nick, host);
			else 
                		tprintf(n->sock, ":%s 311 %s %s spectator %s * :spectator client\n", myhostname, n->nick, nsock->nick, host);
			if (strlen(nsock->channel->name))
                                tprintf(n->sock, ":%s 319 %s %s :@#%s\n", myhostname, n->nick, nsock->nick, nsock->channel->name);
                        tprintf(n->sock, ":%s 312 %s %s %s :Tetrinet Server\n", myhostname, n->nick, nsock->nick, myhostname);
			if (nsock->type == NET_QUERY_CONNECTED)
                        tprintf(n->sock, ":%s 313 %s %s :is an IRC Janitor (IRC Operator)\n", myhostname, n->nick, nsock->nick);
                        	tprintf(n->sock, ":%s 318 %s %s :End of /WHOIS list.\n", myhostname, n->nick, nsock->nick);
			found = 1;
			break;
		}
	}
	
        if (!found) {
		tprintf(n->sock, ":%s 401 %s %s :No such nick/channel\n", myhostname, n->nick, p);
		tprintf(n->sock, ":%s 318 %s %s :End of /WHOIS list.\n", myhostname, n->nick, p);
	}
	return 1;
}
	
/* WHO and NAMES */
/* still looks ugly... need surgery */
int net_query_cwho(struct net_t *n, char *buf) {
        char *pstats[7], chan_status[40];
        char *p;
        struct channel_t *chan;
        struct net_t *nsock;
        int argc, plen, i,  cmdnum, found, nusers, nchans, done;
	char nick[NICKLEN+1], host[UHOSTLEN+1], team[TEAMLEN+1];


	cmdnum = 315;
	argc = 0;
	found = 0;
	nusers = 0; nchans = 0;
	done = 0;
	p = strtok(buf, " ");
	if (!strncasecmp(p, "names", 5))
		cmdnum = 366;
	argc++;
        p = strtok(NULL, " ");
	net_query_TrimStr(p);
	if (p && p[0] != 0) argc++;
			
	if (argc == 2 && p[0] == '#') p++;
	net_query_TrimStr(p);
        for (chan = chanlist; chan; chan = chan->next, nchans++) {
		net_query_ChanStatStr(chan->status, chan_status);
                nsock = chan->net;
                if (argc==2 && !strcasecmp(p, chan->name) && cmdnum == 315) {
                	tprintf(n->sock, "NOTICE AUTH :#%s Users: %-2d Priority: %-2d  State: %s\n", chan->name, numplayers(chan), chan->priority, chan_status);
                        for (i = 1; i <= 6; i++) {
                                plen = NICKLEN + TEAMLEN + 512;
                                pstats[i] = (char *)malloc(plen);
                                sprintf(pstats[i], "empty");
                        }
                        while (nsock) {
				strcpy(nick, nsock->nick);
				net_query_StripCtrlStr(nick);
                                if (nsock->type == NET_CONNECTED) {
                                        if (nsock->gameslot < 1 || nsock->gameslot > 6) {
                                                killsock(n->sock);
                                                net_query_lostnet(n);
                                                return -1;
                                        }
					net_query_IPconvert(nsock, host);
					if (strlen (nsock->team) == 0)
						strcpy(team, "No team");
					else
						strcpy(team, nsock->team);
					if (nsock->securitylevel != LEVEL_AUTHOP) 
                                        sprintf(pstats[nsock->gameslot], ":%s 352 %s #%s slot%d %s %s %s H * :%s\n", myhostname, n->nick, nsock->channel->name, nsock->gameslot, host, myhostname, nick, team);
					else
                                        sprintf(pstats[nsock->gameslot], ":%s 352 %s #%s slot%d %s %s %s H@ * :%s\n", myhostname, n->nick, nsock->channel->name, nsock->gameslot, host, myhostname, nick, team);
					found = 1;
                                }
                                nsock = nsock->next;
                        }
                        for (i = 1; i <= 6; i++) {
				if (strcmp(pstats[i], "empty"))
                                	tprintf(n->sock, "%s\n", pstats[i]);
                                free(pstats[i]);
                        }
                }
                else if (argc == 1 || cmdnum == 366) { /* NAMES */
			if (argc == 2 && strcasecmp(chan->name,p)) continue;
                        tprintf(n->sock, ":%s 353 %s = #%s :", myhostname, n->nick, chan->name);
			done = 1;
                        while (nsock) {
				strcpy(nick, nsock->nick);
				net_query_StripCtrlStr(nick);
                                if (nsock->type == NET_CONNECTED ||
				    nsock->type == NET_WAITINGFORTEAM ) {
					if (nsock->securitylevel == LEVEL_AUTHOP)
                                          tprintf(n->sock, "@%s ", nick);
					else 
                                          tprintf(n->sock, "%s ", nick);
					nusers++;
                                }
                                nsock = nsock->next;
                        }
			if (argc == 1)
                        	tprintf(n->sock, "\n");
                }
        }
	if (argc == 2 && cmdnum == 315) {   /* check for query operator */
		for (nsock = gnet; nsock; nsock = nsock->next) {
			if (nsock->channel->name[0] == '\0') continue;
			if (nsock->type == NET_QUERY_CONNECTED &&
			    !strcasecmp(nsock->channel->name, p)) {
				net_query_IPconvert(nsock, host);
				strcpy(nick, nsock->nick);
				net_query_StripCtrlStr(nick);
                        	tprintf(n->sock, ":%s 352 %s #%s query %s %s %s H*@ * :query client\n", myhostname, n->nick, nsock->channel->name, host, myhostname, nick);
			}
		}
	}
	if (argc == 2 && cmdnum == 366) { 
		if (!done)
                	tprintf(n->sock, ":%s 353 %s = #%s :", myhostname, n->nick, p);
		for (nsock = gnet; nsock; nsock = nsock->next) {
			strcpy(nick, nsock->nick);
			net_query_StripCtrlStr(nick);
			if (strlen(nsock->channel->name) == 0) continue;
			if (nsock->type == NET_QUERY_CONNECTED &&
			    !strcasecmp(nsock->channel->name, p)) {
                        	tprintf(n->sock, "@%s ", nick);
			}
		}
                tprintf(n->sock, "\n");
	}
	if (argc == 1 && cmdnum != 315)
       		tprintf(n->sock, "NOTICE AUTH :Total: %d users in %d channels\n", nusers, nchans);
	if (argc != 1 && cmdnum == 315) {
		if (p)
         		tprintf(n->sock,":%s 315 %s #%s :End of /WHO list.\n", myhostname, n->nick, p); 
		else 
         		tprintf(n->sock,":%s 315 %s * :End of /WHO list.\n", myhostname, n->nick); 
	} 
	else
         tprintf(n->sock,":%s 366 %s * :End of /NAMES list.\n", myhostname, n->nick); 

        return 1;
}

/* 2nd time and after NICK */
int net_query_cnick(struct net_t *n, char *buf) {
	char *p;
	p = strtok(buf, " ");
	p = strtok(NULL, " ");
	net_query_TrimStr(p);
	if (p && (p[0] != '\0')) {
		if (p[0] == ':') p++;
		if (!net_query_nickfound(p, n)) {
			tprintf(n->sock, ":%s!query@%s NICK :%s\n", n->nick, n->host, p); 
			strncpy(n->nick, p, NICKLEN);
			n->nick[NICKLEN] = 0;
		}
		else 
			tprintf(n->sock, ":%s 433 * %s :Nickname is already in use.\n", myhostname, p);
	}
	else
		tprintf(n->sock, ":%s 431 %s :No nickname given\n", myhostname, n->nick);
	return 1;
}

/* TOPIC */
int net_query_ctopic(struct net_t *n, char *buf) {
	char *p;
	struct channel_t *chan;
	p = strtok(buf, " ");
	p = strtok(NULL, " ");
	if (p) {
		if (p[0] == '#') p++;
		else return -1;
		net_query_TrimStr(p);
		for (chan = chanlist; chan; chan = chan->next)
			if (!strcasecmp(chan->name, p))
				break;
		if (!chan) {
			tprintf(n->sock, ":%s 442 %s #%s :Not a gameplay channel\n", myhostname, n->nick, p);
			return -1;
		}
		p = strtok(NULL, "");
		net_query_TrimStr(p);
		if (!p || (p && p[0] == '\0')) {
			if (chan->description[0] != '\0') {
				tprintf(n->sock, ":%s 332 %s #%s :%s\n", myhostname, n->nick, chan->name, chan->description);
				tprintf(n->sock, ":%s 333 %s #%s server 0\n", myhostname, n->nick,  chan->name); 
			}

			else
				tprintf(n->sock, ":%s 331 %s #%s :No topic is set\n", myhostname, n->nick, chan->name, chan->description);
			return 1;
		}
		if (p[0] == ':') p++; 
		strncpy(chan->description, p, DESCRIPTIONLEN);
		chan->description[DESCRIPTIONLEN] = 0;
		net_query_parser("topic %s 0 %s #%s %s", n->nick, n->host, chan->name, p);
	}
	else
		tprintf(n->sock, ":%s 461 %s TOPIC :Not enough parameters", myhostname, n->nick);
	return 1;
}

/* JOIN */
int net_query_cjoin(struct net_t *n, char *buf) {
	char *p;
	char whocmd[128];
	struct channel_t *chan;

	p = strtok(buf, " ");
	p = strtok(NULL, " ");
	net_query_TrimStr(p);
	
	if (!p) return -1; 
	if (p[0] == '#') p++;
	if (p[0] == '\0') return -1;
	if (!is_valid_channelname(p)) {
		tprintf(n->sock, "NOTICE AUTH :Invalid channel name!\n");
		return -1;
	}
    chan = chanlist;
    while ((chan != NULL) && (strcasecmp(chan->name, p))) chan=chan->next;
	if (chan == NULL) {
		tprintf(n->sock, "NOTICE AUTH :The channel you request does not exist\n");
		return -1;
	}
	if (n->channel->name[0] == '\0' || strcasecmp(n->channel->name, p)) {
		if (n->channel->name[0] != 0)
			tprintf(n->sock, ":%s!query@%s PART :#%s\n", n->nick, n->host, n->channel->name);
		tprintf(n->sock, ":%s!query@%s JOIN :#%s\n", n->nick, n->host, p);
		strncpy(n->channel->name, p, CHANLEN-1);
		sprintf(whocmd, "names #%s\n", n->channel->name);
		net_query_cwho(n, whocmd);
        }
	return 1;
}
	
/* PART */
int net_query_cpart(struct net_t *n, char *buf) {
	char *p;
	p = strtok(buf, " ");
	p = strtok(NULL, " ");
	net_query_TrimStr(p);
	if (!p) return -1;
	if (p[0] == '#') p++;
	if (p[0] == '\0') return -1;
	if (!strcasecmp(n->channel->name, p)) {
		tprintf(n->sock, ":%s!query@%s PART :#%s\n", n->nick, n->host, p);
		n->channel->name[0] = '\0';
	}
	return 1;
}

/* KILL */
int net_query_ckill(struct net_t *n, char *buf) {
	char *p;
	char nick[NICKLEN + 1];
	struct channel_t *chan;
	struct net_t *nn, *nnn;
	p = strtok(buf, " ");
	p = strtok(NULL, " ");
	net_query_TrimStr(p);
	if (!p || (p && p[0] == '\0')) {
		tprintf(n->sock, ":%s 461 %s KILL :Not enough parameters\n", myhostname, n->nick);
		return -1;
	}
        for (chan = chanlist; chan; chan = chan->next) {
		nn = chan->net;
		for (nn = chan->net; nn; nn = nn->next) {
			strcpy(nick, nn->nick);
			net_query_StripCtrlStr(nick);
			if (!strcasecmp(nick, p)) {
				for (nnn = gnet; nnn; nnn = nnn->next)
				if (nnn->type == NET_QUERY_CONNECTED)
				tprintf(nnn->sock, "NOTICE AUTH :*** Notice -- killed %s (requested by %s)\n",  nn->nick, n->nick);
				killsock(nn->sock); lostnet(nn);
				return 1;
			}
		}
	}

        for (nn = gnet; nn; nn = nn->next) {
                if (!strcasecmp(nn->nick, p) && nn->type == NET_PLAYBACK_CONNECTED) {
                        for (nnn = gnet; nnn; nnn = nnn->next)
                        if (nnn->type == NET_QUERY_CONNECTED)
                        tprintf(nnn->sock, "NOTICE AUTH :*** Notice -- killed %s (requested by %s)\n",  nn->nick, n->nick);
                        if (!strcasecmp(nn->nick, p)) {
                                killsock(nn->sock); net_query_lostnet(nn);
                                return 1;
                        }
                }
        }

	tprintf(n->sock, ":%s 401 %s %s :No such nick\n", myhostname, n->nick, p);
	return 1;
}

int net_query_ckick(struct net_t *n, char *buf) {
	char *p;
	struct channel_t *chan;
	char chan_name[80];
	struct net_t *nn, *nnn;
	int slot;
	p = strtok(buf, " ");
	p = strtok(NULL, " ");
	if (p) {
		if (p[0] == '#') p++;
		if(p[0] != '\0')
			strncpy(chan_name, p, CHANLEN-1);
	}
	p = strtok(NULL, " ");
	net_query_TrimStr(p);
	if (!p || (p && p[0] == '\0')) {
		tprintf(n->sock, ":%s 461 %s KICK :Not enough parameters\n", myhostname, n->nick);
		return -1;
	}
	if (strlen(p) > 4) p+=4;   /* strip the text "slot" if any */
	slot = atoi(p);
	if (slot >= 1 && slot <= 6) {
                for (chan = chanlist; chan; chan = chan->next) {
                        if (!strcasecmp(chan->name, chan_name)) {
				for (nn = chan->net; nn; nn = nn->next) {
					if (nn->gameslot == slot && (nn->type == NET_CONNECTED || nn->type == NET_WAITINGFORTEAM)) {
						if (strlen(n->host) == 0)
							strcpy(n->host, "unknown");
                                        	/* tprintf(n->sock, ":%s!query@%s KICK #%s slot%d :...\n",n->nick, n->host, chan_name, slot, slot);
						*/
						for (nnn = gnet; nnn; nnn = nnn->next)
						if (nnn->type == NET_QUERY_CONNECTED)
						tprintf(nnn->sock, "NOTICE AUTH :*** Notice -- killed %s (requested by %s)\n", nn->nick, n->nick);
						killsock(nn->sock); lostnet(nn);
						return 1;
					}
				}
                        }
                }
                if (!chan)
                        tprintf(n->sock, ":%s 403 %s #%s :No such channel\n", myhostname, n->nick, chan_name);
        }
	else {
		tprintf(n->sock, "NOTICE AUTH :Invalid slot number.\n");
		tprintf(n->sock, "NOTICE AUTH :Usage: kick #channel slot\n");
	}
	return 1;
}

int net_query_cprivmsg(struct net_t *n, char *buf)
{
	struct channel_t *chan;
	struct net_t *nn;
	char *p;
	p = strtok(buf, " ");
	p = strtok(NULL, " ");
	if (p) {
		if (p[0] != '#') {             /* privmsg to a nick */
			/* Check for query clients */
			for (nn = gnet; nn; nn = nn->next) {
				if ((!strcasecmp(nn->nick, p) && (nn->type == NET_QUERY_CONNECTED || nn->type == NET_PLAYBACK_CONNECTED)) || nn->type == NET_CONNECTED) {
				p = strtok (NULL, "");
				if (!p) return -1;
				if (p[0] == ':') p++;
				net_query_TrimStr(p);
				if (nn->type == NET_QUERY_CONNECTED)
					tprintf(nn->sock, ":%s!query@%s PRIVMSG %s :%s\n", n->nick, n->host, nn->nick, p);
				else if (nn->type == NET_PLAYBACK_CONNECTED)
					tprintf(nn->sock, "pline 0 <*%s*> %s\xff", n->nick, p);
				else 
					tprintf(nn->sock, "pline 0 <*%s*> %s\xff", n->nick, p);
				break;
				}
			}
			if (!nn)
				tprintf(n->sock, "NOTICE AUTH :Only private message to query/spectator/tetrinet clients is implemented.\n");
			return -1;
		}
		/* else if it's channel name #channel */
		p++;
		if (p[0] == '\0') return -1;   /* can this happen? */
		for (chan = chanlist; chan; chan = chan->next)
			if (!strcasecmp(chan->name, p))
				break;
		if (chan) {
			p = strtok (NULL, "");
			if (!p) return -1;
			if (p[0] == ':') p++;
			net_query_TrimStr(p);
			for (nn = chan->net; nn; nn = nn->next)
				if (nn->type == NET_CONNECTED)
					tprintf(nn->sock, "pline 0 %c%c{ %c%s%c } %c%s\xff", DARKGRAY, BOLD, UNDERLINE, n->nick, UNDERLINE, BOLD, p);
			for (nn = gnet; nn; nn = nn->next)
				if (nn != n && nn->type == NET_QUERY_CONNECTED)
					tprintf(nn->sock, ":%s!query@%s PRIVMSG #%s :%s\n", n->nick, n->host, chan->name, p);
                                else if (nn != n && nn->type == NET_PLAYBACK_CONNECTED && !strcasecmp(n->channel->name, nn->channel->name))
                                        tprintf(nn->sock, "pline 0 %c%c{ %c%s%c } %c%s\xff", DARKGRAY, BOLD, UNDERLINE, n->nick, UNDERLINE, BOLD, p);


		}
		else
			tprintf(n->sock, ":%s 401 %s %s :No such gameplay channel\n", myhostname, n->nick, p);
	}
	return 1;
}

int net_query_cwall(struct net_t *n, char *buf) {
        struct net_t *nn;
        struct channel_t *chan;
        char *p;
        p = strtok(buf, " ");
        p = strtok(NULL, "");
        if (!p) return -1;
        net_query_TrimStr(p);
        for (chan = chanlist; chan; chan = chan->next)
                for (nn = chan->net; nn; nn = nn->next)
                        if (nn->type == NET_CONNECTED) {
                                tprintf(nn->sock, "pline 0 %c\xff", RED);
                                tprintf(nn->sock, "pline 0 %c%c ***** SERVER BROADCAST MESSAGE FROM: %c%s\xff", RED, BOLD, BLUE, n->nick);
                                tprintf(nn->sock, "pline 0 %c%c ***** %s\xff" , RED, BOLD, p);
                                tprintf(nn->sock, "pline 0 %c\xff", RED);
                        }
        for (nn = gnet; nn; nn = nn->next)
                if (nn->type == NET_QUERY_CONNECTED)
                        tprintf(nn->sock, "NOTICE AUTH: Broadcast message from %c: %s\n", RED, n->nick, p);  
	return 1;
}

/* /quote OP or DEOP */
int net_query_copdeop(struct net_t *n, char *buf) {
	struct net_t *nn;
	struct channel_t *chan;
	int op;
	char *p;
	op = 0;
	p = strtok (buf, " ");
	if (!strcasecmp(buf, "op")) op = 1;
	p = strtok (NULL, " ");
	if (p) {
		if (p[0] != '#') {
			tprintf(n->sock, "NOTICE AUTH :First parameter must be a gameplay channel\n");	
			return -1;
		}
		p++;
		for (chan = chanlist; chan; chan = chan->next)
			if (!strcasecmp(chan->name, p)) break;
		if (!chan) {
			tprintf(n->sock, "NOTICE AUTH :Invalid (gameplay) channel #%s\n",p);	
			return -1;
		}
		p = strtok (NULL, " ");
		if (!p) {
			tprintf(n->sock, "NOTICE AUTH :Usage: /quote op|deop #channel slot\n");
			return -1;
		}
		for (nn = chan->net; nn; nn = nn->next)
			if (nn->gameslot == atoi(p)) break;
		if (nn) {
			if (op) {
				nn->securitylevel = LEVEL_AUTHOP;
				net_query_parser ("op %s %d %s #%s %s", nn->nick, nn->gameslot, nn->host, nn->channel->name, n->nick);
				tprintf(nn->sock, "pline 0 %cYou are now a server operator (requested by %s)\xff", BLACK, n->nick);
			}
			else 
				if (nn->securitylevel == LEVEL_AUTHOP) {
					nn->securitylevel = LEVEL_NORMAL;
					net_query_parser ("deop %s %d %s #%s %s", nn->nick, nn->gameslot, nn->host, nn->channel->name, n->nick);
					tprintf(nn->sock, "pline 0 %cYou are now a normal user (requested by %s)\xff", BLACK, n->nick);
				}
		}
		else {
			tprintf(n->sock, "NOTICE AUTH :Invalid slot number %s\n", p);
			tprintf(n->sock, "NOTICE AUTH :Usage: /quote op|deop #channel slot\n");
			return -1;
		}
	}
	return 1;
}

int net_query_cmove(struct net_t *n, char *buf) {
	char pbuf[1280];
	struct channel_t *chan;
	if (strlen(buf) > 12)
		buf[12] = '\0';
	sprintf(pbuf, "pline 0 /%s", buf);
	if (strlen(n->channel->name) == 0) return -1;
	for (chan = chanlist; chan; chan = chan->next)
		if (!strcasecmp(chan->name, n->channel->name)) break;
	if (chan) 
		/* This is scary *sigh* :P I will not do it anymore */
		if (chan->net) {
			if (chan->status != STATE_ONLINE) {
				tprintf(n->sock, "NOTICE AUTH :You cannot move when game is in play\n");
				return -1;
			}
			n->gameslot        = 0;
			n->channel->status = STATE_ONLINE;
			strcpy(n->channel->name, chan->name);
			n->channel->net    = chan->net;
			/* this is so lame :P */
			net_connected (n, pbuf);
		}
	return 1;
}

/* /quote SET */
int net_query_cset(struct net_t *n, char *buf) {
	char *p, *q;
	struct channel_t *chan;
	p = strtok(buf, " ");
	if (n->channel->name[0] == '\0') {
		tprintf(n->sock, "NOTICE AUTH :You must be in a gameplay channel to use this command\n");
		return -1;
	}
	for (chan = chanlist; chan; chan=chan->next) 
		if (!strcasecmp(chan->name, n->channel->name)) break;
	if (!chan) {
		tprintf(n->sock, "NOTICE AUTH :You must be in a gameplay channel to use this command\n");
		return -1;
	}
	p = strtok(NULL, " ");
	if (!p || p[0] < 48) { 
tprintf(n->sock, "NOTICE AUTH :Current basic setting for #%s\n", n->channel->name);
tprintf(n->sock, "NOTICE AUTH :starting_level        [%2d]\n", chan->starting_level);
tprintf(n->sock, "NOTICE AUTH :lines_per_special     [%2d]\n", chan->lines_per_special);
tprintf(n->sock, "NOTICE AUTH :special_added         [%2d]\n", chan->special_added);
tprintf(n->sock, "NOTICE AUTH :classic_rules         [%2d]\n", chan->classic_rules);
tprintf(n->sock, "NOTICE AUTH :sd_timeout            [%2d]\n", chan->sd_timeout);
tprintf(n->sock, "NOTICE AUTH :sd_lines_per_add      [%2d]\n", chan->sd_lines_per_add);
tprintf(n->sock, "NOTICE AUTH :sd_secs_between_lines [%2d]\n", chan->sd_secs_between_lines);
tprintf(n->sock, "NOTICE AUTH :priority              [%2d]\n", chan->priority);
tprintf(n->sock, "NOTICE AUTH :maxplayers            [%2d]\n", chan->maxplayers);
tprintf(n->sock, "NOTICE AUTH :End of basic settings, type /quote set varname value to set new value\n");
	}
	else {
		q = strtok(NULL, " ");
		net_query_TrimStr(q);
		if (!q || (q && q[0] == '\0')) return -1;
		if (!strcasecmp(p, "starting_level"))
			chan->starting_level = atoi(q);
		else if (!strcasecmp(p, "lines_per_special")) {
			if (atoi(q) <= 0) {
				tprintf(n->sock, "NOTICE AUTH :Must be positive integer\n"); 
				return -1;
			}
			else
				chan->lines_per_special = atoi(q);
		}
		else if (!strcasecmp(p, "special_added"))
			chan->special_added = atoi(q);
		else if (!strcasecmp(p, "classic_rules"))
			chan->classic_rules = atoi(q);
		else if (!strcasecmp(p, "sd_timeout"))
			chan->sd_timeout = atoi(q);
		else if (!strcasecmp(p, "sd_lines_per_add"))
			chan->sd_lines_per_add = atoi(q);
		else if (!strcasecmp(p, "sd_secs_between_lines"))
			chan->sd_secs_between_lines = atoi(q);
		else if (!strcasecmp(p, "priority"))
			chan->priority = atoi(q);
		else if (!strcasecmp(p, "maxplayers")) {
			if (atoi(q) <= 0 || atoi(q) > 6) {
				tprintf(n->sock, "NOTICE AUTH :Must be integer (1,2,3..6)\n"); 
				return -1;
			}
			else
			chan->maxplayers = atoi(q);
		}
		else  {
			tprintf(n->sock , "NOTICE AUTH :Invalid varname %s\n", p);
			return -1;
		}
		tprintf(n->sock , "NOTICE AUTH :%s is now %d\n", p, atoi(q));
	}
	return 1;
}

/* REHASH */
int net_query_crehash(struct net_t *n, char *buf) {
	struct net_t *nn;
	gameread();
        for (nn = gnet; nn; nn = nn->next) {
                if (nn->type == NET_QUERY_CONNECTED)
                        tprintf(nn->sock, ":%s NOTICE %s :*** Notice -- %s is rehashing Server config file while whistling innocently\n", myhostname, nn->nick, n->nick);
        } 
	return 1;

}

/* READWINLIST */
int net_query_creadwinlist(struct net_t *n, char *buf) {
	readwinlist(FILE_WINLIST, winlist, MAXWINLIST);
	readwinlist(FILE_WINLIST2, winlist2, MAXWINLIST);
	return 1;
}

int net_query_creadaccesslist(struct net_t *n, char *buf)
{
	struct net_t *nn;
	init_banlist(banlist, MAXBAN);
	init_banlist(combanlist, MAXBAN);
	init_allowlist(allowlist, MAXALLOW);
	read_banlist(FILE_BAN, banlist, MAXBAN);
	read_banlist(FILE_BAN_COMPROMISE, combanlist, MAXBAN);
	read_allowlist();
    for (nn = gnet; nn; nn = nn->next) {
		if (nn->type == NET_QUERY_CONNECTED)
			tprintf(nn->sock, ":%s NOTICE %s :*** Notice -- %s is rehashing server access list while whistling innocently\n", myhostname, nn->nick, n->nick);
	}
	return 1;
}

/* /quote HELP */
int net_query_chelp(struct net_t *n, char *buf)
{
        tprintf(n->sock, "NOTICE AUTH :Basic commands\n");
        tprintf(n->sock, "NOTICE AUTH :-------------------------------------------------------\n");
        tprintf(n->sock, "NOTICE AUTH :/list  or /quote list\n");
        tprintf(n->sock, "NOTICE AUTH :/who #channel\n");
        tprintf(n->sock, "NOTICE AUTH :/whois nickname\n");
        tprintf(n->sock, "NOTICE AUTH :/names or /quote names\n");
        tprintf(n->sock, "NOTICE AUTH :/kill nickname\n");
        tprintf(n->sock, "NOTICE AUTH :/kick #channel slotnumber\n");
        tprintf(n->sock, "NOTICE AUTH :/join #channel  /part #channel\n");
        tprintf(n->sock, "NOTICE AUTH :/quote move slot1 slot2\n");
        tprintf(n->sock, "NOTICE AUTH :/quote op #channel slotnumber\n");
        tprintf(n->sock, "NOTICE AUTH :/quote deop #channel slotnumber\n");
        tprintf(n->sock, "NOTICE AUTH :/quote wall or /quote broadcast\n");
	tprintf(n->sock, "NOTICE AUTH :/quote set or /quote set varname value\n");
        tprintf(n->sock, "NOTICE AUTH :/stats w for winlist\n");
        tprintf(n->sock, "NOTICE AUTH :/stats p for server operator\n");
        tprintf(n->sock, "NOTICE AUTH :/stats k for kline/ban list\n");
        tprintf(n->sock, "NOTICE AUTH :/rehash or /quote reset\n");
        tprintf(n->sock, "NOTICE AUTH :This is not a real irc server. Weirdness is expected!\n");
        tprintf(n->sock, "NOTICE AUTH :Report bug to drslum@tetrinet.org\n");
	return 1;
}

/* MODE (not completed) just to keep client alive */
int net_query_cmode(struct net_t *n, char *buf)
{
	char *p, *q, *r;
	p = strtok(buf, " ");
	p = strtok(NULL, "");
	if (!p) return -1;
	net_query_TrimStr(p);
	if (p[0] != '#')
		tprintf(n->sock, ":%s MODE %s\n", n->nick, p);
	else {
		q = strtok(p, " ");
		r = strtok(NULL, "");
		if (!r) {
			tprintf(n->sock, ":%s 324 %s %s +\n", myhostname, n->nick, q);
			tprintf(n->sock, ":%s 329 %s %s 0\n", myhostname, n->nick, q);
		}
		else if (r[0] == 'b')
			tprintf(n->sock, ":%s 368 %s %s :End of Channel Ban List\n", myhostname, n->nick, q);
	}
		
	return 1;
}

/* USERHOST (bitchx, mirc)*/
int net_query_cuserhost(struct net_t *n, char *buf)
{
	char *p;
	p = strtok(buf, " ");
	p = strtok(NULL, " ");
	net_query_TrimStr(p);
	if (!p || p[0] == '\0') return -1;
	tprintf(n->sock, ":%s 302 %s :%s=+query@%s\n", myhostname, n->nick, p, myhostname);
	return 1;
}

/* QUIT */
int net_query_cquit(struct net_t *n, char *buf)
{
	close(n->sock); killsock(n->sock); net_query_lostnet(n);
	return 1;
}

/* ISON (dummy) */
int net_query_cison(struct net_t *n, char *buf)
{
	tprintf(n->sock, ":%s 303 %s :%s\n", myhostname, n->nick, n->nick);
	return 1;
}

int net_query_cunsupported(struct net_t *n, char *buf)
{
	char *p;
	p = strtok(buf, " ");
	if (!p) return -1;
	if (p[0] == '\0') return 1;
/*
        tprintf(n->sock,":%s 421 %s %s :Unsupported Command\n", myhostname, n->nick, p);
*/
	return 1;
}

void net_query_connected(struct net_t *n, char *buf)
{
	if (!buf) return;
	if (strlen(buf) <= 2) return;
	if (!strncasecmp(buf, "list", 4))
		net_query_clist(n, buf);
        if (!strncasecmp(buf, "slist", 5))
                net_query_cslist(n, buf);
	else if (!strncasecmp(buf, "whowas", 6))
		net_query_cunsupported(n, buf);
	else if (!strncasecmp(buf, "whois", 5))
		net_query_cwhois(n, buf);
	else if (!strncasecmp(buf, "who", 3))
		net_query_cwho(n, buf);
	else if (!strncasecmp(buf, "nick", 4))
		net_query_cnick(n, buf); 
	else if (!strncasecmp(buf, "join", 4))
		net_query_cjoin(n, buf); 
	else if (!strncasecmp(buf, "part", 4))
		net_query_cpart(n, buf); 
	else if (!strncasecmp(buf, "kick", 4))
		net_query_ckick(n, buf);
	else if (!strncasecmp(buf, "kill", 4))
		net_query_ckill(n, buf);
	else if (!strncasecmp(buf, "names", 5))
		net_query_cwho(n, buf);
	else if (!strncasecmp(buf, "privmsg", 7))
		net_query_cprivmsg(n,buf);
	else if (!strncasecmp(buf, "wall", 4))
		net_query_cwall(n,buf);
        else if (!strncasecmp(buf, "broadcast", 9))
                net_query_cwall(n,buf);
	else if (!strncasecmp(buf, "userhost", 8))
		net_query_cuserhost(n,buf);
	else if (!strncasecmp(buf, "ison", 4))
		net_query_cison(n,buf);
	else if (!strncasecmp(buf, "motd", 4))
		net_query_cmotd(n,buf);
	else if (!strncasecmp(buf, "ping", 4))
		net_query_cping(n,buf);
	else if (!strncasecmp(buf, "mode", 4))
		net_query_cmode(n,buf);
	else if (!strncasecmp(buf, "help", 4))
		net_query_chelp(n,buf);
	else if (!strncasecmp(buf, "stats", 5))
		net_query_cstats(n,buf);
	else if (!strncasecmp(buf, "move", 4))
		net_query_cmove(n,buf);
	else if (!strncasecmp(buf, "set", 3) || !strncasecmp(buf, "tset", 4))
		net_query_cset(n,buf);
	else if (!strncasecmp(buf, "topic", 5))
		net_query_ctopic(n,buf);
	else if (!strncasecmp(buf, "op", 2))
		net_query_copdeop(n,buf);
	else if (!strncasecmp(buf, "deop", 4))
		net_query_copdeop(n,buf);
	else if (!strncasecmp(buf, "rehash", 6))
		net_query_crehash(n,buf);
	else if (!strncasecmp(buf, "reset", 5))
		net_query_crehash(n,buf);
	else if (!strncasecmp(buf, "readwinlist", 11))
		net_query_creadwinlist(n,buf);
	else if (!strncasecmp(buf, "readaccesslist", 14))
		net_query_creadaccesslist(n,buf);
	else if (!strncasecmp(buf, "away", 4)) {}
	else if (!strncasecmp(buf, "quit", 4))
		net_query_cquit(n,buf);
	else 
		net_query_cunsupported(n, buf);
}
	
/* Recieving Query commands */
void net_query_init(struct net_t *n, char *buf)
{
	if (!strncasecmp(buf, "pass", 4))
		net_query_authenticate(n, buf);
	else if (!strncasecmp(buf, "nick", 4)) {
		net_query_nick(n, buf);
	}
	else if (!strncasecmp(buf, "user", 4))
		net_query_user(n, buf);
	else {
       		killsock(n->sock); net_query_lostnet(n);
	}
}

/* Someone has just connected. So lets answer them */
void net_query(struct net_t *n, char *buf)
{
	IP ip;
	/* unsigned char x1, x2, x3, x4; */
	struct net_t *net;

	net=malloc(sizeof(struct net_t));
	net->next=NULL;

	net->sock=answer(n->sock,&ip,0);

	while ((net->sock==(-1)) && (errno==EAGAIN))
	net->sock=answer(n->sock,&ip,0);
	setopt(net->sock, 0);

	net->addr=ip;
	net->port=n->port;
	net->securitylevel=LEVEL_NORMAL;
	net->status=STAT_NOTPLAYING;
	net->channel = malloc(sizeof(struct channel_t));
	net->channel->name[0] = '\0';
	strcpy(net->nick, "(telnet)");
	do_async_dns(net);
	net->type = NET_WAITINGFORDNS;
}

void net_query_donedns (struct net_t *net) {
	struct net_t *nn;

	if(net->sock <0) {
        	killsock(net->sock);
        	free(net);
        	return;
	}
	setsock(net->sock, 0);
	net->type=NET_QUERY_INIT;
	/* add to gnet ;) */
        nn=gnet;
        while(nn->next)
        	nn=nn->next;
	if (nn)
		nn->next = net;
}

void net_query_parser(char *format, ...)
{
        char *token[60]; /* max 60 words */
        int i, j;
        static char BUF[1024], M[2048];
        char nick[NICKLEN+1], msg[1024];
        struct net_t *nn;

        if (format) {
                va_list args;
                va_start (args, format);
                vsnprintf(BUF, 1023, format, args);
		va_end(args);
        }
        else
                return;

	i = 0;
	token[0]=strtok(BUF," ");
	while(i < 59 && (token[++i]=strtok(NULL, " ")));
	token[i]=NULL;
	if(token[0]) {
		/* token[1] should be nickname */
                if (token[1]) {
                        strncpy(nick, token[1], NICKLEN);
                        nick[NICKLEN] = 0;
                        net_query_StripCtrlStr(nick);
                }
		if (!strcasecmp(token[0], "msg")) {
			msg[0] = '\0';
			for (j = 5; j < i; j++) {
				strcat(msg, token[j]);
				strcat(msg, " ");
			}
			net_query_StripCtrlStr2(msg);
			net_query_TrimStr(msg);
			sprintf(M, ":%s!slot%s@%s PRIVMSG %s :%s\n", nick, token[2], token[3], token[4], msg);

		}
		else if (!strcasecmp(token[0], "gmsg")) {
			msg[0] = '\0';
			for (j = 5; j < i; j++) {
				strcat (msg, token[j]);
				strcat(msg, " ");
			}
			net_query_StripCtrlStr2(msg);
			net_query_TrimStr(msg);
			sprintf(M, "NOTICE AUTH :(%s:gameplay) <%s> %s\n", token[4], nick, msg);	
		}
		else if (!strcasecmp(token[0], "topic")) {
			msg[0] = '\0';
			for (j = 5; j < i; j++) {
				strcat (msg, token[j]);
				strcat(msg, " ");
			}
			net_query_StripCtrlStr2(msg);
			net_query_TrimStr(msg);
			sprintf(M, ":%s!slot%s@%s TOPIC %s :%s\n", nick, token[2], token[3], token[4], msg);	
		}
		else if (!strcasecmp(token[0], "plineact")) {
			msg[0] = '\0';
			for (j = 5; j < i; j++) {
				strcat (msg, token[j]);
				strcat(msg, " ");
			}
			net_query_StripCtrlStr2(msg);
			net_query_TrimStr(msg);
			sprintf(M, ":%s!slot%s@%s PRIVMSG %s :\001ACTION %s\001\n", nick, token[2], token[3], token[4], token[5]);	
		}
		else if (!strcasecmp(token[0], "playerleave"))
			sprintf(M, ":%s!slot%s@%s PART :%s\n", nick, token[2], token[3], token[4]);	
                else if (!strcasecmp(token[0], "specleave"))
                        sprintf(M, ":%s!spectator@%s PART :%s\n", nick, token[3], token[4]);
		else if (!strcasecmp(token[0], "playerjoin"))
			sprintf(M, ":%s!slot%s@%s JOIN :%s\n", nick, token[2], token[3], token[4]);	
                else if (!strcasecmp(token[0], "specjoin"))                        sprintf(M, ":%s!spectator@%s JOIN :%s\n", nick, token[3], token[4]);

		else if (!strcasecmp(token[0], "timeout"))
			sprintf(M, "NOTICE AUTH :%s Idle time limit exceed for %s.\n", token[4], nick);	
		else if (!strcasecmp(token[0], "team") && token[5])
			sprintf(M, "NOTICE AUTH :*** %s %s changes team name to %s\n", token[4], nick, token[5]);	
		else if (!strcasecmp(token[0], "pause") ||
			 !strcasecmp(token[0], "unpause")) 
			sprintf(M, "NOTICE AUTH :%s %s %ss the game.\n", token[4], nick, token[0]);	
		else if (!strcasecmp(token[0], "newchan"))
			sprintf(M, "NOTICE AUTH :New channel %s created by %s\n", token[4], nick);	
		else if (!strcasecmp(token[0], "op"))
			sprintf(M, "NOTICE AUTH :%s %s is now a channel operator (requested by %s)\n", token[4], nick, token[5]);	
		else if (!strcasecmp(token[0], "deop"))
			sprintf(M, "NOTICE AUTH :%s %s is now a normal user (requested by %s)\n", token[4], nick, token[5]);	
		else if (!strcasecmp(token[0], "kick"))
			sprintf(M, "NOTICE AUTH :%s %s has been kicked.\n", token[4], nick);	
		else if (!strcasecmp(token[0], "move"))
			sprintf(M, "NOTICE AUTH :%s %s swapped with %s.\n", token[3], nick, token[2]);	
		else if (!strcasecmp(token[0], "gamestart"))
			sprintf(M, "NOTICE AUTH :%s %s started the game.\n", token[4], nick);	
		else if (!strcasecmp(token[0], "gamestop"))
			sprintf(M, "NOTICE AUTH :%s %s stopped the game.\n", token[4], nick);	
		else if (!strcasecmp(token[0], "teamwon"))
			sprintf(M, "NOTICE AUTH :%s Game ended. Team %s won.\n", token[2], nick);	
		else if (!strcasecmp(token[0], "playerwon"))
			sprintf(M, "NOTICE AUTH :%s Game ended. Player %s won.\n", token[2], nick);	
		else 
			return;
	}
	for (nn = gnet; nn; nn = nn->next)
		if (nn->type == NET_QUERY_CONNECTED)
			tputs(nn->sock, M);
} 
  
/*------------------------ End of query section -------------------------- */

int net_playback_isvalidnick(char *p)
{
        char *r;
        int valid=1;
        if (!p) return 1;
        if (isdigit(*p)) return 0;
        if (!strcasecmp(p, "server")) return 0;
        for (r = p; *r; r++) {
                if (  (*r >= '0' && *r <= '9') ||
                      (*r >= 'A' && *r <= 'Z')  ||
                      (*r >= 'a' && *r <= 'z') ||
                      (*r == '_' || *r == '-') ||
                      (*r == '[' || *r ==']')  )
                        continue;
                valid = 0;
                break;
        }
        return(valid);
}


/* Someone has just connected. So lets answer them */
void net_playback(struct net_t *n, char *buf)
{
        IP ip;
        /* unsigned char x1, x2, x3, x4; */
        struct net_t *net;

        net=malloc(sizeof(struct net_t));
        net->next=NULL;

        net->sock=answer(n->sock,&ip,0);

        while ((net->sock==(-1)) && (errno==EAGAIN))
        net->sock=answer(n->sock,&ip,0);
		setopt(net->sock, 0);

        net->addr=ip;
        net->port=n->port;
        net->securitylevel=LEVEL_NORMAL;
        net->status=STAT_NOTPLAYING;
        net->channel = malloc(sizeof(struct channel_t));
        net->channel->name[0] = '\0';
		net->gameslot = 1;
        strcpy(net->nick, "(telnet)");
		do_async_dns(net);
		net->type = NET_WAITINGFORDNS;
}

void net_playback_donedns(struct net_t *net) {
		struct net_t *nn;
		
        if(net->sock <0) {
                killsock(net->sock);
                free(net);
                return;
        }
		setsock(net->sock, 0);
        net->type=NET_PLAYBACK_INIT;
        /* add to gnet ;) */
        nn=gnet;
        while(nn->next)
                nn=nn->next;
        if (nn)
                nn->next = net;

        /* Is this person banned? */

        if (is_explicit_banned(net)) {
        	tprintf(net->sock,"noconnecting Your host is banned from this server.\xff");
        	killsock(net->sock);
        	free(net);
			return;
		}
}


/* The person has sent their init string */
void net_playback_init(struct net_t *n, char *buf)
  {
    int i;
    int tet_err;
    char *dec, *p;
    char strg[1024];
    char *P;
    FILE *file_in;
    struct net_t *nn;
    
    sprintf(strg,"team 1 ");
    if (!strncmp(buf,strg,strlen(strg))) {
	if (!n->authenticate) return;
        P=buf+strlen(strg);
        net_query_TrimStr(P);
    	strncpy(n->team, P, TEAMLEN); n->team[TEAMLEN]='\0';
        if (securityread() < 0)
                securitywrite();
        if ((strlen(security.spec_password) <= 0) ||
             strcmp(n->team, security.spec_password)) {
                tprintf(n->sock, "noconnecting Unauthorized access!  Attempt logged.\xff");
                killsock(n->sock); net_query_lostnet(n); return;
        }

	n->type = NET_PLAYBACK_CONNECTED;
        tprintf(n->sock, "pline 0 %c%c%c%c%c%c%c%c\xff",
                BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK);
        file_in = fopen(FILE_PMOTD,"r");
        if (file_in != NULL)
        {
        	while (fgets(strg, 512, file_in) != NULL)
          	{ 
            		net_query_TrimStr(strg);
            		tprintf(n->sock,"pline 0 %s\xff", strg);
          	}
                fclose(file_in);
        }
        strcpy(n->channel->name, DEFAULTSPECCHANNEL);
        sendwinlist(n);
        /* Send spectator list */
        tprintf(n->sock, "speclist #%s", n->channel->name);
        for (nn = gnet; nn; nn = nn->next)
                if (nn->type == NET_PLAYBACK_CONNECTED && !strcasecmp(nn->channel->name, n->channel->name)) {
                       tprintf(n->sock, " ");
                       tprintf(n->sock, "%s", nn->nick);
                       if (nn != n)
                                tprintf(nn->sock, "specjoin %s\xff", n->nick);
                }
        tprintf(n->sock, "\xff");
        /* Notify query clients */
        net_query_parser("specjoin %s %d %s #%s", n->nick, 0, n->host, n->channel->name);
	lvprintf(3, "Incoming spectator connection: %s(%s)(%s)\n", n->nick, n->ip, n->host);

	return;
    }
    
    if (strlen(buf) < 7)
      {/* Invalid... quit */
        killsock(n->sock); net_query_lostnet(n);
        return;
        
      }
      
    tet_err = tet_decrypt(buf);
    if (tet_err != 0)
      { /* Error */
        killsock(n->sock); net_query_lostnet(n);
        return;
      }
     
    dec = tet_dec2str(buf);

/* This is used instead of sscanf to get more control tetrisstart message
   with the bound check according to NICKLEN and VERLEN. */
    i = 0;
    if ((p = strtok(dec, " "))) {
        i++;
        if (strcmp(p, "tetrisstart")) {
		killsock(n->sock); net_query_lostnet(n); return;
        }
        if ((p = strtok(NULL, " "))) {
                i++;
                if (strlen(p) > NICKLEN) {
                        tprintf(n->sock, "noconnecting Invalid Sign-on (nickname too long)\xff");
                        killsock(n->sock); net_query_lostnet(n); return;
                }
                strncpy(n->nick, p, NICKLEN); n->nick[NICKLEN] = 0;
                if ((p = strtok(NULL, " "))) {
                        strncpy(n->version, p, VERLEN); n->version[VERLEN] = 0;
                        i++;
                }
        }
    }
    if (i != 3) { killsock(n->sock); net_query_lostnet(n); return; }

    // /* OK, so dec should now hold "tetrisstart <nickname> <version number> */
    // i = sscanf(dec,"tetrisstart %30[^\x20] %10s", n->nick, n->version);
    
        
    // if (i < 2)
      // {/* To few conversions - Player dies*/
        // killsock(n->sock); net_query_lostnet(n);
	// return;
      // }
      
    n->team[0] = 0; /* Clear Team */

    if (!net_playback_isvalidnick(n->nick)) {
        tprintf(n->sock, "noconnecting Nickname not allowed!\xff");
        killsock(n->sock); net_query_lostnet(n);
        return;
    }

    if (net_query_nickfound(n->nick, n))
      {
        tprintf(n->sock, "noconnecting Nickname already exists on server!\xff");
        killsock(n->sock); net_query_lostnet(n);
        return;
      } 


    // sendwinlist(n);

    /* Send them their player number */
    tprintf(n->sock, "playernum 1\xff");
    n->authenticate = 1;
    
    
  }

/* Experimental */
void net_playback_connected(struct net_t *n, char *buf)
{
	struct net_t *nsock, *nn, *nnn;
	struct channel_t *ochan, *chan;
	int i, s, num;
	char COMMAND[101], PARAM[601], MSG[601];
	char tmpbuf[1024], *p;
    	COMMAND[0]=0;PARAM[0]=0;MSG[0]=0;

        if (!buf) return;
        if (strlen(buf) <= 2) return;

    	/* Ensure command is proper before passing it to other players */
    	sscanf(buf, "%100s %600[^\n\r]", COMMAND, PARAM);

    /* Party Line Message - pline <playernumber> <message> */
    if ( !strcasecmp(COMMAND, "pline") )
      {
        s = sscanf(PARAM, "%d %600[^\n\r]", &num, MSG);
        if (s < 2) return;
        if (MSG[0]=='/') {
           if ( !strcasecmp(MSG, "/join") ||
		!strcasecmp(MSG, "/j")    ||
                !strcasecmp(MSG, "/ip")   ||
                !strcasecmp(MSG, "/m")    ||
                !strcasecmp(MSG, "/msg")  ||
                !strcasecmp(MSG, "/move") ||
                !strcasecmp(MSG, "/kick") ||
                !strcasecmp(MSG, "//")    ||
                !strcasecmp(MSG, "/view") ||
		!strcasecmp(MSG, "/p")) {
		strcat(MSG, " ");
	   }
	   if (!strncasecmp (MSG, "/join ", 6) ||
	       !strncasecmp (MSG, "/j ", 3)) {
		int off;
                if ( !strncasecmp(MSG,"/join ", 6) )
			off = 0;
		else
			off = -3;
		/* Check syntax first */
		if (buf[14+off] != '#') {
			tprintf(n->sock, "pline 0 %cUsage: /join #channel\xff", RED);
			return;
		}
		if (!strcasecmp(n->channel->name, &buf[15+off])) {
			tprintf(n->sock, "pline 0 %cYou are already in that channel!\xff", RED);
			return;
		}
                chan = chanlist;
                while ((chan != NULL) && (strcasecmp(chan->name, &buf[15+off])))
                        chan=chan->next;


                if (!chan && strcasecmp(DEFAULTSPECCHANNEL, &buf[15+off])) {
                        if (strlen(buf) > 15+off)
                                tprintf(n->sock, "pline 0 %cInvalid channel: #%s\xff", RED, &buf[15+off]);
                        return;
                }

			
		/* send playerleave (old chan) */
                ochan = chanlist;
                while ( (ochan != NULL) && (strcasecmp(ochan->name, n->channel->name) ))
                        ochan=ochan->next;
		if (ochan) {
                        for (nsock = ochan->net; nsock; nsock = nsock->next)
				  if (nsock->type == NET_CONNECTED && nsock->gameslot >= 1 && nsock->gameslot <= 6)
					tprintf(n->sock, "playerleave %d\xff", nsock->gameslot);
		}

		
		/* send playerjoin (new chan) */
		if (strcasecmp(DEFAULTSPECCHANNEL, &buf[15+off])) {
		if (chan->status == STATE_INGAME)
			tprintf(n->sock, "ingame\xff");
		nsock = chan->net;
		for (nsock = chan->net; nsock; nsock = nsock->next) {
			if (nsock->type == NET_CONNECTED && nsock->gameslot >= 1 && nsock->gameslot <= 6) {
				tprintf(n->sock, "playerjoin %d %s\xffteam %d %s\xff", nsock->gameslot, nsock->nick, nsock->gameslot, nsock->team);
				sendfield(n, nsock);
			}
		}
		}
                /* send specleave (old chan) */
                nn = gnet;
                for (nn = gnet; nn; nn = nn->next) {
                        if (nn->type == NET_PLAYBACK_CONNECTED && !strcasecmp(n->channel->name,nn->channel->name) && nn != n) {
                                tprintf(nn->sock, "specleave %s\xff", n->nick);
                                tprintf(nn->sock, "pline 0 %cSpectator %s joined #%s\xff", BLUE, n->nick, &buf[15]);
                        }
                }
                /* Query clients */
                net_query_parser("specleave %s %d %s #%s", n->nick, 0, n->host, n->channel->name);

		strcpy(n->channel->name, &buf[15+off]);
		sendwinlist(n);
		/* Send spectator list */
		tprintf(n->sock, "speclist #%s", n->channel->name);
		for (nn = gnet; nn; nn = nn->next)
			if (nn->type == NET_PLAYBACK_CONNECTED && !strcasecmp(nn->channel->name, n->channel->name)) {
				tprintf(n->sock, " ");
				tprintf(n->sock, "%s", nn->nick); 
				if (nn != n) 
                                        tprintf(nn->sock, "specjoin %s\xff", n->nick);
			}
		tprintf(n->sock, "\xff");
		/* Notify query clients */
 		net_query_parser("specjoin %s %d %s #%s", n->nick, 0, n->host, n->channel->name);
				
	   }
	   else if (!strncasecmp (MSG, "/slist", 6) ||
		 !strncasecmp (MSG, "/specwho", 8)) {
		i = 0;
                tprintf(n->sock,"pline 0 %cSpectator List\xff",NAVY);
                for (nn = gnet; nn; nn = nn->next)
			if (nn->type == NET_PLAYBACK_CONNECTED)
                                tprintf(n->sock, "pline 0 %c%d: #%-20s  %s\xff", BLUE, ++i, nn->channel->name, nn->nick);
	   }
           else if (!strncasecmp (MSG, "/qlist", 6)) {
               if (n->securitylevel != LEVEL_AUTHOP) {
                        tprintf(n->sock, "pline 0 %cYou don't have enough access\xff", RED);
                        return;
                }
                i = 0;
                tprintf(n->sock, "pline 0 %cGlobal Operators: %c", BLUE, DARKGRAY);               
                for (nn = gnet; nn; nn = nn->next)
                        if (nn->type == NET_QUERY_CONNECTED)
                                tprintf(n->sock, "%s ", nn->nick);
                tprintf(n->sock, "\xff");
           }

	   /* Time limit so... */
	   else if (!strncasecmp (MSG, "/list", 5)) {
		net_connected (n, "pline 0 /list\xff");
	   }
           else if (!strncasecmp (MSG, "/who", 4)) {
                net_connected (n, "pline 0 /who\xff");
           }
           else if (!strncasecmp (MSG, "/view ", 6)) {
                i = atoi(&MSG[6]);
                if (i >= 1 && i <= 6)
                        tprintf(n->sock, "playernum %d\xff", i);
           }
	   else if (!strncasecmp (MSG, "/op ", 4)) {
		sprintf(tmpbuf, "pline 0 %s", MSG);
		net_connected (n, tmpbuf);
	   }
           else if (!strncasecmp (MSG, "/kick ", 6)) {
                if (n->securitylevel != LEVEL_AUTHOP) {
                        tprintf(n->sock, "pline 0 %cYou don't have enough access\xff", RED);
                        return;
                }
                i = atoi(&MSG[6]);
                if (i < 1 || i > 6) return;
                for (chan = chanlist; chan; chan = chan->next)
                        if (!strcasecmp(chan->name, n->channel->name)) break;
                nnn = NULL;
                if (chan) {
                        for (nn = chan->net; nn; nn = nn->next) {
                                if (nn->type == NET_CONNECTED) {
                                        tprintf(nn->sock, "kick %d\xff", i);
                                        if (nn->gameslot == i)
                                                nnn = nn;
                                }
                        }
                }
                else {
                        tprintf(n->sock, "pline 0 %cInvalid gameplay channel: #%s\xff", RED, n->channel->name);
                        return;
                }
                if (!nnn) {
                        tprintf(n->sock, "pline 0 %cgameslot %d does not exist in #%s!\xff", RED, i, n->channel->name);
                        return;
                }
                for (nn = gnet; nn; nn = nn->next)
                          if (nn->type == NET_QUERY_CONNECTED)
                                tprintf(nn->sock, "NOTICE AUTH :*** Notice -- %s kicked (requested by %s)\n",  nnn->nick, n->nick);
                          else if (nn->type == NET_PLAYBACK_CONNECTED && !strcasecmp(nn->channel->name, n->channel->name))
				tprintf(nn->sock, "kick %d\xff", nnn->gameslot);

                killsock(nnn->sock); lostnet(nnn);
           }

	   else if (!strncasecmp (MSG, "/move ", 6)) {
                if (n->securitylevel != LEVEL_AUTHOP) {
                        tprintf(n->sock, "pline 0 %cYou don't have enough access\xff", RED);
                        return;
                }
        	for (chan = chanlist; chan; chan = chan->next)
                	if (!strcasecmp(chan->name, n->channel->name)) break;
        	if (chan)
                	if (chan->net) {
	                        if (chan->status != STATE_ONLINE) {
                                	tprintf(n->sock, "pline 0 %cYou cannot move when game is in play\xff", RED);
									return;
                        	}
				n->gameslot = 0;
                        	n->channel->status = STATE_ONLINE;
                        	strcpy(n->channel->name, chan->name);
	                        n->channel->net    = chan->net;
				sprintf(tmpbuf, "pline 0 %s", MSG);
                        	net_connected (n, tmpbuf);
                	}
	   }
	   else if (!strncasecmp (MSG, "/p ", 3) ||
		 !strncasecmp (MSG, "// ", 3)) {
		if (n->securitylevel != LEVEL_AUTHOP) {
			tprintf(n->sock, "pline 0 %cYou don't have enough access\xff", RED);
			return;
		}
                for (chan = chanlist; chan; chan = chan->next)
                        if (!strcasecmp(chan->name, n->channel->name)) break;
		/* no comment */
		if (chan) {
			tprintf(n->sock, "pline 0 %c%c<%s> %s\xff", BOLD, BLACK, n->nick, &MSG[3]);
			sprintf(tmpbuf, "PRIVMSG #%s :%s", chan->name, &MSG[3]);
			net_query_cprivmsg(n, tmpbuf);
		}
	   }
	   else if (!strncasecmp (MSG, "/m ", 3) ||
		 !strncasecmp (MSG, "/msg ", 5)) {
                char nick[NICKLEN+1];
                p = strtok(MSG, " ");
                p = strtok(NULL, " ");
                if (p && p[0] != 0) {
                        strncpy(nick, p, NICKLEN);
                        nick[NICKLEN] = '\0';
                        p = strtok(NULL, "");
                        if (!p) { 
                                tprintf(n->sock, "pline 0 %cUsage: /m nick message\xff", RED);
                                return;
                        }

                        sprintf(tmpbuf, "PRIVMSG %s :%s", nick, p);
                       	net_query_cprivmsg(n, tmpbuf);
                }
                else 
                        tprintf(n->sock, "pline 0 %cUsage: /m nick message\xff", RED);
	   }
           else if (!strncasecmp (MSG, "/ip ", 4)) {
        	if (n->securitylevel != LEVEL_AUTHOP) {
                	tprintf(n->sock, "pline 0 %cYou do not have enough access\xff", RED);                return;
        	}

                for (chan = chanlist; chan; chan = chan->next)
                        if (!strcasecmp(chan->name, n->channel->name)) break;
                /* no comment */
        	if (chan) {
			for (i = 4; MSG[i] != 0; i++) {
                		for (nn = chan->net; nn; nn = nn->next)
                        		if (nn->type == NET_CONNECTED && MSG[i] == nn->gameslot + '0') {
                                		tprintf(n->sock, "pline 0 %c%d: %-20s [%s]\xff", RED, nn->gameslot, nn->nick, nn->host);
						break;
					}
			}
		}
		else
			tprintf(n->sock, "pline 0 %cInvalid channel / slotnumbers\xff", RED);
	   }
           else if ( !strncasecmp(MSG, "/--version", 9)) {
		tprintf(n->sock,"pline 0 %c%s.%s\xff", WHITE, TETVERSION, SERVERBUILD);
           }
	   else if ( !strncasecmp(MSG, "/help", 5)) {
                tprintf(n->sock,"pline 0 %cBuilt in spectator commands\xff", NAVY);
		tprintf(n->sock,"pline 0 %c[1]  %c/join %c[#channel]\xff", BROWN, RED, BLUE);
		tprintf(n->sock,"pline 0 %c          Join a game play channel\xff", GREEN);
		tprintf(n->sock,"pline 0 %c[2]  %c/list\xff", BROWN, RED, BROWN);
		tprintf(n->sock,"pline 0 %c          List available gameplay channels\xff", GREEN);
		tprintf(n->sock,"pline 0 %c[3]  %c/slist or /specwho\xff", BROWN, RED);
		tprintf(n->sock,"pline 0 %c          List spectators\xff", GREEN);
		tprintf(n->sock,"pline 0 %c[4]  %c/qlist\xff", BROWN, RED);
		tprintf(n->sock,"pline 0 %c          List irc clients\xff", GREEN);
		tprintf(n->sock,"pline 0 %c[5]  %c/who\xff", BROWN, RED);
		tprintf(n->sock,"pline 0 %c          List all playernick / channel\xff", GREEN);
		tprintf(n->sock,"pline 0 %c[6]  %c/op %c[op_password]\xff", BROWN, RED, BLUE);
		tprintf(n->sock,"pline 0 %c          Gain operator priviledge\xff", GREEN);
		tprintf(n->sock,"pline 0 %c[7]  %c/ip %c[slot(s)]\xff", BROWN, RED, BLUE);
		tprintf(n->sock,"pline 0 %c          An oper command to list player IPs\xff", GREEN);
		tprintf(n->sock,"pline 0 %c[8]  %c/kick %c[slot]\xff", BROWN, RED, BLUE);
		tprintf(n->sock,"pline 0 %c          An oper command to kick a player\xff", GREEN);
		tprintf(n->sock,"pline 0 %c[9]  %c/move %c[slot1] [slot2]\xff", BROWN, RED, BLUE);
		tprintf(n->sock,"pline 0 %c          An oper command to move players\xff", GREEN);
		tprintf(n->sock,"pline 0 %c[10] %c/p or // %c[message]\xff", BROWN, RED, BLUE);
		tprintf(n->sock,"pline 0 %c          An oper command to talk in partyline\xff", GREEN);
		tprintf(n->sock,"pline 0 %c[11] %c/msg or /m %c[nick] [message]\xff", BROWN, RED, BLUE);
		tprintf(n->sock,"pline 0 %c          Message to irc clients\xff", GREEN);
	   }
	   else
			tprintf(n->sock,"pline 0 %cInvalid /COMMAND!\xff",RED);
 	} /* End command begin with a "/" */
	
	/* smsg for default pline */
	else {
                for (nn = gnet; nn; nn = nn->next)
			if (nn!=n && nn->type == NET_PLAYBACK_CONNECTED && !strcasecmp(nn->channel->name, n->channel->name))
                                tprintf(nn->sock, "smsg %s %s\xff",  n->nick, &buf[8]);
		net_query_parser("msg %s %d %s #%s %s", n->nick, 0, n->host, n->channel->name, &buf[8]);

	}
      } /* End pline */
      else if ( !strcasecmp(COMMAND, "gmsg") )
      {
	if (n->securitylevel != LEVEL_AUTHOP) {
		tprintf(n->sock, "gmsg %cYou do not have enough access\xff", RED);
		return;
	}
        for (chan = chanlist; chan; chan = chan->next)
        	if (!strcasecmp(chan->name, n->channel->name)) break;
        if (chan)
		for (nn = chan->net; nn; nn = nn->next)
			if (nn->type == NET_CONNECTED)
				tprintf(nn->sock, "gmsg %s\xff", PARAM);
	for (nn = gnet; nn; nn = nn->next) {
		if (nn->type == NET_PLAYBACK_CONNECTED && !strcasecmp(n->channel->name,nn->channel->name)) 
			tprintf(nn->sock, "gmsg %s\xff", PARAM);
		else if (nn->type == NET_QUERY_CONNECTED)
            		net_query_parser("gmsg %s %d %s #%s %s (spec msg)", n->nick, 0, n->host, n->channel->name, PARAM);
	}
      }
	
     lvprintf (10, "playback_conn end\n");
}

void net_playback_send(char *channame, char *format, ...)
{
  static char PBUF[1024];
  struct net_t *nn;
  va_list args;
  if (!channame || !format) return;
  va_start (args, format);
  vsnprintf(PBUF, 1023, format, args);
  va_end(args);
  for (nn = gnet; nn; nn = nn->next) {
        if (nn->type == NET_PLAYBACK_CONNECTED &&
            !strcasecmp(channame, nn->channel->name))
                tprintf(nn->sock, "%s", PBUF);
  }
}

/* The person has sent their init string */
void net_telnet_init(struct net_t *n, char *buf)
  {
    int gameslot,found, i;
    int tet_err;
    char *dec, *p;
    struct channel_t *chan;
    struct net_t *nsock;
    

    /* If init string is in fact "playerquery", return number of logged in players, and kill them */
    if (!strcasecmp(buf, "playerquery"))
      {
        tprintf(n->sock, "Number of players logged in: %d\n", numplayers(n->channel));
        return;
      }
      
    /* Version - Return Full version of server */
    if (!strcasecmp(buf, "version"))
      {
        tprintf(n->sock,"%s.%s\n+OK\n", TETVERSION, SERVERBUILD);
        return;
      }
      
    /* ListChan - List of all channels */
    if (!strcasecmp(buf, "listchan"))
      {
        chan=chanlist;
        while(chan!=NULL)
          {
            tprintf(n->sock,"\"%s\" \"%s\" %d %d %d %d\n",chan->name, chan->description, numplayers(chan), chan->maxplayers, chan->priority, chan->status);
            chan=chan->next;
          }
        tprintf(n->sock,"+OK\n");
        return;
      }
      
    /* Listuser - List of all users */
    if (!strcasecmp(buf,"listuser"))
      {
        chan=chanlist;
        while (chan!=NULL)
          {
            nsock=chan->net;
            while (nsock!=NULL)
              {
                if(nsock->type==NET_CONNECTED)
                  {
                    tprintf(n->sock,"\"%s\" \"%s\" \"%s\" %d %d %d \"%s\"\n",nsock->nick, nsock->team, nsock->version,nsock->gameslot,nsock->status,nsock->securitylevel,nsock->channel->name);
                  }
                nsock=nsock->next;
              }
            chan=chan->next;
          }
        tprintf(n->sock,"+OK\n");
        return;
      }
      
    if (strlen(buf) < 7)
      {/* Invalid... quit */
        killsock(n->sock);lostnet(n);
        return;
        
      }
      
    /* Work out game slot */
    gameslot=0; found=1;
    while ( (gameslot < n->channel->maxplayers) && (found) )
      {
        gameslot++;
        found=0;
        nsock=n->channel->net;
        while (nsock!=NULL)
          {
            if( ( (nsock->type==NET_CONNECTED) || (nsock->type==NET_WAITINGFORTEAM) )&&(nsock->gameslot==gameslot) ) found=1;
            nsock=nsock->next;
          }
      }

    if (found)
      { /* No free Gameslots, so tell that the server is full!! */
        tprintf(n->sock,"noconnecting Server is Full!\xff");
        killsock(n->sock); lostnet(n); return;
      }
    
    /* Set Game Socket */
    n->gameslot = gameslot;
    
    tet_err = tet_decrypt(buf);
    if (tet_err != 0)
      { /* Error */
        killsock(n->sock); lostnet(n);
        return;
      }
     
    dec = tet_dec2str(buf);

/* This is used instead of sscanf to get more control tetrisstart message
   with the bound check according to NICKLEN and VERLEN. */
    i = 0;
    if ((p = strtok(dec, " "))) {
        i++;
        if (strcmp(p, "tetrisstart")) {
                killsock(n->sock); lostnet(n); return;
        }
        if ((p = strtok(NULL, " "))) {
                i++;
                if (strlen(p) > NICKLEN) {
                        tprintf(n->sock, "noconnecting Invalid Sign-on (nickname too long)\xff");
                        killsock(n->sock); lostnet(n); return;
                }
                strncpy(n->nick, p, NICKLEN);
                n->nick[NICKLEN] = 0;
                if ((p = strtok(NULL, " "))) {
                        strncpy(n->version, p, VERLEN);
                        n->version[VERLEN] = 0;
                        i++;
                }
        }
   }
   if (i != 3) { killsock(n->sock); lostnet(n); return; }


    /* OK, so dec should now hold "tetrisstart <nickname> <version number> */
    // i = sscanf(dec,"tetrisstart %30[^\x20] %10s", n->nick, n->version);
    
        
    // if (i < 2)
      // {/* To few conversions - Player dies*/
        // killsock(n->sock); lostnet(n);
	// return;
      // }
      
    /* Ensure a valid nickname */
    if (!strcasecmp(n->nick,"server") || !net_query_isvalidnick(n->nick))
      {
        lvprintf(4,"%s Disconnected due to invalid nickname: %s\n",n->host,n->nick);
        tprintf(n->sock, "noconnecting Nickname not allowed!\xff");
        killsock(n->sock); lostnet(n);
	    return;
      }
    
    /* Ensure that Version is OK */
    if (tet_checkversion(n->version) == -1)
      {
        tprintf(n->sock, "noconnecting TetriNET version (%s) does not match Server's (%s)!\xff", n->version, TETVERSION);
        killsock(n->sock); lostnet(n);
        return;
      }
    
    /* Ensure that no-one else has this nick */
    found=0;
    chan=chanlist;
    if (net_query_nickfound(n->nick, n))
      {
        tprintf(n->sock, "noconnecting Nickname already exists on server!\xff");
        killsock(n->sock); lostnet(n);
        return;        
      }

    n->team[0] = 0; /* Clear Team */
    
    /* Now waiting for team */
    n->type = NET_WAITINGFORTEAM;
    
    /* Send Winlist to this new arrival */
    sendwinlist(n);

    /* Send them their player number */
    tprintf(n->sock, "playernum %d\xff",gameslot);
    
    
  }

/* Someone has just connected. So lets answer them */
void net_telnet(struct net_t *n, char *buf)
  {
    unsigned long ip;
    struct net_t *net;


    net=malloc(sizeof(struct net_t));
    net->next=NULL;
    
    net->sock=answer(n->sock,&ip,0);
    
    while ((net->sock==(-1)) && (errno==EAGAIN))
      net->sock=answer(n->sock,&ip,0);
	setopt(net->sock, 0);
    /* Save the port stuff */
    net->addr=ip;
    net->port=n->port;
    net->securitylevel=LEVEL_NORMAL;
    net->status=STAT_NOTPLAYING;
/*
    net->timeout_outgame = game.timeout_outgame;
    net->timeout_ingame = game.timeout_ingame;
*/
    net->timeout_outgame = 30;
    net->timeout_ingame = 30;
	do_async_dns(net);
    net->type = NET_WAITINGFORDNS;
	/* net has not been added to socket list */
	/* EOF on this will be EOF on unknown socket */
	
}

void do_async_dns (struct net_t *n) {
	char n1[4], n2[4], n3[4], n4[4];
	char buf[1024];
    int res_id;

    sprintf(n1,"%lu", (unsigned long)(n->addr&0xff000000)/(unsigned long)0x1000000);
    sprintf(n2,"%lu", (unsigned long)(n->addr&0x00ff0000)/(unsigned long)0x10000);
    sprintf(n3,"%lu", (unsigned long)(n->addr&0x0000ff00)/(unsigned long)0x100);
    sprintf(n4,"%lu", (unsigned long)n->addr&0x000000ff);
	sprintf(buf, "%s.%s.%s.%s.in-addr.arpa.", n4, n3, n2, n1);
	sprintf(n->host, "%s.%s.%s.%s", n1, n2, n3, n4);
	sprintf(n->ip, "%s.%s.%s.%s", n1, n2, n3, n4);
	res_id = query_do(buf);
	add_rnet(n, res_id);
}

void net_donedns(struct net_t *net) {

	if (net->type != NET_WAITINGFORDNS) return;
	rem_rnet(net);
    switch(net->port) {
    	case TELNET_PORT: { net_telnet_donedns (net); break; }
    	case QUERY_PORT:  { net_query_donedns (net); break; }
    	case PLAYBACK_PORT: { net_playback_donedns (net); break; }
    	default: { break; }
    }
}

void net_telnet_donedns( struct net_t *net) {
	int k, l, x, y;
	char strg[121];
    struct channel_t *chan, *ochan;
	
    if(net->sock <0)
      {
        lvprintf(4,"Failed TELNET incoming connection from %s", net->host);
        killsock(net->sock);
        free(net);
        return;
      }
	setsock(net->sock, 0);
      
    /* Is this person banned? */

    if (is_explicit_banned(net))
      {
        /* tprintf(net->sock,"noconnecting You are banned. Server is closed for testing. Please try again next day!!!\xff");
	*/
        tprintf(net->sock,"noconnecting Your host is banned from this server.\xff");
        killsock(net->sock);
        free(net);
        return;
      }
    /* Find a channel */
    chan = chanlist;
	ochan = NULL;

    while ( chan != NULL )
      {
        if ( ((ochan == NULL) || (chan->priority > ochan->priority)) && (numplayers(chan) < chan->maxplayers) && (chan->priority!=0))
          ochan=chan; /* Found a likely channel */
        chan=chan->next;
      }


    /* Save the port stuff */
    if (ochan == NULL)
      { /* No channels found, so create a new one :P */
        if (numchannels() < game.maxchannels)
          {
            chan=chanlist;
            while ( (chan!=NULL) && (chan->next!=NULL) ) chan=chan->next;
            
            
            if (chan==NULL)
              {
                chanlist = malloc(sizeof(struct channel_t));
                chan=chanlist;
              }
            else
              {
                chan->next = malloc(sizeof(struct channel_t));
                chan=chan->next;
              }
              
            chan->next=NULL;
            chan->net=NULL;
            
            
            
            chan->maxplayers=DEFAULTMAXPLAYERS;
            chan->status=STATE_ONLINE;
            chan->description[0]=0;
            chan->priority=DEFAULTPRIORITY;
            chan->sd_mode=SD_NONE;
            chan->persistant=0;
            
            /* Copy default settings */
            chan->starting_level=game.starting_level;
            chan->lines_per_level=game.lines_per_level;
            chan->level_increase=game.level_increase;
            chan->lines_per_special=game.lines_per_special;
            chan->special_added=game.special_added;
            chan->special_capacity=game.special_capacity;
            chan->classic_rules=game.classic_rules;
            chan->average_levels=game.average_levels;
            chan->sd_timeout=game.sd_timeout;
            chan->sd_lines_per_add=game.sd_lines_per_add;
            chan->sd_secs_between_lines=game.sd_secs_between_lines;
            strcpy(chan->sd_message,game.sd_message);
            chan->block_leftl=game.block_leftl;
            chan->block_leftz=game.block_leftz;
            chan->block_square=game.block_square;
            chan->block_rightl=game.block_rightl;
            chan->block_rightz=game.block_rightz;
            chan->block_halfcross=game.block_halfcross;
            chan->block_line=game.block_line;
            chan->special_addline=game.special_addline;
            chan->special_clearline=game.special_clearline;
            chan->special_nukefield=game.special_nukefield;
            chan->special_randomclear=game.special_randomclear;
            chan->special_switchfield=game.special_switchfield;
            chan->special_clearspecial=game.special_clearspecial;
            chan->special_gravity=game.special_gravity;
            chan->special_quakefield=game.special_quakefield;
            chan->special_blockbomb=game.special_blockbomb;
            chan->stripcolour=game.stripcolour;
            chan->serverannounce=game.serverannounce;
            chan->pingintercept=game.pingintercept;
	    chan->winlist_file=game.winlist_file;
	    strncpy(chan->game_type, game.game_type, GAMETYPELEN-1);
	    chan->game_type[GAMETYPELEN-1] = 0;
  
            k=0;l=1;
            while (l)
              {
                k++;
                ochan=chanlist;
                if (k==1)
                  {
                    sprintf(strg,"%s", DEFAULTCHANNEL);
                    strncpy(chan->name,strg,CHANLEN-1); chan->name[CHANLEN-1]=0;
                  }
                else
                  {
                    sprintf(strg,"%s%d", DEFAULTCHANNEL,k);
                    strncpy(chan->name,strg,CHANLEN-1); chan->name[CHANLEN-1]=0;
                  }
                l=0;
                while( (ochan != NULL) && (!l) )
                  {
                    if ( (!strcasecmp(chan->name,ochan->name)) && (chan!=ochan))
                      l=1;
                    else
                      ochan=ochan->next;
                  }
              }
  
          }
        else
          {
            tprintf(net->sock,"noconnecting Server is Full!\xff");
            killsock(net->sock);  
            free(net);
            return;
          }
      }
    else
      {
        chan=ochan; /* Found a channel */
      }    
    net->channel = chan;
    addnet(chan,net);
    
    net->type=NET_TELNET_INIT;
    strcpy(net->nick,"???");
    
    /* Clear their Field */
    for(y=0;y<FIELD_MAXY;y++)
      for(x=0;x<FIELD_MAXX;x++)
        net->field[x][y]=0; /* Nothing */
    
    /* Now we wait for the client init string */
  }
  
  
void lostnet(struct net_t *n)
  {
    int found,playing;
    char MSG[TEAMLEN+4];
    struct net_t *nsock;
    struct channel_t *chan,*ochan;
    
    /* Inform all other active players this one has left iff this one was connected "properly"! */
    if ( n->type == NET_CONNECTED )
      {
        playing=0;
        found=0;/* Number of different teams/players playing */
        MSG[0]=0; /* Store playing team name here */
        nsock=n->channel->net;
	net_query_parser("playerleave %s %d %s #%s", n->nick, n->gameslot, n->host, n->channel->name);
        net_playback_send(n->channel->name, "playerleave %d\xff", n->gameslot);
        while (nsock!=NULL)
          {
            if ( (nsock!=n) && (nsock->type == NET_CONNECTED) )
              { /* Different player, connected, in same channel */
                tprintf(nsock->sock,"playerleave %d\xff", n->gameslot);
                
                if (strcasecmp(MSG,nsock->team))
                  {/* Different team, so add player */
                    found++;
                    if (nsock->status == STAT_PLAYING) playing++;
                    strcpy(MSG,nsock->team);
                  }
                else if (MSG[0]==0) 
                  {
                    found++;
                    if (nsock->status == STAT_PLAYING) playing++;
                  }
              }
            nsock=nsock->next;
          }
        
        /* If 1 or less players/teams now are playing, AND the player that quit WAS playing, STOPIT */
        if ( (playing <= 1) && (n->channel->status == STATE_INGAME) && (n->status == STAT_PLAYING) )
          {
            nsock=n->channel->net;
            while (nsock!=NULL)
              {
                if ( (nsock!=n) && (nsock->type == NET_CONNECTED))
                  {
                    tprintf(nsock->sock,"endgame\xff");

                    nsock->status=STAT_NOTPLAYING;
                  }
                nsock=nsock->next;
              }
            net_playback_send(n->channel->name, "endgame\xff");
            n->channel->status = STATE_ONLINE;
          }
      }
          
    /* If we're the only numplayers players, then we delete the channel */
    if ( (numallplayers(n->channel)==1) && (!n->channel->persistant) )
      {
        chan=chanlist;
        ochan=NULL;
        while ( (chan != n->channel) && (chan != NULL) ) 
          {
            ochan=chan;
            chan=chan->next;
          }
        if (chan != NULL)
          {
            if (ochan != NULL)
              ochan->next=chan->next;
            else
              chanlist=chan->next;
            free(chan); 
          }
      }
    else
      remnet(n->channel,n);
      
    free(n);
  }

void net_eof(int z)
  {
    struct channel_t *chan;
    struct net_t *nsock, *nn;		
    
    nsock=NULL;
    chan=chanlist;
    while ( (chan!=NULL) && (nsock==NULL))
      {
        nsock=chan->net;
        while ((nsock!=NULL) && (nsock->sock!=z))
          {
            nsock=nsock->next;
          }
        chan=chan->next;
      }
    if (nsock==NULL)  /* retry for non channel related */
      {
	for(nn = gnet; nn; nn = nn->next) {
		if (nn->sock == z) {
			if (nn->type == NET_QUERY_CONNECTED ||
			    nn->type == NET_QUERY_INIT ||
			    nn->type == NET_PLAYBACK_INIT ||
			    nn->type == NET_PLAYBACK_CONNECTED) {
				close(z); killsock(z); net_query_lostnet(nn);
				return;
			}
		}
	}
        lvprintf(3,"Socket(%d): EOF on unknown socket\n",z);
        close(z); killsock(z);
        return;
      }	
 
    killsock(z); lostnet(nsock); 
  }
  
void got_term(int z)
  {
    lvprintf(1,"Got TERM Signal - Quitting\n");
    /* Remove PID */
    delpid();
    /* Done */
    exit(0); 
  }

void init_main(void)
  {
    struct sigaction sv;
    
    lvprintf(0,"\nTetriNET for Linux V%s.%s\n---------------------------------\n", TETVERSION, SERVERBUILD);
    

    gnet=NULL;
    chanlist=NULL;
    getmyhostname(myhostname);
    eff_gamecount = 0;
    visit = 0;
    
    /* set up error traps: We DON'T want Broken pipes!!! In fact, what the hell ARE broken pipes anyway. */
    sv.sa_handler=SIG_IGN; sigaction(SIGPIPE,&sv,NULL);
    
    /* We want to shut down cleanly */
    sv.sa_handler=got_term; sigaction(SIGTERM,&sv,NULL);
  


  }

void init_resolver(void) {
	rnet = NULL;
	ov_asynch = 1;
	ov_type = adns_r_ptr;
	ov_pipe = 0;
	ensure_adns_init();
}

int probe_async_dns(void) {
	adns_query q;
	adns_answer *answer;
	void *qun_v;
	int maxresfd;
	fd_set res_rfds, res_wrfds, res_expfds;
	struct timeval *tv, tvbuf;
	struct timeval tv2;
	int r, ret;
	char result[256];
	struct net_t *nn;
	int found;

	maxresfd = 0;
	FD_ZERO(&res_rfds);
	FD_ZERO(&res_wrfds);
	FD_ZERO(&res_expfds);
	tv2.tv_sec = 0;
	tv2.tv_usec = 0;
	tv = &tv2;
	found = 0;
	adns_beforeselect(ads, &maxresfd, &res_rfds, &res_wrfds, &res_expfds,
					  &tv, &tvbuf, 0);
	r = select(maxresfd, &res_rfds, &res_wrfds, &res_expfds, tv);
	if ( r == -1 ) {
		if (errno == EINTR) return 0;
		lvprintf(1, "Failed res select: %d\n", errno);
		return 0;
	}
	adns_afterselect(ads, maxresfd, &res_rfds, &res_wrfds, &res_expfds, 0);
    for (;;) {
        q = 0;
        result[0] = 0;
        r = adns_check(ads, &q, &answer, &qun_v);
        if (r == EAGAIN) break;
        if (r == ESRCH) return 0;
        ret = query_done(qun_v, answer, result);
        nn = find_rnet(ret);
        if (nn == NULL) return 0;
        if (result[0] != 0)
            strcpy(nn->host, result);
        net_donedns(nn);
        found = 1;
    }
	return found;
}

void check_timeouts(void)
  {
    struct net_t *n, *nextn;
    struct channel_t *chan, *nextchan;
    int found,i;
    
    found=0;
    visit++;
    if ((visit % CYCLE2) == 0) {
    chan=chanlist;
    while ( (chan!=NULL) )
      {
        n=chan->net;
	nextchan = chan->next;

        while ( (n!=NULL) )
          {
	    nextn = n->next;
    	    if (n->type == NET_QUERY_INIT || n->type == NET_PLAYBACK_CONNECTED || n->type == NET_PLAYBACK_INIT) break;
            if (n->status == STAT_PLAYING)
		n->timeout_ingame-=CYCLE2;
	    else
		n->timeout_outgame-=CYCLE2;
	    if (n->timeout_outgame == (120/CYCLE2)*CYCLE2 + (game.timeout_outgame%CYCLE2)) {
		tprintf(n->sock,"pline 0 %cYou are about to get disconnected in %d-%d seconds.\xff",RED, 120-CYCLE2, 120+CYCLE2);
		tprintf(n->sock,"pline 0 %cTo avoid disconnection. Please say something in partyline.\xff",RED);
	    }
            if (n->timeout_ingame <= 0 || n->timeout_outgame == 0)
              { /* Timeout has occurred */
            
                switch (n->type)
                  {
                    case NET_TELNET_INIT:
                    case NET_WAITINGFORTEAM:
                    case NET_CONNECTED:
                      {
			net_query_parser("timeout %s %d %s #%s", n->nick, n->gameslot, n->host, n->channel->name);
					    if (n->type != NET_TELNET_INIT)
                        	tprintf(n->sock,"pline 0 %cYou have timed out! Disconnecting!\xff",RED);
						else 
                        	tprintf(n->sock,"noconnecting You have timed out!\xff");
                        killsock(n->sock);
                        lostnet(n);
					    break;
                      }
                  }
              }
	      n = nextn;
          }
	  chan = nextchan;
      }
    } /* End visit */
   
    /* Sudden Death Timeouts */
    chan=chanlist;
    while (chan!=NULL)
      {
	if (numplayers(chan) < 1) {
		chan = chan->next;
		continue;
	}
        if ( (chan->status==STATE_INGAME) && (chan->sd_mode!=SD_NONE) )
          {
            chan->sd_timeleft-=CYCLE;
            if(chan->sd_timeleft <=0)
              {/* Timeout */
                n=chan->net;
		if (chan->sd_mode == SD_INIT)
                	net_playback_send(n->channel->name, "gmsg %s\xff", n->channel->sd_message);
                while(n!=NULL)
                  {
                    if ((n->type==NET_CONNECTED) )
                      {
                        if (chan->sd_mode==SD_INIT)
                          {
                            tprintf(n->sock,"gmsg %s\xff",n->channel->sd_message);
                          }
                        else
                          {
                            net_playback_send(n->channel->name, "sb 0 a 0\xff");
                            for(i=0;i<n->channel->sd_lines_per_add;i++) {
			      n->field_changes--;
			      n->max_pieces_left += 3;
                              tprintf(n->sock,"sb 0 a 0\xff");
                              net_playback_send(n->channel->name, "sb 0 a 0\xff");
			    }
                          }
                      }
                    n=n->next;
                  }
                chan->sd_timeleft=chan->sd_secs_between_lines;
                chan->sd_mode=SD_WAIT;
              }
          }
        chan=chan->next;
      }
  }
  


int main(int argc, char *argv[])
  {
    int xx;
    char buf[1050];
    int buf_len;
    int forknum;
    long int timeticks, otimeticks;
 
    /* Initialise */
    printf("Loading Tetrix. Please wait...\n");
    init_main();
	init_resolver();
    init_game();
    printf("Initializing security/ban list...\n");
    init_security();
    init_banlist(banlist, MAXBAN);
    read_banlist(FILE_BAN, banlist, MAXBAN);
    init_banlist(combanlist, MAXBAN);
    read_banlist(FILE_BAN_COMPROMISE, combanlist, MAXBAN);
    init_allowlist();
    read_allowlist();
    printf("Initializing winlist...\n");
    init_winlist(winlist, MAXWINLIST);
    init_winlist(winlist2, MAXWINLIST);
    init_winlist(winlist3, MAXWINLIST);
    readwinlist(FILE_WINLIST, winlist, MAXWINLIST); 
    readwinlist(FILE_WINLIST2, winlist2, MAXWINLIST); 
    readwinlist(FILE_WINLIST3, winlist3, MAXWINLIST); 
    sleep(1);
    printf("Initialize network connection...\n");
    init_net();
    printf("Gameplay ... ");
    usleep(300000);
    init_telnet_port();
    printf("Spectator ... ");
    usleep(300000);
    init_playback_port();
    printf("Ircadm ... \n");
    init_query_port();
    printf("Completed!!!\n");

    if (securityread() < 0)
      securitywrite();
    
    /* Now fork out, and start a new process group */
    /* Fork. If we are the parent, quit, if child, continue */
    if ((forknum=fork()) == -1)
      {
        printf("Error: Unable to fork new process\n");
        exit(5);
      }
    if (forknum > 0) 
      {
        exit(0);
      }
    setsid();
    
    /* Close all stdio */
    close(0);
    close(1);
    close(2);
    
    /* Write out PID */
    writepid();                                      
    
    /* Reset time */
    timeticks = time(NULL);
    otimeticks = timeticks;

    while (1)
      {
        
        timeticks=time(NULL);
        if ((timeticks-otimeticks) >= CYCLE)
          { /* Check timeouts */
            check_timeouts();
	    otimeticks = timeticks;
          }

        /* flush sockets */
        dequeue_sockets();

		/* check DNS sockets */
		while(probe_async_dns() == 1);
        
        /* Read data from a currently waiting socket */
        xx=sockgets(buf, &buf_len);
        if (xx>=0)  /* Non-error */
          {
            net_activity(xx, buf, buf_len);
          }
        else if (xx==-1)        /* EOF from someone */
          {
            lvprintf(4,"Close Socket\n");
            net_eof(buf_len);	/* Close this socket */
          }
        else if (xx==-2)	/* Select() error */
          {
            lvprintf(4,"Select error - Whatever the hell that is supposed to be..\n");
          }
          
        
      }
  }
