All pastes #18453 Raw Edit

Unnamed

public text v1 · immutable
#18453 ·published 2005-07-25 13:34 UTC
rendered paste body
Index: src/bzfs/commands.cxx
===================================================================
RCS file: /cvsroot/bzflag/bzflag/src/bzfs/commands.cxx,v
retrieving revision 2.79
diff -u -r2.79 commands.cxx
--- src/bzfs/commands.cxx	22 Jul 2005 15:58:05 -0000	2.79
+++ src/bzfs/commands.cxx	25 Jul 2005 13:26:02 -0000
@@ -1,2809 +1,2921 @@
-/* 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
-
-// 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 sendFlagUpdate(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);
-
-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);
-
-  } 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
+/* 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