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 -#include -#include -#ifdef HAVE_CSTDIO -#include -#else -#include -#endif -#ifdef HAVE_CSTRING -#include -#else -#include -#endif -#include - -// 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 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 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 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 [|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 argv = TextUtils::tokenize(message, " \t", 3, true); - - if (argv.size() < 3) { - sendMessage(ServerPlayer, t, "Syntax: /kick <#slot | PlayerName | \"Player Name\"> "); - 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 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 argv = TextUtils::tokenize(msg, " \t", 4, true); - - if (argv.size() < 4) { - sendMessage(ServerPlayer, t, "Syntax: /ban <#slot | PlayerName | \"Player Name\" | ip> "); - 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 argv = TextUtils::tokenize( msg, " \t", 4 ); - - if (argv.size() < 2) { - sendMessage(ServerPlayer, t, "Syntax: /hostban [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 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& chunks = clOptions->textChunker.getChunkNames(); - sendMessage(ServerPlayer, t, "Available help pages (use /help )"); - for (int i = 0; i < (int) chunks.size(); i++) { - sendMessage(ServerPlayer, t, chunks[i].c_str()); - } - } else { - bool foundChunk = false; - const std::vector& chunks = clOptions->textChunker.getChunkNames(); - for (int i = 0; i < (int)chunks.size() && (!foundChunk); i++) { - if (chunks[i] == (message +6)){ - const std::vector* 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::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::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 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 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 # set the buffer size, and truncate - * /record rate # 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 # 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::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 > 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 >::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 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 +#include +#include +#ifdef HAVE_CSTDIO +#include +#else +#include +#endif +#ifdef HAVE_CSTRING +#include +#else +#include +#endif +#include + +// 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 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 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 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 [|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 argv = TextUtils::tokenize(message, " \t", 3, true); + + if (argv.size() < 3) { + sendMessage(ServerPlayer, t, "Syntax: /kick <#slot | PlayerName | \"Player Name\"> "); + 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 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 argv = TextUtils::tokenize(msg, " \t", 4, true); + + if (argv.size() < 4) { + sendMessage(ServerPlayer, t, "Syntax: /ban <#slot | PlayerName | \"Player Name\" | ip> "); + 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 argv = TextUtils::tokenize( msg, " \t", 4 ); + + if (argv.size() < 2) { + sendMessage(ServerPlayer, t, "Syntax: /hostban [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 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& chunks = clOptions->textChunker.getChunkNames(); + sendMessage(ServerPlayer, t, "Available help pages (use /help )"); + for (int i = 0; i < (int) chunks.size(); i++) { + sendMessage(ServerPlayer, t, chunks[i].c_str()); + } + } else { + bool foundChunk = false; + const std::vector& chunks = clOptions->textChunker.getChunkNames(); + for (int i = 0; i < (int)chunks.size() && (!foundChunk); i++) { + if (chunks[i] == (message +6)){ + const std::vector* 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::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::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 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 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 # set the buffer size, and truncate + * /record rate # 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 # 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::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 > 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 >::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 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