All pastes #18450 Raw Edit

Mine

public text v1 · immutable
#18450 ·published 2005-07-25 13:10 UTC
rendered paste body
/* bzflag
 * Copyright (c) 1993 - 2005 Tim Riker
 *
 * This package is free software;  you can redistribute it and/or
 * modify it under the terms of the license found in the file
 * named LICENSE that should have accompanied this file.
 *
 * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */

#ifdef _MSC_VER
#pragma warning( 4:4786)
#endif

// interface header
#include "commands.h"

#include "WorldEventManager.h"

// system implementation headers
#include <vector>
#include <string>
#include <sstream>
#ifdef HAVE_CSTDIO
#include <cstdio>
#else
#include <stdio.h>
#endif
#ifdef HAVE_CSTRING
#include <cstring>
#else
#include <string.h>
#endif
#include <time.h>

// common implementation headers
#include "Address.h"
#include "CommandManager.h"
#include "LagInfo.h"
#include "NetHandler.h"
#include "PlayerInfo.h"
#include "TimeKeeper.h"
#include "VotingArbiter.h"
#include "global.h"
#include "version.h"

// local implementation headers
#include "CmdLineOptions.h"
#include "FlagHistory.h"
#include "FlagInfo.h"
#include "PackVars.h"
#include "Permissions.h"
#include "RecordReplay.h"
#include "MasterBanList.h"


#if defined(_WIN32)
#define popen _popen
#define pclose _pclose
#endif

//"Race to 7" by RPG
bool match_in_progress = false;
//End "Race to 7" by RPG

// FIXME -- need to pull communication out of bzfs.cxx...

// extern to initialize permission groups
extern void initGroups();

// externs that poll, veto, vote, and clientquery require
extern void sendMessage(int playerIndex, PlayerId dstPlayer, const char *message);
extern void sendPlayerMessage(GameKeeper::Player *playerData, PlayerId dstPlayer,
			      const char *message);
extern CmdLineOptions *clOptions;
extern uint16_t curMaxPlayers;

// externs that ghost needs
extern void removePlayer(int playerIndex, const char *reason, bool notify=true);
extern void playerKilled(int victimIndex, int killerIndex, int reason, int16_t shotIndex, const FlagType* flagType, int phydrv, bool respawnOnBase = false);
// externs that shutdownserver requires
extern bool done;

// externs that superkill and gameover requires
extern bool gameOver;
extern char *getDirectMessageBuffer();
extern void broadcastMessage(uint16_t code, int len, const void *msg);

#include "PlayerInfo.h"
extern TeamInfo team[NumTeams];
extern void sendTeamUpdate(int playerIndex = -1, int teamIndex1 = -1, int teamIndex2 = -1);
extern int numFlags;
extern void zapFlag(FlagInfo & flag);
extern void sendFlagUpdate(FlagInfo &flag);
extern void resetFlag(FlagInfo &flag);
extern void sendDrop(FlagInfo &flag);

// externs that countdown requires
extern bool countdownActive;
extern int countdownDelay;
extern TimeKeeper countdownPauseStart;

// externs that identify and password requires
extern void sendIPUpdate(int targetPlayer, int playerIndex);
extern void sendPlayerInfo(void);

// externs that the flag command requires
extern void zapAllFlags();

tmCustomSlashCommandMap	customCommands;

class NoDigit {
public:
  bool operator() (char c) {return !isdigit(c);}
};

int getSlotNumber(std::string player) {
  int slot = -1; // invalid
  if (player[0] == '#') {
    // string player is a slot number
    player[0] = '0';
    if(find_if(player.begin(), player.end(), NoDigit()) == player.end()) {
      // a valid number is in the string
      slot = atoi(player.c_str());
    }
  } else {
    // invalid number.. may be a player callsign
    slot = GameKeeper::Player::getPlayerIDByName(player);
  }
  
  GameKeeper::Player *p = GameKeeper::Player::getPlayerByIndex(slot);
  if (!p)
    slot = -1; // not a player

  return slot;
}

static void handleMuteCmd(GameKeeper::Player *playerData, const char *message)
{
  int t = playerData->getIndex();
  if (!playerData->accessInfo.hasPerm(PlayerAccessInfo::mute)) {
    sendMessage(ServerPlayer, t, "You do not have permission to run the unmute command");
    return;
  }
  
  
  std::vector<std::string> argv = TextUtils::tokenize(message, " \t", 3, true);

  if (argv.size() < 2) {
    sendMessage(ServerPlayer, t, "Syntax: /mute <#slot | PlayerName | \"Player Name\">");
    return;
  }
  
  int i = getSlotNumber(argv[1]);
  
  char msg[MessageLen];
  
  // Player not found
  if (i < 0) {
    snprintf(msg, MessageLen, "player \"%s\" not found", argv[1].c_str());
    sendMessage(ServerPlayer, t, msg);
    return;
  }
  
  // mute the player
  GameKeeper::Player *muteData = GameKeeper::Player::getPlayerByIndex(i);
  if (muteData) {
    muteData->accessInfo.revokePerm(PlayerAccessInfo::talk);
    snprintf(msg, MessageLen, "You have been muted by %s.", playerData->player.getCallSign());
    sendMessage(ServerPlayer, i, msg);
    // confirm player is muted
    snprintf(msg, MessageLen, "player id #%d \"%s\" is now muted.", i, muteData->player.getCallSign());
    sendMessage(ServerPlayer, t, msg);
  }
}

static void handleUnmuteCmd(GameKeeper::Player *playerData, const char *message)
{
  int t = playerData->getIndex();
  if (!playerData->accessInfo.hasPerm(PlayerAccessInfo::mute)) {
    sendMessage(ServerPlayer, t, "You do not have permission to run the unmute command");
    return;
  }
  
  
  std::vector<std::string> argv = TextUtils::tokenize(message, " \t", 3, true);

  if (argv.size() < 2) {
    sendMessage(ServerPlayer, t, "Syntax: /unmute <#slot | PlayerName | \"Player Name\">");
    return;
  }
  
  int i = getSlotNumber(argv[1]);
  
  char msg[MessageLen];
  
  // Player not found
  if (i < 0) {
    snprintf(msg, MessageLen, "player \"%s\" not found", argv[1].c_str());
    sendMessage(ServerPlayer, t, msg);
    return;
  }
  
  // unmute the player
  GameKeeper::Player *unmuteData = GameKeeper::Player::getPlayerByIndex(i);
  if (unmuteData) {
    unmuteData->accessInfo.grantPerm(PlayerAccessInfo::talk);
    snprintf(msg, MessageLen, "You have been unmuted by %s.", playerData->player.getCallSign());
    sendMessage(ServerPlayer, i, msg);
    // confirm player is unmuted
    snprintf(msg, MessageLen, "player id #%d \"%s\" is now unmuted.", i, unmuteData->player.getCallSign());
    sendMessage(ServerPlayer, t, msg);
  }
}

static void handleUptimeCmd(GameKeeper::Player *playerData, const char *)
{
  float rawTime;
  int t = playerData->getIndex();
  char reply[MessageLen] = {0};

  rawTime = float(TimeKeeper::getCurrent() - TimeKeeper::getStartTime());
  snprintf(reply, MessageLen, "%s.", TimeKeeper::printTime(rawTime).c_str());
  sendMessage(ServerPlayer, t, reply);
}

static void handleServerQueryCmd(GameKeeper::Player *playerData, const char *)
{
  int t = playerData->getIndex();
  DEBUG2("Server query requested by %s [%d]\n",
	 playerData->player.getCallSign(), t);

  sendMessage(ServerPlayer, t,
	      TextUtils::format("BZFS Version: %s", getAppVersion()).c_str());
  return;
}


static void handlePartCmd(GameKeeper::Player *playerData, const char *message)
{
  std::string byeStatement = "";

  if (strlen(message) > 5) {
    if (!TextUtils::isWhitespace(*(message+5))) {
      char reply[MessageLen] = {0};
      snprintf(reply, MessageLen, "Unknown command [%s]", message);
      sendMessage(ServerPlayer, playerData->getIndex(), reply);
      return;
    }
    byeStatement = message + 6;
  }

  if (byeStatement[0] != '\0') {
    std::string message2;
    message2 = TextUtils::format("%s has left (\"%s\") ",
				 playerData->player.getCallSign(),  byeStatement.c_str());

    DEBUG2("%s has quit with the message \"%s\"\n", playerData->player.getCallSign(), byeStatement.c_str());
    sendMessage(ServerPlayer, AllPlayers, message2.c_str());
  }

  // now to kick the player
  int t = playerData->getIndex();
  removePlayer(t, byeStatement.c_str());
}


static void handleQuitCmd(GameKeeper::Player *playerData, const char *message)
{
  std::string byeStatement = "";

  if (strlen(message) > 5) {
    if (!TextUtils::isWhitespace(*(message+5))) {
      char reply[MessageLen] = {0};
      snprintf(reply, MessageLen, "Unknown command [%s]", message);
      sendMessage(ServerPlayer, playerData->getIndex(), reply);
      return;
    }
    byeStatement = message + 6;
  }

  if (byeStatement[0] != '\0') {
    std::string message2;
    message2 = TextUtils::format("%s has quit (\"%s\") ",
				 playerData->player.getCallSign(),  byeStatement.c_str());

    DEBUG2("%s has quit with the message \"%s\"\n", playerData->player.getCallSign(), byeStatement.c_str());
    sendMessage(ServerPlayer, AllPlayers, message2.c_str());
  }

  // now to kick the player
  int t = playerData->getIndex();
  removePlayer(t, byeStatement.c_str());
}


static void handleMsgCmd(GameKeeper::Player *playerData, const char *message)
{
  int from = playerData->getIndex();
  int to= -1;

  std::string message2;
  size_t callsignStart=0, callsignEnd=0, messageStart=0;

  if (!playerData->accessInfo.hasPerm(PlayerAccessInfo::privateMessage)) {
    char reply[MessageLen] = {0};
    snprintf(reply, MessageLen, "%s, you are not presently authorized to /msg people privately", playerData->player.getCallSign());
    sendMessage(ServerPlayer, from, reply);
    return;
  }

  // start from after "/msg"
  std::string arguments = &message[4];
  std::string recipient = std::string("");

  // skip any leading whitespace
  callsignStart = 0;
  while ((callsignStart < arguments.size()) &&
	 (isspace(arguments[callsignStart]))) {
    callsignStart++;
  }

  // make sure there was _some_ whitespace after /msg
  if (callsignStart == 0) {
    sendMessage(ServerPlayer, from, "Usage: /msg \"some callsign\" some message");
    return;
  }

  // find the player name, optionally quoted
  if (arguments[callsignStart] == '"') {
    callsignStart++;

    // find the trailing quote
    bool foundQuote = false;
    callsignEnd = callsignStart;
    while ((callsignEnd + 1 < arguments.size()) &&
	   (!foundQuote)) {
      callsignEnd++;
      if (arguments[callsignEnd] == '"') {
	foundQuote = true;
	messageStart = callsignEnd + 1;
	callsignEnd--;
      }
    }

    // no quote means a mismatch
    if (!foundQuote) {
      sendMessage(ServerPlayer, from, "Quote mismatch?");
      sendMessage(ServerPlayer, from, "Usage: /msg \"some callsign\" some message");
      return;
    }

  } else {
    // unquoted callsign

    // find the first matching name (not the longest for sake of performance)
    bool foundCallsign = false;
    callsignEnd = callsignStart;
    while ((callsignEnd + 1 < arguments.size()) &&
	   (!foundCallsign)) {
      callsignEnd++;
      if (!isspace(arguments[callsignEnd])) {
	continue;
      }

      // we have a space
      recipient = arguments.substr(callsignStart, callsignEnd - callsignStart);
      messageStart = callsignEnd;

      to = GameKeeper::Player::getPlayerIDByName(recipient);
      if (to < curMaxPlayers) {
	callsignEnd--;
	foundCallsign = true;
      }
    }
  }

  recipient = arguments.substr(callsignStart, callsignEnd - callsignStart + 1);

  if (recipient[0] == '>') {
    // /msg >admin sends on admin channel, /msg >team on team channel
    recipient.erase(0,1);
    if (TextUtils::toupper(recipient) == "ADMIN") 
      to = AdminPlayers;				
    else if (TextUtils::toupper(recipient) == "TEAM") 
      to = 250 - (int)playerData->player.getTeam();
  } else {
    to = GameKeeper::Player::getPlayerIDByName(recipient);

    // valid callsign
    if ((to < 0) || (to >= curMaxPlayers)) {
      message2 = TextUtils::format("\"%s\" is not here.  No such callsign.", recipient.c_str());
      sendMessage(ServerPlayer, from, message2.c_str());
      return;
    }
  }

  // make sure there is something to send
  if ((messageStart >= arguments.size() - 1) || (messageStart == 0)) {
    // found player, but nothing to send
    message2 = TextUtils::format("No text to send to \"%s\".", recipient.c_str());
    sendMessage(ServerPlayer, from, message2.c_str());
    return;
  }

  // send the message
  sendPlayerMessage(playerData, to, arguments.c_str() + messageStart + 1);
  return;
}


