about summary refs log tree commit diff
diff options
context:
space:
mode:
authoroy <Tom_Adams@web.de>2011-07-05 21:54:10 +0200
committeroy <Tom_Adams@web.de>2011-07-05 21:54:10 +0200
commit1ae474689d564e1feba9924842f05e06600d83a1 (patch)
treebaac3e231cc2a1e1d35ea7d2a8c109e23fd8b8af
parent5008b5f8a52a2692e5396cd67bf3ad13332e074b (diff)
downloadzcatch-1ae474689d564e1feba9924842f05e06600d83a1.tar.gz
zcatch-1ae474689d564e1feba9924842f05e06600d83a1.zip
added moderator support for the remote console. #518
-rw-r--r--src/engine/console.h7
-rw-r--r--src/engine/server/server.cpp58
-rw-r--r--src/engine/server/server.h8
-rw-r--r--src/engine/shared/config_variables.h3
-rw-r--r--src/engine/shared/console.cpp129
-rw-r--r--src/engine/shared/console.h6
6 files changed, 172 insertions, 39 deletions
diff --git a/src/engine/console.h b/src/engine/console.h
index f8ec67b0..e650ac47 100644
--- a/src/engine/console.h
+++ b/src/engine/console.h
@@ -14,7 +14,10 @@ public:
 	{
 		OUTPUT_LEVEL_STANDARD=0,
 		OUTPUT_LEVEL_ADDINFO,
-		OUTPUT_LEVEL_DEBUG
+		OUTPUT_LEVEL_DEBUG,
+
+		ACCESS_LEVEL_ADMIN=0,
+		ACCESS_LEVEL_MOD,
 	};
 
 	// TODO: rework this interface to reduce the amount of virtual calls
@@ -62,6 +65,8 @@ public:
 
 	virtual void RegisterPrintCallback(FPrintCallback pfnPrintCallback, void *pUserData) = 0;
 	virtual void Print(int Level, const char *pFrom, const char *pStr) = 0;
+
+	virtual void SetAccessLevel(int AccessLevel) = 0;
 };
 
 extern IConsole *CreateConsole(int FlagMask);
diff --git a/src/engine/server/server.cpp b/src/engine/server/server.cpp
index 0e43e73f..14467b2b 100644
--- a/src/engine/server/server.cpp
+++ b/src/engine/server/server.cpp
@@ -1,6 +1,7 @@
 /* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
 /* If you are missing that file, acquire a complete release at teeworlds.com.                */
 
+#include <base/math.h>
 #include <base/system.h>
 
 #include <engine/config.h>
@@ -181,6 +182,7 @@ CServer::CServer() : m_DemoRecorder(&m_SnapshotDelta)
 	m_MapReload = 0;
 
 	m_RconClientID = -1;
+	m_RconAuthLevel = AUTHED_ADMIN;
 
 	Init();
 }
@@ -280,6 +282,11 @@ void CServer::Kick(int ClientID, const char *pReason)
 		Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", "you can't kick yourself");
  		return;
 	}
+	else if(m_aClients[ClientID].m_Authed > m_RconAuthLevel)
+	{
+		Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", "kick command denied");
+ 		return;
+	}
 
 	m_NetServer.Drop(ClientID, pReason);
 }
@@ -571,7 +578,7 @@ int CServer::NewClientCallback(int ClientID, void *pUser)
 	pThis->m_aClients[ClientID].m_aName[0] = 0;
 	pThis->m_aClients[ClientID].m_aClan[0] = 0;
 	pThis->m_aClients[ClientID].m_Country = -1;
-	pThis->m_aClients[ClientID].m_Authed = 0;
+	pThis->m_aClients[ClientID].m_Authed = AUTHED_NO;
 	pThis->m_aClients[ClientID].m_AuthTries = 0;
 	pThis->m_aClients[ClientID].Reset();
 	return 0;
@@ -596,7 +603,7 @@ int CServer::DelClientCallback(int ClientID, const char *pReason, void *pUser)
 	pThis->m_aClients[ClientID].m_aName[0] = 0;
 	pThis->m_aClients[ClientID].m_aClan[0] = 0;
 	pThis->m_aClients[ClientID].m_Country = -1;
