jedi-academy/codemp/game/q_shared.c
Jonathan Gray 61687fff0c CVE-2011-2764/CVE-2011-3012 check for dangerous file extensions
CVE-2011-2764
The FS_CheckFilenameIsNotExecutable function in qcommon/files.c in the
ioQuake3 engine 1.36 and earlier, as used in World of Padman, Smokin'
Guns, OpenArena, Tremulous, and ioUrbanTerror, does not properly
determine dangerous file extensions, which allows remote attackers to
execute arbitrary code via a crafted third-party addon that creates a
Trojan horse DLL file.

CVE-2011-3012
The ioQuake3 engine, as used in World of Padman 1.2 and earlier,
Tremulous 1.1.0, and ioUrbanTerror 2007-12-20, does not check for
dangerous file extensions before writing to the quake3 directory, which
allows remote attackers to execute arbitrary code via a crafted
third-party addon that creates a Trojan horse DLL file, a different
vulnerability than CVE-2011-2764.

bugzilla #3695

from Tim Angus in ioquake3
svn 1405 git 2c0861c1cea44861c5ceba2dc39e601d6bc3f0af

* (bug 3695) Not allowing to write file with lib extention (.dll/.so/...)
  (TsT <tst2006@gmail.com>)

from Tim Angus in ioquake3
svn 1499 git 48d8c8876b6ec035b0bb85f4d3c47c9210c3ca30

* s/FS_FilenameIsExecutable/FS_CheckFilenameIsNotExecutable/g
* Fix potential buffer under run in FS_CheckFilenameIsNotExecutable

from Thilo Schulz in ioquake3
svn 2098 git c4f739b8d03ca203435744c4a96e3561863ccdfe

Fix extension name comparison for DLL files

from Zack Middleton in ioquake3
git 6c88bf8aeee3c1e5449682f874f91e86cb393ef4

Rename FS_CheckFilenameIsNotExecutable to ..NotImmutable

from Harley Laue in ioquake3
git 1b2a6abed996b43eb108486abbda449b3d16e019

Rename FS_CheckFilenameIsNotImmutable to ..IsMutable
2013-05-07 22:20:02 +10:00

1477 lines
24 KiB
C

