/*
 * Copyright (C) 1997-2001 Id Software, Inc.
 *
 * 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.
 *
 * =======================================================================
 *
 * Game side of server CMDs. At this time only the ipfilter.
 *
 * =======================================================================
 */

#include "header/local.h"

#define MAX_IPFILTERS 1024

void
Svcmd_Test_f(void)
{
	gi.cprintf(NULL, PRINT_HIGH, "Svcmd_Test_f()\n");
}

/*
 * ==============================================================================
 *
 * PACKET FILTERING
 *
 *
 * You can add or remove addresses from the filter list with:
 *
 * addip <ip>
 * removeip <ip>
 *
 * The ip address is specified in dot format, and any unspecified
 * digits will match any value, so you can specify an entire class
 * C network with "addip 192.246.40".
 *
 * Removeip will only remove an address specified exactly the same
 * way. You cannot addip a subnet, then removeip a single host.
 *
 * listip
 *  Prints the current list of filters.
 *
 * writeip
 *  Dumps "addip <ip>" commands to listip.cfg so it can be execed
 *  at a later date. The filter lists are not saved and restored
 *  by default, because I belive it would cause too much confusion.
 *
 * filterban <0 or 1>
 *  If 1 (the default), then ip addresses matching the current list
 *  will be prohibited from entering the game.This is the default
 *  setting.
 *  If 0, then only addresses matching the list will be allowed.
 *  This lets you easily set up a private game, or a game that only
 *  allows players from your local network.
 *
 * ==============================================================================
 */

typedef struct
{
	unsigned mask;
	unsigned compare;
} ipfilter_t;

static ipfilter_t ipfilters[MAX_IPFILTERS];
static int numipfilters;

static qboolean
StringToFilter(char *s, ipfilter_t *f)
{
	char num[128];
	int i, j;
	YQ2_ALIGNAS_TYPE(unsigned) byte b[4];
	YQ2_ALIGNAS_TYPE(unsigned) byte m[4];

	if (!s || !f)
	{
		return false;
	}

	for (i = 0; i < 4; i++)
	{
		b[i] = 0;
		m[i] = 0;
	}

	for (i = 0; i < 4; i++)
	{
		if ((*s < '0') || (*s > '9'))
		{
			gi.cprintf(NULL, PRINT_HIGH, "Bad filter address: %s\n", s);
			return false;
		}

		j = 0;

		while (*s >= '0' && *s <= '9')
		{
			num[j++] = *s++;
		}

		num[j] = 0;
		b[i] = (int)strtol(num, (char **)NULL, 10);

		if (b[i] != 0)
		{
			m[i] = 255;
		}

		if (!*s)
		{
			break;
		}

		s++;
	}

	f->mask = *(unsigned *)m;
	f->compare = *(unsigned *)b;

	return true;
}

qboolean
SV_FilterPacket(char *from)
{
	int i;
	unsigned in;
	YQ2_ALIGNAS_TYPE(unsigned) byte m[4];
	char *p;

	if (!from)
	{
		return false;
	}

	i = 0;
	p = from;

	while (*p && i < 4)
	{
		m[i] = 0;

		while (*p >= '0' && *p <= '9')
		{
			m[i] = m[i] * 10 + (*p - '0');
			p++;
		}

		if (!*p || (*p == ':'))
		{
			break;
		}

		i++, p++;
	}

	in = *(unsigned *)m;

	for (i = 0; i < numipfilters; i++)
	{
		if ((in & ipfilters[i].mask) == ipfilters[i].compare)
		{
			return (filterban->value != 0);
		}
	}

	return (filterban->value == 0);
}