-	pThis->m_aClients[ClientID].m_Authed = 0;
+	pThis->m_aClients[ClientID].m_Authed = AUTHED_NO;
 	pThis->m_aClients[ClientID].m_AuthTries = 0;
 	pThis->m_aClients[ClientID].m_Snapshots.PurgeAll();
 	return 0;
@@ -635,7 +642,7 @@ void CServer::SendRconLineAuthed(const char *pLine, void *pUser)
 
 	for(i = 0; i < MAX_CLIENTS; i++)
 	{
-		if(pThis->m_aClients[i].m_State != CClient::STATE_EMPTY && pThis->m_aClients[i].m_Authed)
+		if(pThis->m_aClients[i].m_State != CClient::STATE_EMPTY && pThis->m_aClients[i].m_Authed >= pThis->m_RconAuthLevel)
 			pThis->SendRconLine(i, pLine);
 	}
 
@@ -813,8 +820,12 @@ void CServer::ProcessClientPacket(CNetChunk *pPacket)
 				str_format(aBuf, sizeof(aBuf), "ClientID=%d rcon='%s'", ClientID, pCmd);
 				Console()->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "server", aBuf);
 				m_RconClientID = ClientID;
+				m_RconAuthLevel = m_aClients[ClientID].m_Authed;
+				Console()->SetAccessLevel(m_aClients[ClientID].m_Authed == AUTHED_ADMIN ? IConsole::ACCESS_LEVEL_ADMIN : IConsole::ACCESS_LEVEL_MOD);
 				Console()->ExecuteLine(pCmd);
+				Console()->SetAccessLevel(IConsole::ACCESS_LEVEL_ADMIN);
 				m_RconClientID = -1;
+				m_RconAuthLevel = AUTHED_ADMIN;
 			}
 		}
 		else if(Msg == NETMSG_RCON_AUTH)
@@ -825,9 +836,9 @@ void CServer::ProcessClientPacket(CNetChunk *pPacket)
 
 			if(Unpacker.Error() == 0)
 			{
-				if(g_Config.m_SvRconPassword[0] == 0)
+				if(g_Config.m_SvRconPassword[0] == 0 && g_Config.m_SvRconModPassword[0])
 				{
-					SendRconLine(ClientID, "No rcon password set on server. Set sv_rcon_password to enable the remote console.");
+					SendRconLine(ClientID, "No rcon password set on server. Set sv_rcon_password and/or sv_rcon_mod_password to enable the remote console.");
 				}
 				else if(str_comp(pPw, g_Config.m_SvRconPassword) == 0)
 				{
@@ -835,10 +846,22 @@ void CServer::ProcessClientPacket(CNetChunk *pPacket)
 					Msg.AddInt(1);
 					SendMsgEx(&Msg, MSGFLAG_VITAL, ClientID, true);
 
-					m_aClients[ClientID].m_Authed = 1;
-					SendRconLine(ClientID, "Authentication successful. Remote console access granted.");
+					m_aClients[ClientID].m_Authed = AUTHED_ADMIN;
+					SendRconLine(ClientID, "Admin authentication successful. Full remote console access granted.");
+					char aBuf[256];
+					str_format(aBuf, sizeof(aBuf), "ClientID=%d authed (admin)", ClientID);
+					Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", aBuf);
+				}
+				else if(str_comp(pPw, g_Config.m_SvRconModPassword) == 0)
+				{
+					CMsgPacker Msg(NETMSG_RCON_AUTH_STATUS);
+					Msg.AddInt(1);
+					SendMsgEx(&Msg, MSGFLAG_VITAL, ClientID, true);
+
+					m_aClients[ClientID].m_Authed = AUTHED_MOD;
+					SendRconLine(ClientID, "Moderator authentication successful. Limited remote console access granted.");
 					char aBuf[256];
-					str_format(aBuf, sizeof(aBuf), "ClientID=%d authed", ClientID);
+					str_format(aBuf, sizeof(aBuf), "ClientID=%d authed (moderator)", ClientID);
 					Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", aBuf);
 				}
 				else if(g_Config.m_SvRconMaxTries)
@@ -1308,6 +1331,20 @@ void CServer::ConBan(IConsole::IResult *pResult, void *pUser)
 				pServer->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", "you can't ban yourself");
 				return;
 			}
