diff options
Diffstat (limited to 'utils/bugreporter_public')
| -rw-r--r-- | utils/bugreporter_public/bugreporter_public.cpp | 745 | ||||
| -rw-r--r-- | utils/bugreporter_public/bugreporter_public.vpc | 65 | ||||
| -rw-r--r-- | utils/bugreporter_public/bugreporter_upload.cpp | 969 |
3 files changed, 1779 insertions, 0 deletions
diff --git a/utils/bugreporter_public/bugreporter_public.cpp b/utils/bugreporter_public/bugreporter_public.cpp new file mode 100644 index 0000000..87ecb27 --- /dev/null +++ b/utils/bugreporter_public/bugreporter_public.cpp @@ -0,0 +1,745 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// +#define PROTECTED_THINGS_DISABLE +#undef PROTECT_FILEIO_FUNCTIONS +#undef fopen +#include "winlite.h" +#include "basetypes.h" + +#include "utlvector.h" +#include "utlsymbol.h" +#include "utldict.h" +#include "utlbuffer.h" +#include "steamcommon.h" + +#include "bugreporter/bugreporter.h" +#include "filesystem_tools.h" +#include "KeyValues.h" +#include "netadr.h" +#include "steam/steamclientpublic.h" + +bool UploadBugReport( + const netadr_t& cserIP, + const CSteamID& userid, + int build, + char const *title, + char const *body, + char const *exename, + char const *gamedir, + char const *mapname, + char const *reporttype, + char const *email, + char const *accountname, + int ram, + int cpu, + char const *processor, + unsigned int high, + unsigned int low, + unsigned int vendor, + unsigned int device, + char const *osversion, + char const *attachedfile, + unsigned int attachedfilesize +); + +IBaseFileSystem *g_pFileSystem = NULL; + +class CBug +{ +public: + CBug() + { + Clear(); + } + + void Clear() + { + Q_memset( title, 0, sizeof( title ) ); + Q_memset( desc, 0, sizeof( desc ) ); + Q_memset( submitter, 0, sizeof( submitter ) ); + Q_memset( owner, 0, sizeof( owner ) ); + Q_memset( severity, 0, sizeof( severity ) ); + Q_memset( priority, 0, sizeof( priority ) ); + Q_memset( area, 0, sizeof( area ) ); + Q_memset( mapnumber, 0, sizeof( mapnumber) ); + Q_memset( reporttype, 0, sizeof( reporttype ) ); + Q_memset( level, 0, sizeof( level ) ); + Q_memset( build, 0, sizeof( build ) ); + Q_memset( position, 0, sizeof( position ) ); + Q_memset( orientation, 0, sizeof( orientation ) ); + Q_memset( screenshot_unc, 0, sizeof( screenshot_unc ) ); + Q_memset( savegame_unc, 0, sizeof( savegame_unc ) ); + Q_memset( bsp_unc, 0, sizeof( bsp_unc ) ); + Q_memset( vmf_unc, 0, sizeof( vmf_unc ) ); + Q_memset( driverinfo, 0, sizeof( driverinfo ) ); + Q_memset( misc, 0, sizeof( misc ) ); + Q_memset( zip, 0, sizeof( zip ) ); + + Q_memset( exename, 0, sizeof( exename ) ); + Q_memset( gamedir, 0, sizeof( gamedir ) ); + ram = 0; + cpu = 0; + Q_memset( processor, 0, sizeof( processor ) ); + dxversionhigh = 0; + dxversionlow = 0; + dxvendor = 0; + dxdevice = 0; + Q_memset( osversion, 0, sizeof( osversion ) ); + + includedfiles.Purge(); + } + + char title[ 256 ]; + char desc[ 8192 ]; + char owner[ 256 ]; + char submitter[ 256 ]; + char severity[ 256 ]; + char priority[ 256 ]; + char area[ 256 ]; + char mapnumber[ 256 ]; + char reporttype[ 256 ]; + char level[ 256 ]; + char build[ 256 ]; + char position[ 256 ]; + char orientation[ 256 ]; + char screenshot_unc[ 256 ]; + char savegame_unc[ 256 ]; + char bsp_unc[ 256 ]; + char vmf_unc[ 256 ]; + char driverinfo[ 2048 ]; + char misc[ 1024 ]; + + char exename[ 256 ]; + char gamedir[ 256 ]; + unsigned int ram; + unsigned int cpu; + char processor[ 256 ]; + unsigned int dxversionhigh; + unsigned int dxversionlow; + unsigned int dxvendor; + unsigned int dxdevice; + char osversion[ 256 ]; + + char zip[ 256 ]; + + struct incfile + { + char name[ 256 ]; + }; + + CUtlVector< incfile > includedfiles; +}; + +class CBugReporter : public IBugReporter +{ +public: + + CBugReporter(); + virtual ~CBugReporter(); + + // Initialize and login with default username/password for this computer (from resource/bugreporter.res) + virtual bool Init( CreateInterfaceFn engineFactory ); + virtual void Shutdown(); + + virtual bool IsPublicUI() { return true; } + + virtual char const *GetUserName(); + virtual char const *GetUserName_Display(); + + virtual int GetNameCount(); + virtual char const *GetName( int index ); + + virtual int GetDisplayNameCount(); + virtual char const *GetDisplayName( int index ); + + virtual char const *GetDisplayNameForUserName( char const *username ); + virtual char const *GetUserNameForDisplayName( char const *display ); + + virtual int GetSeverityCount(); + virtual char const *GetSeverity( int index ); + + virtual int GetPriorityCount(); + virtual char const *GetPriority( int index ); + + virtual int GetAreaCount(); + virtual char const *GetArea( int index ); + + virtual int GetAreaMapCount(); + virtual char const *GetAreaMap( int index ); + + virtual int GetMapNumberCount(); + virtual char const *GetMapNumber( int index ); + + virtual int GetReportTypeCount(); + virtual char const *GetReportType( int index ); + + virtual char const *GetRepositoryURL( void ) { return NULL; } + virtual char const *GetSubmissionURL( void ) { return NULL; } + + virtual int GetLevelCount(int area) { return 0; } + virtual char const *GetLevel(int area, int index ) { return ""; } + +// Submission API + virtual void StartNewBugReport(); + virtual void CancelNewBugReport(); + virtual bool CommitBugReport( int& bugSubmissionId ); + + virtual void SetTitle( char const *title ); + virtual void SetDescription( char const *description ); + + // NULL for current user + virtual void SetSubmitter( char const *username = 0 ); + virtual void SetOwner( char const *username ); + virtual void SetSeverity( char const *severity ); + virtual void SetPriority( char const *priority ); + virtual void SetArea( char const *area ); + virtual void SetMapNumber ( char const *mapnumber ); + virtual void SetReportType( char const *reporttype ); + + virtual void SetLevel( char const *levelnamne ); + virtual void SetPosition( char const *position ); + virtual void SetOrientation( char const *pitch_yaw_roll ); + virtual void SetBuildNumber( char const *build_num ); + + virtual void SetScreenShot( char const *screenshot_unc_address ); + virtual void SetSaveGame( char const *savegame_unc_address ); + + virtual void SetBSPName( char const *bsp_unc_address ); + virtual void SetVMFName( char const *vmf_unc_address ); + + virtual void AddIncludedFile( char const *filename ); + virtual void ResetIncludedFiles(); + + virtual void SetDriverInfo( char const *info ); + + virtual void SetZipAttachmentName( char const *zipfilename ); + + virtual void SetMiscInfo( char const *info ); + + virtual void SetCSERAddress( const struct netadr_s& adr ); + virtual void SetExeName( char const *exename ); + virtual void SetGameDirectory( char const *gamedir ); + virtual void SetRAM( int ram ); + virtual void SetCPU( int cpu ); + virtual void SetProcessor( char const *processor ); + virtual void SetDXVersion( unsigned int high, unsigned int low, unsigned int vendor, unsigned int device ); + virtual void SetOSVersion( char const *osversion ); + virtual void SetSteamUserID( void *steamid, int idsize ); + +private: + + void SubstituteBugId( int bugid, char *out, int outlen, CUtlBuffer& src ); + + CUtlSymbolTable m_BugStrings; + + CUtlVector< CUtlSymbol > m_Severity; + CUtlVector< CUtlSymbol > m_Area; + CUtlVector< CUtlSymbol > m_MapNumber; + CUtlVector< CUtlSymbol > m_ReportType; + + CUtlSymbol m_UserName; + + CBug *m_pBug; + netadr_t m_cserIP; + CSteamID m_SteamID; +}; + +CBugReporter::CBugReporter() +{ + Q_memset( &m_cserIP, 0, sizeof( m_cserIP ) ); + m_pBug = NULL; + + m_Severity.AddToTail( m_BugStrings.AddString( "Zero" ) ); + m_Severity.AddToTail( m_BugStrings.AddString( "Low" ) ); + m_Severity.AddToTail( m_BugStrings.AddString( "Medium" ) ); + m_Severity.AddToTail( m_BugStrings.AddString( "High" ) ); + m_Severity.AddToTail( m_BugStrings.AddString( "Showstopper" ) ); + + + m_ReportType.AddToTail( m_BugStrings.AddString( "<<Choose Item>>" ) ); + m_ReportType.AddToTail( m_BugStrings.AddString( "Video / Display Problems" ) ); + m_ReportType.AddToTail( m_BugStrings.AddString( "Network / Connectivity Problems" ) ); + m_ReportType.AddToTail( m_BugStrings.AddString( "Download / Installation Problems" ) ); + m_ReportType.AddToTail( m_BugStrings.AddString( "In-game Crash" ) ); + m_ReportType.AddToTail( m_BugStrings.AddString( "Game play / Strategy Problems" ) ); + m_ReportType.AddToTail( m_BugStrings.AddString( "Steam Problems" ) ); + m_ReportType.AddToTail( m_BugStrings.AddString( "Unlisted Bug" ) ); + m_ReportType.AddToTail( m_BugStrings.AddString( "Feature Request / Suggestion" ) ); +} + +CBugReporter::~CBugReporter() +{ + m_BugStrings.RemoveAll(); + m_Severity.Purge(); + m_Area.Purge(); + m_MapNumber.Purge(); + m_ReportType.Purge(); + + delete m_pBug; +} +//----------------------------------------------------------------------------- +// Purpose: Initialize and login with default username/password for this computer (from resource/bugreporter.res) +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CBugReporter::Init( CreateInterfaceFn engineFactory ) +{ + g_pFileSystem = (IFileSystem *)engineFactory( FILESYSTEM_INTERFACE_VERSION, NULL ); + if ( !g_pFileSystem ) + { + AssertMsg( 0, "Failed to create/get IFileSystem" ); + return false; + } + return true; +} + +void CBugReporter::Shutdown() +{ +} + +char const *CBugReporter::GetUserName() +{ + return m_UserName.String(); +} + +char const *CBugReporter::GetUserName_Display() +{ + return GetUserName(); +} + +int CBugReporter::GetNameCount() +{ + return 1; +} + +char const *CBugReporter::GetName( int index ) +{ + if ( index < 0 || index >= 1 ) + return "<<Invalid>>"; + + return GetUserName(); +} + +int CBugReporter::GetDisplayNameCount() +{ + return 1; +} + +char const *CBugReporter::GetDisplayName( int index ) +{ + if ( index < 0 || index >= 1 ) + return "<<Invalid>>"; + + return GetUserName(); +} + +char const *CBugReporter::GetDisplayNameForUserName( char const *username ) +{ + return username; +} + +char const *CBugReporter::GetUserNameForDisplayName( char const *display ) +{ + return display; +} + +int CBugReporter::GetSeverityCount() +{ + return m_Severity.Count() ; +} + +char const *CBugReporter::GetSeverity( int index ) +{ + if ( index < 0 || index >= m_Severity.Count() ) + return "<<Invalid>>"; + + return m_BugStrings.String( m_Severity[ index ] ); +} + +int CBugReporter::GetPriorityCount() +{ + return 0; +} + +char const *CBugReporter::GetPriority( int index ) +{ + return "<<Invalid>>"; +} + +int CBugReporter::GetAreaCount() +{ + return 0; +} + +char const *CBugReporter::GetArea( int index ) +{ + return "<<Invalid>>"; +} + +int CBugReporter::GetAreaMapCount() +{ + return 0; +} + +char const *CBugReporter::GetAreaMap( int index ) +{ + return "<<Invalid>>"; +} + +int CBugReporter::GetMapNumberCount() +{ + return 0; +} + +char const *CBugReporter::GetMapNumber( int index ) +{ + return "<<Invalid>>"; +} + +int CBugReporter::GetReportTypeCount() +{ + return m_ReportType.Count(); +} + +char const *CBugReporter::GetReportType( int index ) +{ + if ( index < 0 || index >= m_ReportType.Count() ) + return "<<Invalid>>"; + + return m_BugStrings.String( m_ReportType[ index ] ); +} + +void CBugReporter::StartNewBugReport() +{ + if ( !m_pBug ) + { + m_pBug = new CBug(); + } + else + { + m_pBug->Clear(); + } +} + +void CBugReporter::CancelNewBugReport() +{ + if ( !m_pBug ) + return; + + m_pBug->Clear(); +} + +void CBugReporter::SubstituteBugId( int bugid, char *out, int outlen, CUtlBuffer& src ) +{ + out[ 0 ] = 0; + + char *dest = out; + + src.SeekGet( CUtlBuffer::SEEK_HEAD, 0 ); + + char const *replace = "\\BugId\\"; + int replace_len = Q_strlen( replace ); + + for ( int pos = 0; pos <= src.TellPut() && ( ( dest - out ) < outlen ); ) + { + char const *str = ( char const * )src.PeekGet( pos ); + if ( !Q_strnicmp( str, replace, replace_len ) ) + { + *dest++ = '\\'; + + char num[ 32 ]; + Q_snprintf( num, sizeof( num ), "%i", bugid ); + char *pnum = num; + while ( *pnum ) + { + *dest++ = *pnum++; + } + + *dest++ = '\\'; + pos += replace_len; + continue; + } + + *dest++ = *str; + ++pos; + } + *dest = 0; +} + +bool CBugReporter::CommitBugReport( int& bugSubmissionId ) +{ + bugSubmissionId = -1; + + if ( !m_pBug ) + return false; + + CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER ); + + buf.Printf( "%s\n\n", m_pBug->desc ); + + buf.Printf( "level: %s\nbuild: %s\nposition: setpos %s; setang %s\n", + m_pBug->level, + m_pBug->build, + m_pBug->position, + m_pBug->orientation ); + + if ( m_pBug->screenshot_unc[ 0 ] ) + { + buf.Printf( "screenshot: %s\n", m_pBug->screenshot_unc ); + } + if ( m_pBug->savegame_unc[ 0 ] ) + { + buf.Printf( "savegame: %s\n", m_pBug->savegame_unc ); + } + /* + if ( m_pBug->bsp_unc[ 0 ] ) + { + buf.Printf( "bsp: %s\n", m_pBug->bsp_unc ); + } + if ( m_pBug->vmf_unc[ 0 ] ) + { + buf.Printf( "vmf: %s\n", m_pBug->vmf_unc ); + } + if ( m_pBug->includedfiles.Count() > 0 ) + { + int c = m_pBug->includedfiles.Count(); + for ( int i = 0 ; i < c; ++i ) + { + buf.Printf( "include: %s\n", m_pBug->includedfiles[ i ].name ); + } + } + */ + if ( m_pBug->driverinfo[ 0 ] ) + { + buf.Printf( "%s\n", m_pBug->driverinfo ); + } + if ( m_pBug->misc[ 0 ] ) + { + buf.Printf( "%s\n", m_pBug->misc ); + } + + buf.PutChar( 0 ); + + // Store desc + + int bugId = 0; + + bugSubmissionId = (int)bugId; + + int attachedfilesize = ( m_pBug->zip[ 0 ] == 0 ) ? 0 : g_pFileSystem->Size( m_pBug->zip ); + + if ( !UploadBugReport( + m_cserIP, + m_SteamID, + atoi( m_pBug->build ), + m_pBug->title, + (char const *)buf.Base(), + m_pBug->exename, + m_pBug->gamedir, + m_pBug->level, + m_pBug->reporttype, + m_pBug->owner, + m_pBug->submitter, + m_pBug->ram, + m_pBug->cpu, + m_pBug->processor, + m_pBug->dxversionhigh, + m_pBug->dxversionlow, + m_pBug->dxvendor, + m_pBug->dxdevice, + m_pBug->osversion, + m_pBug->zip, + attachedfilesize + ) ) + { + Msg( "Unable to upload bug...\n" ); + } + + m_pBug->Clear(); + + return true; +} + +void CBugReporter::SetTitle( char const *title ) +{ + Assert( m_pBug ); + Q_strncpy( m_pBug->title, title, sizeof( m_pBug->title ) ); +} + +void CBugReporter::SetDescription( char const *description ) +{ + Assert( m_pBug ); + Q_strncpy( m_pBug->desc, description, sizeof( m_pBug->desc ) ); +} + +void CBugReporter::SetSubmitter( char const *username /*= 0*/ ) +{ + m_UserName = username; + /* + if ( !username ) + { + username = GetUserName(); + } + */ + + Assert( m_pBug ); + Q_strncpy( m_pBug->submitter, username ? username : "", sizeof( m_pBug->submitter ) ); +} + +void CBugReporter::SetOwner( char const *username ) +{ + Q_strncpy( m_pBug->owner, username, sizeof( m_pBug->owner ) ); +} + +void CBugReporter::SetSeverity( char const *severity ) +{ +} + +void CBugReporter::SetPriority( char const *priority ) +{ +} + +void CBugReporter::SetArea( char const *area ) +{ +} + +void CBugReporter::SetMapNumber( char const *mapnumber ) +{ +} + +void CBugReporter::SetReportType( char const *reporttype ) +{ + Q_strncpy( m_pBug->reporttype, reporttype, sizeof( m_pBug->reporttype ) ); +} + +void CBugReporter::SetLevel( char const *levelnamne ) +{ + Assert( m_pBug ); + Q_strncpy( m_pBug->level, levelnamne, sizeof( m_pBug->level ) ); +} + +void CBugReporter::SetDriverInfo( char const *info ) +{ + Assert( m_pBug ); + Q_strncpy( m_pBug->driverinfo, info, sizeof( m_pBug->driverinfo ) ); +} + +void CBugReporter::SetZipAttachmentName( char const *zipfilename ) +{ + Assert( m_pBug ); + + Q_strncpy( m_pBug->zip, zipfilename, sizeof( m_pBug->zip ) ); +} + +void CBugReporter::SetMiscInfo( char const *info ) +{ + Assert( m_pBug ); + Q_strncpy( m_pBug->misc, info, sizeof( m_pBug->misc ) ); +} + +void CBugReporter::SetPosition( char const *position ) +{ + Assert( m_pBug ); + Q_strncpy( m_pBug->position, position, sizeof( m_pBug->position ) ); +} + +void CBugReporter::SetOrientation( char const *pitch_yaw_roll ) +{ + Assert( m_pBug ); + Q_strncpy( m_pBug->orientation, pitch_yaw_roll, sizeof( m_pBug->orientation ) ); +} + +void CBugReporter::SetBuildNumber( char const *build_num ) +{ + Assert( m_pBug ); + Q_strncpy( m_pBug->build, build_num, sizeof( m_pBug->build ) ); +} + +void CBugReporter::SetScreenShot( char const *screenshot_unc_address ) +{ + Assert( m_pBug ); + Q_strncpy( m_pBug->screenshot_unc, screenshot_unc_address, sizeof( m_pBug->screenshot_unc ) ); +} + +void CBugReporter::SetSaveGame( char const *savegame_unc_address ) +{ + Assert( m_pBug ); + Q_strncpy( m_pBug->savegame_unc, savegame_unc_address, sizeof( m_pBug->savegame_unc ) ); +} + +void CBugReporter::SetBSPName( char const *bsp_unc_address ) +{ +} + +void CBugReporter::SetVMFName( char const *vmf_unc_address ) +{ +} + +void CBugReporter::AddIncludedFile( char const *filename ) +{ +} + +void CBugReporter::ResetIncludedFiles() +{ +} + +void CBugReporter::SetExeName( char const *exename ) +{ + Assert( m_pBug ); + Q_strncpy( m_pBug->exename, exename, sizeof( m_pBug->exename ) ); +} + +void CBugReporter::SetGameDirectory( char const *pchGamedir ) +{ + Assert( m_pBug ); + + Q_FileBase( pchGamedir, m_pBug->gamedir, sizeof( m_pBug->gamedir ) ); +} + +void CBugReporter::SetRAM( int ram ) +{ + Assert( m_pBug ); + m_pBug->ram = ram; +} + +void CBugReporter::SetCPU( int cpu ) +{ + Assert( m_pBug ); + m_pBug->cpu = cpu; +} + +void CBugReporter::SetProcessor( char const *processor ) +{ + Assert( m_pBug ); + Q_strncpy( m_pBug->processor, processor, sizeof( m_pBug->processor ) ); +} + +void CBugReporter::SetDXVersion( unsigned int high, unsigned int low, unsigned int vendor, unsigned int device ) +{ + Assert( m_pBug ); + m_pBug->dxversionhigh = high; + m_pBug->dxversionlow = low; + m_pBug->dxvendor = vendor; + m_pBug->dxdevice = device; +} + +void CBugReporter::SetOSVersion( char const *osversion ) +{ + Assert( m_pBug ); + Q_strncpy( m_pBug->osversion, osversion, sizeof( m_pBug->osversion ) ); +} + +void CBugReporter::SetCSERAddress( const struct netadr_s& adr ) +{ + m_cserIP = adr; +} + +void CBugReporter::SetSteamUserID( void *steamid, int idsize ) +{ + Assert( idsize == sizeof( uint64 ) ); + m_SteamID.SetFromUint64( *((uint64*)steamid) ); +} + +EXPOSE_SINGLE_INTERFACE( CBugReporter, IBugReporter, INTERFACEVERSION_BUGREPORTER ); diff --git a/utils/bugreporter_public/bugreporter_public.vpc b/utils/bugreporter_public/bugreporter_public.vpc new file mode 100644 index 0000000..2f540b2 --- /dev/null +++ b/utils/bugreporter_public/bugreporter_public.vpc @@ -0,0 +1,65 @@ +//----------------------------------------------------------------------------- +// BUGREPORTER_PUBLIC.VPC +// +// Project Script +//----------------------------------------------------------------------------- + +$Macro SRCDIR "..\.." +$Macro OUTBINDIR "$SRCDIR\..\game\bin" + +$Include "$SRCDIR\vpc_scripts\source_dll_base.vpc" + +$Configuration +{ + $Compiler + { + $AdditionalIncludeDirectories "$BASE,..\common" + $PreprocessorDefinitions "$BASE;bugreporter_public_EXPORTS" + } + + $Linker + { + $AdditionalDependencies "$BASE Ws2_32.lib odbc32.lib odbccp32.lib" [$WIN32] + $SystemLibraries "iconv" [$OSXALL] + } +} + +$Project "Bugreporter_public" +{ + $Folder "Source Files" + { + $File "$SRCDIR\public\blockingudpsocket.cpp" + $File "bugreporter_public.cpp" + $File "bugreporter_upload.cpp" + $File "$SRCDIR\public\mathlib\IceKey.H" + } + + $Folder "Header Files" + { + $File "$SRCDIR\public\tier0\basetypes.h" + $File "$SRCDIR\public\blockingudpsocket.h" + $File "$SRCDIR\public\Color.h" + $File "$SRCDIR\public\tier0\commonmacros.h" + $File "$SRCDIR\public\tier0\dbg.h" + $File "$SRCDIR\public\tier0\fasttimer.h" + $File "$SRCDIR\public\filesystem.h" + $File "$SRCDIR\public\appframework\IAppSystem.h" + $File "$SRCDIR\public\vstdlib\IKeyValuesSystem.h" + $File "$SRCDIR\public\tier0\mem.h" + $File "$SRCDIR\public\tier0\memdbgoff.h" + $File "$SRCDIR\public\tier0\memdbgon.h" + $File "$SRCDIR\public\tier0\platform.h" + $File "$SRCDIR\public\tier0\protected_things.h" + $File "$SRCDIR\public\string_t.h" + $File "$SRCDIR\public\tier1\strtools.h" + $File "$SRCDIR\public\tier1\utlmemory.h" + $File "$SRCDIR\public\tier1\utlrbtree.h" + $File "$SRCDIR\public\vstdlib\vstdlib.h" + } + + $Folder "Link Libraries" + { + $Lib mathlib + } + +} diff --git a/utils/bugreporter_public/bugreporter_upload.cpp b/utils/bugreporter_public/bugreporter_upload.cpp new file mode 100644 index 0000000..5c99eaf --- /dev/null +++ b/utils/bugreporter_public/bugreporter_upload.cpp @@ -0,0 +1,969 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// +#include "basetypes.h" +#ifdef WIN32 +#include <winsock.h> +#elif defined(POSIX) +#define INVALID_SOCKET -1 +#define SOCKET_ERROR -1 +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#define closesocket close +#else +#error +#endif +#include "netadr.h" +#include "tier1/strtools.h" +#include <stdio.h> +#include <stdarg.h> +#include "utlbuffer.h" +#include "utlvector.h" +#include "filesystem_tools.h" +#include "cserserverprotocol_engine.h" +#include "mathlib/IceKey.H" +#include "bitbuf.h" +#include "blockingudpsocket.h" +#include "steamcommon.h" +#include "steam/steamclientpublic.h" + +typedef unsigned int u32; +typedef unsigned char u8; +typedef unsigned short u16; + +namespace BugReportHarvester +{ + + enum EFileType + { + eFileTypeBugReport, + + eFILETYPECOUNT // Count number of legal values + }; + + + enum ESendMethod + { + eSendMethodWholeRawFileNoBlocks, + eSendMethodCompressedBlocks, // TODO: Reenable compressed sending of minidumps + + eSENDMETHODCOUNT // Count number of legal values + }; + +} + +using namespace BugReportHarvester; + +// TODO: cut protocol version down to u8 if possible, to reduce bandwidth usage +// for very frequent but tiny commands. +typedef u32 ProtocolVersion_t; + +typedef u8 ProtocolAcceptanceFlag_t; +typedef u8 ProtocolUnacceptableAck_t; + +typedef u32 MessageSequenceId_t; + +typedef u32 ServerSessionHandle_t; +typedef u32 ClientSessionHandle_t; + +typedef u32 NetworkTransactionId_t; + +// Command codes are intentionally as small as possible to minimize bandwidth usage +// for very frequent but tiny commands (e.g. GDS 'FindServer' commands). +typedef u8 Command_t; + +// ... likewise response codes are as small as possible - we use this when we +// ... can and revert to large types on a case by case basis. +typedef u8 CommandResponse_t; + + +// This define our standard type for length prefix for variable length messages +// in wire protocols. +// This is specifically used by CWSABUFWrapper::PrepareToReceiveLengthPrefixedMessage() +// and its supporting functions. +// It is defined here for generic (portable) network code to use when constructing +// messages to be sent to peers that use the above function. +// e.g. SteamValidateUserIDTickets.dll uses this for that purpose. + +// We support u16 or u32 (obviously switching between them breaks existing protocols +// unless all components are switched simultaneously). +typedef u32 NetworkMessageLengthPrefix_t; + + +// Similarly, strings should be preceeded by their length. +typedef u16 StringLengthPrefix_t; + + +const ProtocolAcceptanceFlag_t cuProtocolIsNotAcceptable + = static_cast<ProtocolAcceptanceFlag_t>( 0 ); + +const ProtocolAcceptanceFlag_t cuProtocolIsAcceptable + = static_cast<ProtocolAcceptanceFlag_t>( 1 ); + +const Command_t cuMaxCommand + = static_cast<Command_t>(255); + +const CommandResponse_t cuMaxCommandResponse + = static_cast<CommandResponse_t>(255); + +// This is for mapping requests back to error ids for placing into the database appropriately. +typedef u32 ContextID_t; + +// This is the version of the protocol used by latest-build clients. +const ProtocolVersion_t cuCurrentProtocolVersion = 1; + +// This is the minimum protocol version number that the client must +// be able to speak in order to communicate with the server. +// The client sends its protocol version this before every command, and if we +// don't support that version anymore then we tell it nicely. The client +// should respond by doing an auto-update. +const ProtocolVersion_t cuRequiredProtocolVersion = 1; + + +namespace Commands +{ + const Command_t cuGracefulClose = 0; + const Command_t cuSendBugReport = 1; + const Command_t cuNumCommands = 2; + const Command_t cuNoCommandReceivedYet = cuMaxCommand; +} + + +namespace HarvestFileCommand +{ + typedef u32 SenderTypeId_t; + typedef u32 SenderTypeUniqueId_t; + typedef u32 SenderSourceCodeControlId_t; + typedef u32 FileSize_t; + + // Legal values defined by EFileType + typedef u32 FileType_t; + + // Legal values defined by ESendMethod + typedef u32 SendMethod_t; + + const CommandResponse_t cuOkToSendFile = 0; + const CommandResponse_t cuFileTooBig = 1; + const CommandResponse_t cuInvalidSendMethod = 2; + const CommandResponse_t cuInvalidMaxCompressedChunkSize = 3; + const CommandResponse_t cuInvalidBugReportContext = 4; + const uint cuNumCommandResponses = 5; +} + +//############################################################################# +// +// Class declaration: CWin32UploadBugReport +// +//############################################################################# +// +// Authors: +// +// Yahn Bernier +// +// Description and general notes: +// +// Handles uploading bug report data blobs to the CSERServer +// (Client Stats & Error Reporting Server) + +typedef enum +{ + // General status + eBugReportUploadSucceeded = 0, + eBugReportUploadFailed, + + // Specific status + eBugReportBadParameter, + eBugReportUnknownStatus, + eBugReportSendingBugReportHeaderSucceeded, + eBugReportSendingBugReportHeaderFailed, + eBugReportReceivingResponseSucceeded, + eBugReportReceivingResponseFailed, + eBugReportConnectToCSERServerSucceeded, + eBugReportConnectToCSERServerFailed, + eBugReportUploadingBugReportSucceeded, + eBugReportUploadingBugReportFailed +} EBugReportUploadStatus; + +struct TBugReportProgress +{ + // A text string describing the current progress + char m_sStatus[ 512 ]; +}; + +typedef void ( *BUGREPORTREPORTPROGRESSFUNC )( u32 uContext, const TBugReportProgress & rBugReportProgress ); + +static void BugUploadProgress( u32 uContext, const TBugReportProgress & rBugReportProgress ) +{ + // DevMsg( "%s\n", rBugReportProgress.m_sStatus ); +} + +struct TBugReportParameters +{ + // IP Address of the CSERServer to send the report to + netadr_t m_ipCSERServer; + + TSteamGlobalUserID m_userid; + + // Source Control Id (or build_number) of the product + u32 m_uEngineBuildNumber; + // Name of the .exe + char m_sExecutableName[ 64 ]; + // Game directory + char m_sGameDirectory[ 64 ]; + // Map name the server wants to upload statistics about + char m_sMapName[ 64 ]; + + u32 m_uRAM; + u32 m_uCPU; + + char m_sProcessor[ 128 ]; + + u32 m_uDXVersionHigh; + u32 m_uDXVersionLow; + u32 m_uDXVendorId; + u32 m_uDXDeviceId; + + char m_sOSVersion[ 64 ]; + + char m_sReportType[ 40 ]; + char m_sEmail[ 80 ]; + char m_sAccountName[ 64 ]; + + char m_sTitle[ 128 ]; + + char m_sBody[ 1024 ]; + + u32 m_uAttachmentFileSize; + char m_sAttachmentFile[ 128 ]; + + u32 m_uProgressContext; + BUGREPORTREPORTPROGRESSFUNC m_pOptionalProgressFunc; +}; + +// Note that this API is blocking, though the callback, if passed, can occur during execution. +EBugReportUploadStatus Win32UploadBugReportBlocking +( + const TBugReportParameters & rBugReportParameters // Input +); + + //----------------------------------------------------------------------------- +// Purpose: encrypts an 8-byte sequence +//----------------------------------------------------------------------------- +inline void Encrypt8ByteSequence( IceKey& cipher, const unsigned char *plainText, unsigned char *cipherText) +{ + cipher.encrypt(plainText, cipherText); +} +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void EncryptBuffer( IceKey& cipher, unsigned char *bufData, uint bufferSize) +{ + unsigned char *cipherText = bufData; + unsigned char *plainText = bufData; + uint bytesEncrypted = 0; + + while (bytesEncrypted < bufferSize) + { + // encrypt 8 byte section + Encrypt8ByteSequence( cipher, plainText, cipherText); + bytesEncrypted += 8; + cipherText += 8; + plainText += 8; + } +} + +bool UploadBugReport( + const netadr_t& cserIP, + const CSteamID &userid, + int build, + char const *title, + char const *body, + char const *exename, + char const *pchGamedir, + char const *mapname, + char const *reporttype, + char const *email, + char const *accountname, + int ram, + int cpu, + char const *processor, + unsigned int high, + unsigned int low, + unsigned int vendor, + unsigned int device, + char const *osversion, + char const *attachedfile, + unsigned int attachedfilesize +) +{ + TBugReportParameters params; + Q_memset( ¶ms, 0, sizeof( params ) ); + + params.m_ipCSERServer = cserIP; + params.m_userid.m_SteamLocalUserID.As64bits = userid.ConvertToUint64(); + + params.m_uEngineBuildNumber = build; + Q_strncpy( params.m_sExecutableName, exename, sizeof( params.m_sExecutableName ) ); + Q_strncpy( params.m_sGameDirectory, pchGamedir, sizeof( params.m_sGameDirectory ) ); + Q_strncpy( params.m_sMapName, mapname, sizeof( params.m_sMapName ) ); + + params.m_uRAM = ram; + params.m_uCPU = cpu; + + Q_strncpy( params.m_sProcessor, processor, sizeof( params.m_sProcessor) ); + + params.m_uDXVersionHigh = high; + params.m_uDXVersionLow = low; + params.m_uDXVendorId = vendor; + params.m_uDXDeviceId = device; + + Q_strncpy( params.m_sOSVersion, osversion, sizeof( params.m_sOSVersion ) ); + + Q_strncpy( params.m_sReportType, reporttype, sizeof( params.m_sReportType ) ); + Q_strncpy( params.m_sEmail, email, sizeof( params.m_sEmail ) ); + Q_strncpy( params.m_sAccountName, accountname, sizeof( params.m_sAccountName ) ); + + Q_strncpy( params.m_sTitle, title, sizeof( params.m_sTitle ) ); + Q_strncpy( params.m_sBody, body, sizeof( params.m_sBody ) ); + + Q_strncpy( params.m_sAttachmentFile, attachedfile, sizeof( params.m_sAttachmentFile ) ); + params.m_uAttachmentFileSize = attachedfilesize; + + params.m_uProgressContext = 1u; + params.m_pOptionalProgressFunc = BugUploadProgress; + + EBugReportUploadStatus result = Win32UploadBugReportBlocking( params ); + return ( result == eBugReportUploadSucceeded ) ? true : false; + +} + +void UpdateProgress( const TBugReportParameters & params, char const *fmt, ... ) +{ + if ( !params.m_pOptionalProgressFunc ) + { + return; + } + + char str[ 2048 ]; + va_list argptr; + va_start( argptr, fmt ); + _vsnprintf( str, sizeof( str ) - 1, fmt, argptr ); + va_end( argptr ); + + char outstr[ 2060 ]; + Q_snprintf( outstr, sizeof( outstr ), "(%u): %s", params.m_uProgressContext, str ); + + TBugReportProgress progress; + Q_strncpy( progress.m_sStatus, outstr, sizeof( progress.m_sStatus ) ); + + // Invoke the callback + ( *params.m_pOptionalProgressFunc )( params.m_uProgressContext, progress ); +} + +class CWin32UploadBugReport +{ +public: + explicit CWin32UploadBugReport( + const netadr_t & harvester, + const TBugReportParameters & rBugReportParameters, + u32 contextid ); + ~CWin32UploadBugReport(); + + EBugReportUploadStatus Upload( CUtlBuffer& buf ); + +private: + + enum States + { + eCreateTCPSocket = 0, + eConnectToHarvesterServer, + eSendProtocolVersion, + eReceiveProtocolOkay, + eSendUploadCommand, + eReceiveOKToSendFile, + eSendWholeFile, // This could push chunks onto the wire, but we'll just use a whole buffer for now. + eReceiveFileUploadSuccess, + eSendGracefulClose, + eCloseTCPSocket + }; + + bool CreateTCPSocket( EBugReportUploadStatus& status, CUtlBuffer& buf ); + bool ConnectToHarvesterServer( EBugReportUploadStatus& status, CUtlBuffer& buf ); + bool SendProtocolVersion( EBugReportUploadStatus& status, CUtlBuffer& buf ); + bool ReceiveProtocolOkay( EBugReportUploadStatus& status, CUtlBuffer& buf ); + bool SendUploadCommand( EBugReportUploadStatus& status, CUtlBuffer& buf ); + bool ReceiveOKToSendFile( EBugReportUploadStatus& status, CUtlBuffer& buf ); + bool SendWholeFile( EBugReportUploadStatus& status, CUtlBuffer& buf ); + bool ReceiveFileUploadSuccess( EBugReportUploadStatus& status, CUtlBuffer& buf ); + bool SendGracefulClose( EBugReportUploadStatus& status, CUtlBuffer& buf ); + bool CloseTCPSocket( EBugReportUploadStatus& status, CUtlBuffer& buf ); + + typedef bool ( CWin32UploadBugReport::*pfnProtocolStateHandler )( EBugReportUploadStatus& status, CUtlBuffer& buf ); + struct FSMState_t + { + FSMState_t( uint f, pfnProtocolStateHandler s ) : + first( f ), + second( s ) + { + } + + uint first; + pfnProtocolStateHandler second; + }; + + void AddState( uint StateIndex, pfnProtocolStateHandler handler ); + void SetNextState( uint StateIndex ); + bool DoBlockingReceive( uint bytesExpected, CUtlBuffer& buf ); + + CUtlVector< FSMState_t > m_States; + uint m_uCurrentState; + struct sockaddr_in m_HarvesterSockAddr; + uint m_SocketTCP; + const TBugReportParameters &m_rBugReportParameters; //lint !e1725 + u32 m_ContextID; +}; + +CWin32UploadBugReport::CWin32UploadBugReport( + const netadr_t & harvester, + const TBugReportParameters & rBugReportParameters, + u32 contextid ) : + m_States(), + m_uCurrentState( eCreateTCPSocket ), + m_HarvesterSockAddr(), + m_SocketTCP( 0 ), + m_rBugReportParameters( rBugReportParameters ), + m_ContextID( contextid ) +{ + harvester.ToSockadr( (struct sockaddr *)&m_HarvesterSockAddr ); + + AddState( eCreateTCPSocket, &CWin32UploadBugReport::CreateTCPSocket ); + AddState( eConnectToHarvesterServer, &CWin32UploadBugReport::ConnectToHarvesterServer ); + AddState( eSendProtocolVersion, &CWin32UploadBugReport::SendProtocolVersion ); + AddState( eReceiveProtocolOkay, &CWin32UploadBugReport::ReceiveProtocolOkay ); + AddState( eSendUploadCommand, &CWin32UploadBugReport::SendUploadCommand ); + AddState( eReceiveOKToSendFile, &CWin32UploadBugReport::ReceiveOKToSendFile ); + AddState( eSendWholeFile, &CWin32UploadBugReport::SendWholeFile ); + AddState( eReceiveFileUploadSuccess, &CWin32UploadBugReport::ReceiveFileUploadSuccess ); + AddState( eSendGracefulClose, &CWin32UploadBugReport::SendGracefulClose ); + AddState( eCloseTCPSocket, &CWin32UploadBugReport::CloseTCPSocket ); +} + +CWin32UploadBugReport::~CWin32UploadBugReport() +{ + if ( m_SocketTCP != 0 ) + { + closesocket( m_SocketTCP ); //lint !e534 + m_SocketTCP = 0; + } +} + +//----------------------------------------------------------------------------- +// +// Function: DoBlockingReceive() +// +//----------------------------------------------------------------------------- +bool CWin32UploadBugReport::DoBlockingReceive( uint bytesExpected, CUtlBuffer& buf ) +{ + uint totalReceived = 0; + + buf.Purge(); + for ( ;; ) + { + char temp[ 8192 ]; + + int bytesReceived = recv( m_SocketTCP, temp, sizeof( temp ), 0 ); + if ( bytesReceived <= 0 ) + return false; + + buf.Put( ( const void * )temp, (u32)bytesReceived ); + totalReceived = buf.TellPut(); + if ( totalReceived >= bytesExpected ) + break; + + } + return true; +} + +void CWin32UploadBugReport::AddState( uint StateIndex, pfnProtocolStateHandler handler ) +{ + FSMState_t newState( StateIndex, handler ); + m_States.AddToTail( newState ); +} + +EBugReportUploadStatus CWin32UploadBugReport::Upload( CUtlBuffer& buf ) +{ + UpdateProgress( m_rBugReportParameters, "Commencing bug report upload connection." ); + + EBugReportUploadStatus result = eBugReportUploadSucceeded; + // Run the state machine + while ( 1 ) + { + Assert( m_States[ m_uCurrentState ].first == m_uCurrentState ); + pfnProtocolStateHandler handler = m_States[ m_uCurrentState ].second; + + if ( !(this->*handler)( result, buf ) ) + { + return result; + } + } +} + +void CWin32UploadBugReport::SetNextState( uint StateIndex ) +{ + Assert( StateIndex > m_uCurrentState ); + m_uCurrentState = StateIndex; +} + +bool CWin32UploadBugReport::CreateTCPSocket( EBugReportUploadStatus& status, CUtlBuffer& /*buf*/ ) +{ + UpdateProgress( m_rBugReportParameters, "Creating bug report upload socket." ); + + m_SocketTCP = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP ); + if ( m_SocketTCP == (uint)SOCKET_ERROR ) + { + UpdateProgress( m_rBugReportParameters, "Socket creation failed." ); + + status = eBugReportUploadFailed; + return false; + } + + SetNextState( eConnectToHarvesterServer ); + return true; +} + +bool CWin32UploadBugReport::ConnectToHarvesterServer( EBugReportUploadStatus& status, CUtlBuffer& /*buf*/ ) +{ + UpdateProgress( m_rBugReportParameters, "Connecting to bug report harvesting server." ); + + if ( connect( m_SocketTCP, (const sockaddr *)&m_HarvesterSockAddr, sizeof( m_HarvesterSockAddr ) ) == SOCKET_ERROR ) + { + UpdateProgress( m_rBugReportParameters, "Connection failed." ); + + status = eBugReportConnectToCSERServerFailed; + return false; + } + + SetNextState( eSendProtocolVersion ); + return true; +} + +bool CWin32UploadBugReport::SendProtocolVersion( EBugReportUploadStatus& status, CUtlBuffer& buf ) +{ + UpdateProgress( m_rBugReportParameters, "Sending bug report harvester protocol info." ); + buf.SetBigEndian( true ); + // Send protocol version + buf.Purge(); + buf.PutInt( cuCurrentProtocolVersion ); + + if ( send( m_SocketTCP, (const char *)buf.Base(), (int)buf.TellPut(), 0 ) == SOCKET_ERROR ) + { + UpdateProgress( m_rBugReportParameters, "Send failed." ); + + status = eBugReportUploadFailed; + return false; + } + + SetNextState( eReceiveProtocolOkay ); + return true; +} + +bool CWin32UploadBugReport::ReceiveProtocolOkay( EBugReportUploadStatus& status, CUtlBuffer& buf ) +{ + UpdateProgress( m_rBugReportParameters, "Receiving harvesting protocol acknowledgement." ); + buf.Purge(); + + // Now receive the protocol is acceptable token from the server + if ( !DoBlockingReceive( 1, buf ) ) + { + UpdateProgress( m_rBugReportParameters, "Didn't receive protocol failure data." ); + + status = eBugReportUploadFailed; + return false; + } + + bool protocolokay = buf.GetChar() ? true : false; + if ( !protocolokay ) + { + UpdateProgress( m_rBugReportParameters, "Server rejected protocol." ); + + status = eBugReportUploadFailed; + return false; + } + + UpdateProgress( m_rBugReportParameters, "Protocol OK." ); + + SetNextState( eSendUploadCommand ); + return true; +} + +bool CWin32UploadBugReport::SendUploadCommand( EBugReportUploadStatus& status, CUtlBuffer& buf ) +{ + UpdateProgress( m_rBugReportParameters, "Sending harvesting protocol upload request." ); +// Send upload command + buf.Purge(); + + NetworkMessageLengthPrefix_t messageSize + ( + sizeof( Command_t ) + + sizeof( ContextID_t ) + + sizeof( HarvestFileCommand::FileSize_t ) + + sizeof( HarvestFileCommand::SendMethod_t ) + + sizeof( HarvestFileCommand::FileSize_t ) + ); + + // Prefix the length to the command + buf.PutInt( (int)messageSize ); + buf.PutChar( Commands::cuSendBugReport ); + buf.PutInt( (int)m_ContextID ); + + buf.PutInt( (int)m_rBugReportParameters.m_uAttachmentFileSize ); + buf.PutInt( static_cast<HarvestFileCommand::SendMethod_t>( eSendMethodWholeRawFileNoBlocks ) ); + buf.PutInt( static_cast<HarvestFileCommand::FileSize_t>( 0 ) ); + + // Send command to server + if ( send( m_SocketTCP, (const char *)buf.Base(), (int)buf.TellPut(), 0 ) == SOCKET_ERROR ) + { + UpdateProgress( m_rBugReportParameters, "Send failed." ); + + status = eBugReportUploadFailed; + return false; + } + + SetNextState( eReceiveOKToSendFile ); + return true; +} + +bool CWin32UploadBugReport::ReceiveOKToSendFile( EBugReportUploadStatus& status, CUtlBuffer& buf ) +{ + UpdateProgress( m_rBugReportParameters, "Receive bug report harvesting protocol upload permissible." ); + + // Now receive the protocol is acceptable token from the server + if ( !DoBlockingReceive( 1, buf ) ) + { + UpdateProgress( m_rBugReportParameters, "Receive failed." ); + status = eBugReportUploadFailed; + return false; + } + + bool dosend = false; + CommandResponse_t cmd = (CommandResponse_t)buf.GetChar(); + switch ( cmd ) + { + case HarvestFileCommand::cuOkToSendFile: + { + dosend = true; + } + break; + case HarvestFileCommand::cuFileTooBig: + case HarvestFileCommand::cuInvalidSendMethod: + case HarvestFileCommand::cuInvalidMaxCompressedChunkSize: + case HarvestFileCommand::cuInvalidBugReportContext: + default: + break; + } + + if ( !dosend ) + { + UpdateProgress( m_rBugReportParameters, "Server rejected upload command." ); + + status = eBugReportUploadFailed; + return false; + } + + SetNextState( eSendWholeFile ); + return true; +} + +bool CWin32UploadBugReport::SendWholeFile( EBugReportUploadStatus& status, CUtlBuffer& /*buf*/ ) +{ + UpdateProgress( m_rBugReportParameters, "Uploading bug report data." ); + // Send to server + char *filebuf = NULL; + size_t sizeactual = g_pFileSystem->Size( m_rBugReportParameters.m_sAttachmentFile ); + if ( sizeactual > 0 ) + { + filebuf = new char[ sizeactual + 1 ]; + if ( filebuf ) + { + FileHandle_t fh; + fh = g_pFileSystem->Open( m_rBugReportParameters.m_sAttachmentFile, "rb" ); + if ( FILESYSTEM_INVALID_HANDLE != fh ) + { + g_pFileSystem->Read( (void *)filebuf, sizeactual, fh ); + + g_pFileSystem->Close( fh ); + } + filebuf[ sizeactual ] = 0; + } + } + if ( !sizeactual || !filebuf ) + { + UpdateProgress( m_rBugReportParameters, "bug .zip file size zero or unable to allocate memory for file." ); + + status = eBugReportUploadFailed; + if ( filebuf ) + { + delete[] filebuf; + } + return false; + } + + // Send to server + bool bret = true; + if ( send( m_SocketTCP, filebuf, (int)sizeactual, 0 ) == SOCKET_ERROR ) + { + bret = false; + UpdateProgress( m_rBugReportParameters, "Send failed." ); + + status = eBugReportUploadFailed; + } + else + { + SetNextState( eReceiveFileUploadSuccess ); + } + + delete[] filebuf; + + return bret; +} + +bool CWin32UploadBugReport::ReceiveFileUploadSuccess( EBugReportUploadStatus& status, CUtlBuffer& buf ) +{ + UpdateProgress( m_rBugReportParameters, "Receiving bug report upload success/fail message." ); + + // Now receive the protocol is acceptable token from the server + if ( !DoBlockingReceive( 1, buf ) ) + { + UpdateProgress( m_rBugReportParameters, "Receive failed." ); + + status = eBugReportUploadFailed; + return false; + } + + bool success = buf.GetChar() == 1 ? true : false; + if ( !success ) + { + UpdateProgress( m_rBugReportParameters, "Upload failed." ); + + status = eBugReportUploadFailed; + return false; + } + + UpdateProgress( m_rBugReportParameters, "Upload OK." ); + + SetNextState( eSendGracefulClose ); + return true; +} + +bool CWin32UploadBugReport::SendGracefulClose( EBugReportUploadStatus& status, CUtlBuffer& buf ) +{ + UpdateProgress( m_rBugReportParameters, "Closing connection to server." ); + + // Now send disconnect command + buf.Purge(); + + size_t messageSize = sizeof( Command_t ); + + buf.PutInt( (int)messageSize ); + buf.PutChar( Commands::cuGracefulClose ); + + if ( send( m_SocketTCP, (const char *)buf.Base(), (int)buf.TellPut(), 0 ) == SOCKET_ERROR ) + { + UpdateProgress( m_rBugReportParameters, "Send failed." ); + + status = eBugReportUploadFailed; + return false; + } + + SetNextState( eCloseTCPSocket ); + return true; +} + +bool CWin32UploadBugReport::CloseTCPSocket( EBugReportUploadStatus& status, CUtlBuffer& /*buf*/ ) +{ + UpdateProgress( m_rBugReportParameters, "Closing socket, upload succeeded." ); + + closesocket( m_SocketTCP );//lint !e534 + m_SocketTCP = 0; + + status = eBugReportUploadSucceeded; + // NOTE: Returning false here ends the state machine!!! + return false; +} + +EBugReportUploadStatus Win32UploadBugReportBlocking +( + const TBugReportParameters & rBugReportParameters +) +{ + EBugReportUploadStatus status = eBugReportUploadFailed; + + CUtlBuffer buf( 2048 ); + + UpdateProgress( rBugReportParameters, "Creating initial report." ); + + buf.SetBigEndian( false ); + + buf.Purge(); + buf.PutChar( C2M_BUGREPORT ); + buf.PutChar( '\n' ); + buf.PutChar( C2M_BUGREPORT_PROTOCOL_VERSION ); + + // See CSERServerProtocol.h for format + + // encryption object + IceKey cipher(1); /* medium encryption level */ + unsigned char ucEncryptionKey[8] = { 200,145,10,149,195,190,108,243 }; + + cipher.set( ucEncryptionKey ); + + CUtlBuffer encrypted( 2000 ); + + u8 corruption_identifier = 0x01; + + encrypted.PutChar( corruption_identifier ); + + encrypted.PutInt( rBugReportParameters.m_uEngineBuildNumber ); // build_identifier + + encrypted.PutString( rBugReportParameters.m_sExecutableName ); + encrypted.PutString( rBugReportParameters.m_sGameDirectory ); + encrypted.PutString( rBugReportParameters.m_sMapName ); + + encrypted.PutInt( rBugReportParameters.m_uRAM ); // ram mb + encrypted.PutInt( rBugReportParameters.m_uCPU ); // cpu mhz + + encrypted.PutString( rBugReportParameters.m_sProcessor ); + + encrypted.PutInt( rBugReportParameters.m_uDXVersionHigh ); // driver version high part + encrypted.PutInt( rBugReportParameters.m_uDXVersionLow ); // driver version low part + encrypted.PutInt( rBugReportParameters.m_uDXVendorId ); // dxvendor id + encrypted.PutInt( rBugReportParameters.m_uDXDeviceId ); // dxdevice id + + encrypted.PutString( rBugReportParameters.m_sOSVersion ); + + encrypted.PutInt( rBugReportParameters.m_uAttachmentFileSize ); + + // protocol version 2 stuff + { + encrypted.PutString( rBugReportParameters.m_sReportType ); + encrypted.PutString( rBugReportParameters.m_sEmail ); + encrypted.PutString( rBugReportParameters.m_sAccountName ); + } + + // protocol version 3 stuff + { + encrypted.Put( &rBugReportParameters.m_userid, sizeof( rBugReportParameters.m_userid ) ); + } + + encrypted.PutString( rBugReportParameters.m_sTitle ); + + int bodylen = Q_strlen( rBugReportParameters.m_sBody ) + 1; + + encrypted.PutInt( bodylen ); + encrypted.Put( rBugReportParameters.m_sBody, bodylen ); + + while ( encrypted.TellPut() % 8 ) + { + encrypted.PutChar( 0 ); + } + + EncryptBuffer( cipher, (unsigned char *)encrypted.Base(), encrypted.TellPut() ); + + buf.PutShort( (int)encrypted.TellPut() ); + buf.Put( (unsigned char *)encrypted.Base(), encrypted.TellPut() ); + + CBlockingUDPSocket bcs; + if ( !bcs.IsValid() ) + { + return eBugReportUploadFailed; + } + + struct sockaddr_in sa; + rBugReportParameters.m_ipCSERServer.ToSockadr( (struct sockaddr *)&sa ); + + UpdateProgress( rBugReportParameters, "Sending bug report to server." ); + + bcs.SendSocketMessage( sa, (const u8 *)buf.Base(), buf.TellPut() ); //lint !e534 + + UpdateProgress( rBugReportParameters, "Waiting for response." ); + + if ( bcs.WaitForMessage( 2.0f ) ) + { + UpdateProgress( rBugReportParameters, "Received response." ); + + struct sockaddr_in replyaddress; + buf.EnsureCapacity( 2048 ); + + uint bytesReceived = bcs.ReceiveSocketMessage( &replyaddress, (u8 *)buf.Base(), 2048 ); + if ( bytesReceived > 0 ) + { + // Fixup actual size + buf.SeekPut( CUtlBuffer::SEEK_HEAD, bytesReceived ); + + UpdateProgress( rBugReportParameters, "Checking response." ); + + // Parse out data + u8 msgtype = (u8)buf.GetChar(); + if ( M2C_ACKBUGREPORT != msgtype ) + { + UpdateProgress( rBugReportParameters, "Request denied, invalid message type." ); + return eBugReportSendingBugReportHeaderFailed; + } + bool validProtocol = (u8)buf.GetChar() == 1 ? true : false; + if ( !validProtocol ) + { + UpdateProgress( rBugReportParameters, "Request denied, invalid message protocol." ); + return eBugReportSendingBugReportHeaderFailed; + } + + u8 disposition = (u8)buf.GetChar(); + if ( BR_REQEST_FILES != disposition ) + { + // Server doesn't want a bug report, oh well + if ( rBugReportParameters.m_uAttachmentFileSize > 0 ) + { + UpdateProgress( rBugReportParameters, "Bug report accepted, attachment rejected (server too busy)" ); + } + else + { + UpdateProgress( rBugReportParameters, "Bug report accepted." ); + } + + return eBugReportUploadSucceeded; + } + + // Read in the bug report info parameters + u32 harvester_ip = (u32)buf.GetInt(); + u16 harvester_port = (u16)buf.GetShort(); + u32 dumpcontext = (u32)buf.GetInt(); + + sockaddr_in adr; + adr.sin_family = AF_INET; + adr.sin_port = htons( harvester_port ); +#ifdef WIN32 + adr.sin_addr.S_un.S_addr = harvester_ip; +#else + adr.sin_addr.s_addr = harvester_ip; +#endif + netadr_t BugReportHarvesterFSMIPAddress; + BugReportHarvesterFSMIPAddress.SetFromSockadr( (struct sockaddr *)&adr ); + + UpdateProgress( rBugReportParameters, "Server requested bug report upload." ); + + // Keep using the same scratch buffer for messaging + CWin32UploadBugReport uploader( BugReportHarvesterFSMIPAddress, rBugReportParameters, dumpcontext ); + status = uploader.Upload( buf ); + } + } + else + { + UpdateProgress( rBugReportParameters, "No response from server." ); + } + + return status; +} |