static void handlePasswordCmd(GameKeeper::Player *playerData, const char *message)
{
  int t = playerData->getIndex();
  if (playerData->accessInfo.passwordAttemptsMax()) {
    DEBUG1("\"%s\" (%s) has attempted too many /password tries\n",
	   playerData->player.getCallSign(),
	   playerData->netHandler->getTargetIP());
    sendMessage(ServerPlayer, t, "Too many attempts");
  } else {
    if ((clOptions->password != "") && strncmp(message + 10, clOptions->password.c_str(), clOptions->password.size()) == 0 && clOptions->password.length() == strlen(message + 10)) {
      playerData->accessInfo.setAdmin();
      sendPlayerInfo();
      sendMessage(ServerPlayer, t, "You are now an administrator!");
    } else {
      sendMessage(ServerPlayer, t, "Wrong Password!");
    }
  }
  return;
}


static void handleSetCmd(GameKeeper::Player *playerData, const char *message)
{
  int t = playerData->getIndex();
  int setvar = playerData->accessInfo.hasPerm(PlayerAccessInfo::setVar) ? 1 : 0;
  int setall = playerData->accessInfo.hasPerm(PlayerAccessInfo::setAll) ? 1 : 0;
  char message2[MessageLen];
  if (!playerData->accessInfo.hasPerm(PlayerAccessInfo::setVar)
      && !playerData->accessInfo.hasPerm(PlayerAccessInfo::setAll)) {
    sendMessage(ServerPlayer, t, "You do not have permission to run the set command");
	DEBUG3("set failed by %s, setvar=%d, setall=%d\n",playerData->player.getCallSign(),setvar,setall);
   return;
  }
  if (Replay::enabled()) {
    sendMessage(ServerPlayer, t, "You can't /set variables in replay mode");
    return;
  }
  DEBUG3("set executed by %s, setvar=%d, setall=%d\n",playerData->player.getCallSign(),setvar,setall);
  std::string command = (message + 1);
  // we aren't case sensitive but CMDMGR is
  for (int i = 0; i < 3 /*"set"*/; ++i)
    command[i] = tolower(command[i]);
  sendMessage(ServerPlayer, t, CMDMGR.run(command).c_str());
  snprintf(message2, MessageLen, "Variable Modification Notice by %s of %s",
	   playerData->player.getCallSign(), command.c_str());
  sendMessage(ServerPlayer, AllPlayers, message2);
  return;
}


static void handleResetCmd(GameKeeper::Player *playerData, const char *message)
{
  int t = playerData->getIndex();
  if (!playerData->accessInfo.hasPerm(PlayerAccessInfo::setVar)
      && !playerData->accessInfo.hasPerm(PlayerAccessInfo::setAll)) {
    sendMessage(ServerPlayer, t, "You do not have permission to run the reset command");
    return;
  }
  if (Replay::enabled()) {
    sendMessage(ServerPlayer, t, "You can't /reset variables in replay mode");
    return;
  }
  std::string command = (message + 1);
  // we aren't case sensitive but CMDMGR is
  for (int i = 0; i < 5 /*"reset"*/; ++i)
    command[i] = tolower(command[i]);
  sendMessage(ServerPlayer, t, CMDMGR.run(command).c_str());
  return;
}


static void handleShutdownserverCmd(GameKeeper::Player *playerData, const char *)
{
  int t = playerData->getIndex();
  if (!playerData->accessInfo.hasPerm(PlayerAccessInfo::shutdownServer)) {
    sendMessage(ServerPlayer, t, "You do not have permission to run the shutdown command");
    return;
  }
  done = true;
  return;
}


static void handleSuperkillCmd(GameKeeper::Player *playerData, const char *)
{
  int t = playerData->getIndex();
  if (!playerData->accessInfo.hasPerm(PlayerAccessInfo::superKill)) {
    sendMessage(ServerPlayer, t, "You do not have permission to run the superkill command");
    return;
  }
  for (int i = 0; i < curMaxPlayers; i++)
    removePlayer(i, "/superkill");
  gameOver = true;
  if (clOptions->timeManualStart)
    countdownActive = false;
  return;
}


static void handleGameoverCmd(GameKeeper::Player *playerData, const char *)
{
  int t = playerData->getIndex();
  if (!playerData->accessInfo.hasPerm(PlayerAccessInfo::endGame)) {
    sendMessage(ServerPlayer, t, "You do not have permission to run the gameover command");
    return;
  }

  void *buf, *bufStart = getDirectMessageBuffer();
  buf = nboPackUByte(bufStart, t);
  buf = nboPackUShort(buf, uint16_t(NoTeam));
  broadcastMessage(MsgScoreOver, (char*)buf - (char*)bufStart, bufStart);
  gameOver = true;
  if (clOptions->timeManualStart) {
    countdownActive = false;
    countdownPauseStart = TimeKeeper::getNullTime();
    clOptions->countdownPaused = false;
    return;
  }
}


static void handleCountdownCmd(GameKeeper::Player *playerData, const char *message)
{
  // /countdown starts timed game, if start is manual, everyone is allowed to
  int t = playerData->getIndex();
  if (!playerData->accessInfo.hasPerm(PlayerAccessInfo::countdown)) {
    sendMessage(ServerPlayer, t, "You do not have permission to run the countdown command");
    return;
  } else if (!clOptions->timeManualStart) {
    sendMessage(ServerPlayer, t, "This server was not configured for manual clock countdowns");
    return;
  } else if (countdownDelay > 0) {
    sendMessage(ServerPlayer, t, "There is a countdown already in progress");
    return;
  }

  // if the timelimit is not set .. don't countdown
  if (clOptions->timeLimit > 1.0f) {
    std::vector<std::string> parts = TextUtils::tokenize(message, " \t",2);
    
    if (parts.size() > 1) {
      // we have an argument
      
      if (parts[1] == "pause") {
	// pause the countdown
	if (!countdownActive) {
	  sendMessage(ServerPlayer, t, "There is no active game to pause");
	  return;
	} else if (clOptions->countdownPaused) {
	  sendMessage(ServerPlayer, t, "The game is already paused");
	  return;
	}
	clOptions->countdownPaused = true;
	sendMessage(ServerPlayer, AllPlayers, TextUtils::format("Countdown paused by %s",playerData->player.getCallSign()).c_str());
	return;
      } else if (parts[1] == "resume") {
	// resume countdown if it was paused before
	if (!clOptions->countdownPaused) {
	  sendMessage(ServerPlayer, t, "The game is not paused");
	  return;
	}
	clOptions->countdownPaused = false;
	sendMessage(ServerPlayer, AllPlayers, TextUtils::format("Countdown resumed by %s",playerData->player.getCallSign()).c_str());
	return;
	      
      } else {
	// so it's the countdown delay? else tell the player how to use /countdown
	std::istringstream timespec(message+10);
	if (!(timespec >> countdownDelay)) {
	  sendMessage(ServerPlayer, t, "Usage: /countdown [<seconds>|pause|resume]");
	  return;
	}
      }
    } else {
      countdownDelay = 10;
    }
    
    // cancel here if a game is already running
    if (countdownActive) {
      sendMessage(ServerPlayer, t, "A game is already in progress");
      countdownDelay = -1;
      return;
    }

    // limit/sanity check
    const int max_delay = 120;
    if (countdownDelay > max_delay) {
      sendMessage(ServerPlayer, t, TextUtils::format("Countdown set to %d instead of %d", max_delay, countdownDelay).c_str());
      countdownDelay = max_delay;
    } else if (countdownDelay < 0) {
      sendMessage(ServerPlayer, t, TextUtils::format("Countdown set to 0 instead of %d", countdownDelay).c_str());
      countdownDelay = 0;
    }

    sendMessage(ServerPlayer, AllPlayers, TextUtils::format("Team scores reset, countdown started by %s.",playerData->player.getCallSign()).c_str());

    // let everyone know what's going on
    long int timeArray[4];
    std::string matchBegins;
    if (countdownDelay == 0) {
      matchBegins = "Match begins now!";
    } else {
      TimeKeeper::convertTime(countdownDelay, timeArray);
      std::string countdowntime = TimeKeeper::printTime(timeArray);
      matchBegins = TextUtils::format("Match begins in about %s", countdowntime.c_str());
    }
    sendMessage(ServerPlayer, AllPlayers, matchBegins.c_str());

    TimeKeeper::convertTime(clOptions->timeLimit, timeArray);
    std::string timelimit = TimeKeeper::printTime(timeArray);
    matchBegins = TextUtils::format("Match duration is %s", timelimit.c_str());
    sendMessage(ServerPlayer, AllPlayers, matchBegins.c_str());
		
    // make sure the game always start unpaused
    clOptions->countdownPaused = false;
    countdownPauseStart = TimeKeeper::getNullTime();

  } else {
    sendMessage(ServerPlayer, AllPlayers, "Team scores reset.");
    sendMessage(ServerPlayer, t, "The server is not configured for timed matches.");
  }

  // reset team scores
  for (int i = RedTeam; i <= PurpleTeam; i++) {
    team[i].team.lost = team[i].team.won = 0;
  }
  sendTeamUpdate();

  return;
}


static void handleFlagCmd(GameKeeper::Player *playerData, const char *message)
{
  int t = playerData->getIndex();
  if (!playerData->accessInfo.hasPerm(PlayerAccessInfo::flagMod)) {
    sendMessage(ServerPlayer, t, "You do not have permission to run the flag command");
    return;
  }
  if (strncasecmp(message + 6, "reset", 5) == 0) {
    bool onlyUnused = strncasecmp(message + 11, " unused", 7) == 0;
    bz_resetFlags(onlyUnused);
  } else if (strncasecmp(message + 6, "up", 2) == 0) {
    for (int i = 0; i < numFlags; i++) {
      FlagInfo &flag = *FlagInfo::get(i);
      if (flag.flag.type->flagTeam == ::NoTeam) {
	sendDrop(flag);
	flag.flag.status = FlagGoing;
	if (!flag.required)
	  flag.flag.type = Flags::Null;
	sendFlagUpdate(flag);
      }
    }

  } else if (strncasecmp(message + 6, "show", 4) == 0) {
    for (int i = 0; i < numFlags; i++) {
      char showMessage[MessageLen];
      FlagInfo::get(i)->getTextualInfo(showMessage);
      sendMessage(ServerPlayer, t, showMessage);
    }
  } else {
    sendMessage(ServerPlayer, t, "reset|show|up");
  }
  return;
}


static void handleKickCmd(GameKeeper::Player *playerData, const char *message)
{
  int t = playerData->getIndex();
  if (!playerData->accessInfo.hasPerm(PlayerAccessInfo::kick)) {
    sendMessage(ServerPlayer, t, "You do not have permission to run the kick command");
    return;
  }
  int i;
  std::vector<std::string> argv = TextUtils::tokenize(message, " \t", 3, true);

  if (argv.size() < 3) {
    sendMessage(ServerPlayer, t, "Syntax: /kick <#slot | PlayerName | \"Player Name\"> <reason>");
    sendMessage(ServerPlayer, t, "	Please keep in mind that reason is displayed to the user.");
    return;
  }
  

  i = getSlotNumber(argv[1]);

  if (i >= 0) {
    char kickmessage[MessageLen];
    
    GameKeeper::Player *p = GameKeeper::Player::getPlayerByIndex(i);

    // admins can override antiperms
    if (!playerData->accessInfo.isAdmin()) {
      // otherwise make sure the player is not protected with an antiperm      
      if ((p != NULL) && (p->accessInfo.hasPerm(PlayerAccessInfo::antikick))) {
	snprintf(kickmessage, MessageLen, "%s is protected from being kicked.", p->player.getCallSign());
	sendMessage(ServerPlayer, i, kickmessage);
	return;
      }
    }

    snprintf(kickmessage, MessageLen, "You were kicked off the server by %s",
	    playerData->player.getCallSign());
    sendMessage(ServerPlayer, i, kickmessage);
    if (argv.size() > 2) {
      snprintf(kickmessage, MessageLen, " reason given : %s",argv[2].c_str());
      sendMessage(ServerPlayer, i, kickmessage);
    }
    snprintf(kickmessage, MessageLen, "%s kicked by %s, reason: %s", p->player.getCallSign(), playerData->player.getCallSign(),argv[2].c_str());
    sendMessage(ServerPlayer, AdminPlayers, kickmessage);
    removePlayer(i, "/kick");
  } else {
    char errormessage[MessageLen];
    snprintf(errormessage, MessageLen, "player \"%s\" not found", argv[1].c_str());
    sendMessage(ServerPlayer, t, errormessage);
  }
  return;
}