+
+			for(int i = 0; i < MAX_CLIENTS; ++i)
+			{
+				if(i == pServer->m_RconClientID)
+					continue;
+
+				AddrCheck = pServer->m_NetServer.ClientAddr(i);
+				AddrCheck.port = 0;
+				if(net_addr_comp(&Addr, &AddrCheck) == 0 && pServer->m_aClients[i].m_Authed > pServer->m_RconAuthLevel)
+				{
+					pServer->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", "ban command denied");
+					return;
+				}
+			}
 		}
 		pServer->BanAdd(Addr, Minutes*60, pReason);
 	}
@@ -1325,6 +1362,11 @@ void CServer::ConBan(IConsole::IResult *pResult, void *pUser)
 			pServer->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", "you can't ban yourself");
 			return;
 		}
+		else if(pServer->m_aClients[ClientID].m_Authed > pServer->m_RconAuthLevel)
+		{
+			pServer->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", "ban command denied");
+			return;
+		}
 
 		Addr = pServer->m_NetServer.ClientAddr(ClientID);
 		pServer->BanAdd(Addr, Minutes*60, pReason);
diff --git a/src/engine/server/server.h b/src/engine/server/server.h
index be36a856..72c82d4d 100644
--- a/src/engine/server/server.h
+++ b/src/engine/server/server.h
@@ -49,6 +49,13 @@ public:
 	class IConsole *Console() { return m_pConsole; }
 	class IStorage *Storage() { return m_pStorage; }
 
