1 /*
2 ===========================================================================
3 
4 Return to Castle Wolfenstein single player GPL Source Code
5 Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company.
6 
7 This file is part of the Return to Castle Wolfenstein single player GPL Source Code (“RTCW SP Source Code”).
8 
9 RTCW SP Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13 
14 RTCW SP Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License for more details.
18 
19 You should have received a copy of the GNU General Public License
20 along with RTCW SP Source Code.  If not, see <http://www.gnu.org/licenses/>.
21 
22 In addition, the RTCW SP Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the RTCW SP Source Code.  If not, please request a copy in writing from id Software at the address below.
23 
24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
25 
26 ===========================================================================
27 */
28 
29 
30 
31 
32 // this file holds commands that can be executed by the server console, but not remote clients
33 
34 #include "g_local.h"
35 
36 
37 /*
38 ==============================================================================
39 
40 PACKET FILTERING
41 
42 
43 You can add or remove addresses from the filter list with:
44 
45 addip <ip>
46 removeip <ip>
47 
48 The ip address is specified in dot format, and any unspecified digits will match any value, so you can specify an entire class C network with "addip 192.246.40".
49 
50 Removeip will only remove an address specified exactly the same way.  You cannot addip a subnet, then removeip a single host.
51 
52 listip
53 Prints the current list of filters.
54 
55 g_filterban <0 or 1>
56 
57 If 1 (the default), then ip addresses matching the current list will be prohibited from entering the game.  This is the default setting.
58 
59 If 0, then only addresses matching the list will be allowed.  This lets you easily set up a private game, or a game that only allows players from your local network.
60 
61 
62 ==============================================================================
63 */
64 
65 
66 
67 
68 
69 typedef struct ipFilter_s
70 {
71 	unsigned mask;
72 	unsigned compare;
73 } ipFilter_t;
74 
75 #define MAX_IPFILTERS   1024
76 
77 static ipFilter_t ipFilters[MAX_IPFILTERS];
78 static int numIPFilters;
79 
80 /*
81 =================
82 StringToFilter
83 =================
84 */
StringToFilter(char * s,ipFilter_t * f)85 static qboolean StringToFilter( char *s, ipFilter_t *f ) {
86 	char num[128];
87 	int i, j;
88 	byte b[4];
89 	byte m[4];
90 
91 	for ( i = 0 ; i < 4 ; i++ )
92 	{
93 		b[i] = 0;
94 		m[i] = 0;
95 	}
96 
97 	for ( i = 0 ; i < 4 ; i++ )
98 	{
99 		if ( *s < '0' || *s > '9' ) {
100 			G_Printf( "Bad filter address: %s\n", s );
101 			return qfalse;
102 		}
103 
104 		j = 0;
105 		while ( *s >= '0' && *s <= '9' )
106 		{
107 			num[j++] = *s++;
108 		}
109 		num[j] = 0;
110 		b[i] = atoi( num );
111 		if ( b[i] != 0 ) {
112 			m[i] = 255;
113 		}
114 
115 		if ( !*s ) {
116 			break;
117 		}
118 		s++;
119 	}
120 
121 	f->mask = *(unsigned *)m;
122 	f->compare = *(unsigned *)b;
123 
124 	return qtrue;
125 }
126 
127 /*
128 =================
129 UpdateIPBans
130 =================
131 */
UpdateIPBans(void)132 static void UpdateIPBans( void ) {
133 	byte b[4] = {0};
134 	byte m[4] = {0};
135 	int i,j;
136 	char iplist_final[MAX_CVAR_VALUE_STRING] = {0};
137 	char ip[64] = {0};
138 
139 	*iplist_final = 0;
140 	for ( i = 0 ; i < numIPFilters ; i++ )
141 	{
142 		if ( ipFilters[i].compare == 0xffffffff ) {
143 			continue;
144 		}
145 
146 		*(unsigned *)b = ipFilters[i].compare;
147 		*(unsigned *)m = ipFilters[i].mask;
148 		*ip = 0;
149 		for ( j = 0 ; j < 4 ; j++ )
150 		{
151 			if ( m[j] != 255 ) {
152 				Q_strcat( ip, sizeof( ip ), "*" );
153 			} else {
154 				Q_strcat( ip, sizeof( ip ), va( "%i", b[j] ) );
155 			}
156 			Q_strcat( ip, sizeof( ip ), ( j < 3 ) ? "." : " " );
157 		}
158 		if ( strlen( iplist_final ) + strlen( ip ) < MAX_CVAR_VALUE_STRING ) {
159 			Q_strcat( iplist_final, sizeof( iplist_final ), ip );
160 		} else
161 		{
162 			Com_Printf( "g_banIPs overflowed at MAX_CVAR_VALUE_STRING\n" );
163 			break;
164 		}
165 	}
166 
167 	trap_Cvar_Set( "g_banIPs", iplist_final );
168 }
169 
170 /*
171 =================
172 G_FilterPacket
173 =================
174 */
G_FilterPacket(char * from)175 qboolean G_FilterPacket( char *from ) {
176 	int i;
177 	unsigned in;
178 	byte m[4] = {0};
179 	char *p;
180 
181 	i = 0;
182 	p = from;
183 	while ( *p && i < 4 ) {
184 		m[i] = 0;
185 		while ( *p >= '0' && *p <= '9' ) {
186 			m[i] = m[i] * 10 + ( *p - '0' );
187 			p++;
188 		}
189 		if ( !*p || *p == ':' ) {
190 			break;
191 		}
192 		i++, p++;
193 	}
194 
195 	in = *(unsigned *)m;
196 
197 	for ( i = 0 ; i < numIPFilters ; i++ )
198 		if ( ( in & ipFilters[i].mask ) == ipFilters[i].compare ) {
199 			return g_filterBan.integer != 0;
200 		}
201 
202 	return g_filterBan.integer == 0;
203 }
204 
205 /*
206 =================
207 AddIP
208 =================
209 */
AddIP(char * str)210 static void AddIP( char *str ) {
211 	int i;
212 
213 	for ( i = 0 ; i < numIPFilters ; i++ )
214 		if ( ipFilters[i].compare == 0xffffffff ) {
215 			break;
216 		}               // free spot
217 	if ( i == numIPFilters ) {
218 		if ( numIPFilters == MAX_IPFILTERS ) {
219 			G_Printf( "IP filter list is full\n" );
220 			return;
221 		}
222 		numIPFilters++;
223 	}
224 
225 	if ( !StringToFilter( str, &ipFilters[i] ) ) {
226 		ipFilters[i].compare = 0xffffffffu;
227 	}
228 
229 	UpdateIPBans();
230 }
231 
232 /*
233 =================
234 G_ProcessIPBans
235 =================
236 */
G_ProcessIPBans(void)237 void G_ProcessIPBans( void ) {
238 	char *s, *t;
239 	char str[MAX_TOKEN_CHARS];
240 
241 	Q_strncpyz( str, g_banIPs.string, sizeof( str ) );
242 
243 	for ( t = s = g_banIPs.string; *t; /* */ ) {
244 		s = strchr( s, ' ' );
245 		if ( !s ) {
246 			break;
247 		}
248 		while ( *s == ' ' )
249 			*s++ = 0;
250 		if ( *t ) {
251 			AddIP( t );
252 		}
253 		t = s;
254 	}
255 }
256 
257 
258 /*
259 =================
260 Svcmd_AddIP_f
261 =================
262 */
Svcmd_AddIP_f(void)263 void Svcmd_AddIP_f( void ) {
264 	char str[MAX_TOKEN_CHARS];
265 
266 	if ( trap_Argc() < 2 ) {
267 		G_Printf( "Usage: addip <ip-mask>\n" );
268 		return;
269 	}
270 
271 	trap_Argv( 1, str, sizeof( str ) );
272 
273 	AddIP( str );
274 
275 }
276 
277 /*
278 =================
279 Svcmd_RemoveIP_f
280 =================
281 */
Svcmd_RemoveIP_f(void)282 void Svcmd_RemoveIP_f( void ) {
283 	ipFilter_t f;
284 	int i;
285 	char str[MAX_TOKEN_CHARS];
286 
287 	if ( trap_Argc() < 2 ) {
288 		G_Printf( "Usage: removeip <ip-mask>\n" );
289 		return;
290 	}
291 
292 	trap_Argv( 1, str, sizeof( str ) );
293 
294 	if ( !StringToFilter( str, &f ) ) {
295 		return;
296 	}
297 
298 	for ( i = 0 ; i < numIPFilters ; i++ ) {
299 		if ( ipFilters[i].mask == f.mask &&
300 			 ipFilters[i].compare == f.compare ) {
301 			ipFilters[i].compare = 0xffffffffu;
302 			G_Printf( "Removed.\n" );
303 
304 			UpdateIPBans();
305 			return;
306 		}
307 	}
308 
309 	G_Printf( "Didn't find %s.\n", str );
310 }
311 
312 /*
313 ===================
314 Svcmd_EntityList_f
315 ===================
316 */
Svcmd_EntityList_f(void)317 void    Svcmd_EntityList_f( void ) {
318 	int e;
319 	gentity_t       *check;
320 
321 	check = g_entities;
322 	for ( e = 0; e < level.num_entities ; e++, check++ ) {
323 		if ( !check->inuse ) {
324 			continue;
325 		}
326 		G_Printf( "%3i:", e );
327 		switch ( check->s.eType ) {
328 		case ET_GENERAL:
329 			G_Printf( "ET_GENERAL          " );
330 			break;
331 		case ET_PLAYER:
332 			G_Printf( "ET_PLAYER           " );
333 			break;
334 		case ET_ITEM:
335 			G_Printf( "ET_ITEM             " );
336 			break;
337 		case ET_MISSILE:
338 			G_Printf( "ET_MISSILE          " );
339 			break;
340 		case ET_MOVER:
341 			G_Printf( "ET_MOVER            " );
342 			break;
343 		case ET_BEAM:
344 			G_Printf( "ET_BEAM             " );
345 			break;
346 		case ET_PORTAL:
347 			G_Printf( "ET_PORTAL           " );
348 			break;
349 		case ET_SPEAKER:
350 			G_Printf( "ET_SPEAKER          " );
351 			break;
352 		case ET_PUSH_TRIGGER:
353 			G_Printf( "ET_PUSH_TRIGGER     " );
354 			break;
355 		case ET_TELEPORT_TRIGGER:
356 			G_Printf( "ET_TELEPORT_TRIGGER " );
357 			break;
358 		case ET_INVISIBLE:
359 			G_Printf( "ET_INVISIBLE        " );
360 			break;
361 		case ET_GRAPPLE:
362 			G_Printf( "ET_GRAPPLE          " );
363 			break;
364 		case ET_EXPLOSIVE:
365 			G_Printf( "ET_EXPLOSIVE        " );
366 			break;
367 		case ET_TESLA_EF:
368 			G_Printf( "ET_TESLA_EF         " );
369 			break;
370 		case ET_SPOTLIGHT_EF:
371 			G_Printf( "ET_SPOTLIGHT_EF     " );
372 			break;
373 		case ET_EFFECT3:
374 			G_Printf( "ET_EFFECT3          " );
375 			break;
376 		case ET_ALARMBOX:
377 			G_Printf( "ET_ALARMBOX          " );
378 			break;
379 		default:
380 			G_Printf( "%3i                 ", check->s.eType );
381 			break;
382 		}
383 
384 		if ( check->classname ) {
385 			G_Printf( "%s", check->classname );
386 		}
387 		G_Printf( "\n" );
388 	}
389 }
390 
ClientForString(const char * s)391 gclient_t   *ClientForString( const char *s ) {
392 	gclient_t   *cl;
393 	int i;
394 	int idnum;
395 
396 	// numeric values are just slot numbers
397 	if ( s[0] >= '0' && s[0] <= '9' ) {
398 		idnum = atoi( s );
399 		if ( idnum < 0 || idnum >= level.maxclients ) {
400 			Com_Printf( "Bad client slot: %i\n", idnum );
401 			return NULL;
402 		}
403 
404 		cl = &level.clients[idnum];
405 		if ( cl->pers.connected == CON_DISCONNECTED ) {
406 			G_Printf( "Client %i is not connected\n", idnum );
407 			return NULL;
408 		}
409 		return cl;
410 	}
411 
412 	// check for a name match
413 	for ( i = 0 ; i < level.maxclients ; i++ ) {
414 		cl = &level.clients[i];
415 		if ( cl->pers.connected == CON_DISCONNECTED ) {
416 			continue;
417 		}
418 		if ( !Q_stricmp( cl->pers.netname, s ) ) {
419 			return cl;
420 		}
421 	}
422 
423 	G_Printf( "User %s is not on the server\n", s );
424 
425 	return NULL;
426 }
427 
428 /*
429 ===================
430 Svcmd_ForceTeam_f
431 
432 forceteam <player> <team>
433 ===================
434 */
Svcmd_ForceTeam_f(void)435 void    Svcmd_ForceTeam_f( void ) {
436 	gclient_t   *cl;
437 	char str[MAX_TOKEN_CHARS];
438 
439 	if ( trap_Argc() < 3 ) {
440 		G_Printf("Usage: forceteam <player> <team>\n");
441 		return;
442 	}
443 
444 	// find the player
445 	trap_Argv( 1, str, sizeof( str ) );
446 	cl = ClientForString( str );
447 	if ( !cl ) {
448 		return;
449 	}
450 
451 	// set the team
452 	trap_Argv( 2, str, sizeof( str ) );
453 	SetTeam( &g_entities[cl - level.clients], str );
454 }
455 
456 char    *ConcatArgs( int start );
457 
458 /*
459 =================
460 ConsoleCommand
461 
462 =================
463 */
ConsoleCommand(void)464 qboolean    ConsoleCommand( void ) {
465 	char cmd[MAX_TOKEN_CHARS];
466 
467 	trap_Argv( 0, cmd, sizeof( cmd ) );
468 
469 	// Ridah, savegame
470 	if ( Q_stricmp( cmd, "savegame" ) == 0 ) {
471 
472 		// don't allow a manual savegame command while we are waiting for the game to start/exit
473 		if ( g_reloading.integer ) {
474 			return qtrue;
475 		}
476 		if ( saveGamePending ) {
477 			return qtrue;
478 		}
479 
480 		trap_Argv( 1, cmd, sizeof( cmd ) );
481 		if ( strlen( cmd ) > 0 ) {
482 			// strip the extension if provided
483 			if ( strrchr( cmd, '.' ) ) {
484 				cmd[strrchr( cmd,'.' ) - cmd] = '\0';
485 			}
486 			if ( !Q_stricmp( cmd, "current" ) ) {     // beginning of map
487 				Com_Printf( "sorry, '%s' is a reserved savegame name.  please use another name.\n", cmd );
488 				return qtrue;
489 			}
490 
491 			if ( G_SaveGame( cmd ) ) {
492 				trap_SendServerCommand( -1, "cp gamesaved" );  // deletedgame
493 			} else {
494 				G_Printf( "Unable to save game.\n" );
495 			}
496 
497 		} else {    // need a name
498 			G_Printf( "syntax: savegame <name>\n" );
499 		}
500 
501 		return qtrue;
502 	}
503 	// done.
504 
505 	if ( Q_stricmp( cmd, "entitylist" ) == 0 ) {
506 		Svcmd_EntityList_f();
507 		return qtrue;
508 	}
509 
510 	if ( Q_stricmp( cmd, "forceteam" ) == 0 ) {
511 		Svcmd_ForceTeam_f();
512 		return qtrue;
513 	}
514 
515 	if ( Q_stricmp( cmd, "game_memory" ) == 0 ) {
516 		Svcmd_GameMem_f();
517 		return qtrue;
518 	}
519 
520 	if ( Q_stricmp( cmd, "addbot" ) == 0 ) {
521 		Svcmd_AddBot_f();
522 		return qtrue;
523 	}
524 
525 	// TTimo: took out games/g_arenas.c
526 	/*
527 	  if (Q_stricmp (cmd, "abort_podium") == 0) {
528 		  Svcmd_AbortPodium_f();
529 		  return qtrue;
530 	  }
531 	*/
532 
533 	if ( Q_stricmp( cmd, "addip" ) == 0 ) {
534 		Svcmd_AddIP_f();
535 		return qtrue;
536 	}
537 
538 	if ( Q_stricmp( cmd, "removeip" ) == 0 ) {
539 		Svcmd_RemoveIP_f();
540 		return qtrue;
541 	}
542 
543 	if ( Q_stricmp( cmd, "listip" ) == 0 ) {
544 		trap_SendConsoleCommand( EXEC_INSERT, "g_banIPs\n" );
545 		return qtrue;
546 	}
547 
548 	if ( g_dedicated.integer ) {
549 		if ( Q_stricmp( cmd, "say" ) == 0 ) {
550 			trap_SendServerCommand( -1, va( "print \"server: %s\n\"", ConcatArgs( 1 ) ) );
551 			return qtrue;
552 		}
553 		// everything else will also be printed as a say command
554 		trap_SendServerCommand( -1, va( "print \"server: %s\n\"", ConcatArgs( 0 ) ) );
555 		return qtrue;
556 	}
557 
558 	return qfalse;
559 }
560 
561