// Emacs style mode select -*- C++ -*-
//---------------------------------------------------------------------------
//
// Copyright(C) 2000 Simon Howard
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
//--------------------------------------------------------------------------
//
// Server listing system
// clients can connect and add themselves to the server list,
// or retrieve a copy of the list.
// other options also: kill server, get stats
//
// By Simon Howard
//
//--------------------------------------------------------------------------

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <time.h>
#include <netinet/in.h>
#include <sys/socket.h>

#include "cmds.h"
#include "main.h"
#include "serv.h"

struct in_addr server_list[MAX_SERVERS];
int server_num = 0;
int num_adds = 0, num_retrieves = 0;   // stats

// add to server list

void Cmd_AddServer(connection_t *conn, char *extra_data)
{
  int i;

  // check if already in list first

  for(i=0; i<MAX_SERVERS; i++)
    {
      if(!memcmp(&server_list[i], &conn->remote_addr, sizeof(*server_list)))
	break;
    }

  // add to list, only if not already in list
  
  if(i == MAX_SERVERS)
    {
      num_adds++;
      
      memcpy(&server_list[server_num], &conn->remote_addr,
	     sizeof(*server_list));
      
      // store next server in the next slot
      // loop around when we get to the end of the list
      
      server_num = (server_num + 1) % MAX_SERVERS;
    }

  // disconnect, now we are done
  
  connection_message(conn, "added to server list");
  //  Disconnect(conn);
}

// send server list

void Cmd_ListServers(connection_t *conn, char *extra_data)
{
  char buffer[BUFFER_SIZE];
  int i;

  num_retrieves++;
  
  // go thru server list
  // we start from the most recently added and go backwards

  i = server_num;
  
  do
    {
      i = (i - 1) % MAX_SERVERS;

      if(i == server_num) break;
      if(!server_list[i].s_addr) break;
      
      WriteMessage(conn, "%s", inet_ntoa(server_list[i]));
    } while(1);

  // disconnect client, we're done now

  connection_message(conn, "sent server list");
  //  Disconnect(conn);
}

void Cmd_ListStats(connection_t *conn, char *extra_data)
{
  char buffer[BUFFER_SIZE];
  int uptime; // uptime in seconds

  uptime = time(NULL) - startup_time;

  WriteMessage(conn, "stats");
  WriteMessage(conn, "");
  WriteMessage(conn, "uptime: %02i:%02i:%02i",
		uptime / (60*60), (uptime / 60) % 60, uptime % 60);
  WriteMessage(conn, "adds: %i", num_adds);
  WriteMessage(conn, "lists: %i", num_retrieves);

  connection_message(conn, "sent stats");
}


void Cmd_KillServer(connection_t *conn, char *extra_data)
{ 
  connection_message(conn, "user killed server");
  exit(0);
}

void Cmd_SetMOTD(connection_t *conn, char *extra_data)
{    
  connection_message(conn, "user changed MOTD");

  if(motd)
    free(motd);

  while(*extra_data &&
	(*extra_data==' ' || !isprint(*extra_data)))
    extra_data++;

  if(*extra_data)
    motd = strdup(extra_data);
  else
    motd = NULL;
}

void Cmd_Help(connection_t *conn, char *extra_data)
{
  int i;

  WriteMessage(conn, "commands:");

  for(i=0; i<num_server_commands; i++)
    {
      WriteMessage(conn, "%c %s",
		   server_commands[i].local_only ? '*' : ' ',
		   server_commands[i].name);
    }
  WriteMessage(conn, "(* -- localhost only)");

  WriteMessage(conn, "use add to add your ip to server list");
  WriteMessage(conn, "use list to view the server list");
}

void Cmd_Version(connection_t *conn, char *extra_data)
{
  WriteMessage(conn, "%s by Fraggle", s_argv[0]);
  WriteMessage(conn, __DATE__ __TIME__);
}

void Cmd_Disconnect(connection_t *conn, char *extra_data)
{
  Disconnect(conn);
}

// kick all connected 

void Cmd_MassKick(connection_t *conn, char *extra_data)
{
  char *reason;

  reason = extra_data[0] ? extra_data : "administrative kick";

  connection_message(conn, "mass kick(%s)", reason);

  while(connections.next)
    {
      KickConnection(connections.next, reason);
    }
}

extern void BuildHTML();

void Cmd_HTML()
{
  BuildHTML();
}

//---------------------------------------------------------------------------
//
// Server Commands
//

servercommand_t server_commands[] =
  {
    {"kill",       Cmd_KillServer, true},
    {"masskick",   Cmd_MassKick, true},
    {"motd",       Cmd_SetMOTD, true},
    {"html",       Cmd_HTML, true},
    {"add",        Cmd_AddServer},
    {"list",       Cmd_ListServers},
    {"stats",      Cmd_ListStats},
    {"version",    Cmd_Version},
    {"help",       Cmd_Help},
    {"exit",       Cmd_Disconnect},
    {"disconnect", Cmd_Disconnect},
  };

int num_server_commands = sizeof(server_commands) / sizeof(*server_commands);
