/* ================== SV_WriteBans_f Save bans to file. ================== */ static void SV_WriteBans(void) { int index; fileHandle_t writeto; char filepath[MAX_QPATH]; if(!sv_banFile->string || !*sv_banFile->string) return; Com_sprintf(filepath, sizeof(filepath), "%s/%s", FS_GetCurrentGameDir(), sv_banFile->string); if((writeto = FS_SV_FOpenFileWrite(filepath))) { char writebuf[128]; serverBan_t *curban; for(index = 0; index < serverBansCount; index++) { curban = &serverBans[index]; Com_sprintf(writebuf, sizeof(writebuf), "%d %s %d\n", curban->isexception, NET_AdrToString(curban->ip), curban->subnet); FS_Write(writebuf, strlen(writebuf), writeto); } FS_FCloseFile(writeto); } }
/* ============== Sys_ErrorDialog Display an error message ============== */ void Sys_ErrorDialog( const char *error ) { char buffer[ 1024 ]; unsigned int size; fileHandle_t f; const char *fileName = "crashlog.txt"; // Shut down now so that the curses console doesn't clear the screen when it's really shut down CON_Shutdown( ); Sys_Print( va( "%s\n", error ) ); // Write console log to file and to stderr f = FS_SV_FOpenFileWrite( fileName ); if( !f ) { Com_Printf( "ERROR: couldn't open %s\n", fileName ); return; } while( ( size = CON_LogRead( buffer, sizeof( buffer ) ) ) > 0 ) { FS_Write( buffer, size, f ); fputs( buffer, stderr ); } FS_FCloseFile( f ); }
/* ================== SV_WriteBans_f Save bans to file. ================== */ static void SV_WriteBans(void) { int index; fileHandle_t writeto; char *curpos, filepath[MAX_QPATH]; if (!sv_banFile->string || !*sv_banFile->string) return; if (!(curpos = Cvar_VariableString("fs_game")) || !*curpos) curpos = BASEGAME; Com_sprintf(filepath, sizeof (filepath), "%s/%s", curpos, sv_banFile->string); if ((writeto = FS_SV_FOpenFileWrite(filepath))) { char writebuf[128]; serverBan_t *curban; for (index = 0; index < serverBansCount; index++) { curban = &serverBans[index]; Com_sprintf(writebuf, sizeof (writebuf), "%d %s %d\n", curban->isexception, NET_AdrToString(curban->ip), curban->subnet); FS_Write(writebuf, strlen(writebuf), writeto); } FS_FCloseFile(writeto); } }
void HStorage_WriteDataToFile(varStorage_t* vobj, const char* filename){ fileHandle_t file; char infostring[8192], buf[128]; int i; char name[MAX_VARNAME]; char *string; int count; vsMemObj_t* obj; vsValue_t value; varType_t type; mvabuf; obj = vobj->memObj; file = FS_SV_FOpenFileWrite(va("%s.tmp", filename)); if(!file){ Com_PrintError("HStorage_WriteDataToFile: Can not open %s for writing\n", filename); return; } HStorage_IterInit( obj ); while(HStorage_IterHasNext( obj )) { count = HStorage_IterGetNextInfo( obj, name, &type ); if(count == 0) { continue; } *infostring = 0; BigInfo_SetValueForKey(infostring, "name", name); BigInfo_SetValueForKey(infostring, "type", HStorage_EnumToVarType(type)); BigInfo_SetValueForKey(infostring, "count", va("%d", count)); for(i = 0; i < count; i++) { if(HStorage_GetDataInternal(obj, &value) == 0) { break; } if(type == VSVAR_STRING) { string = HStorage_ValueToString(type, &value, buf, sizeof(buf)); BigInfo_SetEncodedValueForKey(infostring, va("v%d", i), string, strlen(string)); }else{ BigInfo_SetValueForKey(infostring, va("v%d", i), HStorage_ValueToString(type, &value, buf, sizeof(buf))); } } Q_strcat(infostring, sizeof(infostring), "\\\n"); FS_Write(infostring, strlen(infostring), file); } FS_FCloseFile(file); FS_SV_HomeCopyFile(va("%s.tmp", filename) , (char*)filename); }
static size_t Curl_WriteCallback_f(void *ptr, size_t size, size_t nmemb, void *stream) { if (!f) { char dir[MAX_OSPATH]; char dirt[MAX_OSPATH]; char *c; // make sure Content-Type is either "application/octet-stream" or "application/zip". if (curl_easy_getinfo(curl, CURLINFO_CONTENT_TYPE, &c) != CURLE_OK || !c || (Q_stricmp(c, "application/octet-stream") && Q_stricmp(c, "application/zip"))) { Q_strncpyz(dl_error, "No pk3 returned - requested map is probably unknown.", sizeof(dl_error)); return 0; } // make sure the path doesn't have directory information. for (c=path; *c; c++) { if (*c == '\\' || *c == '/' || *c == ':') { Com_sprintf(dl_error, sizeof(dl_error), "Destination filename \"%s\" is not valid.", path); return 0; } } // make sure the file has an appropriate extension. c = path +strlen(path) -4; if (c <= path || strcmp(c, ".pk3")) { Com_sprintf(dl_error, sizeof(dl_error), "Returned file \"%s\" has wrong extension.", path); return 0; } // make out the directory in which to place the file Q_strncpyz(dir, (dl_usebaseq3->integer)?"baseq3":FS_GetCurrentGameDir(), sizeof(dir)); if (strlen(path) +strlen(dir) +1 >= sizeof(path)) { Com_sprintf(dl_error, sizeof(dl_error), "Returned filename is too large."); return 0; } Com_sprintf(dirt, sizeof(dirt), "%s/%s", dir, path); strcpy(path,dirt); // in case of a name collision, just fail - leave it to the user to sort out. if (FS_FileExists(path)) { Com_sprintf(dl_error, sizeof(dl_error), "Failed to download \"%s\", a pk3 by that name exists locally.", path); return 0; } // change the extension to .tmp - it will be changed back once the download is complete. c = path +strlen(path) -4; strcpy(c, ".tmp"); // FS should write the file in the appropriate gamedir and catch unsanitary paths. f = FS_SV_FOpenFileWrite(path); if (!f) { Com_sprintf(dl_error, sizeof(dl_error), "Failed to open \"%s\" for writing.\n", path); return 0; } Com_Printf("Writing to: %s\n", path); } return FS_Write(ptr, size*nmemb, f); }
/* ==================== LAN_SaveServersToCache ==================== */ void LAN_SaveServersToCache( void ) { int size; fileHandle_t fileOut = FS_SV_FOpenFileWrite("servercache.dat"); FS_Write(&cls.numglobalservers, sizeof(int), fileOut); FS_Write(&cls.numfavoriteservers, sizeof(int), fileOut); size = sizeof(cls.globalServers) + sizeof(cls.favoriteServers); FS_Write(&size, sizeof(int), fileOut); FS_Write(&cls.globalServers, sizeof(cls.globalServers), fileOut); FS_Write(&cls.favoriteServers, sizeof(cls.favoriteServers), fileOut); FS_FCloseFile(fileOut); }
/* =============== inspired from http://www.w3.org/Library/Examples/LoadToFile.c setup the download, return once we have a connection =============== */ int DL_BeginDownload( const char *localName, const char *remoteName, int debug ) { char referer[ MAX_STRING_CHARS + URI_SCHEME_LENGTH ]; if ( dl_request ) { curl_multi_remove_handle( dl_multi, dl_request ); curl_easy_cleanup( dl_request ); dl_request = NULL; } if ( dl_file ) { FS_FCloseFile( dl_file ); dl_file = 0; } if ( !localName || !remoteName ) { Com_DPrintf( "Empty download URL or empty local file name\n" ); return 0; } dl_file = FS_SV_FOpenFileWrite( localName ); if ( !dl_file ) { Com_Printf( "ERROR: DL_BeginDownload unable to open '%s' for writing\n", localName ); return 0; } DL_InitDownload(); strcpy( referer, URI_SCHEME ); Q_strncpyz( referer + URI_SCHEME_LENGTH, Cvar_VariableString( "cl_currentServerIP" ), MAX_STRING_CHARS ); dl_request = curl_easy_init(); curl_easy_setopt( dl_request, CURLOPT_USERAGENT, va( "%s %s", PRODUCT_NAME "/" PRODUCT_VERSION, curl_version() ) ); curl_easy_setopt( dl_request, CURLOPT_REFERER, referer ); curl_easy_setopt( dl_request, CURLOPT_URL, remoteName ); curl_easy_setopt( dl_request, CURLOPT_WRITEFUNCTION, DL_cb_FWriteFile ); curl_easy_setopt( dl_request, CURLOPT_WRITEDATA, ( void * )( intptr_t ) dl_file ); curl_easy_setopt( dl_request, CURLOPT_PROGRESSFUNCTION, DL_cb_Progress ); curl_easy_setopt( dl_request, CURLOPT_NOPROGRESS, 0 ); curl_easy_setopt( dl_request, CURLOPT_FAILONERROR, 1 ); curl_multi_add_handle( dl_multi, dl_request ); Cvar_Set( "cl_downloadName", remoteName ); return 1; }
void SaveHistory() { fileHandle_t f = FS_SV_FOpenFileWrite(HISTORY_FILE); if (!f) { Com_Printf("Couldn't write %s.\n", HISTORY_FILE); return; } for (unsigned i = std::max(0L, ((long)lines.size()) - SAVED_HISTORY_LINES); i < lines.size(); i++) { FS_Write(lines[i].data(), lines[i].size(), f); FS_Write("\n", 1, f); } FS_FCloseFile(f); }
/* ==================== LAN_SaveServersToCache ==================== */ void LAN_SaveServersToCache( ) { #ifndef _XBOX int size; fileHandle_t fileOut = FS_SV_FOpenFileWrite("servercache.dat"); FS_Write(&cls.numglobalservers, sizeof(int), fileOut); FS_Write(&cls.nummplayerservers, sizeof(int), fileOut); FS_Write(&cls.numfavoriteservers, sizeof(int), fileOut); size = sizeof(cls.globalServers) + sizeof(cls.favoriteServers) + sizeof(cls.mplayerServers); FS_Write(&size, sizeof(int), fileOut); FS_Write(&cls.globalServers, sizeof(cls.globalServers), fileOut); FS_Write(&cls.mplayerServers, sizeof(cls.mplayerServers), fileOut); FS_Write(&cls.favoriteServers, sizeof(cls.favoriteServers), fileOut); FS_FCloseFile(fileOut); #endif }
void SV_WriteBanlist(){ banList_t *this; time_t aclock; time(&aclock); fileHandle_t file; char infostring[1024]; int i; file = FS_SV_FOpenFileWrite(va("%s.tmp", banlistfile->string)); if(!file){ Com_PrintError("SV_WriteBanlist: Can not open %s for writing\n",banlistfile->string); return; } this = banlist; if(!this) return; for(i = 0 ; i < current_banindex; this++, i++){ if(this->expire == (time_t)-1 || this->expire > aclock){ *infostring = 0; if(this->playeruid > 0){ Info_SetValueForKey(infostring, "uid", va("%i", this->playeruid)); }else if(this->pbguid[7]){ Info_SetValueForKey(infostring, "guid", this->pbguid); }else{ continue; } Info_SetValueForKey(infostring, "nick", this->playername); Info_SetValueForKey(infostring, "rsn", this->reason); Info_SetValueForKey(infostring, "exp", va("%i", this->expire)); Info_SetValueForKey(infostring, "auid", va("%i", this->adminuid)); Q_strcat(infostring, sizeof(infostring), "\\\n"); FS_Write(infostring,strlen(infostring),file); } } FS_FCloseFile(file); FS_SV_HomeCopyFile(va("%s.tmp", banlistfile->string) ,banlistfile->string); // FS_SV_Rename(va("%s.tmp", banlist->string),banlist->string); }
/* ================= CL_CloseJoystickRemap Write out "<event> <key>" lines ================= */ void CL_CloseJoystickRemap( int localPlayerNum ) { fileHandle_t f; char filename[MAX_QPATH]; int i; joyDevice_t *device; if ( playerJoyRemapIndex[ localPlayerNum ] == -1 ) { return; } device = &joyDevice[ playerJoyRemapIndex[ localPlayerNum ] ]; device->references--; playerJoyRemapIndex[ localPlayerNum ] = -1; if ( !device->modified ) { return; } device->modified = qfalse; Com_sprintf( filename, sizeof ( filename ), "joy-%s-%s.txt", JOY_PLATFORM, device->ident ); f = FS_SV_FOpenFileWrite( filename ); if ( !f ) { Com_Printf ("Couldn't write %s.\n", filename ); return; } FS_Printf (f, "// Joystick remap created using " PRODUCT_NAME " on " JOY_PLATFORM " for %s\n", device->name); for ( i = 0 ; i < MAX_JOY_REMAPS ; i++ ) { if ( device->remap[i].event.type == JOYEVENT_NONE ) { continue; } FS_Printf (f, "%s %s\n", CL_JoyEventToString( &device->remap[i].event ), Key_KeynumToString( device->remap[i].keynum ) ); } FS_FCloseFile( f ); }
/* ==================== LAN_SaveServersToCache ==================== */ void LAN_SaveServersToCache() { int size; fileHandle_t fileOut; #ifdef __MACOS__ //DAJ MacOS file typing { extern _MSL_IMP_EXP_C long _fcreator, _ftype; _ftype = 'WlfB'; _fcreator = 'WlfM'; } #endif fileOut = FS_SV_FOpenFileWrite( "servercache.dat" ); FS_Write( &cls.numglobalservers, sizeof( int ), fileOut ); FS_Write( &cls.nummplayerservers, sizeof( int ), fileOut ); FS_Write( &cls.numfavoriteservers, sizeof( int ), fileOut ); size = sizeof( cls.globalServers ) + sizeof( cls.favoriteServers ) + sizeof( cls.mplayerServers ); FS_Write( &size, sizeof( int ), fileOut ); FS_Write( &cls.globalServers, sizeof( cls.globalServers ), fileOut ); FS_Write( &cls.mplayerServers, sizeof( cls.mplayerServers ), fileOut ); FS_Write( &cls.favoriteServers, sizeof( cls.favoriteServers ), fileOut ); FS_FCloseFile( fileOut ); }
/* ================== CL_SystemInfoChanged The systeminfo configstring has been changed, so parse new information out of it. This will happen at every gamestate, and possibly during gameplay. ================== */ void CL_SystemInfoChanged( void ) { char *systemInfo; const char *s, *t; char key[BIG_INFO_KEY]; char value[BIG_INFO_VALUE]; qboolean gameSet; systemInfo = cl.gameState.stringData + cl.gameState.stringOffsets[ CS_SYSTEMINFO ]; // NOTE TTimo: // when the serverId changes, any further messages we send to the server will use this new serverId // https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=475 // in some cases, outdated cp commands might get sent with this news serverId cl.serverId = atoi( Info_ValueForKey( systemInfo, "sv_serverid" ) ); #ifdef USE_VOIP s = Info_ValueForKey( systemInfo, "sv_voipProtocol" ); clc.voipEnabled = !Q_stricmp(s, "opus"); #endif // don't set any vars when playing a demo if ( clc.demoplaying ) { return; } s = Info_ValueForKey( systemInfo, "sv_cheats" ); cl_connectedToCheatServer = atoi( s ); if ( !cl_connectedToCheatServer ) { Cvar_SetCheatState(); } // check pure server string s = Info_ValueForKey( systemInfo, "sv_paks" ); t = Info_ValueForKey( systemInfo, "sv_pakNames" ); FS_PureServerSetLoadedPaks( s, t ); s = Info_ValueForKey( systemInfo, "sv_referencedPaks" ); t = Info_ValueForKey( systemInfo, "sv_referencedPakNames" ); FS_PureServerSetReferencedPaks( s, t ); gameSet = qfalse; // scan through all the variables in the systeminfo and locally set cvars to match s = systemInfo; while ( s ) { Info_NextPair( &s, key, value ); if ( !key[0] ) { break; } // ehw! if (!Q_stricmp(key, "fs_game")) { char filename[MAX_QPATH]; char *title; if ( gameSet ) continue; if(FS_CheckDirTraversal(value)) { Com_Printf(S_COLOR_YELLOW "WARNING: Server sent invalid fs_game value %s\n", value); continue; } // create game title file if does not exist title = Info_ValueForKey( systemInfo, "sv_gameTitle" ); Com_sprintf( filename, sizeof ( filename ), "%s/description.txt", value ); if ( ( cl_allowDownload->integer & DLF_ENABLE ) && *title && !FS_SV_RW_FileExists( filename ) ) { fileHandle_t f = FS_SV_FOpenFileWrite( filename ); FS_Write( s, strlen( title ), f ); FS_FCloseFile( f ); } gameSet = qtrue; } Cvar_Server_Set(key, value); } // game folder must be set if ( !gameSet ) { Com_Error( ERR_DROP, "fs_game not set on server" ); } }
static void CLQW_ParseDownload( QMsg& message ) { // read the data int size = message.ReadShort(); int percent = message.ReadByte(); if ( clc.demoplaying ) { if ( size > 0 ) { message.readcount += size; } return; // not in demo playback } if ( size == -1 ) { common->Printf( "File not found.\n" ); if ( clc.download ) { common->Printf( "cls.download shouldn't have been set\n" ); FS_FCloseFile( clc.download ); clc.download = 0; } CLQW_RequestNextDownload(); return; } // open the file if not opened yet if ( !clc.download ) { if ( String::NCmp( clc.downloadTempName, "skins/", 6 ) ) { clc.download = FS_FOpenFileWrite( clc.downloadTempName ); } else { char name[ 1024 ]; sprintf( name, "qw/%s", clc.downloadTempName ); clc.download = FS_SV_FOpenFileWrite( name ); } if ( !clc.download ) { message.readcount += size; common->Printf( "Failed to open %s\n", clc.downloadTempName ); CLQW_RequestNextDownload(); return; } } FS_Write( message._data + message.readcount, size, clc.download ); message.readcount += size; if ( percent != 100 ) { // change display routines by zoid // request next block clc.downloadPercent = percent; CL_AddReliableCommand( "nextdl" ); } else { FS_FCloseFile( clc.download ); // rename the temp file to it's final name if ( String::Cmp( clc.downloadTempName, clc.downloadName ) ) { if ( String::NCmp( clc.downloadTempName,"skins/",6 ) ) { FS_Rename( clc.downloadTempName, clc.downloadName ); } else { char oldn[ MAX_OSPATH ]; sprintf( oldn, "qw/%s", clc.downloadTempName ); char newn[ MAX_OSPATH ]; sprintf( newn, "qw/%s", clc.downloadName ); FS_SV_Rename( oldn, newn ); } } clc.download = 0; clc.downloadPercent = 0; // get another file if needed CLQW_RequestNextDownload(); } }
/* ===================== CL_ParseDownload A download message has been received from the server ===================== */ void CL_ParseDownload ( msg_t *msg ) { int size; unsigned char data[MAX_MSGLEN]; uint16_t block; if (!*clc.downloadTempName) { Com_Printf("Server sending download, but no download was requested\n"); CL_AddReliableCommand("stopdl", qfalse); return; } // read the data block = MSG_ReadShort ( msg ); if(!block && !clc.downloadBlock) { // block zero is special, contains file size clc.downloadSize = MSG_ReadLong ( msg ); Cvar_SetValue( "cl_downloadSize", clc.downloadSize ); if (clc.downloadSize < 0) { Com_Error( ERR_DROP, "%s", MSG_ReadString( msg ) ); return; } } size = MSG_ReadShort ( msg ); if (size < 0 || size > sizeof(data)) { Com_Error(ERR_DROP, "CL_ParseDownload: Invalid size %d for download chunk", size); return; } MSG_ReadData(msg, data, size); if((clc.downloadBlock & 0xFFFF) != block) { Com_DPrintf( "CL_ParseDownload: Expected block %d, got %d\n", (clc.downloadBlock & 0xFFFF), block); return; } // open the file if not opened yet if (!clc.download) { clc.download = FS_SV_FOpenFileWrite( clc.downloadTempName ); if (!clc.download) { Com_Printf( "Could not create %s\n", clc.downloadTempName ); CL_AddReliableCommand("stopdl", qfalse); CL_NextDownload(); return; } } if (size) FS_Write( data, size, clc.download ); CL_AddReliableCommand(va("nextdl %d", clc.downloadBlock), qfalse); clc.downloadBlock++; clc.downloadCount += size; // So UI gets access to it Cvar_SetValue( "cl_downloadCount", clc.downloadCount ); if (!size) { // A zero length block means EOF if (clc.download) { FS_FCloseFile( clc.download ); clc.download = 0; // rename the file FS_SV_Rename ( clc.downloadTempName, clc.downloadName, qfalse ); } // send intentions now // We need this because without it, we would hold the last nextdl and then start // loading right away. If we take a while to load, the server is happily trying // to send us that last block over and over. // Write it twice to help make sure we acknowledge the download CL_WritePacket(); CL_WritePacket(); // get another file if needed CL_NextDownload (); } }
/* ==================== LAN_SaveServersToCache ==================== */ void LAN_SaveServersToCache() { #if defined RTCW_SP // TTimo: stub, this is only relevant to MP, SP kills the servercache.dat (and favorites) // show_bug.cgi?id=445 /* int size; fileHandle_t fileOut; #ifdef __MACOS__ //DAJ MacOS file typing { extern _MSL_IMP_EXP_C long _fcreator, _ftype; _ftype = 'WlfB'; _fcreator = 'WlfS'; } #endif fileOut = FS_SV_FOpenFileWrite("servercache.dat"); FS_Write(&cls.numglobalservers, sizeof(int), fileOut); FS_Write(&cls.nummplayerservers, sizeof(int), fileOut); FS_Write(&cls.numfavoriteservers, sizeof(int), fileOut); size = sizeof(cls.globalServers) + sizeof(cls.favoriteServers) + sizeof(cls.mplayerServers); FS_Write(&size, sizeof(int), fileOut); FS_Write(&cls.globalServers, sizeof(cls.globalServers), fileOut); FS_Write(&cls.mplayerServers, sizeof(cls.mplayerServers), fileOut); FS_Write(&cls.favoriteServers, sizeof(cls.favoriteServers), fileOut); FS_FCloseFile(fileOut); */ #elif defined RTCW_MP int size; fileHandle_t fileOut; #ifdef __MACOS__ //DAJ MacOS file typing { extern _MSL_IMP_EXP_C long _fcreator, _ftype; _ftype = 'WlfB'; _fcreator = 'WlfM'; } #endif fileOut = FS_SV_FOpenFileWrite( "servercache.dat" ); FS_Write( &cls.numglobalservers, sizeof( int ), fileOut ); FS_Write( &cls.nummplayerservers, sizeof( int ), fileOut ); FS_Write( &cls.numfavoriteservers, sizeof( int ), fileOut ); size = sizeof( cls.globalServers ) + sizeof( cls.favoriteServers ) + sizeof( cls.mplayerServers ); FS_Write( &size, sizeof( int ), fileOut ); FS_Write( &cls.globalServers, sizeof( cls.globalServers ), fileOut ); FS_Write( &cls.mplayerServers, sizeof( cls.mplayerServers ), fileOut ); FS_Write( &cls.favoriteServers, sizeof( cls.favoriteServers ), fileOut ); FS_FCloseFile( fileOut ); #else int size; fileHandle_t fileOut; char filename[MAX_QPATH]; if ( com_gameInfo.usesProfiles && cl_profile->string[0] ) { Com_sprintf( filename, sizeof( filename ), "profiles/%s/servercache.dat", cl_profile->string ); } else { Q_strncpyz( filename, "servercache.dat", sizeof( filename ) ); } // Arnout: moved to mod/profiles dir //fileOut = FS_SV_FOpenFileWrite(filename); fileOut = FS_FOpenFileWrite( filename ); FS_Write( &cls.numglobalservers, sizeof( int ), fileOut ); FS_Write( &cls.numfavoriteservers, sizeof( int ), fileOut ); size = sizeof( cls.globalServers ) + sizeof( cls.favoriteServers ); FS_Write( &size, sizeof( int ), fileOut ); FS_Write( &cls.globalServers, sizeof( cls.globalServers ), fileOut ); FS_Write( &cls.favoriteServers, sizeof( cls.favoriteServers ), fileOut ); FS_FCloseFile( fileOut ); #endif // RTCW_XX }
void CL_cURL_BeginDownload( const char *localName, const char *remoteURL ) { clc.cURLUsed = qtrue; Com_Printf("URL: %s\n", remoteURL); Com_DPrintf("***** CL_cURL_BeginDownload *****\n" "Localname: %s\n" "RemoteURL: %s\n" "****************************\n", localName, remoteURL); CL_cURL_Cleanup(); Q_strncpyz(clc.downloadURL, remoteURL, sizeof(clc.downloadURL)); Q_strncpyz(clc.downloadName, localName, sizeof(clc.downloadName)); Com_sprintf(clc.downloadTempName, sizeof(clc.downloadTempName), "%s.tmp", localName); // Set so UI gets access to it Cvar_Set("cl_downloadName", localName); Cvar_Set("cl_downloadSize", "0"); Cvar_Set("cl_downloadCount", "0"); Cvar_SetValue("cl_downloadTime", cls.realtime); clc.downloadBlock = 0; // Starting new file clc.downloadCount = 0; clc.downloadCURL = qcurl_easy_init(); if(!clc.downloadCURL) { Com_Error(ERR_DROP, "CL_cURL_BeginDownload: qcurl_easy_init() " "failed\n"); return; } clc.download = FS_SV_FOpenFileWrite(clc.downloadTempName); if(!clc.download) { Com_Error(ERR_DROP, "CL_cURL_BeginDownload: failed to open " "%s for writing\n", clc.downloadTempName); return; } qcurl_easy_setopt(clc.downloadCURL, CURLOPT_WRITEDATA, clc.download); if(com_developer->integer) qcurl_easy_setopt(clc.downloadCURL, CURLOPT_VERBOSE, 1); qcurl_easy_setopt(clc.downloadCURL, CURLOPT_URL, clc.downloadURL); qcurl_easy_setopt(clc.downloadCURL, CURLOPT_TRANSFERTEXT, 0); qcurl_easy_setopt(clc.downloadCURL, CURLOPT_REFERER, va("ioQ3://%s", NET_AdrToString(clc.serverAddress))); qcurl_easy_setopt(clc.downloadCURL, CURLOPT_USERAGENT, va("%s %s", Q3_VERSION, qcurl_version())); qcurl_easy_setopt(clc.downloadCURL, CURLOPT_WRITEFUNCTION, CL_cURL_CallbackWrite); qcurl_easy_setopt(clc.downloadCURL, CURLOPT_WRITEDATA, &clc.download); qcurl_easy_setopt(clc.downloadCURL, CURLOPT_NOPROGRESS, 0); qcurl_easy_setopt(clc.downloadCURL, CURLOPT_PROGRESSFUNCTION, CL_cURL_CallbackProgress); qcurl_easy_setopt(clc.downloadCURL, CURLOPT_PROGRESSDATA, NULL); qcurl_easy_setopt(clc.downloadCURL, CURLOPT_FAILONERROR, 1); qcurl_easy_setopt(clc.downloadCURL, CURLOPT_FOLLOWLOCATION, 1); qcurl_easy_setopt(clc.downloadCURL, CURLOPT_MAXREDIRS, 5); clc.downloadCURLM = qcurl_multi_init(); if(!clc.downloadCURLM) { qcurl_easy_cleanup(clc.downloadCURL); clc.downloadCURL = NULL; Com_Error(ERR_DROP, "CL_cURL_BeginDownload: qcurl_multi_init() " "failed\n"); return; } qcurl_multi_add_handle(clc.downloadCURLM, clc.downloadCURL); if(!(clc.sv_allowDownload & DLF_NO_DISCONNECT) && !clc.cURLDisconnected) { CL_AddReliableCommand("disconnect"); CL_WritePacket(); CL_WritePacket(); CL_WritePacket(); clc.cURLDisconnected = qtrue; } }
/* ===================== CL_ParseDownload A download message has been received from the server ===================== */ void CL_ParseDownload( msg_t *msg ) { int size; unsigned char data[ MAX_MSGLEN ]; int block; if ( !*cls.downloadTempName ) { Com_Printf("%s", _( "Server sending download, but no download was requested\n" )); CL_AddReliableCommand( "stopdl" ); return; } // read the data block = MSG_ReadShort( msg ); // TTimo - www dl // if we haven't acked the download redirect yet if ( block == -1 ) { if ( !clc.bWWWDl ) { // server is sending us a www download Q_strncpyz( cls.originalDownloadName, cls.downloadName, sizeof( cls.originalDownloadName ) ); Q_strncpyz( cls.downloadName, MSG_ReadString( msg ), sizeof( cls.downloadName ) ); clc.downloadSize = MSG_ReadLong( msg ); clc.downloadFlags = MSG_ReadLong( msg ); Cvar_SetValue( "cl_downloadSize", clc.downloadSize ); Com_DPrintf( "Server redirected download: %s\n", cls.downloadName ); clc.bWWWDl = qtrue; // activate wwwdl client loop CL_AddReliableCommand( "wwwdl ack" ); // make sure the server is not trying to redirect us again on a bad checksum if ( strstr( clc.badChecksumList, va( "@%s", cls.originalDownloadName ) ) ) { Com_Printf(_( "refusing redirect to %s by server (bad checksum)\n"), cls.downloadName ); CL_AddReliableCommand( "wwwdl fail" ); clc.bWWWDlAborting = qtrue; return; } // make downloadTempName an OS path Q_strncpyz( cls.downloadTempName, FS_BuildOSPath( Cvar_VariableString( "fs_homepath" ), cls.downloadTempName, "" ), sizeof( cls.downloadTempName ) ); cls.downloadTempName[ strlen( cls.downloadTempName ) - 1 ] = '\0'; if ( !DL_BeginDownload( cls.downloadTempName, cls.downloadName, com_developer->integer ) ) { // setting bWWWDl to false after sending the wwwdl fail doesn't work // not sure why, but I suspect we have to eat all remaining block -1 that the server has sent us // still leave a flag so that CL_WWWDownload is inactive // we count on server sending us a gamestate to start up clean again CL_AddReliableCommand( "wwwdl fail" ); clc.bWWWDlAborting = qtrue; Com_Printf(_( "Failed to initialize download for '%s'\n"), cls.downloadName ); } // Check for a disconnected download // we'll let the server disconnect us when it gets the bbl8r message if ( clc.downloadFlags & ( 1 << DL_FLAG_DISCON ) ) { CL_AddReliableCommand( "wwwdl bbl8r" ); cls.bWWWDlDisconnected = qtrue; } return; } else { // server keeps sending that message till we ack it, eat and ignore //MSG_ReadLong( msg ); MSG_ReadString( msg ); MSG_ReadLong( msg ); MSG_ReadLong( msg ); return; } } if ( !block ) { // block zero is special, contains file size clc.downloadSize = MSG_ReadLong( msg ); Cvar_SetValue( "cl_downloadSize", clc.downloadSize ); if ( clc.downloadSize < 0 ) { Com_Error( ERR_DROP, "%s", MSG_ReadString( msg ) ); } } size = MSG_ReadShort( msg ); if ( size < 0 || size > sizeof( data ) ) { Com_Error( ERR_DROP, "CL_ParseDownload: Invalid size %d for download chunk.", size ); } MSG_ReadData( msg, data, size ); if ( clc.downloadBlock != block ) { Com_DPrintf( "CL_ParseDownload: Expected block %d, got %d\n", clc.downloadBlock, block ); return; } // open the file if not opened yet if ( !clc.download ) { clc.download = FS_SV_FOpenFileWrite( cls.downloadTempName ); if ( !clc.download ) { Com_Printf(_( "Could not create %s\n"), cls.downloadTempName ); CL_AddReliableCommand( "stopdl" ); CL_NextDownload(); return; } } if ( size ) { FS_Write( data, size, clc.download ); } CL_AddReliableCommand( va( "nextdl %d", clc.downloadBlock ) ); clc.downloadBlock++; clc.downloadCount += size; // So UI gets access to it Cvar_SetValue( "cl_downloadCount", clc.downloadCount ); if ( !size ) { // A zero length block means EOF if ( clc.download ) { FS_FCloseFile( clc.download ); clc.download = 0; // rename the file FS_SV_Rename( cls.downloadTempName, cls.downloadName ); } *cls.downloadTempName = *cls.downloadName = 0; Cvar_Set( "cl_downloadName", "" ); // send intentions now // We need this because without it, we would hold the last nextdl and then start // loading right away. If we take a while to load, the server is happily trying // to send us that last block over and over. // Write it twice to help make sure we acknowledge the download CL_WritePacket(); CL_WritePacket(); // get another file if needed CL_NextDownload(); } }