static void handleKillCmd(GameKeeper::Player *playerData, const char *message)
{
  int t = playerData->getIndex();
  if (!playerData->accessInfo.hasPerm(PlayerAccessInfo::kill)) {
    sendMessage(ServerPlayer, t, "You do not have permission to run the kill command");
    return;
  }
  int i;
  std::vector<std::string> argv = TextUtils::tokenize(message, " \t", 3, true);

  if (argv.size() < 2) {
    sendMessage(ServerPlayer, t, "Syntax: /kill <#slot | PlayerName | \"Player Name\">  [reason]");
    sendMessage(ServerPlayer, t, "	Please keep in mind that reason is displayed to the user.");
    return;
  }

  i = getSlotNumber(argv[1]);

  if (i >= 0) {
    char killmessage[MessageLen];

    // admins can override antiperms
    if (!playerData->accessInfo.isAdmin()) {
      // otherwise make sure the player is not protected with an antiperm
      GameKeeper::Player *p = GameKeeper::Player::getPlayerByIndex(i);
      if ((p != NULL) && (p->accessInfo.hasPerm(PlayerAccessInfo::antikill))) {
	snprintf(killmessage, MessageLen, "%s is protected from being killed.", p->player.getCallSign());
	sendMessage(ServerPlayer, i, killmessage);
	return;
      }
    }

    snprintf(killmessage, MessageLen, "You were killed by %s",
	     playerData->player.getCallSign());
    sendMessage(ServerPlayer, i, killmessage);
    if (argv.size() > 2) {
      snprintf(killmessage, MessageLen, " reason given : %s",argv[2].c_str());
      sendMessage(ServerPlayer, i, killmessage);
    }
    // kill the player
    playerKilled(i, ServerPlayer, 0, -1, Flags::Null, -1);

  } else {
    char errormessage[MessageLen];
    snprintf(errormessage, MessageLen, "player \"%s\" not found", argv[1].c_str());
    sendMessage(ServerPlayer, t, errormessage);
  }
  return;
}

static void handleBanlistCmd(GameKeeper::Player *playerData, const char *)
{
  int t = playerData->getIndex();
  if (!playerData->accessInfo.hasPerm(PlayerAccessInfo::banlist)) {
    sendMessage(ServerPlayer, t, "You do not have permission to run the banlist command");
    return;
  }
  clOptions->acl.sendBans(t);
  return;
}


static void handleHostBanlistCmd(GameKeeper::Player *playerData, const char *)
{
  int t = playerData->getIndex();
  if (!playerData->accessInfo.hasPerm(PlayerAccessInfo::banlist)) {
    sendMessage(ServerPlayer, t, "You do not have permission to run the banlist command");
    return;
  }
  clOptions->acl.sendHostBans(t);
  return;
}


static void handleBanCmd(GameKeeper::Player *playerData, const char *message)
{
  int t = playerData->getIndex();
  if (!playerData->accessInfo.hasPerm(PlayerAccessInfo::ban) &&
      !playerData->accessInfo.hasPerm(PlayerAccessInfo::shortBan)) {
    sendMessage(ServerPlayer, t, "You do not have permission to run the ban command");
    return;
  }

  std::string msg = message;
  std::vector<std::string> argv = TextUtils::tokenize(msg, " \t", 4, true);

  if (argv.size() < 4) {
    sendMessage(ServerPlayer, t, "Syntax: /ban <#slot | PlayerName | \"Player Name\" | ip> <duration> <reason>");
    sendMessage(ServerPlayer, t, "	Please keep in mind that reason is displayed to the user.");
  } else {
    std::string ip = argv[1];
    std::string reason;
    int durationInt = clOptions->banTime;
    
    int victim = getSlotNumber(argv[1]);
    
    if (victim >= 0) {
      // valid slot or callsign
      GameKeeper::Player *playerBannedData  = GameKeeper::Player::getPlayerByIndex(victim);
      if (playerBannedData)
	ip = playerBannedData->netHandler->getTargetIP();
    }

    // set the ban time
    if (argv.size() >= 3) {
      int specifiedDuration = TextUtils::parseDuration(argv[2]);
      if ((durationInt > 0) &&
	  ((specifiedDuration > durationInt) || (specifiedDuration <= 0)) &&
	  !playerData->accessInfo.hasPerm(PlayerAccessInfo::ban)) {
	sendMessage (ServerPlayer, t, "You only have SHORTBAN privileges,"
				      " using default ban time");
      } else {
	durationInt = specifiedDuration;
      }
    }

    // set the ban reason
    if (argv.size() == 4) {
      reason = argv[3];
    }

    // reload the banlist in case anyone else has added
    clOptions->acl.load();

    if (clOptions->acl.ban(ip, playerData->player.getCallSign(), durationInt,
			   reason.c_str())) {
      clOptions->acl.save();

      sendMessage(ServerPlayer, t, "IP pattern added to banlist");

      char kickmessage[MessageLen];
      GameKeeper::Player *otherPlayer;
      for (int i = 0; i < curMaxPlayers; i++) {
	otherPlayer = GameKeeper::Player::getPlayerByIndex(i);
	if (otherPlayer && !clOptions->acl.validate
	    (otherPlayer->netHandler->getIPAddress())) {

	  // admins can override antiperms
	  if (!playerData->accessInfo.isAdmin()) {
	    // make sure this player isn't protected
	    GameKeeper::Player *p = GameKeeper::Player::getPlayerByIndex(i);
	    if ((p != NULL)
		&& (p->accessInfo.hasPerm(PlayerAccessInfo::antiban))) {
	      snprintf(kickmessage, MessageLen, 
		      "%s is protected from being banned (skipped).",
		      p->player.getCallSign());
	      sendMessage(ServerPlayer, t, kickmessage);
	      continue;
	    }
	  }

	  snprintf(kickmessage, MessageLen, "You were banned from this server by %s",
		  playerData->player.getCallSign());
	  sendMessage(ServerPlayer, i, kickmessage);
	  if (reason.length() > 0) {
	    snprintf(kickmessage, MessageLen, "Reason given: %s", reason.c_str());
	    sendMessage(ServerPlayer, i, kickmessage);
	  }
	  if (otherPlayer) {
	    snprintf(kickmessage, MessageLen, "%s banned by %s, reason: %s", otherPlayer->player.getCallSign(), playerData->player.getCallSign(),reason.c_str());
	    sendMessage(ServerPlayer, AdminPlayers, kickmessage);
	  }
	  removePlayer(i, "/ban");
	}
      }
    } else {
      char errormessage[MessageLen];
      snprintf(errormessage, MessageLen, "Malformed address or invalid Player/Slot: %s", argv[1].c_str());
      sendMessage(ServerPlayer, t, errormessage);
    }
  }
  return;
}


static void handleHostBanCmd(GameKeeper::Player *playerData, const char *message)
{
  int t = playerData->getIndex();
  if (!playerData->accessInfo.hasPerm(PlayerAccessInfo::ban) &&
      !playerData->accessInfo.hasPerm(PlayerAccessInfo::shortBan)) {
    sendMessage(ServerPlayer, t, "You do not have permission to run the ban command");
    return;
  }

  std::string msg = message;
  std::vector<std::string> argv = TextUtils::tokenize( msg, " \t", 4 );

  if (argv.size() < 2) {
    sendMessage(ServerPlayer, t, "Syntax: /hostban <host pattern> [duration] [reason]");
    sendMessage(ServerPlayer, t, "	Please keep in mind that reason is displayed to the user.");
  } else {
    std::string hostpat = argv[1];
    std::string reason;
    int durationInt = clOptions->banTime;

    // set the ban time
    if (argv.size() >= 3) {
      int specifiedDuration = TextUtils::parseDuration(argv[2]);
      if ((durationInt > 0) &&
	  ((specifiedDuration > durationInt) || (specifiedDuration <= 0)) &&
	  !playerData->accessInfo.hasPerm(PlayerAccessInfo::ban)) {
	sendMessage (ServerPlayer, t, "You only have SHORTBAN privileges,"
				      " using default ban time");
      } else {
	durationInt = specifiedDuration;
      }
    }

    // set the ban reason
    if( argv.size() == 4 ) {
      reason = argv[3];
    }

    clOptions->acl.hostBan(hostpat, playerData->player.getCallSign(),
			   durationInt,
			   reason.c_str());
    clOptions->acl.save();

    GameKeeper::Player::setAllNeedHostbanChecked(true);

    sendMessage(ServerPlayer, t, "Host pattern added to banlist");
  }
  return;
}


static void handleUnbanCmd(GameKeeper::Player *playerData, const char *message)
{
  int t = playerData->getIndex();
  if (!playerData->accessInfo.hasPerm(PlayerAccessInfo::unban)) {
    sendMessage(ServerPlayer, t, "You do not have permission to run the unban command");
    return;
  }

  if (clOptions->acl.unban(message + 7)) {
    sendMessage(ServerPlayer, t, "Removed IP pattern from the ban list");
    clOptions->acl.save();
  } else {
    sendMessage(ServerPlayer, t, "No pattern removed");
  }
  return;
}

static void handleHostUnbanCmd(GameKeeper::Player *playerData, const char *message)
{
  int t = playerData->getIndex();
  if (!playerData->accessInfo.hasPerm(PlayerAccessInfo::unban)) {
    sendMessage(ServerPlayer, t, "You do not have permission to run the unban command");
    return;
  }

  if (clOptions->acl.hostUnban(message + 11)) {
    sendMessage(ServerPlayer, t, "Removed host pattern from the ban list");
    clOptions->acl.save();
  } else {
    sendMessage(ServerPlayer, t, "No pattern removed");
  }
  return;
}


static void handleLagwarnCmd(GameKeeper::Player *playerData, const char *message)
{
  int t = playerData->getIndex();
  if (!playerData->accessInfo.hasPerm(PlayerAccessInfo::lagwarn)) {
    sendMessage(ServerPlayer, t, "You do not have permission to run the lagwarn command");
    return;
  }

  char reply[MessageLen] = {0};

  if (message[8] == ' ') {
    const char *maxlag = message + 9;
    clOptions->lagwarnthresh = (float) (atoi(maxlag) / 1000.0);
    snprintf(reply, MessageLen, "lagwarn is now %d ms", int(clOptions->lagwarnthresh * 1000 + 0.5));
  } else {
    snprintf(reply, MessageLen, "lagwarn is set to %d ms", int(clOptions->lagwarnthresh * 1000 + 0.5));
  }
  sendMessage(ServerPlayer, t, reply);
  return;
}


bool lagCompare(const GameKeeper::Player *a, const GameKeeper::Player *b)
{
  if (a->player.isObserver() && !b->player.isObserver())
    return true;
  if (!a->player.isObserver() && b->player.isObserver())
    return false;
  return a->lagInfo.getLag() < b->lagInfo.getLag();
}

static void handleLagstatsCmd(GameKeeper::Player *playerData, const char *)
{
  int t = playerData->getIndex();
  if (!playerData->accessInfo.hasPerm(PlayerAccessInfo::lagStats)) {
    sendMessage(ServerPlayer, t, "You do not have permission to run the lagstats command");
    return;
  }
  // yeah, ok this is ugly but it works - curMaxPlayers is never >255
  GameKeeper::Player *sortedPlayer[256];
  int i = 0, j = 0;
  for (i = 0; i < curMaxPlayers; i++) {
    GameKeeper::Player *p = GameKeeper::Player::getPlayerByIndex(i);
    if (p != NULL) {
      sortedPlayer[j++] = p;
    }
  }
  std::sort(sortedPlayer, sortedPlayer + j, lagCompare);

  char reply[MessageLen];
  for (i = 0; i < j; i++) {
    GameKeeper::Player *p = sortedPlayer[i];
    p->lagInfo.getLagStats(reply);
    if (reply[0])
      sendMessage(ServerPlayer, t, reply);
  }
}


static void handleIdlestatsCmd(GameKeeper::Player *playerData, const char *)
{
  int t = playerData->getIndex();
  if (!playerData->accessInfo.hasPerm(PlayerAccessInfo::idleStats)) {
    sendMessage(ServerPlayer, t, "You do not have permission to run the idlestats command");
    return;
  }

  GameKeeper::Player *otherData;
  std::string reply;
  for (int i = 0; i < curMaxPlayers; i++) {
    otherData = GameKeeper::Player::getPlayerByIndex(i);
    if (!otherData)
      continue;
    reply = otherData->player.getIdleStat();
    if (reply != "")
      sendMessage(ServerPlayer, t, reply.c_str());
  }
  return;
}


static void handleFlaghistoryCmd(GameKeeper::Player *playerData, const char *)
{
  int t = playerData->getIndex();
  if (!playerData->accessInfo.hasPerm(PlayerAccessInfo::flagHistory)) {
    sendMessage(ServerPlayer, t, "You do not have permission to run the flaghistory command");
    return;
  }

  char reply[MessageLen];
  for (int i = 0; i < curMaxPlayers; i++) {
    GameKeeper::Player *otherData = GameKeeper::Player::getPlayerByIndex(i);
    if (otherData != NULL && otherData->player.isPlaying()
	&& !otherData->player.isObserver()) {
      snprintf(reply, MessageLen, "%-16s : ", otherData->player.getCallSign());
      otherData->flagHistory.get(reply+strlen(reply));
      sendMessage(ServerPlayer, t, reply);
    }
  }
}


static void handlePlayerlistCmd(GameKeeper::Player *playerData, const char *)
{
  int t = playerData->getIndex();
  if (!playerData->accessInfo.hasPerm(PlayerAccessInfo::playerList)) {
    sendMessage(ServerPlayer, t, "You do not have permission to run the playerlist command");
    return;
  }

  GameKeeper::Player *otherData;
  char reply[MessageLen] = {0};

  for (int i = 0; i < curMaxPlayers; i++) {
    otherData = GameKeeper::Player::getPlayerByIndex(i);
    if (otherData && otherData->player.isPlaying()) {
      otherData->netHandler->getPlayerList(reply);
      sendMessage(ServerPlayer, t, reply);
    }
  }
  return;
}