+	enum
+	{
+		AUTHED_NO=0,
+		AUTHED_MOD,
+		AUTHED_ADMIN,
+	};
+
 	class CClient
 	{
 	public:
@@ -110,6 +117,7 @@ public:
 	int m_RunServer;
 	int m_MapReload;
 	int m_RconClientID;
+	int m_RconAuthLevel;
 
 	int64 m_Lastheartbeat;
 	//static NETADDR4 master_server;
diff --git a/src/engine/shared/config_variables.h b/src/engine/shared/config_variables.h
index 31a8128a..213ebf26 100644
--- a/src/engine/shared/config_variables.h
+++ b/src/engine/shared/config_variables.h
@@ -82,7 +82,8 @@ MACRO_CONFIG_INT(SvMaxClients, sv_max_clients, 8, 1, MAX_CLIENTS, CFGFLAG_SERVER
 MACRO_CONFIG_INT(SvMaxClientsPerIP, sv_max_clients_per_ip, 4, 1, MAX_CLIENTS, CFGFLAG_SERVER, "Maximum number of clients with the same IP that can connect to the server")
 MACRO_CONFIG_INT(SvHighBandwidth, sv_high_bandwidth, 0, 0, 1, CFGFLAG_SERVER, "Use high bandwidth mode. Doubles the bandwidth required for the server. LAN use only")
 MACRO_CONFIG_INT(SvRegister, sv_register, 1, 0, 1, CFGFLAG_SERVER, "Register server with master server for public listing")
-MACRO_CONFIG_STR(SvRconPassword, sv_rcon_password, 32, "", CFGFLAG_SERVER, "Remote console password")
+MACRO_CONFIG_STR(SvRconPassword, sv_rcon_password, 32, "", CFGFLAG_SERVER, "Remote console password (full access)")
+MACRO_CONFIG_STR(SvRconModPassword, sv_rcon_mod_password, 32, "", CFGFLAG_SERVER, "Remote console password for moderators (limited access)")
 MACRO_CONFIG_INT(SvRconMaxTries, sv_rcon_max_tries, 3, 0, 100, CFGFLAG_SERVER, "Maximum number of tries for remote console authentication")
 MACRO_CONFIG_INT(SvRconBantime, sv_rcon_bantime, 5, 0, 1440, CFGFLAG_SERVER, "The time a client gets banned if remote console authentication fails. 0 makes it just use kick")
 
diff --git a/src/engine/shared/console.cpp b/src/engine/shared/console.cpp
index 3fd73543..588abd61 100644
--- a/src/engine/shared/console.cpp
+++ b/src/engine/shared/console.cpp
@@ -1,11 +1,15 @@
 /* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
 /* If you are missing that file, acquire a complete release at teeworlds.com.                */
 #include <new>
+
+#include <base/math.h>
 #include <base/system.h>
-#include <engine/shared/protocol.h>
+
 #include <engine/storage.h>
-#include "console.h"
+#include <engine/shared/protocol.h>
+
 #include "config.h"
+#include "console.h"
 #include "linereader.h"
 
 const char *CConsole::CResult::GetString(unsigned Index)
@@ -247,38 +251,47 @@ void CConsole::ExecuteLineStroked(int Stroke, const char *pStr)
 		if(ParseStart(&Result, pStr, (pEnd-pStr) + 1) != 0)
 			return;
 
-		if (!*Result.m_pCommand)
+		if(!*Result.m_pCommand)
 			return;
 
 		CCommand *pCommand = FindCommand(Result.m_pCommand, m_FlagMask);
 
 		if(pCommand)
 		{
-			int IsStrokeCommand = 0;
-			if(Result.m_pCommand[0] == '+')
-			{
-				// insert the stroke direction token
-				Result.AddArgument(m_paStrokeStr[Stroke]);
-				IsStrokeCommand = 1;
-			}
-
-			if(Stroke || IsStrokeCommand)
+			if(pCommand->m_AccessLevel >= m_AccessLevel)
 			{
-				if(ParseArgs(&Result, pCommand->m_pParams))
+				int IsStrokeCommand = 0;
+				if(Result.m_pCommand[0] == '+')
 				{
-					char aBuf[256];
-					str_format(aBuf, sizeof(aBuf), "Invalid arguments... Usage: %s %s", pCommand->m_pName, pCommand->m_pParams);
-					Print(OUTPUT_LEVEL_STANDARD, "Console", aBuf);
+					// insert the stroke direction token
+					Result.AddArgument(m_paStrokeStr[Stroke]);
+					IsStrokeCommand = 1;
 				}
-				else if(m_StoreCommands && pCommand->m_Flags&CFGFLAG_STORE)
+
+				if(Stroke || IsStrokeCommand)
 				{
-					m_ExecutionQueue.AddEntry();
-					m_ExecutionQueue.m_pLast->m_pfnCommandCallback = pCommand->m_pfnCallback;
-					m_ExecutionQueue.m_pLast->m_pCommandUserData = pCommand->m_pUserData;
-					m_ExecutionQueue.m_pLast->m_Result = Result;
+					if(ParseArgs(&Result, pCommand->m_pParams))
+					{
+						char aBuf[256];
+						str_format(aBuf, sizeof(aBuf), "Invalid arguments... Usage: %s %s", pCommand->m_pName, pCommand->m_pParams);
+						Print(OUTPUT_LEVEL_STANDARD, "Console", aBuf);
+					}
+					else if(m_StoreCommands && pCommand->m_Flags&CFGFLAG_STORE)
+					{
+						m_ExecutionQueue.AddEntry();
+						m_ExecutionQueue.m_pLast->m_pfnCommandCallback = pCommand->m_pfnCallback;
+						m_ExecutionQueue.m_pLast->m_pCommandUserData = pCommand->m_pUserData;
+						m_ExecutionQueue.m_pLast->m_Result = Result;
+					}
+					else
+						pCommand->m_pfnCallback(&Result, pCommand->m_pUserData);
 				}
-				else
-					pCommand->m_pfnCallback(&Result, pCommand->m_pUserData);
+			}
+			else if(Stroke)
+			{
+				char aBuf[256];
+				str_format(aBuf, sizeof(aBuf), "Access for command %s denied.", Result.m_pCommand);
+				Print(OUTPUT_LEVEL_STANDARD, "Console", aBuf);
 			}
 		}
 		else if(Stroke)
@@ -294,8 +307,7 @@ void CConsole::ExecuteLineStroked(int Stroke, const char *pStr)
 
 void CConsole::PossibleCommands(const char *pStr, int FlagMask, FPossibleCallback pfnCallback, void *pUser)
 {
-	CCommand *pCommand;
-	for(pCommand = m_pFirstCommand; pCommand; pCommand = pCommand->m_pNext)
+	for(CCommand *pCommand = m_pFirstCommand; pCommand; pCommand = pCommand->m_pNext)
 	{
 		if(pCommand->m_Flags&FlagMask)
 		{
@@ -307,8 +319,7 @@ void CConsole::PossibleCommands(const char *pStr, int FlagMask, FPossibleCallbac
 
 CConsole::CCommand *CConsole::FindCommand(const char *pName, int FlagMask)
 {
-	CCommand *pCommand;
-	for (pCommand = m_pFirstCommand; pCommand; pCommand = pCommand->m_pNext)
+	for(CCommand *pCommand = m_pFirstCommand; pCommand; pCommand = pCommand->m_pNext)
 	{
 		if(pCommand->m_Flags&FlagMask)
 		{
@@ -383,6 +394,62 @@ void CConsole::Con_Exec(IResult *pResult, void *pUserData)
 	((CConsole*)pUserData)->ExecuteFile(pResult->GetString(0));
 }
 
+void CConsole::ConModCommandAccess(IResult *pResult, void *pUser)
+{
+	CConsole* pConsole = static_cast<CConsole *>(pUser);
+	char aBuf[128];
+	CCommand *pCommand = pConsole->FindCommand(pResult->GetString(0), CFGFLAG_SERVER);
+	if(pCommand)
+	{
+		if(pResult->NumArguments() == 2)
+		{
+			pCommand->m_AccessLevel = clamp(pResult->GetInteger(1), (int)(ACCESS_LEVEL_ADMIN), (int)(ACCESS_LEVEL_MOD));
+			str_format(aBuf, sizeof(aBuf), "moderator access for '%s' is now %s", pResult->GetString(0), pCommand->m_AccessLevel ? "enabled" : "disabled");
+		}
+		else
+			str_format(aBuf, sizeof(aBuf), "moderator access for '%s' is %s", pResult->GetString(0), pCommand->m_AccessLevel ? "enabled" : "disabled");
+	}
+	else
+		str_format(aBuf, sizeof(aBuf), "No such command: '%s'.", pResult->GetString(0));
+
+	pConsole->Print(OUTPUT_LEVEL_STANDARD, "Console", aBuf);
+}
+
+void CConsole::ConModCommandStatus(IResult *pResult, void *pUser)
+{
+	CConsole* pConsole = static_cast<CConsole *>(pUser);
+	char aBuf[240];
+	mem_zero(aBuf, sizeof(aBuf));
+	int Used = 0;
+
+	for(CCommand *pCommand = pConsole->m_pFirstCommand; pCommand; pCommand = pCommand->m_pNext)
+	{
+		if(pCommand->m_Flags&pConsole->m_FlagMask && pCommand->m_AccessLevel == ACCESS_LEVEL_MOD)
+		{
+			int Length = str_length(pCommand->m_pName);
+			if(Used + Length + 2 < (int)(sizeof(aBuf)))
+			{
+				if(Used > 0)
+				{
+					Used += 2;
+					str_append(aBuf, ", ", sizeof(aBuf));
+				}
+				str_append(aBuf, pCommand->m_pName, sizeof(aBuf));
+				Used += Length;
+			}
+			else
+			{
+				pConsole->Print(OUTPUT_LEVEL_STANDARD, "Console", aBuf);
+				mem_zero(aBuf, sizeof(aBuf));
+				str_copy(aBuf, pCommand->m_pName, sizeof(aBuf));
+				Used = Length;
+			}
+		}
+	}
+	if(Used > 0)
+		pConsole->Print(OUTPUT_LEVEL_STANDARD, "Console", aBuf);
+}
+
 struct CIntVariableData
 {
 	IConsole *m_pConsole;
@@ -463,6 +530,7 @@ static void StrVariableCommand(IConsole::IResult *pResult, void *pUserData)
 CConsole::CConsole(int FlagMask)
 {
 	m_FlagMask = FlagMask;
+	m_AccessLevel = ACCESS_LEVEL_ADMIN;
 	m_StoreCommands = true;
 	m_paStrokeStr[0] = "0";
 	m_paStrokeStr[1] = "1";
@@ -478,6 +546,9 @@ CConsole::CConsole(int FlagMask)
 	Register("echo", "r", CFGFLAG_SERVER|CFGFLAG_CLIENT, Con_Echo, this, "Echo the text");
 	Register("exec", "r", CFGFLAG_SERVER|CFGFLAG_CLIENT, Con_Exec, this, "Execute the specified file");
 
+	Register("mod_command", "s?i", CFGFLAG_SERVER, ConModCommandAccess, this, "Specify command accessibility for moderators");
+	Register("mod_status", "", CFGFLAG_SERVER, ConModCommandStatus, this, "List all commands which are accessible for moderators");
+
 	// TODO: this should disappear
 	#define MACRO_CONFIG_INT(Name,ScriptName,Def,Min,Max,Flags,Desc) \
 	{ \
@@ -524,14 +595,14 @@ void CConsole::ParseArguments(int NumArgs, const char **ppArguments)
 void CConsole::Register(const char *pName, const char *pParams,
 	int Flags, FCommandCallback pfnFunc, void *pUser, const char *pHelp)
 {
-	CCommand *pCommand = (CCommand *)mem_alloc(sizeof(CCommand), sizeof(void*));
+	CCommand *pCommand = new(mem_alloc(sizeof(CCommand), sizeof(void*))) CCommand;
 	pCommand->m_pfnCallback = pfnFunc;
 	pCommand->m_pUserData = pUser;
 	pCommand->m_pHelp = pHelp;
 	pCommand->m_pName = pName;
 	pCommand->m_pParams = pParams;
 	pCommand->m_Flags = Flags;
-
+	pCommand->m_AccessLevel = ACCESS_LEVEL_ADMIN;
 
 	pCommand->m_pNext = m_pFirstCommand;
 	m_pFirstCommand = pCommand;
diff --git a/src/engine/shared/console.h b/src/engine/shared/console.h
index 0866d8e3..14c48581 100644
--- a/src/engine/shared/console.h
+++ b/src/engine/shared/console.h
@@ -13,6 +13,7 @@ class CConsole : public IConsole
 	public:
 		CCommand *m_pNext;
 		int m_Flags;
+		int m_AccessLevel;
 		FCommandCallback m_pfnCallback;
 		void *m_pUserData;
 	};
@@ -41,10 +42,13 @@ class CConsole : public IConsole
 
 	CExecFile *m_pFirstExec;
 	class IStorage *m_pStorage;
+	int m_AccessLevel;
 
 	static void Con_Chain(IResult *pResult, void *pUserData);
 	static void Con_Echo(IResult *pResult, void *pUserData);
 	static void Con_Exec(IResult *pResult, void *pUserData);
+	static void ConModCommandAccess(IResult *pResult, void *pUser);
+	static void ConModCommandStatus(IConsole::IResult *pResult, void *pUser);
 
 	void ExecuteFileRecurse(const char *pFilename);
 	void ExecuteLineStroked(int Stroke, const char *pStr);
@@ -153,6 +157,8 @@ public:
 
 	virtual void RegisterPrintCallback(FPrintCallback pfnPrintCallback, void *pUserData);
 	virtual void Print(int Level, const char *pFrom, const char *pStr);
+
+	void SetAccessLevel(int AccessLevel) { m_AccessLevel = clamp(AccessLevel, (int)(ACCESS_LEVEL_ADMIN), (int)(ACCESS_LEVEL_MOD)); }
 };
 
 #endif