/* * SV_Download_f * Download command issued from server */ static void SV_Download_f( void ) { qboolean success; char *s; char url[MAX_STRING_CHARS], filepath[MAX_QPATH], writepath[MAX_QPATH]; if( Cmd_Argc() != 2 ) { Com_Printf( "Usage: %s <url>\n", Cmd_Argv( 0 ) ); Com_Printf( "Downloads .pk3 or .pak from URL to gamedir and adds it to the server\n" ); Com_Printf( "Note, server will not function properly while downloading\n" ); return; } s = Cmd_Argv( 1 ); if( !Com_GlobMatch( "*://*", s, qfalse ) ) Q_strncpyz( url, "http://", sizeof( url ) ); else url[0] = 0; Q_strncatz( url, s, sizeof( url ) ); s = strrchr( url, '/' ); if( !s ) { Com_Printf( "%s: invalid URL\n", Cmd_Argv( 0 ) ); return; } Q_strncpyz( filepath, va( "%s/%s", FS_GameDirectory(), s + 1 ), sizeof( filepath ) ); Q_strncpyz( writepath, va( "%s.tmp", filepath ), sizeof( writepath ) ); if( !FS_CheckPakExtension( writepath ) ) { Com_Printf( "Missing or invalid archive extension. Only download of pack files is supported\n" ); return; } Com_Printf( "download url: %s\n", url ); webDownloadPercentPrint = 0; success = Web_Get( url, NULL, writepath, qtrue, 60 * 30, 60, SV_WebDownloadProgress, qfalse ); if( !success ) { Com_Printf( "Server web download failed\n" ); return; } if( !FS_MoveBaseFile( writepath, filepath ) ) { Com_Printf( "Couldn't rename the downloaded file. Download failed\n" ); return; } Com_Printf( "Download successful\n" ); // update the map list, which also does a filesystem rescan ML_Update(); }
/* * 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_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; }