static void handleReportCmd(GameKeeper::Player *playerData, const char *message)
{
  int t = playerData->getIndex();
  if (!playerData->accessInfo.hasPerm(PlayerAccessInfo::talk)) {
    sendMessage(ServerPlayer, t, "You do not have permission to run the report command");
    return;
  }

  if (strlen(message + 1) < 8) {
    sendMessage(ServerPlayer, t, "Nothing reported");
  } else {
    time_t now = time(NULL);
    char* timeStr = ctime(&now);
    std::string reportStr;
    reportStr = reportStr + timeStr + "Reported by " +
      playerData->player.getCallSign() + ": " + (message + 8);
    if (clOptions->reportFile.size() > 0) {
      std::ofstream ofs(clOptions->reportFile.c_str(), std::ios::out | std::ios::app);
      ofs << reportStr << std::endl << std::endl;
    }
    if (clOptions->reportPipe.size() > 0) {
      FILE* pipeWrite = popen(clOptions->reportPipe.c_str(), "w");
      if (pipeWrite != NULL) {
	fprintf(pipeWrite, "%s\n\n", reportStr.c_str());
      } else {
	DEBUG1("Couldn't write report to the pipe\n");
      }
      pclose(pipeWrite);
    }
    if (clOptions->reportFile.size() == 0 && clOptions->reportPipe.size() == 0) {
      sendMessage(ServerPlayer, t, "The report command is disabled on this server");
    } else {
      std::string temp = std::string("**\"") + playerData->player.getCallSign() + "\" reports: " +
			 (message + 8);
      if (temp.size() <= (unsigned) MessageLen) {
	sendMessage (ServerPlayer, AdminPlayers, temp.c_str());
	return;
      }
      const std::vector<std::string> words = TextUtils::tokenize(temp, " \t");
      unsigned int cur = 0;
      const unsigned int wordsize = words.size();
      while (cur != wordsize) {
	std::string temp2;
	while (temp2.size() <= (unsigned) MessageLen &&
	       cur != wordsize &&
	       (temp2.size() + words[cur].size()) <= (unsigned) MessageLen) {
	    temp2 += words[cur] + " ";
	    ++cur;
	}
	sendMessage (ServerPlayer, AdminPlayers, temp2.c_str());
      }
      sendMessage (ServerPlayer, AdminPlayers, message);
      DEBUG1("Player %s [%d] has filed a report (time: %s).\n",
	     playerData->player.getCallSign(), t, timeStr);

      sendMessage(ServerPlayer, t, "Your report has been filed. Thank you.");
    }
  }

  return;
}


static void handleHelpCmd(GameKeeper::Player *playerData, const char *message)
{
  int t = playerData->getIndex();
  char reply[MessageLen] = {0};

  if (strlen(message + 1) == 4) {
    const std::vector<std::string>& chunks = clOptions->textChunker.getChunkNames();
    sendMessage(ServerPlayer, t, "Available help pages (use /help <page>)");
    for (int i = 0; i < (int) chunks.size(); i++) {
      sendMessage(ServerPlayer, t, chunks[i].c_str());
    }
  } else {
    bool foundChunk = false;
    const std::vector<std::string>& chunks = clOptions->textChunker.getChunkNames();
    for (int i = 0; i < (int)chunks.size() && (!foundChunk); i++) {
      if (chunks[i] == (message +6)){
	const std::vector<std::string>* lines = clOptions->textChunker.getTextChunk((message + 6));
	if (lines != NULL) {
	  for (int j = 0; j < (int)lines->size(); j++) {
	    sendMessage(ServerPlayer, t, (*lines)[j].c_str());
	  }
	  foundChunk = true;
	  break;
	}
      }
    }
    if (!foundChunk) {
      snprintf(reply, MessageLen, "Help command %s not found", message + 6);
      sendMessage(ServerPlayer, t, reply);
    }
  }
  return;
}


static void handleIdentifyCmd(GameKeeper::Player *playerData, const char *message)
{
  int t = playerData->getIndex();
  if (!passFile.size()){
    sendMessage(ServerPlayer, t, "/identify command disabled");
    return;
  }
  // player is trying to send an ID
  if (playerData->accessInfo.isVerified()) {
    sendMessage(ServerPlayer, t, "You have already identified");
  } else if (playerData->accessInfo.gotAccessFailure()) {
    sendMessage(ServerPlayer, t, "You have attempted to identify too many times");
  } else {
    // get their info
    if (!playerData->accessInfo.isRegistered()) {
      // not in DB, tell them to reg
      sendMessage(ServerPlayer, t, "This callsign is not registered,"
		  " please register it with a /register command");
    } else {
      if (playerData->accessInfo.isPasswordMatching(message + 10)) {
	sendMessage(ServerPlayer, t, "Password Accepted, welcome back.");

	// get their real info
	playerData->accessInfo.setPermissionRights();

	// if they have the PLAYERLIST permission, send the IP list
	sendIPUpdate(t, -1);
	sendPlayerInfo();
      } else {
	playerData->accessInfo.setLoginFail();
	sendMessage(ServerPlayer, t, "Identify Failed, please make sure"
		    " your password was correct");
      }
    }
  }
  return;
}


static void handleRegisterCmd(GameKeeper::Player *playerData, const char *message)
{
  int t = playerData->getIndex();
  if (!passFile.size()){
    sendMessage(ServerPlayer, t, "/register command disabled");
    return;
  }
  if (playerData->accessInfo.isVerified()) {
    sendMessage(ServerPlayer, t, "You have already registered and"
		" identified this callsign");
  } else {
    if (playerData->accessInfo.isRegistered()) {
      sendMessage(ServerPlayer, t, "This callsign is already registered,"
		  " if it is yours /identify to login");
    } else {
      if (strlen(message) > 12) {
	playerData->accessInfo.storeInfo(message + 10);
	sendMessage(ServerPlayer, t, "Callsign registration confirmed,"
		    " please /identify to login");
      } else {
	sendMessage(ServerPlayer, t, "Your password must be 3 or more characters");
      }
    }
  }
  return;
}


static void handleGhostCmd(GameKeeper::Player *playerData, const char *message)
{
  int t = playerData->getIndex();
  if (!passFile.size()){
    sendMessage(ServerPlayer, t, "/ghost command disabled");
    return;
  }
  char *p1 = (char*)strchr(message + 1, '\"');
  char *p2 = 0;
  if (p1) p2 = strchr(p1 + 1, '\"');
  if (!p2) {
    sendMessage(ServerPlayer, t, "not enough parameters, usage"
		" /ghost \"CALLSIGN\" PASSWORD");
  } else {
    std::string ghostie(p1 + 1, p2 - p1 - 1);
    std::string ghostPass = p2 + 2;

    makeupper(ghostie);

    int user = GameKeeper::Player::getPlayerIDByName(ghostie);
    if (user == -1) {
      sendMessage(ServerPlayer, t, "There is no user logged in by that name");
    } else {
      if (!userExists(ghostie)) {
	sendMessage(ServerPlayer, t, "That callsign is not registered");
      } else {
	if (!verifyUserPassword(ghostie, ghostPass)) {
	  sendMessage(ServerPlayer, t, "Invalid Password");
	} else {
	  sendMessage(ServerPlayer, t, "Ghosting User");
	  char temp[MessageLen];
	  snprintf(temp, MessageLen, "Your Callsign is registered to another user,"
		  " You have been ghosted by %s",
		  playerData->player.getCallSign());
	  sendMessage(ServerPlayer, user, temp);
	  removePlayer(user, "Ghost");
	}
      }
    }
  }
  return;
}


static void handleDeregisterCmd(GameKeeper::Player *playerData, const char *message)
{
  int t = playerData->getIndex();
  if (!passFile.size()){
    sendMessage(ServerPlayer, t, "/deregister command disabled");
    return;
  }
  if (!playerData->accessInfo.isVerified()) {
    sendMessage(ServerPlayer, t, "You must be registered and verified to run the deregister command");
    return;
  }

  if (strlen(message) == 11) {
    // removing own callsign
    passwordDatabase.erase(playerData->accessInfo.getName());
    userDatabase.erase(playerData->accessInfo.getName());
    PlayerAccessInfo::updateDatabases();
    sendMessage(ServerPlayer, t, "Your callsign has been deregistered");
  } else if (strlen(message) > 12
	     && playerData->accessInfo.hasPerm(PlayerAccessInfo::setAll)) {
    char reply[MessageLen];

    // removing someone else's
    std::string name = message + 12;
    makeupper(name);
    if (userExists(name)) {

      // admins can override antiperms
      if (!playerData->accessInfo.isAdmin()) {
	// make sure this player isn't protected
	int v = GameKeeper::Player::getPlayerIDByName(name);
	GameKeeper::Player *p = GameKeeper::Player::getPlayerByIndex(v);
	if ((p != NULL) && (p->accessInfo.hasPerm(PlayerAccessInfo::antideregister))) {
	  snprintf(reply, MessageLen, "%s is protected from being deregistered.", p->player.getCallSign());
	  sendMessage(ServerPlayer, t, reply);
	  return;
	}
      }

      passwordDatabase.erase(name);
      userDatabase.erase(name);
      PlayerAccessInfo::updateDatabases();

      snprintf(reply, MessageLen, "%s has been deregistered", name.c_str());
      sendMessage(ServerPlayer, t, reply);
    } else {
      snprintf(reply, MessageLen, "user %s does not exist", name.c_str());
      sendMessage(ServerPlayer, t, reply);
    }
  } else if (!playerData->accessInfo.hasPerm(PlayerAccessInfo::setAll)) {
    sendMessage(ServerPlayer, t, "You do not have permission to deregister this user");
  }

  return;
}


static void handleSetpassCmd(GameKeeper::Player *playerData, const char *message)
{
  int t = playerData->getIndex();
  if (!passFile.size()){
    sendMessage(ServerPlayer, t, "/setpass command disabled");
    return;
  }
  if (!playerData->accessInfo.isVerified()) {
    sendMessage(ServerPlayer, t, "You must be registered and verified to run the setpass command");
    return;
  }

  size_t startPosition = 7;
  /* skip any leading whitespace */
  while (isspace(message[++startPosition]))
    ;
  if (startPosition == strlen(message) || !isspace(message[8])) {
    sendMessage(ServerPlayer, t, "Not enough parameters: usage /setpass PASSWORD");
    return;
  }
  std::string pass = message + startPosition;
  playerData->accessInfo.setPasswd(pass);
  char text[MessageLen];
  snprintf(text, MessageLen, "Your password is now set to \"%s\"", pass.c_str());
  sendMessage(ServerPlayer, t, text);
  return;
}


static void handleGrouplistCmd(GameKeeper::Player *playerData, const char *)
{
  int t = playerData->getIndex();
  sendMessage(ServerPlayer, t, "Group List:");
  PlayerAccessMap::iterator itr = groupAccess.begin();
  while (itr != groupAccess.end()) {
    sendMessage(ServerPlayer, t, itr->first.c_str());
    itr++;
  }
  return;
}


static void handleShowgroupCmd(GameKeeper::Player *playerData, const char *message)
{
  int t = playerData->getIndex();
  std::string settie;

  if (strlen(message) == 10) {	 // show own groups
    if (playerData->accessInfo.isVerified()) {
      settie = playerData->accessInfo.getName();
    } else {
      sendMessage(ServerPlayer, t, "You are not identified");
    }
  } else if (playerData->accessInfo.hasPerm(PlayerAccessInfo::showOthers)) {
    // show groups for other player
    char *p1 = (char*) strchr(message + 1, '\"');
    char *p2 = 0;
    if (p1) p2 = strchr(p1 + 1, '\"');
    if (p2) {
      settie = std::string(p1 + 1, p2 - p1 - 1);
      makeupper(settie);
    } else {
      sendMessage(ServerPlayer, t, "wrong format, usage"
		  " /showgroup  or  /showgroup \"CALLSIGN\"");
    }
  } else {
    sendMessage(ServerPlayer, t, "No permission!");
  }

  // something is wrong
  if (settie != "") {
    int playerIndex = GameKeeper::Player::getPlayerIDByName(settie);
    // once for global groups
    if (playerIndex < curMaxPlayers) {
      GameKeeper::Player* target = GameKeeper::Player::getPlayerByIndex(playerIndex);
      if (target != NULL) {
	PlayerAccessInfo &info = target->accessInfo;
	// FIXME remove local groups from this list. better yet unify the two.
	std::string line = "Global Groups (only extras) for ";
	line += settie;
	line += ": ";
	std::vector<std::string>::iterator itr = info.groups.begin();
	while (itr != info.groups.end()) {
	  line += *itr;
	  line += " ";
	  itr++;
	}
	while (line.size() > (unsigned int)MessageLen) {
	  sendMessage(ServerPlayer, t, line.substr(0, MessageLen).c_str());
	  line.erase(line.begin(), line.begin() + (MessageLen - 1));
	}
	sendMessage(ServerPlayer, t, line.c_str());
      }
    }
    // once for local groups
    if (userExists(settie)) {
      PlayerAccessInfo &info = PlayerAccessInfo::getUserInfo(settie);

      std::string line = "Local groups for ";
      line += settie;
      line += ": ";
      std::vector<std::string>::iterator itr = info.groups.begin();
      while (itr != info.groups.end()) {
	line += *itr;
	line += " ";
	itr++;
      }
      while (line.size() > (unsigned int)MessageLen) {
	sendMessage(ServerPlayer, t, line.substr(0, MessageLen).c_str());
	line.erase(line.begin(), line.begin() + (MessageLen - 1));
      }
      sendMessage(ServerPlayer, t, line.c_str());
    } else {
      sendMessage(ServerPlayer, t, "There is no user by that name");
    }
  }
  return;
}


