/* * CL_DownloadComplete * * Checks downloaded file's checksum, renames it and adds to the filesystem. */ static void CL_DownloadComplete( void ) { unsigned checksum = 0; int length; qboolean updateMapList = qfalse; FS_FCloseFile( cls.download.filenum ); cls.download.filenum = 0; // verify checksum if( FS_CheckPakExtension( cls.download.name ) ) { if( !FS_IsPakValid( cls.download.tempname, &checksum ) ) { Com_Printf( "Downloaded file is not a valid pack file. Removing\n" ); FS_RemoveBaseFile( cls.download.tempname ); return; } } else { length = FS_LoadBaseFile( cls.download.tempname, NULL, NULL, 0 ); if( length < 0 ) { Com_Printf( "Error: Couldn't load downloaded file\n" ); return; } checksum = FS_ChecksumBaseFile( cls.download.tempname ); } if( cls.download.checksum != checksum ) { Com_Printf( "Downloaded file has wrong checksum. Removing: %u %u %s\n", cls.download.checksum, checksum, cls.download.tempname ); FS_RemoveBaseFile( cls.download.tempname ); return; } if( !FS_MoveBaseFile( cls.download.tempname, cls.download.name ) ) { Com_Printf( "Failed to rename the downloaded file\n" ); return; } if( FS_CheckPakExtension( cls.download.name ) ) updateMapList = qtrue; else if( !Q_stricmp( COM_FileExtension( cls.download.name ), ".bsp" ) ) updateMapList = qtrue; // Maplist hook so we also know when a new map is added if( updateMapList ) ML_Update (); cls.download.successCount++; cls.download.timeout = 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 ) ); } } }
/* * SV_AddPurePak */ static void SV_AddPurePak( const char *pakname ) { if( !Com_FindPakInPureList( svs.purelist, pakname ) ) Com_AddPakToPureList( &svs.purelist, pakname, FS_ChecksumBaseFile( pakname ), NULL ); }
/* * 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; } }