void
SVCmd_AddIP_f(void)
{
	int i;

	if (gi.argc() < 3)
	{
		gi.cprintf(NULL, PRINT_HIGH, "Usage:  addip <ip-mask>\n");
		return;
	}

	for (i = 0; i < numipfilters; i++)
	{
		if (ipfilters[i].compare == 0xffffffff)
		{
			break; /* free spot */
		}
	}

	if (i == numipfilters)
	{
		if (numipfilters == MAX_IPFILTERS)
		{
			gi.cprintf(NULL, PRINT_HIGH, "IP filter list is full\n");
			return;
		}

		numipfilters++;
	}

	if (!StringToFilter(gi.argv(2), &ipfilters[i]))
	{
		ipfilters[i].compare = 0xffffffff;
	}
}

void
SVCmd_RemoveIP_f(void)
{
	ipfilter_t f;
	int i, j;

	if (gi.argc() < 3)
	{
		gi.cprintf(NULL, PRINT_HIGH, "Usage:  sv removeip <ip-mask>\n");
		return;
	}

	if (!StringToFilter(gi.argv(2), &f))
	{
		return;
	}

	for (i = 0; i < numipfilters; i++)
	{
		if ((ipfilters[i].mask == f.mask) &&
			(ipfilters[i].compare == f.compare))
		{
			for (j = i + 1; j < numipfilters; j++)
			{
				ipfilters[j - 1] = ipfilters[j];
			}

			numipfilters--;
			gi.cprintf(NULL, PRINT_HIGH, "Removed.\n");
			return;
		}
	}

	gi.cprintf(NULL, PRINT_HIGH, "Didn't find %s.\n", gi.argv(2));
}

void
SVCmd_ListIP_f(void)
{
	int i;
	YQ2_ALIGNAS_TYPE(unsigned) byte b[4];

	gi.cprintf(NULL, PRINT_HIGH, "Filter list:\n");

	for (i = 0; i < numipfilters; i++)
	{
		*(unsigned *)b = ipfilters[i].compare;
		gi.cprintf(NULL, PRINT_HIGH, "%3i.%3i.%3i.%3i\n", b[0],
				b[1], b[2], b[3]);
	}
}

void
SVCmd_WriteIP_f(void)
{
	FILE *f;
	char name[MAX_OSPATH];
	YQ2_ALIGNAS_TYPE(unsigned) byte b[4];
	int i;
	cvar_t *game;

	game = gi.cvar("game", "", 0);

	if (!*game->string)
	{
		sprintf(name, "%s/listip.cfg", GAMEVERSION);
	}
	else
	{
		sprintf(name, "%s/listip.cfg", game->string);
	}

	gi.cprintf(NULL, PRINT_HIGH, "Writing %s.\n", name);

	f = Q_fopen(name, "wb");

	if (!f)
	{
		gi.cprintf(NULL, PRINT_HIGH, "Couldn't open %s\n", name);
		return;
	}

	fprintf(f, "set filterban %d\n", (int)filterban->value);

	for (i = 0; i < numipfilters; i++)
	{
		*(unsigned *)b = ipfilters[i].compare;
		fprintf(f, "sv addip %i.%i.%i.%i\n", b[0], b[1], b[2], b[3]);
	}

	fclose(f);
}

/*
 * ServerCommand will be called when an "sv" command is issued.
 * The game can issue gi.argc() / gi.argv() commands to get the rest
 * of the parameters
 */
void
ServerCommand(void)
{
	char *cmd;

	cmd = gi.argv(1);

	if (Q_stricmp(cmd, "test") == 0)
	{
		Svcmd_Test_f();
	}
	else if (Q_stricmp(cmd, "addip") == 0)
	{
		SVCmd_AddIP_f();
	}
	else if (Q_stricmp(cmd, "removeip") == 0)
	{
		SVCmd_RemoveIP_f();
	}
	else if (Q_stricmp(cmd, "listip") == 0)
	{
		SVCmd_ListIP_f();
	}
	else if (Q_stricmp(cmd, "writeip") == 0)
	{
		SVCmd_WriteIP_f();
	}
	else
	{
		gi.cprintf(NULL, PRINT_HIGH, "Unknown server command \"%s\"\n", cmd);
	}
}