static void handleGrouppermsCmd(GameKeeper::Player *playerData, const char *)
{
  int t = playerData->getIndex();
  sendMessage(ServerPlayer, t, "Group List:");
  PlayerAccessMap::iterator itr = groupAccess.begin();
  std::string line;
  while (itr != groupAccess.end()) {
    line = itr->first + ":   ";
    sendMessage(ServerPlayer, t, line.c_str());

    // allows first
    if (itr->second.explicitAllows.any()) {
      sendMessage(ServerPlayer, t, "  Allows");
      for (int i = 0; i < PlayerAccessInfo::lastPerm; i++) {
     	if (itr->second.explicitAllows.test(i) && !itr->second.explicitDenys.test(i) ) {
	  line = "     ";
	  line += nameFromPerm((PlayerAccessInfo::AccessPerm)i);
	  sendMessage(ServerPlayer, t, line.c_str());
      	}
      }
    }
		
    // same about denys
    if (itr->second.explicitDenys.any()) {
      sendMessage(ServerPlayer, t, "  Denys");
      for (int i = 0; i < PlayerAccessInfo::lastPerm; i++) {
     	if (itr->second.explicitDenys.test(i) ) {
	  line = "     ";
	  line += nameFromPerm((PlayerAccessInfo::AccessPerm)i);
	  sendMessage(ServerPlayer, t, line.c_str());
      	}
      }
    }
			
    itr++;
  }
  return;
}


static void handleSetgroupCmd(GameKeeper::Player *playerData, const char *message)
{
  int t = playerData->getIndex();
  if (!userDatabaseFile.size()) {
    sendMessage(ServerPlayer, t, "/setgroup command disabled");
    return;
  }
  char *p1 = (char*)strchr(message + 1, '\"');
  char *p2 = 0;
  if (p1) p2 = strchr(p1 + 1, '\"');
  if (!p2) {
    sendMessage(ServerPlayer, t, "not enough parameters, usage /setgroup \"CALLSIGN\" GROUP");
  } else {
    std::string settie(p1 + 1, p2 - p1 - 1);
    std::string group = p2 + 2;

    makeupper(settie);
    makeupper(group);

    if (userExists(settie)) {
      if (!playerData->accessInfo.canSet(group)) {
	sendMessage(ServerPlayer, t, "You do not have permission to set this group");
      } else {
	PlayerAccessInfo &info = PlayerAccessInfo::getUserInfo(settie);

	if (info.addGroup(group)) {
	  sendMessage(ServerPlayer, t, "Group Add successful");
	  int getID = GameKeeper::Player::getPlayerIDByName(settie);
	  if (getID != -1) {
	    char temp[MessageLen];
	    snprintf(temp, MessageLen, "you have been added to the %s group, by %s",
		    group.c_str(), playerData->player.getCallSign());
	    sendMessage(ServerPlayer, getID, temp);
	    GameKeeper::Player::getPlayerByIndex(getID)->accessInfo.
	      addGroup(group);
	  }
	  PlayerAccessInfo::updateDatabases();
	} else {
	  sendMessage(ServerPlayer, t, "Group Add failed (user may already be in that group)");
	}
      }
    } else {
      sendMessage(ServerPlayer, t, "There is no user by that name");
    }
  }
  return;
}


static void handleRemovegroupCmd(GameKeeper::Player *playerData, const char *message)
{
  int t = playerData->getIndex();
  if (!userDatabaseFile.size()) {
    sendMessage(ServerPlayer, t, "/removegroup command disabled");
    return;
  }
  char *p1 = (char*)strchr(message + 1, '\"');
  char *p2 = 0;
  if (p1) p2 = strchr(p1 + 1, '\"');
  if (!p2) {
    sendMessage(ServerPlayer, t, "not enough parameters, usage /removegroup \"CALLSIGN\" GROUP");
  } else {
    std::string settie(p1 + 1, p2 - p1 - 1);
    std::string group = p2 + 2;

    makeupper(settie);
    makeupper(group);
    if (userExists(settie)) { 
      if (!playerData->accessInfo.canSet(group)) {
	sendMessage(ServerPlayer, t, "You do not have permission to remove this group");
      } else {
	PlayerAccessInfo &info = PlayerAccessInfo::getUserInfo(settie);
	if (info.removeGroup(group)) {
	  sendMessage(ServerPlayer, t, "Group Remove successful");
	  int getID = GameKeeper::Player::getPlayerIDByName(settie);
	  if (getID != -1) {
	    char temp[MessageLen];
	    snprintf(temp, MessageLen, "You have been removed from the %s group, by %s",
		    group.c_str(), playerData->player.getCallSign());
	    sendMessage(ServerPlayer, getID, temp);
	    GameKeeper::Player::getPlayerByIndex(getID)->accessInfo.
	      removeGroup(group);
	  }
	  PlayerAccessInfo::updateDatabases();
	} else {
	  sendMessage(ServerPlayer, t, "Group Remove failed (user may not have been in group)");
	}
      }
    } else {
      sendMessage(ServerPlayer, t, "There is no user by that name");
    }
  }
  return;
}


static void handleReloadCmd(GameKeeper::Player *playerData, const char *)
{
  int t = playerData->getIndex();
  if (!playerData->accessInfo.hasPerm(PlayerAccessInfo::setAll)) {
    sendMessage(ServerPlayer, t, "You do not have permission to run the reload command");
    return;
  }

  // reload the banlist
  clOptions->acl.load();

  groupAccess.clear();
  userDatabase.clear();
  passwordDatabase.clear();
  initGroups();
  if (passFile.size())
    readPassFile(passFile);
  if (userDatabaseFile.size())
    PlayerAccessInfo::readPermsFile(userDatabaseFile);
  GameKeeper::Player::reloadAccessDatabase();
  sendMessage(ServerPlayer, t, "Databases reloaded");

  // Validate all of the current players

  std::string reason;
  char kickmessage[MessageLen];

  // Check host bans
  GameKeeper::Player::setAllNeedHostbanChecked(true);

  // Check IP bans
  for (int i = 0; i < curMaxPlayers; i++) {
    GameKeeper::Player *otherPlayer = GameKeeper::Player::getPlayerByIndex(i);
      if (otherPlayer && !clOptions->acl.validate
	(otherPlayer->netHandler->getIPAddress())) {
	  // admins can override antiperms
	  if (!playerData->accessInfo.isAdmin()) {
	    // make sure this player isn't protected
	    GameKeeper::Player *p = GameKeeper::Player::getPlayerByIndex(i);
	    if ((p != NULL)
		&& (p->accessInfo.hasPerm(PlayerAccessInfo::antiban))) {
	      snprintf(kickmessage, MessageLen, 
		    "%s is protected from being banned (skipped).",
		    p->player.getCallSign());
	      sendMessage(ServerPlayer, t, kickmessage);
	      continue;
	    }
	  }

	  snprintf(kickmessage, MessageLen, "You were banned from this server by %s",
		  playerData->player.getCallSign());
	  sendMessage(ServerPlayer, i, kickmessage);
	  if (reason.length() > 0) {
	    snprintf(kickmessage, MessageLen, "Reason given: %s", reason.c_str());
	    sendMessage(ServerPlayer, i, kickmessage);
	  }
	  removePlayer(i, "/ban");
	}
  }
  return;
}


static void handleVoteCmd(GameKeeper::Player *playerData, const char *message)
{
  int t = playerData->getIndex();
  char reply[MessageLen] = {0};
  std::string callsign = std::string(playerData->player.getCallSign());

  if (!playerData->accessInfo.hasPerm(PlayerAccessInfo::vote)) {
    /* permission denied for /vote */
    snprintf(reply, MessageLen, "%s, you are presently not authorized to run /vote", callsign.c_str());
    sendMessage(ServerPlayer, t, reply);
    return;
  }

  /* make sure that there is a poll arbiter */
  if (BZDB.isEmpty("poll")) {
    sendMessage(ServerPlayer, t, "ERROR: the poll arbiter has disappeared (this should never happen)");
    return;
  }

  // only need to get this once
  static VotingArbiter *arbiter = (VotingArbiter *)BZDB.getPointer("poll");

  /* make sure that there is a poll to vote upon */
  if ((arbiter != NULL) && !arbiter->knowsPoll()) {
    sendMessage(ServerPlayer, t, "A poll is not presently in progress.  There is nothing to vote on");
    return;
  }

  std::string voteCmd = &message[5];
  std::string answer;

  /* find the start of the vote answer */
  size_t startPosition = 0;
  while ((startPosition < voteCmd.size()) &&
	 (isspace(voteCmd[startPosition]))) {
    startPosition++;
  }

  /* stash the answer ('yes', 'no', etc) in lowercase to simplify comparison */
  for (size_t i = startPosition;  i < voteCmd.size() && !isspace(voteCmd[i]); i++) {
    answer += tolower(voteCmd[i]);
  }

  std::vector<std::string> yesAnswers;
  yesAnswers.push_back("y");
  yesAnswers.push_back("1");
  yesAnswers.push_back("yes");
  yesAnswers.push_back("yea");
  yesAnswers.push_back("si"); // spanish+
  yesAnswers.push_back("ja");  // german
  yesAnswers.push_back("oui"); // french
  yesAnswers.push_back("sim"); // portuguese
  yesAnswers.push_back("tak"); // polish

  std::vector<std::string> noAnswers;
  noAnswers.push_back("n");
  noAnswers.push_back("0");
  noAnswers.push_back("no");
  noAnswers.push_back("nay");
  noAnswers.push_back("nein"); // german
  noAnswers.push_back("nien"); // german misspelled
  noAnswers.push_back("non"); // french
  noAnswers.push_back("nao"); // portuguese
  noAnswers.push_back("nie"); // polish

  // see if the vote response is a valid yes or no answer
  int vote = -1;
  unsigned int maxAnswerCount = noAnswers.size() > yesAnswers.size() ? noAnswers.size() : yesAnswers.size();
  for (unsigned int v = 0; v < maxAnswerCount; v++) {
    if (v < yesAnswers.size()) {
      if (answer == yesAnswers[v]) {
	vote = 1;
	break;
      }
    }
    if (v < noAnswers.size()) {
      if (answer == noAnswers[v]) {
	vote = 0;
	break;
      }
    }
  }

  // cast the vote or complain
  bool cast = false;
  if (vote == 0) {
    if ((cast = arbiter->voteNo(callsign)) == true) {
      /* player voted no */
      snprintf(reply, MessageLen, "%s, your vote in opposition of the %s has been recorded", callsign.c_str(), arbiter->getPollAction().c_str());
      sendMessage(ServerPlayer, t, reply);
    }
  } else if (vote == 1) {
    if ((cast = arbiter->voteYes(callsign)) == true) {
      /* player voted yes */
      snprintf(reply, MessageLen, "%s, your vote in favor of the %s has been recorded", callsign.c_str(), arbiter->getPollAction().c_str());
      sendMessage(ServerPlayer, t, reply);
    }
  } else {
    if (answer.length() == 0) {
      snprintf(reply, MessageLen, "%s, you did not provide a vote answer", callsign.c_str());
      sendMessage(ServerPlayer, t, reply);
    } else {
      snprintf(reply, MessageLen, "%s, you did not vote in favor or in opposition", callsign.c_str());
      sendMessage(ServerPlayer, t, reply);
    }
    sendMessage(ServerPlayer, t, "Usage: /vote yes|no|y|n|1|0|yea|nay|si|ja|nein|oui|non|sim|nao");
    return;
  }

  if (arbiter->hasVoted(callsign)) {
    /* player already voted */
    snprintf(reply, MessageLen, "%s, you have already voted on the poll to %s %s", callsign.c_str(), arbiter->getPollAction().c_str(), arbiter->getPollTarget().c_str());
    sendMessage(ServerPlayer, t, reply);
    return;
  }

  if (!cast){
    /* There was an error while voting, probably could send a less generic message */
    snprintf(reply, MessageLen, "%s, there was an error while voting on the poll to %s %s", callsign.c_str(), arbiter->getPollAction().c_str(), arbiter->getPollTarget().c_str());
    sendMessage(ServerPlayer, t, reply);
    return;
  }

  return;
}


static void handleVetoCmd(GameKeeper::Player *playerData, const char * /*message*/)
{
  int t = playerData->getIndex();
  if (!playerData->accessInfo.hasPerm(PlayerAccessInfo::veto)) {
    /* permission denied for /veto */
    sendMessage(ServerPlayer, t,
		TextUtils::format
		("%s, you are presently not authorized to run /veto",
		 playerData->player.getCallSign()).c_str());
    return;
  }

  /* make sure that there is a poll arbiter */
  if (BZDB.isEmpty("poll")) {
    sendMessage(ServerPlayer, t, "ERROR: the poll arbiter has disappeared (this should never happen)");
    return;
  }

  // only need to do this once
  static VotingArbiter *arbiter = (VotingArbiter *)BZDB.getPointer("poll");

  /* make sure there is an unexpired poll */
  if ((arbiter != NULL) && !arbiter->knowsPoll()) {
    sendMessage(ServerPlayer, t,
		TextUtils::format
		("%s, there is presently no active poll to veto",
		 playerData->player.getCallSign()).c_str());
    return;
  }

  sendMessage(ServerPlayer, t,
	      TextUtils::format("%s, you have cancelled the poll to %s %s",
				  playerData->player.getCallSign(),
				  arbiter->getPollAction().c_str(),
				  arbiter->getPollTarget().c_str()).c_str());

  /* poof */
  arbiter->forgetPoll();

  sendMessage(ServerPlayer, AllPlayers,
	      TextUtils::format("The poll was cancelled by %s",
				  playerData->player.getCallSign()).c_str());

  return;
}


