core/logic/smn_banning.cpp
author David Anderson <dvander@alliedmods.net>
Sun Mar 31 13:30:22 2013 -0700 (2013-03-31)
changeset 3881 7ec742e7d629
parent 3162 d8f90b56529f
permissions -rw-r--r--
Don't allocate/delete across static CRTs (no bug, irc r=fyren+psychonic).
     1 /**
     2  * vim: set ts=4 :
     3  * =============================================================================
     4  * SourceMod
     5  * Copyright (C) 2004-2008 AlliedModders LLC.  All rights reserved.
     6  * =============================================================================
     7  *
     8  * This program is free software; you can redistribute it and/or modify it under
     9  * the terms of the GNU General Public License, version 3.0, as published by the
    10  * Free Software Foundation.
    11  * 
    12  * This program is distributed in the hope that it will be useful, but WITHOUT
    13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    14  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
    15  * details.
    16  *
    17  * You should have received a copy of the GNU General Public License along with
    18  * this program.  If not, see <http://www.gnu.org/licenses/>.
    19  *
    20  * As a special exception, AlliedModders LLC gives you permission to link the
    21  * code of this program (as well as its derivative works) to "Half-Life 2," the
    22  * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
    23  * by the Valve Corporation.  You must obey the GNU General Public License in
    24  * all respects for all other code used.  Additionally, AlliedModders LLC grants
    25  * this exception to all derivative works.  AlliedModders LLC defines further
    26  * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
    27  * or <http://www.sourcemod.net/license.php>.
    28  *
    29  * Version: $Id$
    30  */
    31 
    32 #include "common_logic.h"
    33 #include <string.h>
    34 #include <IGameHelpers.h>
    35 #include <IPlayerHelpers.h>
    36 #include <IForwardSys.h>
    37 #include "stringutil.h"
    38 
    39 #define BANFLAG_AUTO	(1<<0)	/**< Auto-detects whether to ban by steamid or IP */
    40 #define BANFLAG_IP   	(1<<1)	/**< Always ban by IP address */
    41 #define BANFLAG_AUTHID	(1<<2)	/**< Ban by SteamID */
    42 #define BANFLAG_NOKICK	(1<<3)	/**< Does not kick the client */
    43 #define BANFLAG_NOWRITE	(1<<4)	/**< Ban is not written to SourceDS's files if permanent */
    44 
    45 IForward *g_pOnBanClient = NULL;
    46 IForward *g_pOnBanIdentity = NULL;
    47 IForward *g_pOnRemoveBan = NULL;
    48 
    49 class BanNativeHelpers : public SMGlobalClass
    50 {
    51 public:
    52 	void OnSourceModAllInitialized()
    53 	{
    54 		g_pOnBanClient = forwardsys->CreateForward(
    55 			"OnBanClient",
    56 			ET_Event,
    57 			7,
    58 			NULL,
    59 			Param_Cell,
    60 			Param_Cell,
    61 			Param_Cell,
    62 			Param_String,
    63 			Param_String,
    64 			Param_String,
    65 			Param_Cell);
    66 		g_pOnBanIdentity = forwardsys->CreateForward(
    67 			"OnBanIdentity",
    68 			ET_Event,
    69 			6,
    70 			NULL,
    71 			Param_String,
    72 			Param_Cell,
    73 			Param_Cell,
    74 			Param_String,
    75 			Param_String,
    76 			Param_Cell);
    77 		g_pOnRemoveBan = forwardsys->CreateForward(
    78 			"OnRemoveBan",
    79 			ET_Event,
    80 			4,
    81 			NULL,
    82 			Param_String,
    83 			Param_Cell,
    84 			Param_String,
    85 			Param_Cell);
    86 	}
    87 	void OnSourceModShutdown()
    88 	{
    89 		forwardsys->ReleaseForward(g_pOnBanClient);
    90 		forwardsys->ReleaseForward(g_pOnBanIdentity);
    91 		forwardsys->ReleaseForward(g_pOnRemoveBan);
    92 
    93 		g_pOnBanClient = NULL;
    94 		g_pOnBanIdentity = NULL;
    95 		g_pOnRemoveBan = NULL;
    96 	}
    97 } s_BanNativeHelpers;
    98 
    99 
   100 static cell_t BanIdentity(IPluginContext *pContext, const cell_t *params)
   101 {
   102 	char *r_identity, *ban_reason, *ban_cmd;
   103 	int ban_time, ban_flags, ban_source;
   104 
   105 	pContext->LocalToString(params[1], &r_identity);
   106 	pContext->LocalToString(params[4], &ban_reason);
   107 	pContext->LocalToString(params[5], &ban_cmd);
   108 	ban_time = params[2];
   109 	ban_flags = params[3];
   110 	ban_source = params[6];
   111 
   112 	/* Make sure we can ban by one of the two methods! */
   113 	bool ban_by_ip = ((ban_flags & BANFLAG_IP) == BANFLAG_IP);
   114 	if (!ban_by_ip && ((ban_flags & BANFLAG_AUTHID) != BANFLAG_AUTHID))
   115 	{
   116 		return pContext->ThrowNativeError("No valid ban flags specified");
   117 	}
   118 
   119 	/* Sanitize the input */
   120 	char identity[64];
   121 	strncopy(identity, r_identity, sizeof(identity));
   122 	UTIL_ReplaceAll(identity, sizeof(identity), ";", "", true);
   123 
   124 	cell_t handled = 0;
   125 	if (ban_cmd[0] != '\0' && g_pOnBanIdentity->GetFunctionCount() > 0)
   126 	{
   127 		g_pOnBanIdentity->PushString(identity);
   128 		g_pOnBanIdentity->PushCell(ban_time);
   129 		g_pOnBanIdentity->PushCell(ban_flags);
   130 		g_pOnBanIdentity->PushString(ban_reason);
   131 		g_pOnBanIdentity->PushString(ban_cmd);
   132 		g_pOnBanIdentity->PushCell(ban_source);
   133 		g_pOnBanIdentity->Execute(&handled);
   134 	}
   135 
   136 	if (!handled)
   137 	{
   138 		bool write_ban = ((ban_flags & BANFLAG_NOWRITE) != BANFLAG_NOWRITE);
   139 
   140 		char command[256];
   141 		if (ban_by_ip)
   142 		{
   143 			smcore.Format(
   144 				command,
   145 				sizeof(command),
   146 				"addip %d %s\n",
   147 				ban_time,
   148 				identity);
   149 			engine->ServerCommand(command);
   150 	
   151 			if (write_ban && ban_time == 0)
   152 			{
   153 				engine->ServerCommand("writeip\n");
   154 			}
   155 		}
   156 		else if (!gamehelpers->IsLANServer())
   157 		{
   158 			smcore.Format(
   159 				command,
   160 				sizeof(command),
   161 				"banid %d %s\n",
   162 				ban_time,
   163 				identity);
   164 			engine->ServerCommand(command);
   165 	
   166 			if (write_ban && ban_time == 0)
   167 			{
   168 				engine->ServerCommand("writeid\n");
   169 			}
   170 		}
   171 		else
   172 		{
   173 			return 0;
   174 		}
   175 	}
   176 
   177 	return 1;
   178 }
   179 
   180 static cell_t RemoveBan(IPluginContext *pContext, const cell_t *params)
   181 {
   182 	char *r_identity, *ban_cmd;
   183 	int ban_flags, ban_source;
   184 
   185 	pContext->LocalToString(params[1], &r_identity);
   186 	pContext->LocalToString(params[3], &ban_cmd);
   187 	ban_flags = params[2];
   188 	ban_source = params[4];
   189 
   190 	/* Make sure we can ban by one of the two methods! */
   191 	bool ban_by_ip = ((ban_flags & BANFLAG_IP) == BANFLAG_IP);
   192 	if (!ban_by_ip && ((ban_flags & BANFLAG_AUTHID) != BANFLAG_AUTHID))
   193 	{
   194 		return pContext->ThrowNativeError("No valid ban flags specified");
   195 	}
   196 
   197 	char identity[64];
   198 	strncopy(identity, r_identity, sizeof(identity));
   199 	UTIL_ReplaceAll(identity, sizeof(identity), ";", "", true);
   200 
   201 	cell_t handled = 0;
   202 	if (ban_cmd[0] != '\0' && g_pOnRemoveBan->GetFunctionCount() > 0)
   203 	{
   204 		g_pOnRemoveBan->PushString(identity);
   205 		g_pOnRemoveBan->PushCell(ban_flags);
   206 		g_pOnRemoveBan->PushString(ban_cmd);
   207 		g_pOnRemoveBan->PushCell(ban_source);
   208 		g_pOnRemoveBan->Execute(&handled);
   209 	}
   210 
   211 	char command[256];
   212 	if (ban_by_ip)
   213 	{
   214 		if (!handled)
   215 		{
   216 			smcore.Format(
   217 				command,
   218 				sizeof(command),
   219 				"removeip %s\n",
   220 				identity);
   221 			engine->ServerCommand(command);	
   222 			engine->ServerCommand("writeip\n");
   223 		}
   224 	}
   225 	else if (!gamehelpers->IsLANServer())
   226 	{
   227 		if (!handled)
   228 		{
   229 			smcore.Format(
   230 				command,
   231 				sizeof(command),
   232 				"removeid %s\n",
   233 				identity);
   234 			engine->ServerCommand(command);
   235 			engine->ServerCommand("writeid\n");
   236 		}
   237 	}
   238 	else
   239 	{
   240 		return 0;
   241 	}
   242 
   243 	return 1;
   244 }
   245 
   246 static cell_t BanClient(IPluginContext *pContext, const cell_t *params)
   247 {
   248 	const char *kick_message;
   249 	char *ban_reason, *ban_cmd;
   250 	int client, ban_flags, ban_source, ban_time;
   251 
   252 	client = gamehelpers->ReferenceToIndex(params[1]);
   253 
   254 	IGamePlayer *pPlayer = playerhelpers->GetGamePlayer(client);
   255 	if (!pPlayer || !pPlayer->IsConnected())
   256 	{
   257 		return pContext->ThrowNativeError("Client index %d is invalid", client);
   258 	}
   259 
   260 	if (pPlayer->IsFakeClient())
   261 	{
   262 		return pContext->ThrowNativeError("Cannot ban fake client %d", client);
   263 	}
   264 
   265 	pContext->LocalToString(params[4], &ban_reason);
   266 	pContext->LocalToString(params[5], (char **)&kick_message);
   267 	pContext->LocalToString(params[6], &ban_cmd);
   268 	ban_time = params[2];
   269 	ban_flags = params[3];
   270 	ban_source = params[7];
   271 
   272 	/* Check how we should ban the player */
   273 	if (!strcmp(smcore.GetSourceEngineName(), "darkmessiah"))
   274 	{
   275 		/* Dark Messiah doesn't have Steam IDs so there is only one ban method to choose */
   276 		ban_flags |= BANFLAG_IP;
   277 		ban_flags &= ~BANFLAG_AUTHID;
   278 	}
   279 	else if ((ban_flags & BANFLAG_AUTO) == BANFLAG_AUTO)
   280 	{
   281 		if (gamehelpers->IsLANServer() || !pPlayer->IsAuthorized())
   282 		{
   283 			ban_flags |= BANFLAG_IP;
   284 			ban_flags &= ~BANFLAG_AUTHID;
   285 		}
   286 		else
   287 		{
   288 			ban_flags |= BANFLAG_AUTHID;
   289 			ban_flags &= ~BANFLAG_IP;
   290 		}
   291 	}
   292 	else if ((ban_flags & BANFLAG_IP) == BANFLAG_IP)
   293 	{
   294 		ban_flags |= BANFLAG_IP;
   295 		ban_flags &= ~BANFLAG_AUTHID;
   296 	}
   297 	else if ((ban_flags & BANFLAG_AUTHID) == BANFLAG_AUTHID)
   298 	{
   299 		if (pPlayer->IsAuthorized())
   300 		{
   301 			ban_flags |= BANFLAG_AUTHID;
   302 			ban_flags &= ~BANFLAG_IP;
   303 		}
   304 		else
   305 		{
   306 			return 0;
   307 		}
   308 	}
   309 	else
   310 	{
   311 		return pContext->ThrowNativeError("No valid ban method flags specified");
   312 	}
   313 
   314 	cell_t handled = 0;
   315 	if (ban_cmd[0] != '\0' && g_pOnBanClient->GetFunctionCount() > 0)
   316 	{
   317 		g_pOnBanClient->PushCell(client);
   318 		g_pOnBanClient->PushCell(ban_time);
   319 		g_pOnBanClient->PushCell(ban_flags);
   320 		g_pOnBanClient->PushString(ban_reason);
   321 		g_pOnBanClient->PushString(kick_message);
   322 		g_pOnBanClient->PushString(ban_cmd);
   323 		g_pOnBanClient->PushCell(ban_source);
   324 		g_pOnBanClient->Execute(&handled);
   325 	}
   326 
   327 	/* Sanitize the kick message */
   328 	if (kick_message[0] == '\0')
   329 	{
   330 		kick_message = "Kicked";
   331 	}
   332 
   333 	if (!handled)
   334 	{
   335 		if ((ban_flags & BANFLAG_IP) == BANFLAG_IP)
   336 		{
   337 			/* Get the IP address and strip the port */
   338 			char ip[24], *ptr;
   339 			smcore.strncopy(ip, pPlayer->GetIPAddress(), sizeof(ip));
   340 			if ((ptr = strchr(ip, ':')) != NULL)
   341 			{
   342 				*ptr = '\0';
   343 			}
   344 		
   345 			/* Tell the server to ban the ip */
   346 			char command[256];
   347 			smcore.Format(
   348 				command,
   349 				sizeof(command),
   350 				"addip %d %s\n",
   351 				ban_time,
   352 				ip);
   353 	
   354 			/* Kick, then ban */
   355 			if ((ban_flags & BANFLAG_NOKICK) != BANFLAG_NOKICK)
   356 			{
   357    		 		pPlayer->Kick(kick_message);
   358 			}
   359 			engine->ServerCommand(command);
   360 	
   361 			/* Physically write the ban */
   362 			if ((ban_time == 0) && ((ban_flags & BANFLAG_NOWRITE) != BANFLAG_NOWRITE))
   363 			{
   364 				engine->ServerCommand("writeip\n");
   365 			}	
   366 		} 
   367 		else if ((ban_flags & BANFLAG_AUTHID) == BANFLAG_AUTHID)
   368 		{
   369 			/* Tell the server to ban the auth string */
   370 			char command[256];
   371 			smcore.Format(
   372 				command, 
   373 				sizeof(command), 
   374 				"banid %d %s\n", 
   375 				ban_time,
   376 				pPlayer->GetAuthString());
   377 	
   378 			/* Kick, then ban */
   379 			if ((ban_flags & BANFLAG_NOKICK) != BANFLAG_NOKICK)
   380 			{
   381 				gamehelpers->AddDelayedKick(client, pPlayer->GetUserId(), kick_message);
   382 			}
   383 			engine->ServerCommand(command);
   384 
   385 			/* Physically write the ban if it's permanent */
   386 			if ((ban_time == 0) && ((ban_flags & BANFLAG_NOWRITE) != BANFLAG_NOWRITE))
   387 			{
   388 				engine->ServerCommand("writeid\n");
   389 			}
   390 		}
   391 	}
   392 	else if ((ban_flags & BANFLAG_NOKICK) != BANFLAG_NOKICK)
   393 	{
   394 		gamehelpers->AddDelayedKick(client, pPlayer->GetUserId(), kick_message);
   395 	}
   396 
   397 
   398 	return 1;
   399 }
   400 
   401 REGISTER_NATIVES(banNatives)
   402 {
   403 	{"BanClient",				BanClient},
   404 	{"BanIdentity",				BanIdentity},
   405 	{"RemoveBan",				RemoveBan},
   406 	{NULL,						NULL}
   407 };
   408