/* * 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 ) ); }
/* * 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; bool allow, requestpak; bool 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 = false; // 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 = true; 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 ) { goto local_download; } 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 ) { local_download: alloc_size = sizeof( char ) * ( 6 + strlen( uploadname ) * 3 + 1 ); url = Mem_TempMalloc( alloc_size ); Q_snprintfz( url, alloc_size, "files/" ); Q_urlencode_unsafechars( uploadname, url + 6, alloc_size - 6 ); } 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; } }