- 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

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