// Copyright (C) 1999-2000 Id Software, Inc.
//
// q_shared.c -- stateless support routines that are included in each code dll
#include "q_shared.h"
/*
-------------------------
GetIDForString
-------------------------
*/
int GetIDForString ( stringID_table_t *table, const char *string )
{
int index = 0;
while ( ( table[index].name != NULL ) &&
( table[index].name[0] != 0 ) )
{
if ( !Q_stricmp( table[index].name, string ) )
return table[index].id;
index++;
}
return -1;
}
/*
-------------------------
GetStringForID
-------------------------
*/
const char *GetStringForID( stringID_table_t *table, int id )
{
int index = 0;
while ( ( table[index].name != NULL ) &&
( table[index].name[0] != 0 ) )
{
if ( table[index].id == id )
return table[index].name;
index++;
}
return NULL;
}
int Com_Clampi( int min, int max, int value )
{
if ( value < min )
{
return min;
}
if ( value > max )
{
return max;
}
return value;
}
float Com_Clamp( float min, float max, float value ) {
if ( value < min ) {
return min;
}
if ( value > max ) {
return max;
}
return value;
}
/*
============
COM_SkipPath
============
*/
char *COM_SkipPath (char *pathname)
{
char *last;
last = pathname;
while (*pathname)
{
if (*pathname=='/')
last = pathname+1;
pathname++;
}
return last;
}
/*
============
COM_GetExtension
============
*/
const char *COM_GetExtension( const char *name )
{
const char *dot = strrchr(name, '.'), *slash;
if (dot && (!(slash = strrchr(name, '/')) || slash < dot))
return dot + 1;
else
return "";
}
/*
============
COM_StripExtension
============
*/
void COM_StripExtension( const char *in, char *out, int destsize ) {
const char *dot = strrchr(in, '.'), *slash;
if (dot && (!(slash = strrchr(in, '/')) || slash < dot))
Q_strncpyz(out, in, (destsize < dot-in+1 ? destsize : dot-in+1));
else
Q_strncpyz(out, in, destsize);
}
/*
============
COM_CompareExtension
string compare the end of the strings and return qtrue if strings match
============
*/
qboolean COM_CompareExtension(const char *in, const char *ext)
{
int inlen, extlen;
inlen = strlen(in);
extlen = strlen(ext);
if(extlen <= inlen)
{
in += inlen - extlen;
if(!Q_stricmp(in, ext))
return qtrue;
}
return qfalse;
}
/*
==================
COM_DefaultExtension
==================
*/
void COM_DefaultExtension (char *path, int maxSize, const char *extension ) {
char oldPath[MAX_QPATH];
char *src;
//
// if path doesn't have a .EXT, append extension
// (extension should include the .)
//
src = path + strlen(path) - 1;
while (*src != '/' && src != path) {
if ( *src == '.' ) {
return; // it has an extension
}
src--;
}
Q_strncpyz( oldPath, path, sizeof( oldPath ) );
Com_sprintf( path, maxSize, "%s%s", oldPath, extension );
}
/*
============================================================================
BYTE ORDER FUNCTIONS
============================================================================
*/
/*
// can't just use function pointers, or dll linkage can
// mess up when qcommon is included in multiple places
static short (*_BigShort) (short l);
static short (*_LittleShort) (short l);
static int (*_BigLong) (int l);
static int (*_LittleLong) (int l);
static qint64 (*_BigLong64) (qint64 l);
static qint64 (*_LittleLong64) (qint64 l);
static float (*_BigFloat) (const float *l);
static float (*_LittleFloat) (const float *l);
short BigShort(short l){return _BigShort(l);}
short LittleShort(short l) {return _LittleShort(l);}
int BigLong (int l) {return _BigLong(l);}
int LittleLong (int l) {return _LittleLong(l);}
qint64 BigLong64 (qint64 l) {return _BigLong64(l);}
qint64 LittleLong64 (qint64 l) {return _LittleLong64(l);}
float BigFloat (const float *l) {return _BigFloat(l);}
float LittleFloat (const float *l) {return _LittleFloat(l);}
*/
short ShortSwap (short l)
{
byte b1,b2;
b1 = l&255;
b2 = (l>>8)&255;
return (b1<<8) + b2;
}
short ShortNoSwap (short l)
{
return l;
}
int LongSwap (int l)
{
byte b1,b2,b3,b4;
b1 = l&255;
b2 = (l>>8)&255;
b3 = (l>>16)&255;
b4 = (l>>24)&255;
return ((int)b1<<24) + ((int)b2<<16) + ((int)b3<<8) + b4;
}
int LongNoSwap (int l)
{
return l;
}
qint64 Long64Swap (qint64 ll)
{
qint64 result;
result.b0 = ll.b7;
result.b1 = ll.b6;
result.b2 = ll.b5;
result.b3 = ll.b4;
result.b4 = ll.b3;
result.b5 = ll.b2;
result.b6 = ll.b1;
result.b7 = ll.b0;
return result;
}
qint64 Long64NoSwap (qint64 ll)
{
return ll;
}
typedef union {
float f;
unsigned int i;
} _FloatByteUnion;
float FloatSwap (const float *f) {
const _FloatByteUnion *in;
_FloatByteUnion out;
in = (_FloatByteUnion *)f;
out.i = LongSwap(in->i);
return out.f;
}
float FloatNoSwap (const float *f)
{
return *f;
}
/*
================
Swap_Init
================
*/
/*
void Swap_Init (void)
{
byte swaptest[2] = {1,0};
// set the byte swapping variables in a portable manner
if ( *(short *)swaptest == 1)
{
_BigShort = ShortSwap;
_LittleShort = ShortNoSwap;
_BigLong = LongSwap;
_LittleLong = LongNoSwap;
_BigLong64 = Long64Swap;
_LittleLong64 = Long64NoSwap;
_BigFloat = FloatSwap;
_LittleFloat = FloatNoSwap;
}
else
{
_BigShort = ShortNoSwap;
_LittleShort = ShortSwap;
_BigLong = LongNoSwap;
_LittleLong = LongSwap;
_BigLong64 = Long64NoSwap;
_LittleLong64 = Long64Swap;
_BigFloat = FloatNoSwap;
_LittleFloat = FloatSwap;
}
}
*/
/*
============================================================================
PARSING
============================================================================
*/
static char com_token[MAX_TOKEN_CHARS];
static char com_parsename[MAX_TOKEN_CHARS];
static int com_lines;
void COM_BeginParseSession( const char *name )
{
com_lines = 0;
Com_sprintf(com_parsename, sizeof(com_parsename), "%s", name);
}
int COM_GetCurrentParseLine( void )
{
return com_lines;
}
char *COM_Parse( const char **data_p )
{
return COM_ParseExt( data_p, qtrue );
}
void COM_ParseError( char *format, ... )
{
va_list argptr;
static char string[4096];
va_start (argptr, format);
Q_vsnprintf (string, sizeof(string), format, argptr);
va_end (argptr);
Com_Printf("ERROR: %s, line %d: %s\n", com_parsename, com_lines, string);
}
void COM_ParseWarning( char *format, ... )
{
va_list argptr;
static char string[4096];
va_start (argptr, format);
Q_vsnprintf (string, sizeof(string), format, argptr);
va_end (argptr);
Com_Printf("WARNING: %s, line %d: %s\n", com_parsename, com_lines, string);
}
/*
==============
COM_Parse
Parse a token out of a string
Will never return NULL, just empty strings
If "allowLineBreaks" is qtrue then an empty
string will be returned if the next token is
a newline.
==============
*/
const char *SkipWhitespace( const char *data, qboolean *hasNewLines ) {
int c;
while( (c = *data) <= ' ') {
if( !c ) {
return NULL;
}
if( c == '\n' ) {
com_lines++;
*hasNewLines = qtrue;
}
data++;
}
return data;
}
int COM_Compress( char *data_p ) {
char *in, *out;
int c;
qboolean newline = qfalse, whitespace = qfalse;
in = out = data_p;
if (in) {
while ((c = *in) != 0) {
// skip double slash comments
if ( c == '/' && in[1] == '/' ) {
while (*in && *in != '\n') {
in++;
}
// skip /* */ comments
} else if ( c == '/' && in[1] == '*' ) {
while ( *in && ( *in != '*' || in[1] != '/' ) )
in++;
if ( *in )
in += 2;
// record when we hit a newline
} else if ( c == '\n' || c == '\r' ) {
newline = qtrue;
in++;
// record when we hit whitespace
} else if ( c == ' ' || c == '\t') {
whitespace = qtrue;
in++;
// an actual token
} else {
// if we have a pending newline, emit it (and it counts as whitespace)
if (newline) {
*out++ = '\n';
newline = qfalse;
whitespace = qfalse;
} if (whitespace) {
*out++ = ' ';
whitespace = qfalse;
}
// copy quoted strings unmolested
if (c == '"') {
*out++ = c;
in++;
while (1) {
c = *in;
if (c && c != '"') {
*out++ = c;
in++;
} else {
break;
}
}
if (c == '"') {
*out++ = c;
in++;
}
} else {
*out = c;
out++;
in++;
}
}
}
}
*out = 0;
return out - data_p;
}
char *COM_ParseExt( const char **data_p, qboolean allowLineBreaks )
{
int c = 0, len;
qboolean hasNewLines = qfalse;
const char *data;
data = *data_p;
len = 0;
com_token[0] = 0;
// make sure incoming data is valid
if ( !data )
{
*data_p = NULL;
return com_token;
}
while ( 1 )
{
// skip whitespace
data = SkipWhitespace( data, &hasNewLines );
if ( !data )
{
*data_p = NULL;
return com_token;
}
if ( hasNewLines && !allowLineBreaks )
{
*data_p = data;
return com_token;
}
c = *data;
// skip double slash comments
if ( c == '/' && data[1] == '/' )
{
data += 2;
while (*data && *data != '\n') {
data++;
}
}
// skip /* */ comments
else if ( c=='/' && data[1] == '*' )
{
data += 2;
while ( *data && ( *data != '*' || data[1] != '/' ) )
{
data++;
}
if ( *data )
{
data += 2;
}
}
else
{
break;
}
}
// handle quoted strings
if (c == '\"')
{
data++;
while (1)
{
c = *data++;
if (c=='\"' || !c)
{
com_token[len] = 0;
*data_p = ( char * ) data;
return com_token;
}
if (len < MAX_TOKEN_CHARS - 1)
{
com_token[len] = c;
len++;
}
}
}
// parse a regular word
do
{
if (len < MAX_TOKEN_CHARS - 1)
{
com_token[len] = c;
len++;
}
data++;
c = *data;
if ( c == '\n' )
com_lines++;
} while (c>32);
com_token[len] = 0;
*data_p = ( char * ) data;
return com_token;
}
#if 0
// no longer used
/*
===============
COM_ParseInfos
===============
*/
int COM_ParseInfos( char *buf, int max, char infos[][MAX_INFO_STRING] ) {
char *token;
int count;
char key[MAX_TOKEN_CHARS];
count = 0;
while ( 1 ) {
token = COM_Parse( &buf );
if ( !token[0] ) {
break;
}
if ( strcmp( token, "{" ) ) {
Com_Printf( "Missing { in info file\n" );
break;
}
if ( count == max ) {
Com_Printf( "Max infos exceeded\n" );
break;
}
infos[count][0] = 0;
while ( 1 ) {
token = COM_ParseExt( &buf, qtrue );
if ( !token[0] ) {
Com_Printf( "Unexpected end of info file\n" );
break;
}
if ( !strcmp( token, "}" ) ) {
break;
}
Q_strncpyz( key, token, sizeof( key ) );
token = COM_ParseExt( &buf, qfalse );
if ( !token[0] ) {
strcpy( token, "<NULL>" );
}
Info_SetValueForKey( infos[count], key, token );
}
count++;
}
return count;
}
#endif
/*
===============
COM_ParseString
===============
*/
qboolean COM_ParseString( const char **data, const char **s )
{
// *s = COM_ParseExt( data, qtrue );
*s = COM_ParseExt( data, qfalse );
if ( s[0] == 0 )
{
Com_Printf("unexpected EOF\n");
return qtrue;
}
return qfalse;
}
/*
===============
COM_ParseInt
===============
*/
qboolean COM_ParseInt( const char **data, int *i )
{
const char *token;
token = COM_ParseExt( data, qfalse );
if ( token[0] == 0 )
{
Com_Printf( "unexpected EOF\n" );
return qtrue;
}
*i = atoi( token );
return qfalse;
}
/*
===============
COM_ParseFloat
===============
*/
qboolean COM_ParseFloat( const char **data, float *f )
{
const char *token;
token = COM_ParseExt( data, qfalse );
if ( token[0] == 0 )
{
Com_Printf( "unexpected EOF\n" );
return qtrue;
}
*f = atof( token );
return qfalse;
}
/*
===============
COM_ParseVec4
===============
*/
qboolean COM_ParseVec4( const char **buffer, vec4_t *c)
{
int i;
float f;
for (i = 0; i < 4; i++)
{
if (COM_ParseFloat(buffer, &f))
{
return qtrue;
}
(*c)[i] = f;
}
return qfalse;
}
/*
==================
COM_MatchToken
==================
*/
void COM_MatchToken( const char **buf_p, char *match ) {
char *token;
token = COM_Parse( buf_p );
if ( strcmp( token, match ) ) {
Com_Error( ERR_DROP, "MatchToken: %s != %s", token, match );
}
}
/*
=================
SkipBracedSection
The next token should be an open brace.
Skips until a matching close brace is found.
Internal brace depths are properly skipped.
=================
*/
void SkipBracedSection (const char **program) {
char *token;
int depth;
depth = 0;
do {
token = COM_ParseExt( program, qtrue );
if( token[1] == 0 ) {
if( token[0] == '{' ) {
depth++;
}
else if( token[0] == '}' ) {
depth--;
}
}
} while( depth && *program );
}
/*
=================
SkipRestOfLine
=================
*/
void SkipRestOfLine ( const char **data ) {
const char *p;
int c;
p = *data;
while ( (c = *p++) != 0 ) {
if ( c == '\n' ) {
com_lines++;
break;
}
}
*data = p;
}
void Parse1DMatrix (const char **buf_p, int x, float *m) {
char *token;
int i;
COM_MatchToken( buf_p, "(" );
for (i = 0 ; i < x ; i++) {
token = COM_Parse(buf_p);
m[i] = atof(token);
}
COM_MatchToken( buf_p, ")" );
}
void Parse2DMatrix (const char **buf_p, int y, int x, float *m) {
int i;
COM_MatchToken( buf_p, "(" );
for (i = 0 ; i < y ; i++) {
Parse1DMatrix (buf_p, x, m + i * x);
}
COM_MatchToken( buf_p, ")" );
}
void Parse3DMatrix (const char **buf_p, int z, int y, int x, float *m) {
int i;
COM_MatchToken( buf_p, "(" );
for (i = 0 ; i < z ; i++) {
Parse2DMatrix (buf_p, y, x, m + i * x*y);
}
COM_MatchToken( buf_p, ")" );
}
/*
===================
Com_HexStrToInt
===================
*/
int Com_HexStrToInt( const char *str )
{
if ( !str || !str[ 0 ] )
return -1;
// check for hex code
if( str[ 0 ] == '0' && str[ 1 ] == 'x' )
{
int i, n = 0;
for( i = 2; i < strlen( str ); i++ )
{
char digit;
n *= 16;
digit = tolower( str[ i ] );
if( digit >= '0' && digit <= '9' )
digit -= '0';
else if( digit >= 'a' && digit <= 'f' )
digit = digit - 'a' + 10;
else
return -1;
n += digit;
}
return n;
}
return -1;
}
/*
============================================================================
LIBRARY REPLACEMENT FUNCTIONS
============================================================================
*/
int Q_isprint( int c )
{
if ( c >= 0x20 && c <= 0x7E )
return ( 1 );
return ( 0 );
}
int Q_islower( int c )
{
if (c >= 'a' && c <= 'z')
return ( 1 );
return ( 0 );
}
int Q_isupper( int c )
{
if (c >= 'A' && c <= 'Z')
return ( 1 );
return ( 0 );
}
int Q_isalpha( int c )
{
if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))
return ( 1 );
return ( 0 );
}
char* Q_strrchr( const char* string, int c )
{
char cc = c;
char *s;
char *sp=(char *)0;
s = (char*)string;
while (*s)
{
if (*s == cc)
sp = s;
s++;
}
if (cc == 0)
sp = s;
return sp;
}
#ifdef _MSC_VER
/*
=============
Q_vsnprintf
Special wrapper function for Microsoft's broken _vsnprintf() function.
MinGW comes with its own snprintf() which is not broken.
=============
*/
int Q_vsnprintf(char *str, size_t size, const char *format, va_list ap)
{
int retval;
retval = _vsnprintf(str, size, format, ap);
if(retval < 0 || retval == size)
{
// Microsoft doesn't adhere to the C99 standard of vsnprintf,
// which states that the return value must be the number of
// bytes written if the output string had sufficient length.
//
// Obviously we cannot determine that value from Microsoft's
// implementation, so we have no choice but to return size.
str[size - 1] = '\0';
return size;
}
return retval;
}
#endif
/*
=============
Q_strncpyz
Safe strncpy that ensures a trailing zero
=============
*/
void Q_strncpyz( char *dest, const char *src, int destsize ) {
// bk001129 - also NULL dest
if ( !dest ) {
Com_Error( ERR_FATAL, "Q_strncpyz: NULL dest" );
}
if ( !src ) {
Com_Error( ERR_FATAL, "Q_strncpyz: NULL src" );
}
if ( destsize < 1 ) {
Com_Error(ERR_FATAL,"Q_strncpyz: destsize < 1" );
}
strncpy( dest, src, destsize-1 );
dest[destsize-1] = 0;
}
int Q_stricmpn (const char *s1, const char *s2, int n) {
int c1, c2;
// bk001129 - moved in 1.17 fix not in id codebase
if ( s1 == NULL ) {
if ( s2 == NULL )
return 0;
else
return -1;
}
else if ( s2==NULL )
return 1;
do {
c1 = *s1++;
c2 = *s2++;
if (!n--) {
return 0; // strings are equal until end point
}
if (c1 != c2) {
if (c1 >= 'a' && c1 <= 'z') {
c1 -= ('a' - 'A');
}
if (c2 >= 'a' && c2 <= 'z') {
c2 -= ('a' - 'A');
}
if (c1 != c2) {
return c1 < c2 ? -1 : 1;
}
}
} while (c1);
return 0; // strings are equal
}
int Q_strncmp (const char *s1, const char *s2, int n) {
int c1, c2;
do {
c1 = *s1++;
c2 = *s2++;
if (!n--) {
return 0; // strings are equal until end point
}
if (c1 != c2) {
return c1 < c2 ? -1 : 1;
}
} while (c1);
return 0; // strings are equal
}
int Q_stricmp (const char *s1, const char *s2) {
return (s1 && s2) ? Q_stricmpn (s1, s2, 99999) : -1;
}
char *Q_strlwr( char *s1 ) {
char *s;
s = s1;
while ( *s ) {
*s = tolower(*s);
s++;
}
return s1;
}
char *Q_strupr( char *s1 ) {
char *s;
s = s1;
while ( *s ) {
*s = toupper(*s);
s++;
}
return s1;
}
// never goes past bounds or leaves without a terminating 0
void Q_strcat( char *dest, int size, const char *src ) {
int l1;
l1 = strlen( dest );
if ( l1 >= size ) {
Com_Error( ERR_FATAL, "Q_strcat: already overflowed" );
}
Q_strncpyz( dest + l1, src, size - l1 );
}
int Q_PrintStrlen( const char *string ) {
int len;
const char *p;
if( !string ) {
return 0;
}
len = 0;
p = string;
while( *p ) {
if( Q_IsColorString( p ) ) {
p += 2;
continue;
}
p++;
len++;
}
return len;
}
char *Q_CleanStr( char *string ) {
char* d;
char* s;
int c;
s = string;
d = string;
while ((c = *s) != 0 ) {
if ( Q_IsColorString( s ) ) {
s++;
}
else if ( c >= 0x20 && c <= 0x7E ) {
*d++ = c;
}
s++;
}
*d = '\0';
return string;
}
void QDECL Com_sprintf( char *dest, int size, const char *fmt, ...) {
int len;
va_list argptr;
char bigbuffer[32000]; // big, but small enough to fit in PPC stack
va_start (argptr,fmt);
len = Q_vsnprintf (bigbuffer,sizeof(bigbuffer),fmt,argptr);
va_end (argptr);
if ( len >= sizeof( bigbuffer ) ) {
Com_Error( ERR_FATAL, "Com_sprintf: overflowed bigbuffer" );
}
if (len >= size) {
Com_Printf ("Com_sprintf: overflow of %i in %i\n", len, size);
#ifdef _DEBUG
__asm {
int 3;
}
#endif
}
Q_strncpyz (dest, bigbuffer, size );
}
/*
============
va
does a varargs printf into a temp buffer, so I don't need to have
varargs versions of all text functions.
FIXME: make this buffer size safe someday
============
*/
char * QDECL va( const char *format, ... ) {
va_list argptr;
static char string[2][32000]; // in case va is called by nested functions
static int index = 0;
char *buf;
buf = string[index & 1];
index++;
va_start (argptr, format);
Q_vsnprintf (buf,sizeof(*string),format,argptr);
va_end (argptr);
return buf;
}
/*
=====================================================================
INFO STRINGS
=====================================================================
*/
/*
===============
Info_ValueForKey
Searches the string for the given
key and returns the associated value, or an empty string.
FIXME: overflow check?
===============
*/
char *Info_ValueForKey( const char *s, const char *key ) {
char pkey[BIG_INFO_KEY];
static char value[2][BIG_INFO_VALUE]; // use two buffers so compares
// work without stomping on each other
static int valueindex = 0;
char *o;
if ( !s || !key ) {
return "";
}
if ( strlen( s ) >= BIG_INFO_STRING ) {
Com_Error( ERR_DROP, "Info_ValueForKey: oversize infostring" );
}
valueindex ^= 1;
if (*s == '\\')
s++;
while (1)
{
o = pkey;
while (*s != '\\')
{
if (!*s)
return "";
*o++ = *s++;
}
*o = 0;
s++;
o = value[valueindex];
while (*s != '\\' && *s)
{
*o++ = *s++;
}
*o = 0;
if (!Q_stricmp (key, pkey) )
return value[valueindex];
if (!*s)
break;
s++;
}
return "";
}
/*
===================
Info_NextPair
Used to itterate through all the key/value pairs in an info string
===================
*/
void Info_NextPair( const char **head, char *key, char *value ) {
char *o;
const char *s;
s = *head;
if ( *s == '\\' ) {
s++;
}
key[0] = 0;
value[0] = 0;
o = key;
while ( *s != '\\' ) {
if ( !*s ) {
*o = 0;
*head = s;
return;
}
*o++ = *s++;
}
*o = 0;
s++;
o = value;
while ( *s != '\\' && *s ) {
*o++ = *s++;
}
*o = 0;
*head = s;
}
/*
===================
Info_RemoveKey
===================
*/
void Info_RemoveKey( char *s, const char *key ) {
char *start;
char pkey[MAX_INFO_KEY];
char value[MAX_INFO_VALUE];
char *o;
if ( strlen( s ) >= MAX_INFO_STRING ) {
Com_Error( ERR_DROP, "Info_RemoveKey: oversize infostring" );
}
if (strchr (key, '\\')) {
return;
}
while (1)
{
start = s;
if (*s == '\\')
s++;
o = pkey;
while (*s != '\\')
{
if (!*s)
return;
*o++ = *s++;
}
*o = 0;
s++;
o = value;
while (*s != '\\' && *s)
{
if (!*s)
return;
*o++ = *s++;
}
*o = 0;
if (!strcmp (key, pkey) )
{
strcpy (start, s); // remove this part
return;
}
if (!*s)
return;
}
}
/*
===================
Info_RemoveKey_Big
===================
*/
void Info_RemoveKey_Big( char *s, const char *key ) {
char *start;
char pkey[BIG_INFO_KEY];
char value[BIG_INFO_VALUE];
char *o;
if ( strlen( s ) >= BIG_INFO_STRING ) {
Com_Error( ERR_DROP, "Info_RemoveKey_Big: oversize infostring" );
}
if (strchr (key, '\\')) {
return;
}
while (1)
{
start = s;
if (*s == '\\')
s++;
o = pkey;
while (*s != '\\')
{
if (!*s)
return;
*o++ = *s++;
}
*o = 0;
s++;
o = value;
while (*s != '\\' && *s)
{
if (!*s)
return;
*o++ = *s++;
}
*o = 0;
if (!strcmp (key, pkey) )
{
strcpy (start, s); // remove this part
return;
}
if (!*s)
return;
}
}
/*
==================
Info_Validate
Some characters are illegal in info strings because they
can mess up the server's parsing
==================
*/
qboolean Info_Validate( const char *s ) {
if ( strchr( s, '\"' ) ) {
return qfalse;
}
if ( strchr( s, ';' ) ) {
return qfalse;
}
return qtrue;
}
/*
==================
Info_SetValueForKey
Changes or adds a key/value pair
==================
*/
void Info_SetValueForKey( char *s, const char *key, const char *value ) {
char newi[MAX_INFO_STRING];
if ( strlen( s ) >= MAX_INFO_STRING ) {
Com_Error( ERR_DROP, "Info_SetValueForKey: oversize infostring" );
}
if (strchr (key, '\\') || strchr (value, '\\'))
{
Com_Printf ("Can't use keys or values with a \\\n");
return;
}
if (strchr (key, ';') || strchr (value, ';'))
{
Com_Printf ("Can't use keys or values with a semicolon\n");
return;
}
if (strchr (key, '\"') || strchr (value, '\"'))
{
Com_Printf ("Can't use keys or values with a \"\n");
return;
}
Info_RemoveKey (s, key);
if (!value || !strlen(value))
return;
Com_sprintf (newi, sizeof(newi), "\\%s\\%s", key, value);
if (strlen(newi) + strlen(s) >= MAX_INFO_STRING)
{
Com_Printf ("Info string length exceeded\n");
return;
}
strcat (newi, s);
strcpy (s, newi);
}
/*
==================
Info_SetValueForKey_Big
Changes or adds a key/value pair
==================
*/
void Info_SetValueForKey_Big( char *s, const char *key, const char *value ) {
char newi[BIG_INFO_STRING];
if ( strlen( s ) >= BIG_INFO_STRING ) {
Com_Error( ERR_DROP, "Info_SetValueForKey: oversize infostring" );
}
if (strchr (key, '\\') || strchr (value, '\\'))
{
Com_Printf ("Can't use keys or values with a \\\n");
return;
}
if (strchr (key, ';') || strchr (value, ';'))
{
Com_Printf ("Can't use keys or values with a semicolon\n");
return;
}
if (strchr (key, '\"') || strchr (value, '\"'))
{
Com_Printf ("Can't use keys or values with a \"\n");
return;
}
Info_RemoveKey_Big (s, key);
if (!value || !strlen(value))
return;
Com_sprintf (newi, sizeof(newi), "\\%s\\%s", key, value);
if (strlen(newi) + strlen(s) >= BIG_INFO_STRING)
{
Com_Printf ("BIG Info string length exceeded\n");
return;
}
strcat (s, newi);
}
//====================================================================