- 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:
Adam Olsen 2001-10-04 19:11:39 +00:00
parent cd9832d95f
commit 0785610296
8 changed files with 177 additions and 84 deletions

View file

@ -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
View 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

View file

@ -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))

View file

@ -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
View 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;
}

View file

@ -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

View file

@ -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)) {

View file

@ -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);