static void handlePollCmd(GameKeeper::Player *playerData, const char *message)
{
  int t = playerData->getIndex();
  char reply[MessageLen] = {0};
  std::string callsign = std::string(playerData->player.getCallSign());

  DEBUG2("\"%s\" has requested a poll: %s\n", callsign.c_str(), message);

  /* make sure player has permission to request a poll */
  if (!playerData->accessInfo.hasPerm(PlayerAccessInfo::poll)) {
    snprintf(reply, MessageLen, "%s, you are presently not authorized to run /poll", callsign.c_str());
    sendMessage(ServerPlayer, t, reply);
    return;
  }

  DEBUG3("Player has permission to run /poll\n");

  /* make sure that there is a poll arbiter */
  if (BZDB.isEmpty("poll")) {
    sendMessage(ServerPlayer, t, "ERROR: the poll arbiter has disappeared (this should never happen)");
    return;
  }

  DEBUG3("BZDB poll value is not empty\n");

  // only need to do this once
  static VotingArbiter *arbiter = (VotingArbiter *)BZDB.getPointer("poll");

  DEBUG3("Arbiter was acquired with address 0x%p\n", arbiter);

  /* make sure that there is not a poll active already */
  if (arbiter->knowsPoll()) {
    snprintf(reply, MessageLen, "A poll to %s %s is presently in progress", arbiter->getPollAction().c_str(), arbiter->getPollTarget().c_str());
    sendMessage(ServerPlayer, t, reply);
    sendMessage(ServerPlayer, t, "Unable to start a new poll until the current one is over");
    return;
  }

  DEBUG3("The arbiter says there is not another poll active\n");

  // get available voter count
  unsigned short int available = 0;
  for (int i = 0; i < curMaxPlayers; i++) {
    // any registered/known users on the server (including observers)
    // are eligible to vote
    GameKeeper::Player *otherData = GameKeeper::Player::getPlayerByIndex(i);
    if (otherData && otherData->accessInfo.exists()) {
      available++;
    }
  }

  DEBUG3("There are %d available players for %d votes required\n", available, clOptions->votesRequired);

  /* make sure there are enough players to even make a poll that has a chance
   * of succeeding (not counting the person being acted upon)
   */
  if (available - 1 < clOptions->votesRequired) {
    sendMessage(ServerPlayer, t, "Unable to initiate a new poll.  There are not enough registered players playing.");
    snprintf(reply, MessageLen, "There needs to be at least %d other %s and only %d %s available.",
	    clOptions->votesRequired,
	    clOptions->votesRequired - 1 == 1 ? "player" : "players",
	    available - 1,
	    available - 1 == 1 ? "is" : "are");
    sendMessage(ServerPlayer, t, reply);
    return;
  }

  std::string arguments = &message[5]; /* skip "/poll" */
  std::string cmd = "";

  DEBUG3("The arguments string is [%s]\n", arguments.c_str());

  /* find the start of the command */
  size_t startPosition = 0;
  while ((startPosition < arguments.size()) &&
	 (isspace(arguments[startPosition]))) {
    startPosition++;
  }

  DEBUG3("Start position is %d\n", (int)startPosition);

  /* find the end of the command */
  size_t endPosition = startPosition + 1;
  while ((endPosition < arguments.size()) &&
	 (!isspace(arguments[endPosition]))) {
    endPosition++;
  }

  DEBUG3("End position is %d\n", (int)endPosition);

  /* stash the command ('kick', etc) in lowercase to simplify comparison */
  if ((startPosition != arguments.size()) &&
      (endPosition > startPosition)) {
    for (size_t i = startPosition; i < endPosition; i++) {
      cmd += tolower(arguments[i]);
    }
  }

  DEBUG3("Command is %s\n", cmd.c_str());

  /* handle subcommands */

  if ((cmd == "ban") || (cmd == "kick") || (cmd == "kill") || (cmd == "set") || (cmd == "flagreset")) {
    std::string target;
    std::string targetIP = "";

    arguments = arguments.substr(endPosition);

    DEBUG3("Command arguments are [%s]\n", arguments.c_str());

    /* find the start of the target (e.g. player name) */
    startPosition = 0;
    while ((startPosition < arguments.size()) &&
	   (isspace(arguments[startPosition]))) {
      startPosition++;
    }
    // do not include a starting quote, if given
    if (arguments[startPosition] == '"') {
      startPosition++;
    }

    DEBUG3("Start position for target is %d\n", (int)startPosition);

    /* find the end of the target */
    endPosition = arguments.size() - 1;
    while ((endPosition > 0) &&
	   (isspace(arguments[endPosition]))) {
      endPosition--;
    }
    // do not include a trailing quote, if given
    if (arguments[endPosition] == '"') {
      endPosition--;
    }

    DEBUG3("End position for target is %d\n", (int)endPosition);

    target = arguments.substr(startPosition, endPosition - startPosition + 1);

    DEBUG3("Target specified to vote upon is [%s]\n", target.c_str());

    if ((target.length() == 0) && (cmd != "flagreset")) {
      snprintf(reply, MessageLen, "%s, no target was specified for the [%s] vote", callsign.c_str(), cmd.c_str());
      sendMessage(ServerPlayer, t, reply);
      snprintf(reply, MessageLen, "Usage: /poll %s target", cmd.c_str());
      sendMessage(ServerPlayer, t, reply);
      return;
    }

    // Make sure the specific poll type is allowed
    if ((cmd == "set") && (!playerData->accessInfo.hasPerm(PlayerAccessInfo::pollSet))) {
      snprintf(reply, MessageLen, "%s, you may not /poll set on this server", callsign.c_str());
      sendMessage(ServerPlayer, t, reply);
      DEBUG3("Player %s is not allowed to /poll set\n", callsign.c_str());
      return;
    }
    if ((cmd == "flagreset") && (!playerData->accessInfo.hasPerm(PlayerAccessInfo::pollFlagReset))) {
      snprintf(reply, MessageLen, "%s, you may not /poll flagreset on this server", callsign.c_str());
      sendMessage(ServerPlayer, t, reply);
      DEBUG3("Player %s is not allowed to /poll flagreset\n", callsign.c_str());
      return;
    }
    if ((cmd == "ban") && (!playerData->accessInfo.hasPerm(PlayerAccessInfo::pollBan))) {
      snprintf(reply, MessageLen, "%s, you may not /poll ban on this server", callsign.c_str());
      sendMessage(ServerPlayer, t, reply);
      DEBUG3("Player %s is not allowed to /poll ban\n", callsign.c_str());
      return;
    }
    if ((cmd == "kick") && (!playerData->accessInfo.hasPerm(PlayerAccessInfo::pollKick))) {
      snprintf(reply, MessageLen, "%s, you may not /poll kick on this server", callsign.c_str());
      sendMessage(ServerPlayer, t, reply);
      DEBUG3("Player %s is not allowed to /poll kick\n", callsign.c_str());
      return;
    }

    if ((cmd == "kill") && (!playerData->accessInfo.hasPerm(PlayerAccessInfo::pollKill))) {
      snprintf(reply, MessageLen, "%s, you may not /poll kill on this server", callsign.c_str());
      sendMessage(ServerPlayer, t, reply);
      DEBUG3("Player %s is not allowed to /poll kill\n", callsign.c_str());
      return;
    }

	if ((cmd != "set") && (cmd != "flagreset")) {
      // all polls that are not set or flagreset polls take a player name

      /* make sure the requested player is actually here */
      int v = GameKeeper::Player::getPlayerIDByName(target);
      if (v >= curMaxPlayers) {
	/* wrong name? */
	snprintf(reply, MessageLen, 
		"The player specified for a %s vote is not here", cmd.c_str());
	sendMessage(ServerPlayer, t, reply);
	return;
      }
      GameKeeper::Player* targetData = GameKeeper::Player::getPlayerByIndex(v);
      if (!targetData) {
	/* wrong name? */
	snprintf(reply, MessageLen, "The server has no information on %s.", cmd.c_str());
	sendMessage(ServerPlayer, t, reply);
	return;
      }
      targetIP = targetData->netHandler->getTargetIP();

      // admins can override antiperms
      if (!playerData->accessInfo.isAdmin()) {
	// otherwise make sure the player is not protected with an antiperm
	GameKeeper::Player *p = GameKeeper::Player::getPlayerByIndex(v);
	if (p != NULL) {
	  if (p->accessInfo.hasPerm(PlayerAccessInfo::antipoll)) {
	    snprintf(reply, MessageLen, "%s is protected from being polled against.", p->player.getCallSign());
	    sendMessage(ServerPlayer, t, reply);
	    return;
	  }
	  if (cmd == "ban") {
	    if (p->accessInfo.hasPerm(PlayerAccessInfo::antipollban)) {
	      snprintf(reply, MessageLen, "%s is protected from being poll banned.", p->player.getCallSign());
	      sendMessage(ServerPlayer, t, reply);
	      return;
	    }
	  } else if (cmd == "kick") {
	    if (p->accessInfo.hasPerm(PlayerAccessInfo::antipollkick)) {
	      snprintf(reply, MessageLen, "%s is protected from being poll kicked.", p->player.getCallSign());
	      sendMessage(ServerPlayer, t, reply);
	      return;
	    }
	  } else if (cmd == "kill") {
	    if (p->accessInfo.hasPerm(PlayerAccessInfo::antipollkill)) {
	      snprintf(reply, MessageLen, "%s is protected from being poll killed.", p->player.getCallSign());
	      sendMessage(ServerPlayer, t, reply);
	      return;
	    }
	  }
	}
      } // end admin check

    }

    /* create and announce the new poll */
    bool canDo = false;
    if (cmd == "ban") {
      canDo = (arbiter->pollToBan(target, callsign, targetIP));
    } else if (cmd == "kick") {
      canDo = (arbiter->pollToKick(target, callsign, targetIP));
    } else if (cmd == "kill") {
      canDo = (arbiter->pollToKill(target, callsign, targetIP));
    } else if (cmd == "set") {
      canDo = (arbiter->pollToSet(target, callsign));
    } else if (cmd == "flagreset") {
      canDo = (arbiter->pollToResetFlags(callsign));
    }

    if (!canDo) {
      snprintf(reply, MessageLen, "You are not able to request a %s poll right now, %s", cmd.c_str(), callsign.c_str());
      sendMessage(ServerPlayer, t, reply);
      return;
    } else {
      snprintf(reply, MessageLen, "A poll to %s %s has been requested by %s", cmd.c_str(), target.c_str(), callsign.c_str());
      sendMessage(ServerPlayer, AllPlayers, reply);
    }

    unsigned int necessaryToSucceed = (unsigned int)((clOptions->votePercentage / 100.0) * (double)available);
    snprintf(reply, MessageLen, "%d player%s available, %d additional affirming vote%s required to pass the poll (%f %%)", available, available==1?" is":"s are", necessaryToSucceed, necessaryToSucceed==1?"":"s", clOptions->votePercentage);
    sendMessage(ServerPlayer, AllPlayers, reply);

    // set the number of available voters
    arbiter->setAvailableVoters(available);

    // keep track of who is allowed to vote
    for (int j = 0; j < curMaxPlayers; j++) {
      // any registered/known users on the server (including
      // observers) are eligible to vote
      GameKeeper::Player *otherData = GameKeeper::Player::getPlayerByIndex(j);
      if (otherData && otherData->accessInfo.exists()) {
	arbiter->grantSuffrage(otherData->player.getCallSign());
      }
    }

    // automatically place a vote for the player requesting the poll
    DEBUG3("Attempting to automatically place a vote for [%s]\n", callsign.c_str());

    bool voted = arbiter->voteYes(callsign);
    if (!voted) {
      sendMessage(ServerPlayer, t, "Unable to automatically place your vote for some unknown reason");
      DEBUG3("Unable to automatically place a vote for [%s]\n", callsign.c_str());
    }

  } else if (cmd == "vote") {
    std::string voteCmd = "/vote ";
    voteCmd += arguments;
    handleVoteCmd(playerData, voteCmd.c_str());
    return;

  } else if (cmd == "veto") {
    std::string vetoCmd = "/veto ";
    vetoCmd += arguments;
    handleVetoCmd(playerData, vetoCmd.c_str());
    return;

  } else {
    sendMessage(ServerPlayer, t, "Invalid option to the poll command");
    sendMessage(ServerPlayer, t, "Usage: /poll vote yes|no");
    if (playerData->accessInfo.hasPerm(PlayerAccessInfo::pollBan))
      sendMessage(ServerPlayer, t, "    or /poll ban playername");
    if (playerData->accessInfo.hasPerm(PlayerAccessInfo::pollKick))
      sendMessage(ServerPlayer, t, "    or /poll kick playername");
    if (playerData->accessInfo.hasPerm(PlayerAccessInfo::pollKill))
      sendMessage(ServerPlayer, t, "    or /poll kill playername");
    if (playerData->accessInfo.hasPerm(PlayerAccessInfo::pollSet))
      sendMessage(ServerPlayer, t, "    or /poll set variable value");
    if (playerData->accessInfo.hasPerm(PlayerAccessInfo::pollFlagReset))
      sendMessage(ServerPlayer, t, "    or /poll flagreset");

  } /* end handling of poll subcommands */

  return;
}


