Exemplo n.º 1
0
static void G_Client_AssignTeamSkin( edict_t *ent, char *userinfo )
{
	char skin[MAX_QPATH], model[MAX_QPATH];
	const char *userskin, *usermodel;

	// index skin file
	userskin = GS_TeamSkinName( ent->s.team ); // is it a team skin?
	if( !userskin ) // NULL indicates *user defined*
	{   
		userskin = Info_ValueForKey( userinfo, "skin" );
		if( !userskin || !userskin[0] || !COM_ValidateRelativeFilename( userskin ) ||
			strchr( userskin, '/' ) || strstr( userskin, "invisibility" ) )
			userskin = NULL;
	}

	// index player model
	usermodel = Info_ValueForKey( userinfo, "model" );
	if( !usermodel || !usermodel[0] || !COM_ValidateRelativeFilename( usermodel ) || strchr( usermodel, '/' ) )
		usermodel = NULL;

	if( userskin && usermodel )
	{
		Q_snprintfz( model, sizeof( model ), "$models/players/%s", usermodel );
		Q_snprintfz( skin, sizeof( skin ), "models/players/%s/%s", usermodel, userskin );
	}
	else
	{
		Q_snprintfz( model, sizeof( model ), "$models/players/%s", DEFAULT_PLAYERMODEL );
		Q_snprintfz( skin, sizeof( skin ), "models/players/%s/%s", DEFAULT_PLAYERMODEL, DEFAULT_PLAYERSKIN );
	}

	if( !ent->deadflag )
		ent->s.modelindex = trap_ModelIndex( model );
	ent->s.skinnum = trap_SkinIndex( skin );
}
Exemplo n.º 2
0
/*
* TV_ReleaseModule
*/
void TV_ReleaseModule( const char *game )
{
	tv_module_t *iter;

	assert( game && strlen( game ) < MAX_CONFIGSTRING_CHARS );
	assert( COM_ValidateRelativeFilename( game ) && !strchr( game, '/' ) );

	// see if it's already loaded
	iter = modules;
	if( !iter )
		Com_Error( ERR_FATAL, "Attempting to release non-existing module" );
	assert( iter->count > 0 );

	iter->count--;

	if( !iter->count )
	{
		iter->export->Shutdown();
#ifndef TV_MODULE_HARD_LINKED
		Com_UnloadGameLibrary( &iter->handle );
#endif
		Mem_Free( iter );

		modules = NULL;
	}
Exemplo n.º 3
0
/*
* CL_CheckOrDownloadFile
* 
* Returns true if the file exists or couldn't send download request
* Files with .pk3 or .pak extension have to have gamedir attached
* Other files must not have gamedir
*/
qboolean CL_CheckOrDownloadFile( const char *filename )
{
	const char *ext;

	if( !cl_downloads->integer )
		return qtrue;

	if( !COM_ValidateRelativeFilename( filename ) )
		return qtrue;

	ext = COM_FileExtension( filename );
	if( !ext || !*ext )
		return qtrue;

	if( FS_CheckPakExtension( filename ) )
	{
		if( FS_FOpenBaseFile( filename, NULL, FS_READ ) != -1 )
			return qtrue;
	}
	else
	{
		if( FS_FOpenFile( filename, NULL, FS_READ ) != -1 )
			return qtrue;
	}

	if( !CL_DownloadRequest( filename, qtrue ) )
		return qtrue;

	cls.download.requestnext = qtrue; // call CL_RequestNextDownload when done

	return qfalse;
}
Exemplo n.º 4
0
/*
* ML_ValidateFilename
* Checks that the filename provided is valid
*/
bool ML_ValidateFilename( const char *filename )
{
	const char *extension;

	if( !filename || !*filename )
		return false;

	extension = COM_FileExtension( filename );
	if( !extension )
	{
		if( strlen( "maps/" ) + strlen( filename ) + strlen( ".bsp" ) >= MAX_CONFIGSTRING_CHARS )
			return false;
	}
	else
	{
		if( Q_stricmp( extension, ".bsp" ) )
			return false;
		if( strlen( "maps/" ) + strlen( filename ) >= MAX_CONFIGSTRING_CHARS )
			return false;
	}

	if( !COM_ValidateRelativeFilename( filename ) || strchr( filename, '/' ) )
		return false;

	return true;
}
Exemplo n.º 5
0
/*
* CG_SC_DemoGet
*/
static void CG_SC_DemoGet( void )
{
	if( cgs.demoPlaying )
	{
		// ignore download commands coming from demo files
		return;
	}

	if( !demo_requested )
	{
		CG_Printf( "Warning: demoget when not requested, ignored\n" );
		return;
	}

	demo_requested = qfalse;

	if( trap_Cmd_Argc() < 2 )
	{
		CG_Printf( "No such demo found\n" );
		return;
	}

	if( !COM_ValidateRelativeFilename( trap_Cmd_Argv( 1 ) ) )
	{
		CG_Printf( "Warning: demoget: Invalid filename, ignored\n" );
		return;
	}

	trap_DownloadRequest( trap_Cmd_Argv( 1 ), qfalse );
}
Exemplo n.º 6
0
/*
* Con_Dump_f
*
* Save the console contents out to a file
*/
static void Con_Dump_f( void )
{
    int file;
    size_t buffer_size;
    char *buffer;
    size_t name_size;
    char *name;
    const char *newline = "\r\n";

    if( !con_initialized )
        return;

    if( Cmd_Argc() != 2 )
    {
        Com_Printf( "usage: condump <filename>\n" );
        return;
    }

    name_size = sizeof( char ) * ( strlen( Cmd_Argv( 1 ) ) + strlen( ".txt" ) + 1 );
    name = Mem_TempMalloc( name_size );

    Q_strncpyz( name, Cmd_Argv( 1 ), name_size );
    COM_DefaultExtension( name, ".txt", name_size );
    COM_SanitizeFilePath( name );

    if( !COM_ValidateRelativeFilename( name ) )
    {
        Com_Printf( "Invalid filename.\n" );
        Mem_TempFree( name );
        return;
    }

    if( FS_FOpenFile( name, &file, FS_WRITE ) == -1 )
    {
        Com_Printf( "Couldn't open: %s\n", name );
        Mem_TempFree( name );
        return;
    }

    buffer_size = Con_BufferText( NULL, newline ) + 1;
    buffer = Mem_TempMalloc( buffer_size );

    Con_BufferText( buffer, newline );

    FS_Write( buffer, buffer_size - 1, file );

    FS_FCloseFile( file );

    Mem_TempFree( buffer );

    Com_Printf( "Dumped console text: %s\n", name );
    Mem_TempFree( name );
}
Exemplo n.º 7
0
/*
* CL_DownloadRequest
* 
* Request file download
* return qfalse if couldn't request it for some reason
* Files with .pk3 or .pak extension have to have gamedir attached
* Other files must not have gamedir
*/
qboolean CL_DownloadRequest( const char *filename, qboolean requestpak )
{
	if( cls.download.requestname )
	{
		Com_Printf( "Can't download: %s. Download already in progress.\n", filename );
		return qfalse;
	}

	if( !COM_ValidateRelativeFilename( filename ) )
	{
		Com_Printf( "Can't download: %s. Invalid filename.\n", filename );
		return qfalse;
	}

	if( FS_CheckPakExtension( filename ) )
	{
		if( FS_FOpenBaseFile( filename, NULL, FS_READ ) != -1 )
		{
			Com_Printf( "Can't download: %s. File already exists.\n", filename );
			return qfalse;
		}

		if( !Q_strnicmp( COM_FileBase( filename ), "modules", strlen( "modules" ) ) )
		{
			if( !CL_CanDownloadModules() )
				return qfalse;
		}
	}
	else
	{
		if( FS_FOpenFile( filename, NULL, FS_READ ) != -1 )
		{
			Com_Printf( "Can't download: %s. File already exists.\n", filename );
			return qfalse;
		}
	}

	if( cls.socket->type == SOCKET_LOOPBACK )
	{
		Com_DPrintf( "Can't download: %s. Loopback server.\n", filename );
		return qfalse;
	}

	Com_Printf( "Asking to download: %s\n", filename );

	cls.download.requestpak = requestpak;
	cls.download.requestname = Mem_ZoneMalloc( sizeof( char ) * ( strlen( filename ) + 1 ) );
	Q_strncpyz( cls.download.requestname, filename, sizeof( char ) * ( strlen( filename ) + 1 ) );
	cls.download.timeout = Sys_Milliseconds() + 5000;
	CL_AddReliableCommand( va( "download %i \"%s\"", requestpak, filename ) );

	return qtrue;
}
Exemplo n.º 8
0
/*
* R_ScreenShot_f
*/
void R_ScreenShot_f( void ) {
	int i;
	const char *name;
	const char *mediadir;
	size_t path_size;
	char *path;
	char timestamp_str[MAX_QPATH];
	struct tm newtime;

	R_Localtime( time( NULL ), &newtime );

	name = ri.Cmd_Argv( 1 );

	mediadir = ri.FS_MediaDirectory( FS_MEDIA_IMAGES );
	if( mediadir ) {
		path_size = strlen( mediadir ) + 1 /* '/' */ + strlen( glConfig.applicationName ) + 1 /* '/' */ + 1;
		path = alloca( path_size );
		Q_snprintfz( path, path_size, "%s/%s/", mediadir, glConfig.applicationName );
	} else {
		path_size = strlen( ri.FS_WriteDirectory() ) + 1 /* '/' */ + strlen( ri.FS_GameDirectory() ) + strlen( "/screenshots/" ) + 1;
		path = alloca( path_size );
		Q_snprintfz( path, path_size, "%s/%s/screenshots/", ri.FS_WriteDirectory(), ri.FS_GameDirectory() );
	}

	// validate timestamp string
	for( i = 0; i < 2; i++ ) {
		strftime( timestamp_str, sizeof( timestamp_str ), r_screenshot_fmtstr->string, &newtime );
		if( !COM_ValidateRelativeFilename( timestamp_str ) ) {
			ri.Cvar_ForceSet( r_screenshot_fmtstr->name, r_screenshot_fmtstr->dvalue );
		} else {
			break;
		}
	}

	// hm... shouldn't really happen, but check anyway
	if( i == 2 ) {
		ri.Cvar_ForceSet( r_screenshot_fmtstr->name, glConfig.screenshotPrefix );
	}

	RF_ScreenShot( path, name, r_screenshot_fmtstr->string,
				   ri.Cmd_Argc() >= 3 && !Q_stricmp( ri.Cmd_Argv( 2 ), "silent" ) ? true : false );
}
Exemplo n.º 9
0
/*
* TV_Upstream_StartDemo
*/
void TV_Upstream_StartDemo( upstream_t *upstream, const char *demoname, qboolean randomize )
{
	char *name, *filepath;
	int tempdemofilehandle, tempdemofilelen;

	name = filepath = NULL;
	tempdemofilehandle = 0;
	tempdemofilelen = -1;

	TV_Upstream_NextDemo( demoname, upstream->demo.filename, randomize, &name, &filepath );

	if( filepath )
	{
		if( COM_ValidateRelativeFilename( filepath ) )
			tempdemofilelen = FS_FOpenFile( filepath, &tempdemofilehandle, FS_READ|SNAP_DEMO_GZ );
	}

	TV_Upstream_StopDemo( upstream );

	if( name )
		Com_Printf( "Starting demo from %s\n", filepath );

	upstream->demo.playing = qtrue;
	upstream->demo.filename = name ? TV_Upstream_CopyString( upstream, name ) : NULL;
	upstream->demo.filehandle = tempdemofilehandle;
	upstream->demo.filelen = tempdemofilelen;
	upstream->demo.random = randomize;
	upstream->state = CA_HANDSHAKE;
	upstream->reliable = qfalse;

	upstream->servername = TV_Upstream_CopyString( upstream, demoname );	// can be demo filename/pattern or demolist filename
	upstream->rejected = qfalse;
	upstream->lastPacketReceivedTime = tvs.realtime;	// reset the timeout limit
	upstream->multiview = qfalse;
	upstream->precacheDone = qfalse;

	if( name )
		Mem_TempFree( name );
	if( filepath )
		Mem_TempFree( filepath );
}
Exemplo n.º 10
0
/*
* ML_ValidateFilename
* Checks that the filename provided is valid
*/
qboolean ML_ValidateFilename( const char *filename )
{
    if( !filename || !*filename )
        return qfalse;

    if( !COM_FileExtension( filename ) )
    {
        if( strlen( "maps/" ) + strlen( filename ) + strlen( ".bsp" ) >= MAX_CONFIGSTRING_CHARS )
            return qfalse;
    }
    else
    {
        if( Q_stricmp( COM_FileExtension( filename ), ".bsp" ) )
            return qfalse;
        if( strlen( "maps/" ) + strlen( filename ) >= MAX_CONFIGSTRING_CHARS )
            return qfalse;
    }

    if( !COM_ValidateRelativeFilename( filename ) || strchr( filename, '/' ) )
        return qfalse;

    return qtrue;
}
Exemplo n.º 11
0
/*
* TV_Upstream_StartDemoRecord
*/
void TV_Upstream_StartDemoRecord( upstream_t *upstream, const char *demoname, qboolean silent )
{
	char *servername, *temp;
	size_t name_size;

	assert( upstream );
	assert( demoname );

	if( upstream->demo.playing )
	{
		if( !silent )
			Com_Printf( "You can't record from another demo.\n" );
		return;
	}

	if( upstream->demo.recording )
	{
		if( !silent )
			Com_Printf( "Already recording.\n" );
		return;
	}

	// strip the port number from servername
	servername = TempCopyString( upstream->servername );
	temp = strstr( servername, ":" );
	if( temp ) *temp = '\0';

	// store the name
	name_size = sizeof( char ) * ( strlen( "demos/tvserver" ) + 1 + strlen( servername ) + 1 + strlen( demoname ) + strlen( APP_DEMO_EXTENSION_STR ) + 1 );
	upstream->demo.filename = Mem_ZoneMalloc( name_size );

	Q_snprintfz( upstream->demo.filename, name_size, "demos/tvserver/%s/%s", servername, demoname );
	COM_SanitizeFilePath( upstream->demo.filename );
	COM_DefaultExtension( upstream->demo.filename, APP_DEMO_EXTENSION_STR, name_size );

	Mem_TempFree( servername );

	if( !COM_ValidateRelativeFilename( upstream->demo.filename ) )
	{
		if( !silent )
			Com_Printf( "Invalid filename.\n" );
		Mem_ZoneFree( upstream->demo.filename );
		return;
	}

	// temp name
	name_size = sizeof( char ) * ( strlen( upstream->demo.filename ) + strlen( ".rec" ) + 1 );
	upstream->demo.tempname = Mem_ZoneMalloc( name_size );
	Q_snprintfz( upstream->demo.tempname, name_size, "%s.rec", upstream->demo.filename );

	// open the demo file
	if( FS_FOpenFile( upstream->demo.tempname, &upstream->demo.filehandle, FS_WRITE|SNAP_DEMO_GZ ) == -1 )
	{
		Com_Printf( "Error: Couldn't create the demo file.\n" );

		Mem_ZoneFree( upstream->demo.tempname );
		upstream->demo.tempname = NULL;
		Mem_ZoneFree( upstream->demo.filename );
		upstream->demo.filename = NULL;
		return;
	}

	if( !silent )
		Com_Printf( "Recording demo: %s\n", upstream->demo.filename );

	upstream->demo.recording = qtrue;
	upstream->demo.localtime = 0;
	upstream->demo.basetime = upstream->demo.duration = 0;

	// don't start saving messages until a non-delta compressed message is received
	TV_Upstream_AddReliableCommand( upstream, "nodelta" ); // request non delta compressed frame from server
	upstream->demo.waiting = qtrue;

	// the rest of the demo file will be individual frames
}
Exemplo n.º 12
0
/*
* R_ScreenShot_f
*/
void R_ScreenShot_f( void )
{
	const char *name;
	const char *extension;
	const char *mediadir;
	size_t path_size;
	char *path;
	char *checkname = NULL;
	size_t checkname_size = 0;
	int quality;

	if( !R_ScreenEnabled() )
		return;

	name = ri.Cmd_Argv( 1 );
	if( r_screenshot_jpeg->integer ) {
		extension = ".jpg";
		quality = r_screenshot_jpeg_quality->integer;
	}
	else {
		extension = ".tga";
		quality = 100;
	}

	mediadir = ri.FS_MediaDirectory( FS_MEDIA_IMAGES );
	if( mediadir )
	{
		path_size = strlen( mediadir ) + 1 + strlen( glConfig.applicationName ) + sizeof( " Screenshots/" );
		path = alloca( path_size );
		Q_snprintfz( path, path_size, "%s/%s Screenshots/", mediadir, glConfig.applicationName );
	}
	else
	{
		path_size = strlen( ri.FS_WriteDirectory() ) + 1 + strlen( ri.FS_GameDirectory() ) + sizeof( "/screenshots/" );
		path = alloca( path_size );
		Q_snprintfz( path, path_size, "%s/%s/screenshots/", ri.FS_WriteDirectory(), ri.FS_GameDirectory() );
	}

	if( name && name[0] && Q_stricmp(name, "*") )
	{
		if( !COM_ValidateRelativeFilename( name ) )
		{
			Com_Printf( "Invalid filename\n" );
			return;
		}

		checkname_size = ( path_size - 1 ) + strlen( name ) + strlen( extension ) + 1;
		checkname = alloca( checkname_size );
		Q_snprintfz( checkname, checkname_size, "%s%s", path, name );
		COM_DefaultExtension( checkname, extension, checkname_size );
	}

	//
	// find a file name to save it to
	//
	if( !checkname )
	{
		int i;
		const int maxFiles = 100000;
		static int lastIndex = 0;
		bool addIndex = false;
		time_t timestamp;
		char timestamp_str[MAX_QPATH];
		struct tm *timestampptr;

		timestamp = time( NULL );
		timestampptr = localtime( &timestamp );

		// validate timestamp string
		for( i = 0; i < 2; i++ )
		{
			strftime( timestamp_str, sizeof( timestamp_str ), r_screenshot_fmtstr->string, timestampptr );
			if( !COM_ValidateRelativeFilename( timestamp_str ) )
				ri.Cvar_ForceSet( r_screenshot_fmtstr->name, r_screenshot_fmtstr->dvalue );
			else
				break;
		}

		// hm... shouldn't really happen, but check anyway
		if( i == 2 )
		{
			Q_strncpyz( timestamp_str, glConfig.screenshotPrefix, sizeof( timestamp_str ) );
			ri.Cvar_ForceSet( r_screenshot_fmtstr->name, glConfig.screenshotPrefix );
		}

		checkname_size = ( path_size - 1 ) + strlen( timestamp_str ) + 5 + 1 + strlen( extension );
		checkname = alloca( checkname_size );

		// if the string format is a constant or file already exists then iterate
		if( !*timestamp_str || !strcmp( timestamp_str, r_screenshot_fmtstr->string ) )
		{
			addIndex = true;

			// force a rescan in case some vars have changed..
			if( r_screenshot_fmtstr->modified )
			{
				lastIndex = 0;
				r_screenshot_fmtstr->modified = true;
			}
			if( r_screenshot_jpeg->modified )
			{
				lastIndex = 0;
				r_screenshot_jpeg->modified = false;
			}
		}
		else
		{
			Q_snprintfz( checkname, checkname_size, "%s%s%s", path, timestamp_str, extension );
			if( ri.FS_FOpenAbsoluteFile( checkname, NULL, FS_READ ) != -1 )
			{
				lastIndex = 0;
				addIndex = true;
			}
		}

		for( ; addIndex && lastIndex < maxFiles; lastIndex++ )
		{
			Q_snprintfz( checkname, checkname_size, "%s%s%05i%s", path, timestamp_str, lastIndex, extension );
			if( ri.FS_FOpenAbsoluteFile( checkname, NULL, FS_READ ) == -1 )
				break; // file doesn't exist
		}

		if( lastIndex == maxFiles )
		{
			Com_Printf( "Couldn't create a file\n" );
			return;
		}

		lastIndex++;
	}

	R_ScreenShot( checkname, 
		0, 0, glConfig.width, glConfig.height, quality, 
		false, false, false, 
		ri.Cmd_Argc() >= 3 && !Q_stricmp( ri.Cmd_Argv( 2 ), "silent" ) ? true : false );

	ri.FS_AddFileToMedia( checkname );
}
Exemplo n.º 13
0
/*
* SV_BeginDownload_f
* Responds to reliable download packet with reliable initdownload packet
*/
static void SV_BeginDownload_f( client_t *client )
{
	const char *requestname;
	const char *uploadname;
	size_t alloc_size;
	unsigned checksum;
	char *url;
	const char *errormsg = NULL;
	qboolean allow, requestpak;
	qboolean local_http = SV_Web_Running() && sv_uploads_http->integer != 0;

	requestpak = ( atoi( Cmd_Argv( 1 ) ) == 1 );
	requestname = Cmd_Argv( 2 );

	if( !requestname[0] || !COM_ValidateRelativeFilename( requestname ) )
	{
		SV_DenyDownload( client, "Invalid filename" );
		return;
	}

	if( !SV_FilenameForDownloadRequest( requestname, requestpak, &uploadname, &errormsg ) ) {
		assert( errormsg != NULL );
		SV_DenyDownload( client, errormsg );
		return;
	}

	if( FS_CheckPakExtension( uploadname ) )
	{
		allow = qfalse;

		// allow downloading paks from the pure list, if not spawned
		if( client->state < CS_SPAWNED )
		{
			purelist_t *purefile;

			purefile = svs.purelist;
			while( purefile )
			{
				if( !strcmp( uploadname, purefile->filename ) )
				{
					allow = qtrue;
					break;
				}
				purefile = purefile->next;
			}
		}

		// game module has a change to allow extra downloads
		if( !allow && !SV_GameAllowDownload( client, requestname, uploadname ) )
		{
			SV_DenyDownload( client, "Downloading of this file is not allowed" );
			return;
		}
	}
	else
	{
		if( !SV_GameAllowDownload( client, requestname, uploadname ) )
		{
			SV_DenyDownload( client, "Downloading of this file is not allowed" );
			return;
		}
	}

	// we will just overwrite old download, if any
	if( client->download.name )
	{
		if( client->download.data )
		{
			FS_FreeBaseFile( client->download.data );
			client->download.data = NULL;
		}

		Mem_ZoneFree( client->download.name );
		client->download.name = NULL;

		client->download.size = 0;
		client->download.timeout = 0;
	}

	client->download.size = FS_LoadBaseFile( uploadname, NULL, NULL, 0 );
	if( client->download.size == -1 )
	{
		Com_Printf( "Error getting size of %s for uploading\n", uploadname );
		client->download.size = 0;
		SV_DenyDownload( client, "Error getting file size" );
		return;
	}

	checksum = FS_ChecksumBaseFile( uploadname );
	client->download.timeout = svs.realtime + 1000 * 60 * 60; // this is web download timeout

	alloc_size = sizeof( char ) * ( strlen( uploadname ) + 1 );
	client->download.name = Mem_ZoneMalloc( alloc_size );
	Q_strncpyz( client->download.name, uploadname, alloc_size );

	Com_Printf( "Offering %s to %s\n", client->download.name, client->name );

	if( FS_CheckPakExtension( uploadname ) && ( local_http || sv_uploads_baseurl->string[0] != 0 ) )
	{
		// .pk3 and .pak download from the web
		if( local_http )
		{
			url = TempCopyString( va( "files/%s", uploadname ) );
		}
		else
		{
			alloc_size = sizeof( char ) * ( strlen( sv_uploads_baseurl->string ) + 1 );
			url = Mem_TempMalloc( alloc_size );
			Q_snprintfz( url, alloc_size, "%s/", sv_uploads_baseurl->string );
		}
	}
	else if( SV_IsDemoDownloadRequest( requestname ) && ( local_http || sv_uploads_demos_baseurl->string[0] != 0 ) )
	{
		// demo file download from the web
		if( local_http )
		{
			url = TempCopyString( va( "files/%s", uploadname ) );
		}
		else
		{
			alloc_size = sizeof( char ) * ( strlen( sv_uploads_demos_baseurl->string ) + 1 );
			url = Mem_TempMalloc( alloc_size );
			Q_snprintfz( url, alloc_size, "%s/", sv_uploads_demos_baseurl->string );
		}
	}
	else
	{
		url = NULL;
	}

	// start the download
	SV_InitClientMessage( client, &tmpMessage, NULL, 0 );
	SV_SendServerCommand( client, "initdownload \"%s\" %i %u %i \"%s\"", client->download.name,
		client->download.size, checksum, local_http ? 1 : 0, ( url ? url : "" ) );
	SV_AddReliableCommandsToMessage( client, &tmpMessage );
	SV_SendMessageToClient( client, &tmpMessage );

	if( url )
	{
		Mem_TempFree( url );
		url = NULL;
	}
}
Exemplo n.º 14
0
/*
* CL_ParseServerData
*/
static void CL_ParseServerData( msg_t *msg )
{
	const char *str, *gamedir;
	int i, sv_bitflags, numpure;
	int http_portnum;

	Com_DPrintf( "Serverdata packet received.\n" );

	// wipe the client_state_t struct

	CL_ClearState();
	CL_SetClientState( CA_CONNECTED );

	// parse protocol version number
	i = MSG_ReadLong( msg );

	if( i != APP_PROTOCOL_VERSION )
		Com_Error( ERR_DROP, "Server returned version %i, not %i", i, APP_PROTOCOL_VERSION );

	cl.servercount = MSG_ReadLong( msg );
	cl.snapFrameTime = (unsigned int)MSG_ReadShort( msg );

	// set extrapolation time to half snapshot time
	Cvar_ForceSet( "cl_extrapolationTime", va( "%i", (unsigned int)( cl.snapFrameTime * 0.5 ) ) );
	cl_extrapolationTime->modified = qfalse;

	// base game directory
	str = MSG_ReadString( msg );
	if( !str || !str[0] )
		Com_Error( ERR_DROP, "Server sent an empty base game directory" );
	if( !COM_ValidateRelativeFilename( str ) || strchr( str, '/' ) )
		Com_Error( ERR_DROP, "Server sent an invalid base game directory: %s", str );
	if( strcmp( FS_BaseGameDirectory(), str ) )
	{
		Com_Error( ERR_DROP, "Server has different base game directory (%s) than the client (%s)", str,
			FS_BaseGameDirectory() );
	}

	// game directory
	str = MSG_ReadString( msg );
	if( !str || !str[0] )
		Com_Error( ERR_DROP, "Server sent an empty game directory" );
	if( !COM_ValidateRelativeFilename( str ) || strchr( str, '/' ) )
		Com_Error( ERR_DROP, "Server sent an invalid game directory: %s", str );
	gamedir = FS_GameDirectory();
	if( strcmp( str, gamedir ) )
	{
		// shutdown the cgame module first in case it is running for whatever reason
		// (happens on wswtv in lobby), otherwise precaches that are going to follow
		// will probably f**k up (like models trying to load before the world model)
		CL_GameModule_Shutdown();

		if( !FS_SetGameDirectory( str, qtrue ) )
			Com_Error( ERR_DROP, "Failed to load game directory set by server: %s", str );
		ML_Restart( qtrue );
	}

	// parse player entity number
	cl.playernum = MSG_ReadShort( msg );

	// get the full level name
	Q_strncpyz( cl.servermessage, MSG_ReadString( msg ), sizeof( cl.servermessage ) );

	sv_bitflags = MSG_ReadByte( msg );

	if( cls.demo.playing )
	{
		cls.reliable = ( sv_bitflags & SV_BITFLAGS_RELIABLE );
	}
	else
	{
		if( cls.reliable != ( ( sv_bitflags & SV_BITFLAGS_RELIABLE ) != 0 ) )
			Com_Error( ERR_DROP, "Server and client disagree about connection reliability" );
	}

	// builting HTTP server port
	if( cls.httpbaseurl ) {
		Mem_Free( cls.httpbaseurl );
		cls.httpbaseurl = NULL;
	}

	if( ( sv_bitflags & SV_BITFLAGS_HTTP ) != 0 ) {
		if( ( sv_bitflags & SV_BITFLAGS_HTTP_BASEURL ) != 0 ) {
			// read base upstream url
			cls.httpbaseurl = ZoneCopyString( MSG_ReadString( msg ) );
		}
		else {
			http_portnum = MSG_ReadShort( msg ) & 0xffff;
			cls.httpaddress = cls.serveraddress;
			if( cls.httpaddress.type == NA_IP6 ) {
				cls.httpaddress.address.ipv6.port = BigShort( http_portnum );
			} else {
				cls.httpaddress.address.ipv4.port = BigShort( http_portnum );
			}
			if( http_portnum ) {
				if( cls.httpaddress.type == NA_LOOPBACK ) {
					cls.httpbaseurl = ZoneCopyString( va( "http://localhost:%hu/", http_portnum ) );
				}
				else {
					cls.httpbaseurl = ZoneCopyString( va( "http://%s/", NET_AddressToString( &cls.httpaddress ) ) );
				}
			}
		}
	}

	// pure list

	// clean old, if necessary
	Com_FreePureList( &cls.purelist );

	// add new
	numpure = MSG_ReadShort( msg );
	while( numpure > 0 )
	{
		const char *pakname = MSG_ReadString( msg );
		const unsigned checksum = MSG_ReadLong( msg );

		Com_AddPakToPureList( &cls.purelist, pakname, checksum, NULL );

		numpure--;
	}

	//assert( numpure == 0 );

	// get the configstrings request
	CL_AddReliableCommand( va( "configstrings %i 0", cl.servercount ) );

	cls.sv_pure = ( sv_bitflags & SV_BITFLAGS_PURE ) != 0;
	cls.sv_tv = ( sv_bitflags & SV_BITFLAGS_TVSERVER ) != 0;

#ifdef PURE_CHEAT
	cls.sv_pure = qfalse;
#endif

	// separate the printfs so the server message can have a color
	Com_Printf( S_COLOR_WHITE "\n" "=====================================\n" );
	Com_Printf( S_COLOR_WHITE "%s\n\n", cl.servermessage );
}
Exemplo n.º 15
0
/*
* SV_WebDownload
*/
static qboolean SV_WebDownload( const char *baseUrl, const char *filepath, qboolean overwrite, qboolean silent )
{
	qboolean success;
	int alloc_size;
	char *temppath, *writepath, *url;

	if( developer->integer )
		silent = qfalse;

	if( !baseUrl || !baseUrl[0] || !filepath )
		return qfalse;

	if( !strrchr( baseUrl, '/' ) )
	{
		if( !silent )
			Com_Printf( "SV_WebDownload: Invalid URL\n" );
		return qfalse;
	}

	if( filepath[0] == '/' ) // filepath should never begin with a slash
		filepath++;

	if( !COM_ValidateRelativeFilename( filepath ) )
	{
		if( !silent )
			Com_Printf( "SV_WebDownload: Invalid filename\n" );
		return qfalse;
	}

	if( !COM_FileExtension( filepath ) )
	{
		if( !silent )
			Com_Printf( "SV_WebDownload: no file extension\n" );
		return qfalse;
	}

	// full url (baseurl + path)
	alloc_size = strlen( baseUrl ) + 1 + strlen( filepath ) + 1;
	url = Mem_TempMalloc( alloc_size );
	if( baseUrl[ strlen( baseUrl ) - 1 ] == '/' ) // url includes last slash
		Q_snprintfz( url, alloc_size, "%s%s", baseUrl, filepath );
	else
		Q_snprintfz( url, alloc_size, "%s/%s", baseUrl, filepath );

	// add .tmp (relative + .tmp)
	alloc_size = strlen( filepath ) + strlen( ".tmp" ) + 1;
	temppath = Mem_TempMalloc( alloc_size );
	Q_snprintfz( temppath, alloc_size, "%s.tmp", filepath );

	// full write path for curl
	alloc_size = strlen( FS_WriteDirectory() ) + 1 + strlen( temppath ) + 1;
	writepath = Mem_TempMalloc( alloc_size );
	Q_snprintfz( writepath, alloc_size, "%s/%s", FS_WriteDirectory(), temppath );

	webDownloadPercentPrint = 0;
	webDownloadPercentStarted = qfalse;

	success = Web_Get( url, NULL, writepath, qtrue, 60 * 30, 60, SV_WebDownloadProgress, qfalse );

	if( webDownloadPercentStarted )
		Com_Printf( "\n" );

	if( !success )
	{
		if( !silent )
			Com_Printf( "Failed to download remote file.\n" );
		goto failed;
	}

	// rename the downloaded file
	if( !FS_MoveBaseFile( temppath, filepath ) )
	{
		if( !overwrite )
		{
			if( !silent )
				Com_Printf( "Failed to rename temporary file.\n" );
			goto failed;
		}

		// check if it failed because there already exists a file with the same name
		// and in this case remove this file
		if( FS_FOpenBaseFile( filepath, NULL, FS_READ ) != -1 )
		{
			char *backfile;

			alloc_size = strlen( filepath ) + strlen( ".bak" ) + 1;
			backfile = Mem_TempMalloc( alloc_size );
			Q_snprintfz( backfile, alloc_size, "%s.bak", filepath );

			// if there is already a .bak file, destroy it
			if( FS_FOpenBaseFile( backfile, NULL, FS_READ ) != -1 )
				FS_RemoveBaseFile( backfile );

			// move the current file into .bak file
			if( !FS_MoveBaseFile( filepath, backfile ) )
			{
				Mem_TempFree( backfile );
				if( !silent )
					Com_Printf( "Failed to backup destination file.\n" );
				goto failed;
			}

			// now try renaming the downloaded file again
			if( !FS_MoveBaseFile( temppath, filepath ) )
			{
				// didn't work, so restore the backup file
				if( FS_MoveBaseFile( backfile, filepath ) )
				{
					if( !silent )
						Com_Printf( "Failed to rename temporary file, restoring from backup.\n" );
				}
				else
				{
					if( !silent )
						Com_Printf( "Failed to rename temporary file and restore from backup.\n" );
				}

				Mem_TempFree( backfile );
				goto failed;
			}

			Mem_TempFree( backfile );
		}
	}

	Mem_TempFree( temppath );
	Mem_TempFree( writepath );
	Mem_TempFree( url );

	return qtrue;

failed:
	if( !silent )
		Com_Printf( "Removing temporary file: %s\n", writepath );
	FS_RemoveAbsoluteFile( writepath );
	Mem_TempFree( temppath );
	Mem_TempFree( writepath );
	Mem_TempFree( url );

	return qfalse;
}
Exemplo n.º 16
0
/*
* CL_DownloadRequest
* 
* Request file download
* return false if couldn't request it for some reason
* Files with .pk3 or .pak extension have to have gamedir attached
* Other files must not have gamedir
*/
bool CL_DownloadRequest( const char *filename, bool requestpak )
{
	if( cls.download.requestname )
	{
		Com_Printf( "Can't download: %s. Download already in progress.\n", filename );
		return false;
	}

	if( !COM_ValidateRelativeFilename( filename ) )
	{
		Com_Printf( "Can't download: %s. Invalid filename.\n", filename );
		return false;
	}

	if( FS_CheckPakExtension( filename ) )
	{
		if( FS_PakFileExists( filename ) )
		{
			Com_Printf( "Can't download: %s. File already exists.\n", filename );
			return false;
		}

		if( !Q_strnicmp( COM_FileBase( filename ), "modules", strlen( "modules" ) ) )
		{
			if( !CL_CanDownloadModules() )
				return false;
		}
	}
	else
	{
		if( FS_FOpenFile( filename, NULL, FS_READ ) != -1 )
		{
			Com_Printf( "Can't download: %s. File already exists.\n", filename );
			return false;
		}

		if( !requestpak ) {
			const char *extension;

			// only allow demo downloads
			extension = COM_FileExtension( filename );
			if( !extension || Q_stricmp( extension, APP_DEMO_EXTENSION_STR ) )
			{
				Com_Printf( "Can't download, got arbitrary file type: %s\n", filename );
				return false;
			}
		}
	}

	if( cls.socket->type == SOCKET_LOOPBACK )
	{
		Com_DPrintf( "Can't download: %s. Loopback server.\n", filename );
		return false;
	}

	Com_Printf( "Asking to download: %s\n", filename );

	cls.download.requestpak = requestpak;
	cls.download.requestname = Mem_ZoneMalloc( sizeof( char ) * ( strlen( filename ) + 1 ) );
	Q_strncpyz( cls.download.requestname, filename, sizeof( char ) * ( strlen( filename ) + 1 ) );
	cls.download.timeout = Sys_Milliseconds() + 5000;
	CL_AddReliableCommand( va( "download %i \"%s\"", requestpak, filename ) );

	return true;
}
Exemplo n.º 17
0
/*
* R_TakeScreenShot
*/
void R_TakeScreenShot( const char *path, const char *name, const char *fmtString, int x, int y, int w, int h, bool silent, bool media ) {
	const char *extension;
	size_t path_size = strlen( path ) + 1;
	char *checkname = NULL;
	size_t checkname_size = 0;
	int quality;

	if( !R_IsRenderingToScreen() ) {
		return;
	}

	if( r_screenshot_jpeg->integer ) {
		extension = ".jpg";
		quality = r_screenshot_jpeg_quality->integer;
	} else {
		extension = ".tga";
		quality = 100;
	}

	if( name && name[0] && Q_stricmp( name, "*" ) ) {
		if( !COM_ValidateRelativeFilename( name ) ) {
			Com_Printf( "Invalid filename\n" );
			return;
		}

		checkname_size = ( path_size - 1 ) + strlen( name ) + strlen( extension ) + 1;
		checkname = alloca( checkname_size );
		Q_snprintfz( checkname, checkname_size, "%s%s", path, name );
		COM_DefaultExtension( checkname, extension, checkname_size );
	}

	//
	// find a file name to save it to
	//
	if( !checkname ) {
		const int maxFiles = 100000;
		static int lastIndex = 0;
		bool addIndex = false;
		char timestampString[MAX_QPATH];
		static char lastFmtString[MAX_QPATH];
		struct tm newtime;

		R_Localtime( time( NULL ), &newtime );
		strftime( timestampString, sizeof( timestampString ), fmtString, &newtime );

		checkname_size = ( path_size - 1 ) + strlen( timestampString ) + 5 + 1 + strlen( extension );
		checkname = alloca( checkname_size );

		// if the string format is a constant or file already exists then iterate
		if( !*fmtString || !strcmp( timestampString, fmtString ) ) {
			addIndex = true;

			// force a rescan in case some vars have changed..
			if( strcmp( lastFmtString, fmtString ) ) {
				lastIndex = 0;
				Q_strncpyz( lastFmtString, fmtString, sizeof( lastFmtString ) );
				r_screenshot_fmtstr->modified = false;
			}
			if( r_screenshot_jpeg->modified ) {
				lastIndex = 0;
				r_screenshot_jpeg->modified = false;
			}
		} else {
			Q_snprintfz( checkname, checkname_size, "%s%s%s", path, timestampString, extension );
			if( ri.FS_FOpenAbsoluteFile( checkname, NULL, FS_READ ) != -1 ) {
				lastIndex = 0;
				addIndex = true;
			}
		}

		for( ; addIndex && lastIndex < maxFiles; lastIndex++ ) {
			Q_snprintfz( checkname, checkname_size, "%s%s%05i%s", path, timestampString, lastIndex, extension );
			if( ri.FS_FOpenAbsoluteFile( checkname, NULL, FS_READ ) == -1 ) {
				break; // file doesn't exist
			}
		}

		if( lastIndex == maxFiles ) {
			Com_Printf( "Couldn't create a file\n" );
			return;
		}

		lastIndex++;
	}

	R_ScreenShot( checkname,
				  x, y, w, h, quality,
				  false, false, false, silent );

	if( media ) {
		ri.FS_AddFileToMedia( checkname );
	}
}
Exemplo n.º 18
0
/*
* SV_Demo_Start_f
* 
* Begins server demo recording.
*/
void SV_Demo_Start_f( void )
{
	int demofilename_size, i;

	if( Cmd_Argc() < 2 )
	{
		Com_Printf( "Usage: serverrecord <demoname>\n" );
		return;
	}

	if( svs.demo.file )
	{
		Com_Printf( "Already recording\n" );
		return;
	}

	if( sv.state != ss_game )
	{
		Com_Printf( "Must be in a level to record\n" );
		return;
	}

	for( i = 0; i < sv_maxclients->integer; i++ )
	{
		if( svs.clients[i].state >= CS_SPAWNED && svs.clients[i].edict &&
			!( svs.clients[i].edict->r.svflags & SVF_NOCLIENT ) )
			break;
	}
	if( i == sv_maxclients->integer )
	{
		Com_Printf( "No players in game, can't record a demo\n" );
		return;
	}

	//
	// open the demo file
	//

	// real name
	demofilename_size =
		sizeof( char ) * ( strlen( SV_DEMO_DIR ) + 1 + strlen( Cmd_Args() ) + strlen( APP_DEMO_EXTENSION_STR ) + 1 );
	svs.demo.filename = Mem_ZoneMalloc( demofilename_size );

	Q_snprintfz( svs.demo.filename, demofilename_size, "%s/%s", SV_DEMO_DIR, Cmd_Args() );

	COM_SanitizeFilePath( svs.demo.filename );

	if( !COM_ValidateRelativeFilename( svs.demo.filename ) )
	{
		Mem_ZoneFree( svs.demo.filename );
		svs.demo.filename = NULL;
		Com_Printf( "Invalid filename.\n" );
		return;
	}

	COM_DefaultExtension( svs.demo.filename, APP_DEMO_EXTENSION_STR, demofilename_size );

	// temp name
	demofilename_size = sizeof( char ) * ( strlen( svs.demo.filename ) + strlen( ".rec" ) + 1 );
	svs.demo.tempname = Mem_ZoneMalloc( demofilename_size );
	Q_snprintfz( svs.demo.tempname, demofilename_size, "%s.rec", svs.demo.filename );

	// open it
	if( FS_FOpenFile( svs.demo.tempname, &svs.demo.file, FS_WRITE|SNAP_DEMO_GZ ) == -1 )
	{
		Com_Printf( "Error: Couldn't open file: %s\n", svs.demo.tempname );
		Mem_ZoneFree( svs.demo.filename );
		svs.demo.filename = NULL;
		Mem_ZoneFree( svs.demo.tempname );
		svs.demo.tempname = NULL;
		return;
	}

	Com_Printf( "Recording server demo: %s\n", svs.demo.filename );

	SV_Demo_InitClient();

	// write serverdata, configstrings and baselines
	svs.demo.duration = 0;
	svs.demo.basetime = svs.gametime;
	svs.demo.localtime = time( NULL );
	SV_Demo_WriteStartMessages();

	// write one nodelta frame
	svs.demo.client.nodelta = true;
	SV_Demo_WriteSnap();
	svs.demo.client.nodelta = false;
}
Exemplo n.º 19
0
/*
* SV_AutoUpdateFromWeb
*/
void SV_AutoUpdateFromWeb( qboolean checkOnly )
{
	static const char *autoUpdateBaseUrl = APP_UPDATE_URL APP_SERVER_UPDATE_DIRECTORY;
	char checksumString1[32], checksumString2[32];
	unsigned int checksum;
	qboolean success;
	int length, filenum;
	qbyte *data;
	const char *token, *ptr;
	char path[MAX_QPATH];
	int downloadCount = 0, downloadFailed = 0;
	char newVersionTag[MAX_QPATH];
	qboolean newVersion = qfalse;

	if( !dedicated->integer )
		return;

	assert( svs.mapcmd[0] );

	if( !checkOnly )
		SV_UpdateActivity();

	Com_Printf( "\n" );
	Com_Printf( "========== Starting Auto Update ===========\n" );

	Com_Printf( "Checking for updates\n" );

	// download the update file list
	success = SV_WebDownload( autoUpdateBaseUrl, APP_SERVER_UPDATE_FILE, qtrue, qtrue );

	// set as last updated today
	if( !checkOnly )
		Cvar_ForceSet( "sv_lastAutoUpdate", va( "%i", (int)Com_DaysSince1900() ) );

	if( !success ) // no update to do
		goto done;

	// read the file list
	if( ( length = FS_FOpenBaseFile( APP_SERVER_UPDATE_FILE, &filenum, FS_READ ) ) == -1 )
	{
		Com_Printf( "WARNING: Couldn't find %s\n", path );
		goto done;
	}

	if( !length )
	{
		FS_FCloseFile( filenum );
		goto done;
	}

	data = Mem_TempMalloc( length + 1 );
	FS_Read( data, length, filenum );
	FS_FCloseFile( filenum );
	FS_RemoveBaseFile( APP_SERVER_UPDATE_FILE );

	ptr = (const char *)data;

	// first token is always the current release version
	token = COM_ParseExt( &ptr, qtrue );
	if( !token[0] )
		goto cancel;

	// compare versions
	Q_strncpyz( newVersionTag, token, sizeof( newVersionTag ) );
	if( atof( newVersionTag ) > atof( va( "%4.3f", APP_VERSION ) ) )
		newVersion = qtrue;

	while( ptr )
	{
		// we got what should be a checksum
		token = COM_ParseExt( &ptr, qtrue );
		if( !token[0] )
			goto cancel;

		// copy checksum reported by server
		Q_strncpyz( checksumString1, token, sizeof( checksumString1 ) );

		// get filename
		token = COM_ParseExt( &ptr, qtrue );
		if( !token[0] )
			goto cancel;

		// filename should never begin with a slash
		if( token[0] == '/' )
			token++;

		Q_strncpyz( path, token, sizeof( path ) );

		// we got what should be a file path
		if( !COM_ValidateRelativeFilename( path ) )
		{
			Com_Printf( "WARNING: Invalid filename %s\n", path );
			continue;
		}

		checksum = FS_ChecksumBaseFile( path );
		Q_snprintfz( checksumString2, sizeof( checksumString2 ), "%u", checksum );

		// if same checksum no need to update
		if( !strcmp( checksumString1, checksumString2 ) )
			continue;

		// if it's a pack file and the file exists it can't be replaced, so skip
		if( FS_CheckPakExtension( path ) && checksum )
		{
			Com_Printf( "WARNING: Purity check failed for: %s\n", path );
			Com_Printf( "WARNING: This file has been locally modified. It is highly \n" );
			Com_Printf( "WARNING: recommended to restore the original file.\n" );
			Com_Printf( "WARNING: Reinstalling \""APPLICATION"\" might be convenient.\n" );
			continue;	
		}

		if( checkOnly )
		{
			Com_Printf( "File update available : %s\n", path );
			continue;
		}
		
		if( developer->integer )
			Com_Printf( "Downloading update of %s (checksum %s local checksum %s)\n", path, checksumString1, checksumString2 );
		else
			Com_Printf( "Updating %s\n", path );

		if( !SV_WebDownload( autoUpdateBaseUrl, path, qtrue, qtrue ) )
		{
			Com_Printf( "Failed to update %s\n", path );
			downloadFailed++;
		}

		downloadCount++;
	}

cancel:
	Mem_TempFree( data );
done:
	if( newVersion )
	{
		if( downloadCount )
		{
			if( downloadFailed )
				Com_Printf( "This version of "APPLICATION" was updated incompletely\n" );
			else
				Com_Printf( "This version of "APPLICATION" was updated successfully\n\n" );
		}

		Com_Printf( "****** Version %s of "APPLICATION" is available. ******\n", newVersionTag );
		Com_Printf( "****** Please download the new version at "APP_URL" ******\n" );
	}
	else if( downloadCount )
	{
		if( downloadFailed )
			Com_Printf( APPLICATION" was updated incompletely\n" );
		else
			Com_Printf( APPLICATION" was updated successfully\n" );
	}
	else if( !checkOnly )
	{
		if( downloadFailed )
			Com_Printf( "At least one file failed to update\n" );
		else
			Com_Printf( APPLICATION" is up to date\n" );
	}

	Com_Printf( "========== Auto Update Finished ===========\n" );
	Com_Printf( "\n" );

	// update the map list, which also does a filesystem rescan
	ML_Update();

	// if there are any new filesystem entries, restart
	if( FS_GetNotifications() & FS_NOTIFT_NEWPAKS )
	{
		if( sv.state != ss_dead )
		{
			// restart the current map, SV_Map also rescans the filesystem
			Com_Printf( "The server will now restart...\n\n" );

			// start the default map if current map isn't available
			Cbuf_ExecuteText( EXEC_APPEND, va( "map %s\n", svs.mapcmd[0] ? svs.mapcmd : sv_defaultmap->string ) );
		}
	}
}
Exemplo n.º 20
0
/*
* CL_InitDownload_f
* 
* Hanldles server's initdownload message, starts web or server download if possible
*/
 static void CL_InitDownload_f( void )
{
	const char *filename;
	const char *url;
	int size, alloc_size;
	unsigned checksum;
	qboolean allow_localhttpdownload;
	download_list_t	*dl;
	// ignore download commands coming from demo files
	if( cls.demo.playing )		
		return;

	// read the data
	filename = Cmd_Argv( 1 );
	size = atoi( Cmd_Argv( 2 ) );
	checksum = strtoul( Cmd_Argv( 3 ), NULL, 10 );
	allow_localhttpdownload = ( atoi( Cmd_Argv( 4 ) ) != 0 ) && cls.httpbaseurl != NULL;
	url = Cmd_Argv( 5 );

	if( !cls.download.requestname )
	{
		Com_Printf( "Got init download message without request\n" );
		return;
	}

	if( cls.download.filenum || cls.download.web )
	{
		Com_Printf( "Got init download message while already downloading\n" );
		return;
	}

	if( size == -1 )
	{
		// means that download was refused
		Com_Printf( "Server refused download request: %s\n", url ); // if it's refused, url field holds the reason
		CL_DownloadDone();
		return;
	}

	if( size <= 0 )
	{
		Com_Printf( "Server gave invalid size, not downloading\n" );
		CL_DownloadDone();
		return;
	}

	if( checksum == 0 )
	{
		Com_Printf( "Server didn't provide checksum, not downloading\n" );
		CL_DownloadDone();
		return;
	}

	if( !COM_ValidateRelativeFilename( filename ) )
	{
		Com_Printf( "Not downloading, invalid filename: %s\n", filename );
		CL_DownloadDone();
		return;
	}

	if( FS_CheckPakExtension( filename ) && !cls.download.requestpak )
	{
		Com_Printf( "Got a pak file when requesting normal one, not downloading\n" );
		CL_DownloadDone();
		return;
	}

	if( !FS_CheckPakExtension( filename ) && cls.download.requestpak )
	{
		Com_Printf( "Got a non pak file when requesting pak, not downloading\n" );
		CL_DownloadDone();
		return;
	}

	if( !strchr( filename, '/' ) )
	{
		Com_Printf( "Refusing to download file with no gamedir: %s\n", filename );
		CL_DownloadDone();
		return;
	}

	// check that it is in game or basegame dir
	if( strlen( filename ) < strlen( FS_GameDirectory() )+1 ||
		strncmp( filename, FS_GameDirectory(), strlen( FS_GameDirectory() ) ) ||
		filename[strlen( FS_GameDirectory() )] != '/' )
	{
		if( strlen( filename ) < strlen( FS_BaseGameDirectory() )+1 ||
			strncmp( filename, FS_BaseGameDirectory(), strlen( FS_BaseGameDirectory() ) ) ||
			filename[strlen( FS_BaseGameDirectory() )] != '/' )
		{
			Com_Printf( "Can't download, invalid game directory: %s\n", filename );
			CL_DownloadDone();
			return;
		}
	}

	if( FS_CheckPakExtension( filename ) )
	{
		if( strchr( strchr( filename, '/' ) + 1, '/' ) )
		{
			Com_Printf( "Refusing to download pack file to subdirectory: %s\n", filename );
			CL_DownloadDone();
			return;
		}

		if( !Q_strnicmp( COM_FileBase( filename ), "modules", strlen( "modules" ) ) )
		{
			if( !CL_CanDownloadModules() )
			{
				CL_DownloadDone();
				return;
			}
		}

		if( FS_FOpenBaseFile( filename, NULL, FS_READ ) != -1 )
		{
			Com_Printf( "Can't download, file already exists: %s\n", filename );
			CL_DownloadDone();
			return;
		}
	}
	else
	{
		if( strcmp( cls.download.requestname, strchr( filename, '/' ) + 1 ) )
		{
			Com_Printf( "Can't download, got different file than requested: %s\n", filename );
			CL_DownloadDone();
			return;
		}
	}

	if( cls.download.requestnext )
	{
		dl = cls.download.list;
		while( dl != NULL )
		{
			if( !Q_stricmp( dl->filename, filename ) )
			{
				Com_Printf( "Skipping, already tried downloading: %s\n", filename );
				CL_DownloadDone();
				return;
			}
			dl = dl->next;
		}
	}

	cls.download.name = ZoneCopyString( filename );

	alloc_size = strlen( filename ) + strlen( ".tmp" ) + 1;
	cls.download.tempname = Mem_ZoneMalloc( alloc_size );
	Q_snprintfz( cls.download.tempname, alloc_size, "%s.tmp", filename );

	cls.download.web = qfalse;
	cls.download.cancelled = qfalse;
	cls.download.disconnect = qfalse;
	cls.download.size = size;
	cls.download.checksum = checksum;
	cls.download.percent = 0;
	cls.download.timeout = 0;
	cls.download.retries = 0;
	cls.download.timestart = Sys_Milliseconds();
	cls.download.offset = 0;
	cls.download.baseoffset = 0;
	cls.download.pending_reconnect = qfalse;

	Cvar_ForceSet( "cl_download_name", COM_FileBase( cls.download.name ) );
	Cvar_ForceSet( "cl_download_percent", "0" );

	if( cls.download.requestnext )
	{
		dl = Mem_ZoneMalloc( sizeof( download_list_t ) );
		dl->filename = ZoneCopyString( cls.download.name );
		dl->next = cls.download.list;
		cls.download.list = dl;
	}

	if( cl_downloads_from_web->integer && allow_localhttpdownload && url && url[0] != 0 ) {
		cls.download.web = qtrue;
		Com_Printf( "Web download: %s from %s%s\n", cls.download.tempname, cls.httpbaseurl, url );
	}
	else if( cl_downloads_from_web->integer && url && url[0] != 0 ) {
		cls.download.web = qtrue;
		Com_Printf( "Web download: %s from %s\n", cls.download.tempname, url );
	}
	else {
		Com_Printf( "Server download: %s\n", cls.download.tempname );
	}

	cls.download.baseoffset = cls.download.offset = FS_FOpenBaseFile( cls.download.tempname, &cls.download.filenum, FS_APPEND );
	if( !cls.download.filenum )
	{
		Com_Printf( "Can't download, couldn't open %s for writing\n", cls.download.tempname );

		Mem_ZoneFree( cls.download.name );
		cls.download.name = NULL;
		Mem_ZoneFree( cls.download.tempname );
		cls.download.tempname = NULL;

		cls.download.filenum = 0;
		cls.download.offset = 0;
		cls.download.size = 0;
		CL_DownloadDone();
		return;
	}

	if( cls.download.web ) {
		char *referer, *fullurl;
		const char *headers[] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL };

		alloc_size = strlen( APP_URI_SCHEME ) + strlen( NET_AddressToString( &cls.serveraddress ) ) + 1;
		referer = Mem_ZoneMalloc( alloc_size );
		Q_snprintfz( referer, alloc_size, APP_URI_SCHEME "%s", NET_AddressToString( &cls.serveraddress ) );
		Q_strlwr( referer );

		if( allow_localhttpdownload ) {
			alloc_size = strlen( cls.httpbaseurl ) + 1 + strlen( url ) + 1;
			fullurl = Mem_ZoneMalloc( alloc_size );
			Q_snprintfz( fullurl, alloc_size, "%s/%s", cls.httpbaseurl, url );
		}
		else {
			size_t url_len = strlen( url );
			alloc_size = url_len + 1 + strlen( filename ) * 3 + 1;
			fullurl = Mem_ZoneMalloc( alloc_size );
			Q_snprintfz( fullurl, alloc_size, "%s/", url );
			Q_urlencode_unsafechars( filename, fullurl + url_len + 1, alloc_size - url_len - 1 );
		}

		headers[0] = "Referer";
		headers[1] = referer;

		CL_AddSessionHttpRequestHeaders( fullurl, &headers[2] );

		CL_AsyncStreamRequest( fullurl, headers, cl_downloads_from_web_timeout->integer / 100, cls.download.offset, 
			CL_WebDownloadReadCb, CL_WebDownloadDoneCb, NULL, NULL, qfalse );

		Mem_ZoneFree( fullurl );
		Mem_ZoneFree( referer );
		return;
	}

	// have to use Sys_Milliseconds because cls.realtime might be old from Web_Get
	cls.download.timeout = Sys_Milliseconds() + 3000;
	cls.download.retries = 0;

	CL_AddReliableCommand( va( "nextdl \"%s\" %i", cls.download.name, cls.download.offset ) );
}