/* * 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; } }
/* * SV_NextDownload_f * * Responds to reliable nextdl packet with unreliable download packet * If nextdl packet's offet information is negative, download will be stopped */ static void SV_NextDownload_f( client_t *client ) { int blocksize; int offset; if( !client->download.name ) { Com_Printf( "nextdl message for client with no download active, from: %s\n", client->name ); return; } if( Q_stricmp( client->download.name, Cmd_Argv( 1 ) ) ) { Com_Printf( "nextdl message for wrong filename, from: %s\n", client->name ); return; } offset = atoi( Cmd_Argv( 2 ) ); if( offset > client->download.size ) { Com_Printf( "nextdl message with too big offset, from: %s\n", client->name ); return; } if( offset == -1 ) { Com_Printf( "Upload of %s to %s%s completed\n", client->download.name, client->name, S_COLOR_WHITE ); 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; return; } if( offset < 0 ) { Com_Printf( "Upload of %s to %s%s failed\n", client->download.name, client->name, S_COLOR_WHITE ); 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; return; } if( !client->download.data ) { Com_Printf( "Starting server upload of %s to %s\n", client->download.name, client->name ); FS_LoadBaseFile( client->download.name, (void **)&client->download.data, NULL, 0 ); if( !client->download.data ) { Com_Printf( "Error loading %s for uploading\n", client->download.name ); Mem_ZoneFree( client->download.name ); client->download.name = NULL; client->download.size = 0; client->download.timeout = 0; return; } } SV_InitClientMessage( client, &tmpMessage, NULL, 0 ); SV_AddReliableCommandsToMessage( client, &tmpMessage ); blocksize = client->download.size - offset; // jalfixme: adapt download to user rate setting and sv_maxrate setting. if( blocksize > FRAGMENT_SIZE * 2 ) blocksize = FRAGMENT_SIZE * 2; if( offset + blocksize > client->download.size ) blocksize = client->download.size - offset; MSG_WriteByte( &tmpMessage, svc_download ); MSG_WriteString( &tmpMessage, client->download.name ); MSG_WriteLong( &tmpMessage, offset ); MSG_WriteLong( &tmpMessage, blocksize ); MSG_CopyData( &tmpMessage, client->download.data + offset, blocksize ); SV_SendMessageToClient( client, &tmpMessage ); client->download.timeout = svs.realtime + 10000; }
/* * SV_DropClient * * Called when the player is totally leaving the server, either willingly * or unwillingly. This is NOT called if the entire server is quiting * or crashing. */ void SV_DropClient( client_t *drop, int type, const char *format, ... ) { va_list argptr; char *reason; char string[1024]; if( format ) { va_start( argptr, format ); Q_vsnprintfz( string, sizeof( string ), format, argptr ); va_end( argptr ); reason = string; } else { Q_strncpyz( string, "User disconnected", sizeof( string ) ); reason = NULL; } // remove the rating of the client if( drop->edict ) ge->RemoveRating( drop->edict ); // add the disconnect if( drop->edict && ( drop->edict->r.svflags & SVF_FAKECLIENT ) ) { ge->ClientDisconnect( drop->edict, reason ); SV_ClientResetCommandBuffers( drop ); // make sure everything is clean } else { SV_InitClientMessage( drop, &tmpMessage, NULL, 0 ); SV_SendServerCommand( drop, "disconnect %i \"%s\"", type, string ); SV_AddReliableCommandsToMessage( drop, &tmpMessage ); SV_SendMessageToClient( drop, &tmpMessage ); Netchan_PushAllFragments( &drop->netchan ); if( drop->state >= CS_CONNECTED ) { // call the prog function for removing a client // this will remove the body, among other things ge->ClientDisconnect( drop->edict, reason ); } else if( drop->name[0] ) { Com_Printf( "Connecting client %s%s disconnected (%s%s)\n", drop->name, S_COLOR_WHITE, reason, S_COLOR_WHITE ); } } SV_MM_ClientDisconnect( drop ); SNAP_FreeClientFrames( drop ); if( drop->download.name ) { if( drop->download.data ) { FS_FreeBaseFile( drop->download.data ); drop->download.data = NULL; } Mem_ZoneFree( drop->download.name ); drop->download.name = NULL; drop->download.size = 0; drop->download.timeout = 0; } if( drop->individual_socket ) NET_CloseSocket( &drop->socket ); if( drop->mv ) { sv.num_mv_clients--; drop->mv = qfalse; } drop->tvclient = qfalse; drop->state = CS_ZOMBIE; // become free in a few seconds drop->name[0] = 0; }
/* * SV_CheckTimeouts * * If a packet has not been received from a client for timeout->value * seconds, drop the conneciton. Server frames are used instead of * realtime to avoid dropping the local client while debugging. * * When a client is normally dropped, the client_t goes into a zombie state * for a few seconds to make sure any final reliable message gets resent * if necessary */ static void SV_CheckTimeouts( void ) { client_t *cl; int i; #ifdef TCP_SUPPORT // timeout incoming connections for( i = 0; i < MAX_INCOMING_CONNECTIONS; i++ ) { if( svs.incoming[i].active && svs.incoming[i].time + 1000 * 15 < svs.realtime ) { Com_Printf( "Incoming TCP connection from %s timed out\n", NET_AddressToString( &svs.incoming[i].address ) ); NET_CloseSocket( &svs.incoming[i].socket ); svs.incoming[i].active = qfalse; } } #endif // timeout clients for( i = 0, cl = svs.clients; i < sv_maxclients->integer; i++, cl++ ) { // fake clients do not timeout if( cl->edict && ( cl->edict->r.svflags & SVF_FAKECLIENT ) ) cl->lastPacketReceivedTime = svs.realtime; // message times may be wrong across a changelevel else if( cl->lastPacketReceivedTime > svs.realtime ) cl->lastPacketReceivedTime = svs.realtime; if( cl->state == CS_ZOMBIE && cl->lastPacketReceivedTime + 1000 * sv_zombietime->value < svs.realtime ) { cl->state = CS_FREE; // can now be reused if( cl->individual_socket ) NET_CloseSocket( &cl->socket ); continue; } if( ( cl->state != CS_FREE && cl->state != CS_ZOMBIE ) && ( cl->lastPacketReceivedTime + 1000 * sv_timeout->value < svs.realtime ) ) { SV_DropClient( cl, DROP_TYPE_GENERAL, "Error: Connection timed out" ); cl->state = CS_FREE; // don't bother with zombie state if( cl->socket.open ) NET_CloseSocket( &cl->socket ); } // timeout downloads left open if( ( cl->state != CS_FREE && cl->state != CS_ZOMBIE ) && ( cl->download.name && cl->download.timeout < svs.realtime ) ) { Com_Printf( "Download of %s to %s%s timed out\n", cl->download.name, cl->name, S_COLOR_WHITE ); if( cl->download.data ) { FS_FreeBaseFile( cl->download.data ); cl->download.data = NULL; } Mem_ZoneFree( cl->download.name ); cl->download.name = NULL; cl->download.size = 0; cl->download.timeout = 0; } } }
/* * TV_Downstream_CheckTimeouts */ void TV_Downstream_CheckTimeouts( void ) { client_t *client; int i; #ifdef TCP_SUPPORT // timeout incoming upstreams for( i = 0; i < MAX_INCOMING_CONNECTIONS; i++ ) { if( tvs.incoming[i].active && tvs.incoming[i].time + 1000 * 15 < tvs.realtime ) { Com_Printf( "Incoming TCP upstream from %s timed out\n", NET_AddressToString( &tvs.incoming[i].address ) ); NET_CloseSocket( &tvs.incoming[i].socket ); tvs.incoming[i].active = qfalse; } } #endif // timeout clients for( i = 0, client = tvs.clients; i < tv_maxclients->integer; i++, client++ ) { // message times may be wrong across a changelevel if( client->lastPacketReceivedTime > tvs.realtime ) client->lastPacketReceivedTime = tvs.realtime; if( client->state == CS_ZOMBIE && client->lastPacketReceivedTime + 1000 * tv_zombietime->value < tvs.realtime ) { client->state = CS_FREE; // can now be reused if( client->individual_socket ) NET_CloseSocket( &client->socket ); continue; } if( ( client->state != CS_FREE && client->state != CS_ZOMBIE ) && ( client->lastPacketReceivedTime + 1000 * tv_timeout->value < tvs.realtime ) ) { TV_Downstream_DropClient( client, DROP_TYPE_GENERAL, "Upstream timed out" ); client->state = CS_FREE; // don't bother with zombie state if( client->socket.open ) NET_CloseSocket( &client->socket ); } // timeout downloads left open if( ( client->state != CS_FREE && client->state != CS_ZOMBIE ) && ( client->download.name && client->download.timeout < tvs.realtime ) ) { Com_Printf( "Download of %s to %s" S_COLOR_WHITE " timed out\n", client->download.name, client->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; } } }
/* * TV_Downstream_DropClient */ void TV_Downstream_DropClient( client_t *drop, int type, const char *format, ... ) { va_list argptr; char string[1024]; msg_t Message; qbyte MessageData[MAX_MSGLEN]; va_start( argptr, format ); Q_vsnprintfz( string, sizeof( string ), format, argptr ); va_end( argptr ); Com_Printf( "%s" S_COLOR_WHITE " dropped: %s\n", drop->name, string ); TV_Downstream_InitClientMessage( drop, &Message, MessageData, sizeof( MessageData ) ); TV_Downstream_SendServerCommand( drop, "disconnect %i \"%s\"", type, string ); TV_Downstream_AddReliableCommandsToMessage( drop, &Message ); TV_Downstream_SendMessageToClient( drop, &Message ); Netchan_PushAllFragments( &drop->netchan ); if( drop->relay && /*drop->relay->state == CA_ACTIVE && */drop->state >= CS_CONNECTING ) TV_Relay_ClientDisconnect( drop->relay, drop ); // make sure everything is clean TV_Downstream_ClientResetCommandBuffers( drop, qtrue ); SNAP_FreeClientFrames( drop ); if( drop->download.name ) { if( drop->download.data ) { FS_FreeBaseFile( drop->download.data ); drop->download.data = NULL; } Mem_ZoneFree( drop->download.name ); drop->download.name = NULL; drop->download.size = 0; drop->download.timeout = 0; } if( drop->individual_socket ) NET_CloseSocket( &drop->socket ); if( drop->mv ) { tvs.nummvclients--; drop->mv = qfalse; } memset( &drop->flood, 0, sizeof( drop->flood ) ); drop->edict = NULL; drop->relay = NULL; drop->tv = qfalse; drop->state = CS_ZOMBIE; // become free in a few seconds drop->name[0] = 0; }