static void handleViewReportsCmd(GameKeeper::Player *playerData, const char *)
{
  int t = playerData->getIndex();
  std::string line;
  if (!playerData->accessInfo.hasPerm(PlayerAccessInfo::viewReports)) {
    sendMessage(ServerPlayer, t, "You do not have permission to run the viewreports command");
    return;
  }
  if (clOptions->reportFile.size() == 0 && clOptions->reportPipe.size() == 0) {
    line = "The /report command is disabled on this server or there are no reports filed.";
    sendMessage(ServerPlayer, t, line.c_str());
  }
  std::ifstream ifs(clOptions->reportFile.c_str(), std::ios::in);
  if (ifs.fail()) {
    sendMessage(ServerPlayer, t, "Error reading from report file.");
    return;
  }
  while (std::getline(ifs, line))
    sendMessage(ServerPlayer, t, line.c_str());
}


static void handleClientqueryCmd(GameKeeper::Player *playerData, const char * message)
{
  int t = playerData->getIndex();
  DEBUG2("Clientquery requested by %s [%d]\n",
	 playerData->player.getCallSign(), t);
  if (message[12] != '\0') {
    std::string name = message + 13; // assumes there is a space
    while (isspace(name[0]))
      name.erase(name.begin());
    GameKeeper::Player *target;
    int i;
    for (i = 0; i < curMaxPlayers;i++) {
      target = GameKeeper::Player::getPlayerByIndex(i);
      if (target && strcmp(target->player.getCallSign(), name.c_str()) == 0) {
	sendMessage(i, t, TextUtils::format("Version: %s",
		     target->player.getClientVersion()).c_str());
	return;
      }
    }
    sendMessage(ServerPlayer, t, "Player not found.");
    return;
  }
  sendMessage(ServerPlayer, AllPlayers, "[Sent version information per request]");
  // send server's own version string just for kicks
  sendMessage(ServerPlayer, t,
	      TextUtils::format("BZFS Version: %s", getAppVersion()).c_str());
  // send all players' version strings
  // is faking a message from the remote client rude?
  // did that so that /clientquery and CLIENTQUERY look about the same.
  GameKeeper::Player *otherData;
  for (int i = 0; i < curMaxPlayers;i++) {
    otherData = GameKeeper::Player::getPlayerByIndex(i);
    if (otherData && otherData->player.isPlaying()) {
      sendMessage(i, t, TextUtils::format
		  ("Version: %s",
		   otherData->player.getClientVersion()).c_str());
    }
  }
  return;
}


/** /record command
 *
 *  /record start	       # start buffering
 *  /record stop		# stop buffering (or saving to file)
 *  /record size <Mbytes>       # set the buffer size, and truncate
 *  /record rate <secs>	 # set the state capture rate
 *  /record stats	       # display buffer time and memory information
 *  /record file [filename]     # begin capturing straight to file, flush buffer
 *  /record save [filename]     # save buffer to file (or default filename)
 */
static void handleRecordCmd(GameKeeper::Player *playerData, const char * message)
{
  int t = playerData->getIndex();
  const char *buf = message + 8;
  if (!playerData->accessInfo.hasPerm(PlayerAccessInfo::record)) {
    sendMessage(ServerPlayer, t, "You do not have permission to run the /record command");
    return;
  }
  while ((*buf != '\0') && isspace (*buf)) buf++; // eat whitespace

  if (strncasecmp (buf, "start", 5) == 0) {
    Record::start(t);
  }
  else if (strncasecmp (buf, "stop", 4) == 0) {
    Record::stop(t);
  }
  else if (strncasecmp (buf, "size", 4) == 0) {
    buf = buf + 4;
    while ((*buf != '\0') && isspace (*buf)) buf++; // eat whitespace

    if (*buf == '\0') {
      Record::sendHelp (t);
      return;
    }
    int size = atoi (buf);
    Record::setSize (t, size);
  }
  else if (strncasecmp (buf, "rate", 4) == 0) {
    buf = buf + 4;
    while ((*buf != '\0') && isspace (*buf)) buf++; // eat whitespace

    if (*buf == '\0') {
      Record::sendHelp (t);
      return;
    }
    int seconds = atoi (buf);
    Record::setRate (t, seconds);
  }
  else if (strncasecmp (buf, "stats", 5) == 0) {
    Record::sendStats(t);
  }
  else if (strncasecmp (buf, "list", 4) == 0) {
    Replay::sendFileList (t); // stolen from '/replay'
  }
  else if (strncasecmp (buf, "save", 4) == 0) {
    buf = buf + 4;
    char filename[MessageLen];

    while ((*buf != '\0') && isspace (*buf)) buf++; // eat whitespace
    if (*buf == '\0') {
      Record::sendHelp (t);
    }

    // get the filename
    sscanf (buf, "%s", filename);

    // FIXME - do this a little better? use quotations for strings?
    while ((*buf != '\0') && !isspace (*buf)) buf++; // eat filename
    while ((*buf != '\0') && isspace (*buf)) buf++; // eat whitespace

    if (*buf == '\0') {
      Record::saveBuffer (t, filename, 0);
    }
    else {
      Record::saveBuffer (t, filename, atoi(buf));
    }
  }
  else if (strncasecmp (buf, "file", 4) == 0) {
    buf = buf + 4;
    while ((*buf != '\0') && isspace (*buf)) buf++; // eat whitespace

    if (*buf == '\0') {
      Record::sendHelp (t);
    }
    else {
      Record::saveFile (t, buf);
    }
  }
  else {
    Record::sendHelp (t);
  }

  return;
}


/** /replay command
 *
 *  /replay list		# list available replay files
 *  /replay load [filename]     # set the replay file (or load the default)
 *  /replay play		# began playing
 *  /replay loop		# began playing in looped mode
 *  /replay stats		# report the current replay state
 *  /replay skip <secs>	 # fast foward or rewind in time
 */
static void handleReplayCmd(GameKeeper::Player *playerData, const char * message)
{
  int t = playerData->getIndex();
  const char *buf = message + 7;
  while ((*buf != '\0') && isspace (*buf)) { // eat whitespace
    buf++;
  }
  
  // everyone can use the replay stats command
  if (strncasecmp (buf, "stats", 4) == 0) {
    Replay::sendStats (t);
    return;
  }

  if (!playerData->accessInfo.hasPerm(PlayerAccessInfo::replay)) {
    sendMessage(ServerPlayer, t, "You do not have permission to run the /replay command");
    return;
  }

  if (strncasecmp (buf, "list", 4) == 0) {
    Replay::sendFileList (t);
  }
  else if (strncasecmp (buf, "load", 4) == 0) {
    buf = buf + 4;
    while ((*buf != '\0') && isspace (*buf)) buf++; // eat whitespace

    if (*buf == '\0') {
      Replay::sendHelp (t);
    }
    else {
      Replay::loadFile (t, buf);
    }
  }
  else if (strncasecmp (buf, "play", 4) == 0) {
    Replay::play (t);
  }
  else if (strncasecmp (buf, "loop", 4) == 0) {
    Replay::loop (t);
  }
  else if (strncasecmp (buf, "skip", 4) == 0) {
    buf = buf + 4;
    while ((*buf != '\0') && isspace (*buf)) buf++; // eat whitespace

    if (*buf == '\0') {
      Replay::skip (t, 0);
    }
    else {
      int skip = atoi (buf);
      Replay::skip (t, skip);
    }
  }
  else if (strncasecmp (buf, "pause", 5) == 0) {
    Replay::pause (t);
  }
  else {
    Replay::sendHelp (t);
  }

  return;
}

static void handleSayCmd(GameKeeper::Player *playerData, const char * message)
{
  size_t messageStart = 0;
  int t = playerData->getIndex();

  if (!playerData->accessInfo.hasPerm(PlayerAccessInfo::say)) {
    char reply[MessageLen] = {0};
    snprintf(reply, MessageLen, "%s, you do not have permission to run the /say command", playerData->player.getCallSign());
    sendMessage(ServerPlayer, t, reply);
    return;
  }

  std::string messageText = &message[4];

  // skip any leading whitespace
  while ((messageStart < messageText.size()) &&
	 (isspace(messageText[messageStart]))) {
    messageStart++;
  }

  // make sure there was _some_ whitespace after /say
  if (messageStart == 0) {
    sendMessage(ServerPlayer, t, "Usage: /say some message");
    return;
  }

  // no anonymous messages
  messageText += " (";
  messageText += playerData->player.getCallSign();
  messageText += ")";

  // send the message
  sendMessage(ServerPlayer, AllPlayers, messageText.c_str() + messageStart );
  return;
}



static void handleDateCmd(GameKeeper::Player *playerData, const char * /*message*/)
{
  int t = playerData->getIndex();
  if (!playerData->accessInfo.hasPerm(PlayerAccessInfo::date)) {
    sendMessage(ServerPlayer, t, "You do not have permission to run the /date command");
    return;
  }
  time_t now = time(NULL);
  char* timeStr = ctime(&now);
  timeStr[24] = '\0';
  sendMessage(ServerPlayer, t, timeStr);
}


/** /masterban command
 *
 * /masterban flush	     # remove all master ban entries from this server
 * /masterban reload	    # reread and reload all master ban entries
 * /masterban list	      # output a list of who is banned
 */
static void handleMasterBanCmd(GameKeeper::Player *playerData, const char *message)
{
  int t = playerData->getIndex();
  std::string callsign = std::string(playerData->player.getCallSign());

  DEBUG2("\"%s\" has requested masterban: %s\n", callsign.c_str(), message);

  if (!playerData->accessInfo.hasPerm(PlayerAccessInfo::masterBan)) {
    sendMessage(ServerPlayer, t, TextUtils::format("%s, you are presently not authorized to run /masterban", callsign.c_str()).c_str());
    return;
  }

  DEBUG3("Player has permission to run /masterban\n");

  if (!clOptions->publicizeServer) {
    sendMessage(ServerPlayer, t, "This is not a public server.  Private servers do not use the master ban list.");
  }
  if (clOptions->suppressMasterBanList) {
    sendMessage(ServerPlayer, t, "The master ban list is disabled on this server.");
  }

  std::string argument = &message[10]; /* skip "/masterban" */
  std::string cmd = "";

  // allow for arbitrary whitespace
  size_t start = 0;
  while ((start < argument.size()) &&
	 (isspace(argument[start]))) {
    start++;
  }

  size_t end = 0;
  while ((end < argument.size()) &&
	 (!isspace(argument[end]))) {
    end++;
  }

  // make sure the command is lower case for comparison simplicity/insensitivity
  cmd = argument.substr(start, end - start);
  std::transform(cmd.begin(), cmd.end(), cmd.begin(), tolower);

  if (cmd == "reload") {
    if (!playerData->accessInfo.hasPerm(PlayerAccessInfo::ban) ||
	!playerData->accessInfo.hasPerm(PlayerAccessInfo::unban)) {
      sendMessage(ServerPlayer, t, "You do not have permission to reload the master ban list.");
      sendMessage(ServerPlayer, t, "Permission to ban and unban is required to reload the master ban list.");
      return;
    }

    if (clOptions->publicizeServer && !clOptions->suppressMasterBanList) {
      MasterBanList	banList;
      int               banCount;

      clOptions->acl.purgeMasters();
      sendMessage(ServerPlayer, t, "Previous master ban list entries have been flushed.");

      for (std::vector<std::string>::const_iterator i = clOptions->masterBanListURL.begin(); i != clOptions->masterBanListURL.end(); i++) {
	banCount = clOptions->acl.merge(banList.get(i->c_str()));
	std::string reloadmsg = TextUtils::format("Loaded %d master bans from %s", banCount, i->c_str());
	DEBUG1("%s\n", reloadmsg.c_str());
	sendMessage(ServerPlayer, t, reloadmsg.c_str());
      }

    } else {
      sendMessage(ServerPlayer, t, "No action taken.");
    }

  } else if (cmd == "flush") {
    if (!playerData->accessInfo.hasPerm(PlayerAccessInfo::unban)) {
      sendMessage(ServerPlayer, t, "You do not have permission to reload the master ban list.");
      sendMessage(ServerPlayer, t, "Permission to unban is required to flush the master ban list.");
      return;
    }

    clOptions->acl.purgeMasters();
    sendMessage(ServerPlayer, t, "The master ban list has been flushed.");

  } else if (cmd == "list") {
    std::vector<std::pair<std::string, std::string> > bans = clOptions->acl.listMasterBans();

    if (bans.size() > 20) {
      sendMessage(ServerPlayer, t, TextUtils::format("There are %d bans, only displaying the first 20", bans.size()).c_str());

    } else if (bans.size() == 0) {
      sendMessage(ServerPlayer, t, "There are no master bans loaded.");

    } else {
      // print out a list header
      std::string banmsg = TextUtils::format("Master Bans from %s:", DefaultMasterBanURL);
      sendMessage(ServerPlayer, t, banmsg.c_str());
      for (size_t i = 0; i < banmsg.size(); i++) {
	banmsg[i] = '-';
      }
      sendMessage(ServerPlayer, t, banmsg.c_str());
    }

    // print out the bans
    int counter = 0;
    for (std::vector<std::pair<std::string, std::string> >::const_iterator j = bans.begin(); j != bans.end() && counter < 20; j++, counter++) {
      sendMessage(ServerPlayer, t, TextUtils::format("%s: %s", (j->first).c_str(), (j->second).c_str()).c_str());
    }

  } else {
    if (cmd.size() > 0) {
      sendMessage(ServerPlayer, t, TextUtils::format("Unknown masterban command [%s]", cmd.c_str()).c_str());
    }
    sendMessage(ServerPlayer, t, TextUtils::format("Usage: /masterban list|reload|flush").c_str());
  }

  return;
}


