mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2024-11-23 04:42:32 +00:00
- fix a little mistake in SV_PrintToClient that'd cause it to
segfault if your first call was with "". Probably could cause crashes too - add a string.c file to libQFutil, with a Q_strcasestr function, which strcasestr is defined to if it's not already defined. (we'd get that with glibc if we defined __USE_GNU, but we don't) - make client_t and SV_ExtractFromUserinfo both use NAME_MAX for their name arrays, instead of 32 for one and 80 for the other - rewrite almost all of SV_ExtractFromUserinfo's name handling. - \r, \n, and \t are all converted to spaces - leading/trailing spaces are stripped - consecutive spaces are reduced to a single space - empty names are considered bad - user-* nicks are considered bad (unless forced to them) - a name containing console or admin is considered bad - a name that already exists is considered bad - if they have a bad name it gets forced to user-%d, where %d is their userid - netname in the progs is now updated properly - name changes are always reported unless it's the initial setting, rather than only if they're full connected and not a spectator - finally, if the name change fails (info string exceeded), give them the boot. (before this was only done for duplicate names) That's about it :)
This commit is contained in:
parent
cd9832d95f
commit
0785610296
8 changed files with 177 additions and 84 deletions
|
@ -2,10 +2,10 @@ AUTOMAKE_OPTIONS = foreign
|
|||
SUBDIRS = GL plugin
|
||||
includedir = $(prefix)/include/QF
|
||||
include_HEADERS = bspfile.h cdaudio.h checksum.h clip_hull.h cmd.h \
|
||||
console.h crc.h cvar.h draw.h gcc_attr.h gib.h hash.h hl.h in_event.h \
|
||||
info.h input.h joystick.h keys.h link.h locs.h mathlib.h mdfour.h model.h \
|
||||
modelgen.h msg.h pcx.h plugin.h pr_comp.h pr_debug.h progs.h qargs.h \
|
||||
qdefs.h \
|
||||
qendian.h qfplist.h qtypes.h render.h screen.h sizebuf.h \
|
||||
skin.h sound.h spritegn.h sys.h teamplay.h texture.h tga.h uint32.h \
|
||||
va.h ver_check.h vfile.h vfs.h vid.h wad.h zone.h
|
||||
console.h crc.h cvar.h draw.h gcc_attr.h gib.h hash.h hl.h \
|
||||
in_event.h info.h input.h joystick.h keys.h link.h locs.h \
|
||||
mathlib.h mdfour.h model.h modelgen.h msg.h pcx.h plugin.h \
|
||||
pr_comp.h pr_debug.h progs.h qargs.h qdefs.h qendian.h qfplist.h \
|
||||
qtypes.h render.h screen.h sizebuf.h skin.h sound.h spritegn.h \
|
||||
string.h sys.h teamplay.h texture.h tga.h uint32.h va.h \
|
||||
ver_check.h vfile.h vfs.h vid.h wad.h zone.h
|
||||
|
|
34
include/QF/string.h
Normal file
34
include/QF/string.h
Normal file
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
string.h
|
||||
|
||||
A string helper function
|
||||
|
||||
Copyright (C) 2001 Adam Olsen
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to:
|
||||
|
||||
Free Software Foundation, Inc.
|
||||
59 Temple Place - Suite 330
|
||||
Boston, MA 02111-1307, USA
|
||||
|
||||
$Id$
|
||||
*/
|
||||
|
||||
#ifndef string_h
|
||||
#define string_h
|
||||
|
||||
const char * Q_strcasestr (const char *haystack, const char *needle);
|
||||
|
||||
#endif // string_h
|
|
@ -87,6 +87,10 @@ extern int vsnprintf(char *s, size_t maxlen, const char *format, va_list arg);
|
|||
#if !defined(strncaseequal)
|
||||
# define strncaseequal(a,b,c) (strncasecmp (a, b, c) == 0)
|
||||
#endif
|
||||
// FIXME: glibc has strcasestr, but only declares it if __USE_GNU is defined
|
||||
#if !defined(strcasestr)
|
||||
# define strcasestr Q_strcasestr
|
||||
#endif
|
||||
|
||||
#undef field_offset
|
||||
#define field_offset(type,field) ((int)&(((type *)0)->field))
|
||||
|
|
|
@ -26,7 +26,7 @@ libQFutil_la_DEPENDENCIES= libasm.la
|
|||
libQFutil_la_SOURCES= \
|
||||
checksum.c cmd.c console.c con_print.c crc.c cvar.c fendian.c hash.c \
|
||||
info.c link.c mathlib.c mdfour.c msg.c pcx.c plugin.c qargs.c \
|
||||
qendian.c qfplist.c quakefs.c quakeio.c sizebuf.c sys.c tga.c va.c \
|
||||
ver_check.c wad.c zone.c $(fnmatch)
|
||||
qendian.c qfplist.c quakefs.c quakeio.c sizebuf.c string.c sys.c \
|
||||
tga.c va.c ver_check.c wad.c zone.c $(fnmatch)
|
||||
|
||||
EXTRA_DIST= $(asm_src) $(fnmatch_src)
|
||||
|
|
55
libs/util/string.c
Normal file
55
libs/util/string.c
Normal file
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
string.c
|
||||
|
||||
A string helper function
|
||||
|
||||
Copyright (C) 2001 Adam Olsen
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to:
|
||||
|
||||
Free Software Foundation, Inc.
|
||||
59 Temple Place - Suite 330
|
||||
Boston, MA 02111-1307, USA
|
||||
|
||||
*/
|
||||
static const char rcsid[] =
|
||||
"$Id$";
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "compat.h"
|
||||
#include "string.h"
|
||||
|
||||
|
||||
/*
|
||||
Q_strcasestr
|
||||
|
||||
case-insensitive version of strstr
|
||||
*/
|
||||
const char *
|
||||
Q_strcasestr (const char *haystack, const char *needle)
|
||||
{
|
||||
int len = strlen (needle);
|
||||
while (*haystack) {
|
||||
if (!strncasecmp (haystack, needle, len))
|
||||
return haystack;
|
||||
haystack++;
|
||||
}
|
||||
return 0;
|
||||
}
|
|
@ -136,6 +136,7 @@ typedef struct
|
|||
|
||||
#define MAX_BACK_BUFFERS 8
|
||||
#define MAX_STUFFTEXT 512
|
||||
#define MAX_NAME 32
|
||||
|
||||
typedef enum {
|
||||
ft_ban,
|
||||
|
@ -170,7 +171,7 @@ typedef struct client_s
|
|||
float entgravity; // localized ent gravity
|
||||
|
||||
struct edict_s *edict; // EDICT_NUM(clientnum+1)
|
||||
char name[32]; // for printing to other people
|
||||
char name[MAX_NAME]; // for printing to other people
|
||||
// extracted from userinfo
|
||||
int messagelevel; // for filtering printed messages
|
||||
|
||||
|
|
|
@ -53,6 +53,7 @@ static const char rcsid[] =
|
|||
#include "QF/msg.h"
|
||||
#include "QF/plugin.h"
|
||||
#include "QF/qargs.h"
|
||||
#include "QF/string.h"
|
||||
#include "QF/sys.h"
|
||||
#include "QF/va.h"
|
||||
#include "QF/ver_check.h"
|
||||
|
@ -2159,117 +2160,114 @@ Master_Shutdown (void)
|
|||
}
|
||||
}
|
||||
|
||||
static inline qboolean
|
||||
iswhitespace (char c)
|
||||
{
|
||||
return c == ' ' || c == '\r' || c == '\n' || c == '\t';
|
||||
}
|
||||
|
||||
/*
|
||||
SV_ExtractFromUserinfo
|
||||
|
||||
Pull specific info from a newly changed userinfo string
|
||||
into a more C freindly form.
|
||||
into a more C friendly form.
|
||||
*/
|
||||
void
|
||||
SV_ExtractFromUserinfo (client_t *cl)
|
||||
{
|
||||
const char *val;
|
||||
char *q, *p;
|
||||
char newname[80];
|
||||
const char *p;
|
||||
char *r;
|
||||
char newname[MAX_NAME];
|
||||
client_t *client;
|
||||
int i;
|
||||
int dupc = 1;
|
||||
qboolean badname = false;
|
||||
|
||||
|
||||
// name for C code
|
||||
// name from the info string
|
||||
val = Info_ValueForKey (cl->userinfo, "name");
|
||||
|
||||
// trim user name
|
||||
strncpy (newname, val, sizeof (newname) - 1);
|
||||
newname[sizeof (newname) - 1] = 0;
|
||||
// copy it, while converting \r, \n, and \t to space, trimming any
|
||||
// leading/trailing whitespace, and merging consecutive spaces
|
||||
for (p = val; iswhitespace (*p); p++);
|
||||
for (r = newname; *p && r != newname + sizeof (newname) - 1; p++) {
|
||||
if (iswhitespace (*p)) {
|
||||
if (!iswhitespace (p[1]) && p[1] != '\0')
|
||||
*r++ = *p;
|
||||
} else
|
||||
*r++ = *p;
|
||||
}
|
||||
*r = '\0';
|
||||
|
||||
for (p = newname; (*p == ' ' || *p == '\r' || *p == '\n') && *p; p++);
|
||||
// empty (or whitespace only) name
|
||||
if (!*newname)
|
||||
badname = true;
|
||||
|
||||
if (p != newname && !*p) {
|
||||
// white space only
|
||||
strcpy (newname, "unnamed");
|
||||
p = newname;
|
||||
// impersonating an user-xxx name. if they're using it
|
||||
// legitimately it'll be a no-op later on
|
||||
if (!strncasecmp (newname, "user-", 5))
|
||||
badname = true;
|
||||
|
||||
// impersonating the admin
|
||||
if (strcasestr (newname, "console") || strcasestr (newname, "admin"))
|
||||
badname = true;
|
||||
|
||||
// check for duplicate names
|
||||
for (i = 0, client = svs.clients; i < MAX_CLIENTS; i++, client++) {
|
||||
if (client->state != cs_spawned || client == cl)
|
||||
continue;
|
||||
if (!strcasecmp (client->name, val)) {
|
||||
badname = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (p != newname && *p) {
|
||||
for (q = newname; *p; *q++ = *p++);
|
||||
*q = 0;
|
||||
}
|
||||
for (p = newname + strlen (newname) - 1;
|
||||
p != newname && (*p == ' ' || *p == '\r' || *p == '\n'); p--);
|
||||
p[1] = 0;
|
||||
// give them a name we like
|
||||
if (badname)
|
||||
snprintf (newname, sizeof (newname), "user-%d", cl->userid);
|
||||
|
||||
if (strcmp (val, newname)) {
|
||||
// set the name
|
||||
if (strcmp (cl->name, newname)) {
|
||||
Info_SetValueForKey (cl->userinfo, "name", newname, MAX_INFO_STRING,
|
||||
!sv_highchars->int_val);
|
||||
val = Info_ValueForKey (cl->userinfo, "name");
|
||||
}
|
||||
SVstring (cl->edict, netname) = PR_SetString (&sv_pr_state, newname);
|
||||
|
||||
if (!val[0] || strcaseequal (val, "console")) {
|
||||
Info_SetValueForKey (cl->userinfo, "name", "unnamed", MAX_INFO_STRING,
|
||||
!sv_highchars->int_val);
|
||||
val = Info_ValueForKey (cl->userinfo, "name");
|
||||
}
|
||||
// check to see if another user by the same name exists
|
||||
while (1) {
|
||||
for (i = 0, client = svs.clients; i < MAX_CLIENTS; i++, client++) {
|
||||
if (client->state != cs_spawned || client == cl)
|
||||
continue;
|
||||
if (strcaseequal (client->name, val))
|
||||
break;
|
||||
// If the new name was not set (due to the info string being too
|
||||
// long), drop the client.
|
||||
if (strcmp (val, newname)) {
|
||||
Netchan_OutOfBandPrint (net_from, "%c\nPlease choose a "
|
||||
"different name.\n", A2C_PRINT);
|
||||
SV_ClientPrintf (cl, PRINT_HIGH, "Please choose a "
|
||||
"different name.\n");
|
||||
SV_Printf("Client %d kicked for having a invalid name\n",
|
||||
cl->userid);
|
||||
SV_DropClient (cl);
|
||||
return;
|
||||
}
|
||||
if (i != MAX_CLIENTS) { // dup name
|
||||
if (val[0] == '(') {
|
||||
if (val[2] == ')')
|
||||
val += 3;
|
||||
else if (val[3] == ')')
|
||||
val += 4;
|
||||
}
|
||||
|
||||
snprintf (newname, sizeof (newname), "(%d)%-.40s", dupc++, val);
|
||||
Info_SetValueForKey (cl->userinfo, "name", newname,
|
||||
MAX_INFO_STRING, !sv_highchars->int_val);
|
||||
val = Info_ValueForKey (cl->userinfo, "name");
|
||||
|
||||
// If the new name was not set (due to the info string being too
|
||||
// long), drop the client to prevent an infinite loop.
|
||||
if(strcmp(val, newname)) {
|
||||
Netchan_OutOfBandPrint (net_from, "%c\nPlease choose a "
|
||||
"different name.\n", A2C_PRINT);
|
||||
SV_ClientPrintf (cl, PRINT_HIGH, "Please choose a different "
|
||||
"name.\n");
|
||||
SV_Printf("Client %d kicked for invalid name\n", cl->userid);
|
||||
SV_DropClient (cl);
|
||||
return;
|
||||
}
|
||||
|
||||
} else
|
||||
break;
|
||||
}
|
||||
|
||||
if (strncmp (val, cl->name, strlen (cl->name))) {
|
||||
// check for spamming
|
||||
if (!sv.paused) {
|
||||
if (!cl->lastnametime || realtime - cl->lastnametime > 5) {
|
||||
cl->lastnamecount = 0;
|
||||
cl->lastnametime = realtime;
|
||||
} else if (cl->lastnamecount++ > 4) {
|
||||
SV_BroadcastPrintf (PRINT_HIGH, "%s was kicked for name "
|
||||
"spam\n", cl->name);
|
||||
SV_ClientPrintf (cl, PRINT_HIGH, "You were kicked from the "
|
||||
"game for name spamming\n");
|
||||
SV_BroadcastPrintf (PRINT_HIGH, "%s was kicked for "
|
||||
"name spam\n", cl->name);
|
||||
SV_ClientPrintf (cl, PRINT_HIGH, "You were kicked "
|
||||
"from the game for name spamming\n");
|
||||
SV_DropClient (cl);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (cl->state >= cs_spawned && !cl->spectator)
|
||||
// finally, report it to all our friends
|
||||
// if (cl->state >= cs_spawned && !cl->spectator)
|
||||
if (*cl->name)
|
||||
SV_BroadcastPrintf (PRINT_HIGH, "%s changed name to %s\n",
|
||||
cl->name, val);
|
||||
cl->name, newname);
|
||||
strcpy (cl->name, newname);
|
||||
}
|
||||
|
||||
|
||||
strncpy (cl->name, val, sizeof (cl->name) - 1);
|
||||
|
||||
// rate command
|
||||
val = Info_ValueForKey (cl->userinfo, "rate");
|
||||
if (strlen (val)) {
|
||||
|
@ -2282,6 +2280,7 @@ SV_ExtractFromUserinfo (client_t *cl)
|
|||
}
|
||||
cl->netchan.rate = 1.0 / i;
|
||||
}
|
||||
|
||||
// msg command
|
||||
val = Info_ValueForKey (cl->userinfo, "msg");
|
||||
if (strlen (val)) {
|
||||
|
|
|
@ -225,7 +225,7 @@ SV_PrintToClient (client_t *cl, int level, const char *string)
|
|||
static int buffer_size;
|
||||
|
||||
size = strlen (string) + 1;
|
||||
if (strlen (string) > buffer_size) {
|
||||
if (size > buffer_size) {
|
||||
buffer_size = (size + 1023) & ~1023; // 1k multiples
|
||||
if (buffer)
|
||||
free (buffer);
|
||||
|
|
Loading…
Reference in a new issue