diff --git a/reaction/code/AL/alctypes.h b/reaction/code/AL/alctypes.h index 181384b0..a7c7858f 100644 --- a/reaction/code/AL/alctypes.h +++ b/reaction/code/AL/alctypes.h @@ -123,6 +123,7 @@ typedef void ALCvoid; */ #define ALC_DEFAULT_DEVICE_SPECIFIER 0x1004 #define ALC_DEVICE_SPECIFIER 0x1005 +#define ALC_ALL_DEVICES_SPECIFIER 0x1013 #define ALC_EXTENSIONS 0x1006 #define ALC_MAJOR_VERSION 0x1000 diff --git a/reaction/code/client/cl_cgame.c b/reaction/code/client/cl_cgame.c index 8c01d06d..4ed218b9 100644 --- a/reaction/code/client/cl_cgame.c +++ b/reaction/code/client/cl_cgame.c @@ -442,7 +442,7 @@ intptr_t CL_CgameSystemCalls( intptr_t *args ) { Cvar_Update( VMA(1) ); return 0; case CG_CVAR_SET: - Cvar_Set( VMA(1), VMA(2) ); + Cvar_SetSafe( VMA(1), VMA(2) ); return 0; case CG_CVAR_VARIABLESTRINGBUFFER: Cvar_VariableStringBuffer( VMA(1), VMA(2), args[3] ); @@ -475,7 +475,7 @@ intptr_t CL_CgameSystemCalls( intptr_t *args ) { CL_AddCgameCommand( VMA(1) ); return 0; case CG_REMOVECOMMAND: - Cmd_RemoveCommand( VMA(1) ); + Cmd_RemoveCommandSafe( VMA(1) ); return 0; case CG_SENDCLIENTCOMMAND: CL_AddReliableCommand(VMA(1), qfalse); diff --git a/reaction/code/client/cl_parse.c b/reaction/code/client/cl_parse.c index 83e22754..646fa818 100644 --- a/reaction/code/client/cl_parse.c +++ b/reaction/code/client/cl_parse.c @@ -416,13 +416,13 @@ void CL_SystemInfoChanged( void ) { else { // If this cvar may not be modified by a server discard the value. - if(!(cvar_flags & (CVAR_SYSTEMINFO | CVAR_SERVER_CREATED))) + if(!(cvar_flags & (CVAR_SYSTEMINFO | CVAR_SERVER_CREATED | CVAR_USER_CREATED))) { Com_Printf(S_COLOR_YELLOW "WARNING: server is not allowed to set %s=%s\n", key, value); continue; } - Cvar_Set(key, value); + Cvar_SetSafe(key, value); } } // if game folder should not be set and it is set at the client side diff --git a/reaction/code/client/cl_ui.c b/reaction/code/client/cl_ui.c index 09280e44..13bc6eaf 100644 --- a/reaction/code/client/cl_ui.c +++ b/reaction/code/client/cl_ui.c @@ -729,7 +729,7 @@ intptr_t CL_UISystemCalls( intptr_t *args ) { return 0; case UI_CVAR_SET: - Cvar_Set( VMA(1), VMA(2) ); + Cvar_SetSafe( VMA(1), VMA(2) ); return 0; case UI_CVAR_VARIABLEVALUE: @@ -740,7 +740,7 @@ intptr_t CL_UISystemCalls( intptr_t *args ) { return 0; case UI_CVAR_SETVALUE: - Cvar_SetValue( VMA(1), VMF(2) ); + Cvar_SetValueSafe( VMA(1), VMF(2) ); return 0; case UI_CVAR_RESET: diff --git a/reaction/code/client/snd_openal.c b/reaction/code/client/snd_openal.c index aef86a22..2d5ddf9f 100644 --- a/reaction/code/client/snd_openal.c +++ b/reaction/code/client/snd_openal.c @@ -44,7 +44,9 @@ cvar_t *s_alRolloff; cvar_t *s_alGraceDistance; cvar_t *s_alDriver; cvar_t *s_alDevice; +cvar_t *s_alInputDevice; cvar_t *s_alAvailableDevices; +cvar_t *s_alAvailableInputDevices; cvar_t *s_alEffectsLevel; @@ -2641,11 +2643,17 @@ void S_AL_SoundInfo( void ) Com_Printf( " ALC Extensions: %s\n", qalcGetString( alDevice, ALC_EXTENSIONS ) ); if(qalcIsExtensionPresent(NULL, "ALC_ENUMERATION_EXT")) { - Com_Printf(" Device: %s\n", qalcGetString(alDevice, ALC_DEVICE_SPECIFIER)); + Com_Printf(" Device: %s\n", qalcGetString(alDevice, ALC_DEVICE_SPECIFIER)); Com_Printf("Available Devices:\n%s", s_alAvailableDevices->string); +#ifdef USE_VOIP + Com_Printf("Input Device: %s\n", qalcGetString(alCaptureDevice, ALC_DEVICE_SPECIFIER)); + Com_Printf("Available Input Devices:\n%s", s_alAvailableInputDevices->string); +#endif } } + + static void S_AL_ShutDownEffects(void) { if (!s_alEffects.initialized) @@ -3067,6 +3075,7 @@ qboolean S_AL_Init( soundInterface_t *si ) { #ifdef USE_OPENAL const char* device = NULL; + const char* inputdevice = NULL; int i; if( !si ) { @@ -3091,6 +3100,7 @@ qboolean S_AL_Init( soundInterface_t *si ) s_alGraceDistance = Cvar_Get("s_alGraceDistance", "512", CVAR_CHEAT); s_alDriver = Cvar_Get( "s_alDriver", ALDRIVER_DEFAULT, CVAR_ARCHIVE | CVAR_LATCH ); + s_alInputDevice = Cvar_Get( "s_alInputDevice", ALDRIVER_DEFAULT, CVAR_ARCHIVE | CVAR_LATCH ); s_alDevice = Cvar_Get("s_alDevice", "", CVAR_ARCHIVE | CVAR_LATCH); s_alEffectsLevel = Cvar_Get("s_alEffects", "1", CVAR_ARCHIVE | CVAR_LATCH ); @@ -3106,6 +3116,10 @@ qboolean S_AL_Init( soundInterface_t *si ) if(device && !*device) device = NULL; + inputdevice = s_alInputDevice->string; + if(inputdevice && !*inputdevice) + inputdevice = NULL; + // Device enumeration support (extension is implemented reasonably only on Windows right now). if(qalcIsExtensionPresent(NULL, "ALC_ENUMERATION_EXT")) { @@ -3115,7 +3129,7 @@ qboolean S_AL_Init( soundInterface_t *si ) int curlen; // get all available devices + the default device name. - devicelist = qalcGetString(NULL, ALC_DEVICE_SPECIFIER); + devicelist = qalcGetString(NULL, ALC_ALL_DEVICES_SPECIFIER); defaultdevice = qalcGetString(NULL, ALC_DEFAULT_DEVICE_SPECIFIER); #ifdef _WIN32 @@ -3209,13 +3223,36 @@ qboolean S_AL_Init( soundInterface_t *si ) } else { + char inputdevicenames[1024] = ""; + const char *inputdevicelist; + const char *defaultinputdevice; + int curlen; + + // get all available input devices + the default input device name. + inputdevicelist = qalcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER); + defaultinputdevice = qalcGetString(NULL, ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER); + + // dump a list of available devices to a cvar for the user to see. + while((curlen = strlen(inputdevicelist))) + { + Q_strcat(inputdevicenames, sizeof(inputdevicenames), inputdevicelist); + Q_strcat(inputdevicenames, sizeof(inputdevicenames), "\n"); + inputdevicelist += curlen + 1; + } + + s_alAvailableInputDevices = Cvar_Get("s_alAvailableInputDevices", inputdevicenames, CVAR_ROM | CVAR_NORESTART); + // !!! FIXME: 8000Hz is what Speex narrowband mode needs, but we // !!! FIXME: should probably open the capture device after // !!! FIXME: initializing Speex so we can change to wideband // !!! FIXME: if we like. - Com_Printf("OpenAL default capture device is '%s'\n", - qalcGetString(NULL, ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER)); - alCaptureDevice = qalcCaptureOpenDevice(NULL, 8000, AL_FORMAT_MONO16, 4096); + Com_Printf("OpenAL default capture device is '%s'\n", defaultinputdevice); + alCaptureDevice = qalcCaptureOpenDevice(inputdevice, 8000, AL_FORMAT_MONO16, 4096); + if( !alCaptureDevice && inputdevice ) + { + Com_Printf( "Failed to open OpenAL Input device '%s', trying default.\n", inputdevice ); + alCaptureDevice = qalcCaptureOpenDevice(NULL, 8000, AL_FORMAT_MONO16, 4096); + } Com_Printf( "OpenAL capture device %s.\n", (alCaptureDevice == NULL) ? "failed to open" : "opened"); } diff --git a/reaction/code/qcommon/cmd.c b/reaction/code/qcommon/cmd.c index 8103cd9c..15c2d891 100644 --- a/reaction/code/qcommon/cmd.c +++ b/reaction/code/qcommon/cmd.c @@ -588,6 +588,20 @@ void Cmd_TokenizeStringIgnoreQuotes( const char *text_in ) { Cmd_TokenizeString2( text_in, qtrue ); } +/* +============ +Cmd_FindCommand +============ +*/ +cmd_function_t *Cmd_FindCommand( const char *cmd_name ) +{ + cmd_function_t *cmd; + for( cmd = cmd_functions; cmd; cmd = cmd->next ) + if( !Q_stricmp( cmd_name, cmd->name ) ) + return cmd; + return NULL; +} + /* ============ Cmd_AddCommand @@ -597,14 +611,12 @@ void Cmd_AddCommand( const char *cmd_name, xcommand_t function ) { cmd_function_t *cmd; // fail if the command already exists - for ( cmd = cmd_functions ; cmd ; cmd=cmd->next ) { - if ( !strcmp( cmd_name, cmd->name ) ) { - // allow completion-only commands to be silently doubled - if ( function != NULL ) { - Com_Printf ("Cmd_AddCommand: %s already defined\n", cmd_name); - } - return; - } + if( Cmd_FindCommand( cmd_name ) ) + { + // allow completion-only commands to be silently doubled + if( function != NULL ) + Com_Printf( "Cmd_AddCommand: %s already defined\n", cmd_name ); + return; } // use a small malloc to avoid zone fragmentation @@ -658,6 +670,28 @@ void Cmd_RemoveCommand( const char *cmd_name ) { } } +/* +============ +Cmd_RemoveCommandSafe + +Only remove commands with no associated function +============ +*/ +void Cmd_RemoveCommandSafe( const char *cmd_name ) +{ + cmd_function_t *cmd = Cmd_FindCommand( cmd_name ); + + if( !cmd ) + return; + if( cmd->function ) + { + Com_Error( ERR_DROP, "Restricted source tried to remove " + "system command \"%s\"\n", cmd_name ); + return; + } + + Cmd_RemoveCommand( cmd_name ); +} /* ============ diff --git a/reaction/code/qcommon/cvar.c b/reaction/code/qcommon/cvar.c index bae4154e..fa4c00ce 100644 --- a/reaction/code/qcommon/cvar.c +++ b/reaction/code/qcommon/cvar.c @@ -363,6 +363,18 @@ cvar_t *Cvar_Get( const char *var_name, const char *var_value, int flags ) { flags &= ~CVAR_VM_CREATED; } + // Make sure servers cannot mark engine-added variables as SERVER_CREATED + if(var->flags & CVAR_SERVER_CREATED) + { + if(!(flags & CVAR_SERVER_CREATED)) + var->flags &= ~CVAR_SERVER_CREATED; + } + else + { + if(flags & CVAR_SERVER_CREATED) + flags &= ~CVAR_SERVER_CREATED; + } + var->flags |= flags; // only allow one non-empty reset string without a warning @@ -610,6 +622,28 @@ void Cvar_Set( const char *var_name, const char *value) { Cvar_Set2 (var_name, value, qtrue); } +/* +============ +Cvar_SetSafe +============ +*/ +void Cvar_SetSafe( const char *var_name, const char *value ) +{ + int flags = Cvar_Flags( var_name ); + + if( flags != CVAR_NONEXISTENT && flags & CVAR_PROTECTED ) + { + if( value ) + Com_Error( ERR_DROP, "Restricted source tried to set " + "\"%s\" to \"%s\"\n", var_name, value ); + else + Com_Error( ERR_DROP, "Restricted source tried to " + "modify \"%s\"\n", var_name ); + return; + } + Cvar_Set( var_name, value ); +} + /* ============ Cvar_SetLatched @@ -635,6 +669,21 @@ void Cvar_SetValue( const char *var_name, float value) { Cvar_Set (var_name, val); } +/* +============ +Cvar_SetValueSafe +============ +*/ +void Cvar_SetValueSafe( const char *var_name, float value ) +{ + char val[32]; + + if( Q_isintegral( value ) ) + Com_sprintf( val, sizeof(val), "%i", (int)value ); + else + Com_sprintf( val, sizeof(val), "%f", value ); + Cvar_SetSafe( var_name, val ); +} /* ============ diff --git a/reaction/code/qcommon/files.c b/reaction/code/qcommon/files.c index 7ee553f3..e362f2da 100644 --- a/reaction/code/qcommon/files.c +++ b/reaction/code/qcommon/files.c @@ -2855,13 +2855,13 @@ static void FS_Startup( const char *gameName ) fs_packFiles = 0; fs_debug = Cvar_Get( "fs_debug", "0", 0 ); - fs_basepath = Cvar_Get ("fs_basepath", Sys_DefaultInstallPath(), CVAR_INIT ); + fs_basepath = Cvar_Get ("fs_basepath", Sys_DefaultInstallPath(), CVAR_INIT|CVAR_PROTECTED ); fs_basegame = Cvar_Get ("fs_basegame", "", CVAR_INIT ); homePath = Sys_DefaultHomePath(); if (!homePath || !homePath[0]) { homePath = fs_basepath->string; } - fs_homepath = Cvar_Get ("fs_homepath", homePath, CVAR_INIT ); + fs_homepath = Cvar_Get ("fs_homepath", homePath, CVAR_INIT|CVAR_PROTECTED ); fs_gamedirvar = Cvar_Get ("fs_game", "", CVAR_INIT|CVAR_SYSTEMINFO ); // add search path elements in reverse priority order @@ -2871,7 +2871,7 @@ static void FS_Startup( const char *gameName ) // fs_homepath is somewhat particular to *nix systems, only add if relevant #ifdef MACOS_X - fs_apppath = Cvar_Get ("fs_apppath", Sys_DefaultAppPath(), CVAR_INIT ); + fs_apppath = Cvar_Get ("fs_apppath", Sys_DefaultAppPath(), CVAR_INIT|CVAR_PROTECTED ); // Make MacOSX also include the base path included with the .app bundle if (fs_apppath->string[0]) FS_AddGameDirectory(fs_apppath->string, gameName); diff --git a/reaction/code/qcommon/q_shared.h b/reaction/code/qcommon/q_shared.h index f32d30a0..0f537ea2 100644 --- a/reaction/code/qcommon/q_shared.h +++ b/reaction/code/qcommon/q_shared.h @@ -844,6 +844,7 @@ default values. #define CVAR_SERVER_CREATED 0x0800 // cvar was created by a server the client connected to. #define CVAR_VM_CREATED 0x1000 // cvar was created exclusively in one of the VMs. +#define CVAR_PROTECTED 0x2000 // prevent modifying this var from VMs or the server #define CVAR_NONEXISTENT 0xFFFFFFFF // Cvar doesn't exist. // nothing outside the Cvar_*() functions should modify these fields! diff --git a/reaction/code/qcommon/qcommon.h b/reaction/code/qcommon/qcommon.h index 20497b48..46c14387 100644 --- a/reaction/code/qcommon/qcommon.h +++ b/reaction/code/qcommon/qcommon.h @@ -430,6 +430,9 @@ void Cmd_RemoveCommand( const char *cmd_name ); typedef void (*completionFunc_t)( char *args, int argNum ); +// don't allow VMs to remove system commands +void Cmd_RemoveCommandSafe( const char *cmd_name ); + void Cmd_CommandCompletion( void(*callback)(const char *s) ); // callback with each valid string void Cmd_SetCommandCompletionFunc( const char *command, @@ -501,11 +504,15 @@ void Cvar_Update( vmCvar_t *vmCvar ); void Cvar_Set( const char *var_name, const char *value ); // will create the variable with no flags if it doesn't exist +void Cvar_SetSafe( const char *var_name, const char *value ); +// sometimes we set variables from an untrusted source: fail if flags & CVAR_PROTECTED + void Cvar_SetLatched( const char *var_name, const char *value); // don't set the cvar immediately void Cvar_SetValue( const char *var_name, float value ); -// expands value to a string and calls Cvar_Set +void Cvar_SetValueSafe( const char *var_name, float value ); +// expands value to a string and calls Cvar_Set/Cvar_SetSafe float Cvar_VariableValue( const char *var_name ); int Cvar_VariableIntegerValue( const char *var_name ); diff --git a/reaction/code/server/sv_game.c b/reaction/code/server/sv_game.c index 46653406..3f09033d 100644 --- a/reaction/code/server/sv_game.c +++ b/reaction/code/server/sv_game.c @@ -314,7 +314,7 @@ intptr_t SV_GameSystemCalls( intptr_t *args ) { Cvar_Update( VMA(1) ); return 0; case G_CVAR_SET: - Cvar_Set( (const char *)VMA(1), (const char *)VMA(2) ); + Cvar_SetSafe( (const char *)VMA(1), (const char *)VMA(2) ); return 0; case G_CVAR_VARIABLE_INTEGER_VALUE: return Cvar_VariableIntegerValue( (const char *)VMA(1) );