// parse server comands
void parseServerCommand(const char *message, int t)
{
  if (!message) {
    std::cerr << "WARNING: parseCommand was given a null message?!" << std::endl;
    return;
  }

  GameKeeper::Player *playerData = GameKeeper::Player::getPlayerByIndex(t);
  if (!playerData)
    return;

  if (strncasecmp(message + 1, "msg", 3) == 0) {
    handleMsgCmd(playerData, message);

  } else if (strncasecmp(message + 1, "serverquery", 11) == 0) {
    handleServerQueryCmd(playerData, message);

  } else if (strncasecmp(message + 1, "part", 4) == 0) {
    handlePartCmd(playerData, message);

  } else if (strncasecmp(message + 1, "quit", 4) == 0) {
    handleQuitCmd(playerData, message);

  } else if (strncasecmp(message + 1, "uptime", 6) == 0) {
    handleUptimeCmd(playerData, message);

  } else if (strncasecmp(message + 1, "password", 8) == 0) {
    handlePasswordCmd(playerData, message);

  } else if (strncasecmp(message + 1, "set ", 4) == 0) {
    handleSetCmd(playerData, message);

  } else if (strncasecmp(message + 1, "reset", 5) == 0) {
    handleResetCmd(playerData, message);

  } else if (strncasecmp(message + 1, "shutdownserver", 8) == 0) {
    handleShutdownserverCmd(playerData, message);

  } else if (strncasecmp(message + 1, "superkill", 8) == 0) {
    handleSuperkillCmd(playerData, message);

  } else if (strncasecmp(message + 1, "gameover", 8) == 0) {
    handleGameoverCmd(playerData, message);

  } else if (strncasecmp(message + 1, "countdown", 9) == 0) {
    handleCountdownCmd(playerData, message);

  } else if (strncasecmp(message + 1, "flag ", 5) == 0) {
    handleFlagCmd(playerData,message);

  } else if (strncasecmp(message + 1, "kick", 4) == 0) {
    handleKickCmd(playerData, message);

  } else if (strncasecmp(message + 1, "kill", 4) == 0) {
    handleKillCmd(playerData, message);

  } else if (strncasecmp(message+1, "banlist", 7) == 0) {
    handleBanlistCmd(playerData, message);

  } else if (strncasecmp(message+1, "hostbanlist", 11) == 0) {
    handleHostBanlistCmd(playerData, message);

  } else if (strncasecmp(message+1, "ban", 3) == 0) {
    handleBanCmd(playerData, message);

  } else if (strncasecmp(message+1, "hostban", 7) == 0) {
    handleHostBanCmd(playerData, message);

  } else if (strncasecmp(message+1, "unban", 5) == 0) {
    handleUnbanCmd(playerData, message);

  } else if (strncasecmp(message+1, "hostunban", 9) == 0) {
    handleHostUnbanCmd(playerData, message);

  } else if (strncasecmp(message+1, "lagwarn",7) == 0) {
    handleLagwarnCmd(playerData, message);

  } else if (strncasecmp(message+1, "lagstats",8) == 0) {
    handleLagstatsCmd(playerData, message);

  } else if (strncasecmp(message+1, "idlestats",9) == 0) {
    handleIdlestatsCmd(playerData, message);

  } else if (strncasecmp(message+1, "flaghistory", 11 ) == 0) {
    handleFlaghistoryCmd(playerData, message); 

  } else if (strncasecmp(message+1, "mute", 4 ) == 0 ) {
    handleMuteCmd(playerData, message);

  } else if (strncasecmp(message+1, "unmute", 6 ) == 0 ) {
    handleUnmuteCmd(playerData, message);

  } else if (strncasecmp(message+1, "playerlist", 10) == 0) {
    handlePlayerlistCmd(playerData, message);

  } else if (strncasecmp(message+1, "report", 6) == 0) {
    handleReportCmd(playerData, message);

  } else if (strncasecmp(message+1, "help", 4) == 0) {
    handleHelpCmd(playerData, message);

  } else if (strncasecmp(message + 1, "identify", 8) == 0) {
    handleIdentifyCmd(playerData, message);

  } else if (strncasecmp(message + 1, "register", 8) == 0) {
    handleRegisterCmd(playerData, message);

  } else if (strncasecmp(message + 1, "ghost", 5) == 0) {
    handleGhostCmd(playerData, message);

  } else if (strncasecmp(message + 1, "deregister", 10) == 0) {
    handleDeregisterCmd(playerData, message);

  } else if (strncasecmp(message + 1, "setpass", 7) == 0) {
    handleSetpassCmd(playerData, message);

  } else if (strncasecmp(message + 1, "grouplist", 9) == 0) {
    handleGrouplistCmd(playerData, message);

  } else if (strncasecmp(message + 1, "showgroup", 9) == 0) {
    handleShowgroupCmd(playerData, message);

  } else if (strncasecmp(message + 1, "groupperms", 10) == 0) {
    handleGrouppermsCmd(playerData, message);

  } else if (strncasecmp(message + 1, "setgroup", 8) == 0) {
    handleSetgroupCmd(playerData, message);

  } else if (strncasecmp(message + 1, "removegroup", 11) == 0) {
    handleRemovegroupCmd(playerData, message);

  } else if (strncasecmp(message + 1, "reload", 6) == 0) {
    handleReloadCmd(playerData, message);

  } else if (strncasecmp(message + 1, "poll", 4) == 0) {
    handlePollCmd(playerData, message);

  } else if (strncasecmp(message + 1, "vote", 4) == 0) {
    handleVoteCmd(playerData, message);

  } else if (strncasecmp(message + 1, "veto", 4) == 0) {
    handleVetoCmd(playerData, message);

  } else if (strncasecmp(message + 1, "viewreports", 11) == 0) {
    handleViewReportsCmd(playerData, message);

  } else if (strncasecmp(message + 1, "clientquery", 11) == 0) {
    handleClientqueryCmd(playerData, message);

  } else if (strncasecmp(message + 1, "date", 4) == 0 || strncasecmp(message + 1, "time", 4) == 0) {
    handleDateCmd(playerData, message);

  } else if (strncasecmp(message + 1, "record", 6) == 0) {
    handleRecordCmd(playerData, message);

  } else if (strncasecmp(message + 1, "replay", 6) == 0) {
    handleReplayCmd(playerData, message);

  } else if (strncasecmp(message + 1, "say", 3) == 0) {
    handleSayCmd(playerData, message);

  } else if (strncasecmp(message + 1, "masterban", 9) == 0) {
    handleMasterBanCmd(playerData, message);
    
  //Start "Race to 7" by RPG
  } else if (strncasecmp(message + 1, "match start", 11) == 0) {
    //Start logging in the file
    FILE *f;
    f=fopen("match.txt","w");
    fprintf(f,"Match started at %s\n", TimeKeeper::timestamp());
    
    match_in_progress = true;
    fprintf(f,"match_in_progress set to true");
    sendMessage(ServerPlayer, AllPlayers, "A match has been started!");
    //Do a flagreset
    zapAllFlags();
    fprintf(f,"All flags reset");
    //Reset the team scores
    bz_resetTeamScore(RedTeam);
    bz_resetTeamScore(BlueTeam);
    fprintf(f,"Red and Blue team scores have been reset");
    
    fclose(f);
    
    } else if (strncasecmp(message + 1, "match end", 9) == 0) {
           
    if((bz_getTeamWins(RedTeam) == 7 || bz_getTeamWins(BlueTeam) == 7) && match_in_progress){
                               
    //Get player info
    std::string players;
    players = "\nBelow is a list of players:\n-----";
    for (int i = 0; i < curMaxPlayers; i++) {
       GameKeeper::Player *data;
       data = GameKeeper::Player::getPlayerByIndex(i);
       players += "\nCallsign: ";
       players += data->player.getCallSign();
       players += "\nE-mail: ";
       players += data->player.getEMail();
       players += "\nTeam: ";
       if(data->player.getTeam() == 0) players += "Rogue";
       if(data->player.getTeam() == 1) players += "Red";
       if(data->player.getTeam() == 2) players += "Green";
       if(data->player.getTeam() == 3) players += "Blue";
       if(data->player.getTeam() == 4) players += "Purple";
       if(data->player.getTeam() == 5) players += "Observer";
       players += "\n-----";
    }
    
    //Get team info
    //Red team
    players += "\n\nRed Team:\nScore: ";
    int RedTeaminfo = bz_getTeamScore(RedTeam);
    players += TextUtils::format ("%d",RedTeaminfo);
    players += "\nWins: ";
    RedTeaminfo = bz_getTeamWins(RedTeam);
    if(RedTeaminfo == 7) {players += "\nRED TEAM WINS!!!!!!!!!!!";}
    players += TextUtils::format ("%d",RedTeaminfo);
    players += "\nLosses: ";
    RedTeaminfo = bz_getTeamLosses(RedTeam);
    players += TextUtils::format ("%d",RedTeaminfo);
    //Blue team
    players += "\n\nBlue Team:\nScore: ";
    int BlueTeaminfo = bz_getTeamScore(BlueTeam);
    players += TextUtils::format ("%d",BlueTeaminfo);
    players += "\nWins: ";
    BlueTeaminfo = bz_getTeamWins (BlueTeam);
    if(BlueTeaminfo == 7) {players += "\nBLUE TEAM WINS!!!!!!!!!!!";}
    players += TextUtils::format ("%d",BlueTeaminfo);
    players += "\nLosses: ";
    BlueTeaminfo = bz_getTeamLosses (BlueTeam);
    players += TextUtils::format ("%d",BlueTeaminfo);
    players += "\n\n";
     
    //URL Encode it all
    //players = TextUtils::url_encode(players);
    
    //Write to file
    FILE *f;
    f=fopen("match.txt","a"); 
    fprintf(f,"%s",players.c_str());
    fprintf(f,"\nMatch over at %s", TimeKeeper::timestamp());
    sendMessage( ServerPlayer, AllPlayers, "Match Reported. You will all now lose your flags.");
    
    //DE-FLAG THE PLAYERS!
    zapAllFlags();
    fprintf(f,"\nAll flags reset");
    
    //Reset team scores
    bz_resetTeamScore(RedTeam);
    bz_resetTeamScore(BlueTeam);
    fprintf(f,"\nRed and Blue team scores reset");

    gameOver = true;
    if (clOptions->timeManualStart){
      countdownActive = false;
    }
    //There is no more match, I hope
    match_in_progress = false;
    fprintf(f,"\nmatch_in_progress set to false");
    fclose(f);
    }else{
         sendMessage( ServerPlayer, AllPlayers, "What do you think you're trying to do? Report the match before someone wins!");
         sendMessage( ServerPlayer, AllPlayers, "WHY are you so HELL BENT on screwing up the SYSTEM? HUH?");
    }
    //End "Race to 7" by RPG


  } else {
    // lets see if it is a custom command
    std::vector<std::string> params = TextUtils::tokenize(std::string(message+1),std::string(" "));

    if (params.size() == 0)
      return;
    
    tmCustomSlashCommandMap::iterator itr = customCommands.find(TextUtils::tolower(params[0]));
  
    bzApiString	command = params[0];
    bzApiString param;

    if (params.size()>1)
      param = params[1];
    else
      param = message;

    if (itr != customCommands.end()) {  // see if we have a registerd custom command and call it
      if (itr->second->handle(t, command, param))  // if it handles it, then we are good
        return;
    }
  
    // lets see if anyone wants to handle the unhandled event
    bz_UnknownSlashCommandEventData commandData;
    commandData.from = t;
    commandData.message = message;
    commandData.time = TimeKeeper::getCurrent().getSeconds();
  
    worldEventManager.callEvents(bz_eUnknownSlashCommand, -1, &commandData);
    if (commandData.handled) // did anyone do it?
      return;

    char reply[MessageLen];
    snprintf(reply, MessageLen, "Unknown command [%s]", message + 1);
    sendMessage(ServerPlayer, t, reply);
  }
}

void registerCustomSlashCommand(std::string command, bz_CustomSlashCommandHandler* handler)
{
  if (handler)
    customCommands[TextUtils::tolower(command)] = handler;
}

void removeCustomSlashCommand(std::string command)
{
  tmCustomSlashCommandMap::iterator itr = customCommands.find(TextUtils::tolower(command));
  if (itr != customCommands.end())
    customCommands.erase(itr);
}


// Local Variables: ***
// mode: C++ ***
// tab-width: 8 ***
// c-basic-offset: 2 ***
// indent-tabs-mode: t ***
// End: ***
// ex: shiftwidth=2 tabstop=8