diff --git a/src/common/sdkint.h b/src/common/sdkint.h index 0fd178a3..1f72374b 100644 --- a/src/common/sdkint.h +++ b/src/common/sdkint.h @@ -53,7 +53,7 @@ typedef float float32; typedef double float64; typedef float32 f32; -typedef float32 f64; +typedef float64 f64; //----------------------------------------------------------------------------- // 8-bit <--> 64-bit wide boolean type typedef int8 b8; diff --git a/src/core/dllmain.cpp b/src/core/dllmain.cpp index 819030ac..e680bd62 100644 --- a/src/core/dllmain.cpp +++ b/src/core/dllmain.cpp @@ -7,6 +7,7 @@ #include "tier0/basetypes.h" #include "tier0/crashhandler.h" #include "tier0/commandline.h" +#include "tier2/crashreporter.h" /*****************************************************************************/ #ifndef DEDICATED #include "windows/id3dx.h" @@ -38,12 +39,10 @@ static HMODULE s_hModuleHandle = NULL; // UTILITY //############################################################################# -void Crash_Callback() +void Crash_Callback(const CCrashHandler* handler) { - // Shutdown SpdLog to flush all buffers. - SpdLog_Shutdown(); - - // TODO[ AMOS ]: This is where we want to call backtrace from. + CrashReporter_SubmitToCollector(handler); + SpdLog_Shutdown(); // Shutdown SpdLog to flush all buffers. } void Show_Emblem() diff --git a/src/core/init.cpp b/src/core/init.cpp index 11138c1a..a45be9dc 100644 --- a/src/core/init.cpp +++ b/src/core/init.cpp @@ -401,7 +401,7 @@ void QuerySystemInfo() Msg(eDLL_T::NONE, "%-25s: '%s'\n","CPU model identifier", pi.m_szProcessorBrand); Msg(eDLL_T::NONE, "%-25s: '%s'\n","CPU vendor tag", pi.m_szProcessorID); Msg(eDLL_T::NONE, "%-25s: '%12hhu' ('%2hhu' %s)\n", "CPU core count", pi.m_nPhysicalProcessors, pi.m_nLogicalProcessors, "logical"); - Msg(eDLL_T::NONE, "%-25s: '%12lld' ('%6.1f' %s)\n", "CPU core speed", pi.m_Speed, float(pi.m_Speed / 1000000), "MHz"); + Msg(eDLL_T::NONE, "%-25s: '%12lld' ('%.1lf' %s)\n", "CPU core speed", pi.m_Speed, f64(pi.m_Speed / 1000000.0), "MHz"); Msg(eDLL_T::NONE, "%-20s%s: '%12lu' ('0x%-8X')\n", "L1 cache", "(KiB)", pi.m_nL1CacheSizeKb, pi.m_nL1CacheDesc); Msg(eDLL_T::NONE, "%-20s%s: '%12lu' ('0x%-8X')\n", "L2 cache", "(KiB)", pi.m_nL2CacheSizeKb, pi.m_nL2CacheDesc); Msg(eDLL_T::NONE, "%-20s%s: '%12lu' ('0x%-8X')\n", "L3 cache", "(KiB)", pi.m_nL3CacheSizeKb, pi.m_nL3CacheDesc); diff --git a/src/public/tier0/crashhandler.h b/src/public/tier0/crashhandler.h index c07527bb..e94fe96c 100644 --- a/src/public/tier0/crashhandler.h +++ b/src/public/tier0/crashhandler.h @@ -4,6 +4,26 @@ #define CRASHMESSAGE_MSG_EXECUTABLE "bin\\crashmsg.exe" +class CCrashHandler; +typedef void (*CrashCallback_t)(const CCrashHandler* handler); + +struct CrashHardWareInfo_s +{ + CrashHardWareInfo_s() + { + displayDevice = { sizeof(displayDevice), {0} }; + memoryStatus = { sizeof(memoryStatus) , {0} }; + availDiskSpace = 0; + totalDiskSpace = 0; + } + + DISPLAY_DEVICE displayDevice; + MEMORYSTATUSEX memoryStatus; + + size_t totalDiskSpace; + size_t availDiskSpace; +}; + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- @@ -29,6 +49,12 @@ public: inline bool IsValid() const { return m_hExceptionHandler != nullptr; }; + inline bool CrashingModuleNameKnown() const { return m_CrashingModule.Length() > 0; } + inline const char* GetCrashingModuleName() const { return m_CrashingModule.String(); } + + inline const EXCEPTION_POINTERS* GetExceptionPointers() const { return m_pExceptionPointers; } + inline const CrashHardWareInfo_s& GetHardwareInfo() const { return m_HardWareInfo; } + //------------------------------------------------------------------------- // Formatters: //------------------------------------------------------------------------- @@ -46,13 +72,13 @@ public: const char* ExceptionToString(const DWORD nExceptionCode) const; void SetExceptionPointers(EXCEPTION_POINTERS* const pExceptionPointers) { m_pExceptionPointers = pExceptionPointers; } - void SetCrashCallback(PVOID pCrashCallback) { m_pCrashCallback = pCrashCallback; } + void SetCrashCallback(CrashCallback_t pCrashCallback) { m_pCrashCallback = pCrashCallback; } void CaptureCallStack(); void WriteFile(); void CreateMessageProcess(); - void CrashCallback(); + void CrashCallback() const; private: @@ -83,7 +109,7 @@ private: HMODULE m_ppModuleHandles[MAX_MODULE_HANDLES]; // Custom crash callback that's called after the logs have been written. - PVOID m_pCrashCallback; + CrashCallback_t m_pCrashCallback; // Current exception handler, only kept here for tracking as we need the // handle if we want to remove this handler. @@ -109,6 +135,9 @@ private: // Set when crashmsg.exe is created to prevent recursive messages. bool m_bMessageCreated; + + // Cached hardware info this application crashed on. + CrashHardWareInfo_s m_HardWareInfo; mutable RTL_SRWLOCK m_Lock; }; diff --git a/src/public/tier0/utility.h b/src/public/tier0/utility.h index 3e4c47fa..107daba8 100644 --- a/src/public/tier0/utility.h +++ b/src/public/tier0/utility.h @@ -94,9 +94,17 @@ string Format(const char* szFormat, ...); template Iter ExtremeElementABS(Iter first, Iter last, Compare compare) { - auto abs_compare = [compare](LONG a, LONG b) + using ValueType = typename std::iterator_traits::value_type; + auto abs_compare = [compare](ValueType a, ValueType b) { - return compare(abs(a), abs(b)); + if constexpr (std::is_signed_v) + { + return compare(abs(a), abs(b)); + } + else + { + return compare(a, b); + } }; return std::min_element(first, last, abs_compare); diff --git a/src/public/tier2/crashreporter.h b/src/public/tier2/crashreporter.h new file mode 100644 index 00000000..892c11b2 --- /dev/null +++ b/src/public/tier2/crashreporter.h @@ -0,0 +1,11 @@ +//=============================================================================// +// +// Purpose: Post-mortem crash reporter +// +//=============================================================================// +#ifndef TIER2_CRASHREPORTER_H +#define TIER2_CRASHREPORTER_H + +extern void CrashReporter_SubmitToCollector(const CCrashHandler* const handler); + +#endif // TIER2_CRASHREPORTER_H diff --git a/src/public/tier2/curlutils.h b/src/public/tier2/curlutils.h index 66419dd8..e74c628e 100644 --- a/src/public/tier2/curlutils.h +++ b/src/public/tier2/curlutils.h @@ -12,21 +12,24 @@ struct CURLProgress CURL* curl; const char* name; - void* cust; // custom pointer to anything. + void* cust; // custom pointer to anything, todo(amos): rename to 'user'. size_t size; }; struct CURLParams { CURLParams() - : writeFunction(nullptr) + : readFunction(nullptr) + , writeFunction(nullptr) , statusFunction(nullptr) , timeout(0) , verifyPeer(false) , followRedirect(false) , verbose(false) + , failOnError(true) {} + void* readFunction; void* writeFunction; void* statusFunction; @@ -34,11 +37,17 @@ struct CURLParams bool verifyPeer; bool followRedirect; bool verbose; + bool failOnError; }; +size_t CURLReadFileCallback(void* data, const size_t size, const size_t nmemb, FILE* stream); +size_t CURLWriteFileCallback(void* data, const size_t size, const size_t nmemb, FILE* stream); size_t CURLWriteStringCallback(char* contents, const size_t size, const size_t nmemb, string* userp); -size_t CURLWriteFileCallback(void* data, const size_t size, const size_t nmemb, FILE* userp); +curl_slist* CURLSlistAppend(curl_slist* slist, const char* string); + +bool CURLUploadFile(const char* remote, const char* filePath, const char* options, + void* customPointer, const bool usePost, const curl_slist* slist, const CURLParams& params); bool CURLDownloadFile(const char* remote, const char* savePath, const char* fileName, const char* options, curl_off_t dataSize, void* customPointer, const CURLParams& params); diff --git a/src/tier0/crashhandler.cpp b/src/tier0/crashhandler.cpp index ff6da03a..32d225b0 100644 --- a/src/tier0/crashhandler.cpp +++ b/src/tier0/crashhandler.cpp @@ -171,9 +171,10 @@ void CCrashHandler::FormatSystemInfo() m_Buffer.AppendFormat("\tcpu_model = \"%s\"\n", pi.m_szProcessorBrand); m_Buffer.AppendFormat("\tcpu_speed = %010lld // clock cycles\n", pi.m_Speed); + DISPLAY_DEVICE& dd = m_HardWareInfo.displayDevice; + for (DWORD i = 0; ; i++) { - DISPLAY_DEVICE dd = { sizeof(dd), {0} }; const BOOL f = EnumDisplayDevices(NULL, i, &dd, EDD_GET_DEVICE_INTERFACE_NAME); if (!f) @@ -185,16 +186,28 @@ void CCrashHandler::FormatSystemInfo() { m_Buffer.AppendFormat("\tgpu_model = \"%s\"\n", dd.DeviceString); m_Buffer.AppendFormat("\tgpu_flags = 0x%08X // primary device\n", dd.StateFlags); + + break; } } - MEMORYSTATUSEX statex{}; - statex.dwLength = sizeof(statex); + MEMORYSTATUSEX& statex = m_HardWareInfo.memoryStatus; if (GlobalMemoryStatusEx(&statex)) { - m_Buffer.AppendFormat("\tram_total = [%010d, %010d] // physical/virtual (MiB)\n", (statex.ullTotalPhys / 1024) / 1024, (statex.ullTotalVirtual / 1024) / 1024); - m_Buffer.AppendFormat("\tram_avail = [%010d, %010d] // physical/virtual (MiB)\n", (statex.ullAvailPhys / 1024) / 1024, (statex.ullAvailVirtual / 1024) / 1024); + m_Buffer.AppendFormat("\tram_total = [%.2lf, %.2lf] // physical/virtual (MiB)\n", (f64)(statex.ullTotalPhys / (1024.0 * 1024.0)), (f64)(statex.ullTotalVirtual / (1024.0 * 1024.0))); + m_Buffer.AppendFormat("\tram_avail = [%.2lf, %.2lf] // physical/virtual (MiB)\n", (f64)(statex.ullAvailPhys / (1024.0 * 1024.0)), (f64)(statex.ullAvailVirtual / (1024.0 * 1024.0))); + } + + DWORD sectorsPerCluster, bytesPerSector, freeClusters, totalClusters; + + if (GetDiskFreeSpaceA(NULL, §orsPerCluster, &bytesPerSector, &freeClusters, &totalClusters)) + { + m_HardWareInfo.totalDiskSpace = (u64)totalClusters * sectorsPerCluster * bytesPerSector; + m_HardWareInfo.availDiskSpace = (u64)freeClusters * sectorsPerCluster * bytesPerSector; + + m_Buffer.AppendFormat("\tdsk_total = %.2lf // (MiB)\n", (f64)(m_HardWareInfo.totalDiskSpace / (1024.0 * 1024.0))); + m_Buffer.AppendFormat("\tdsk_avail = %.2lf // (MiB)\n", (f64)(m_HardWareInfo.availDiskSpace / (1024.0 * 1024.0))); } m_Buffer.Append("}\n"); @@ -366,7 +379,7 @@ void CCrashHandler::FormatFPU(const char* const pszRegister, const M128A* const *reinterpret_cast(&nVec[2]), *reinterpret_cast(&nVec[3])); - const LONG nHighest = abs(LONG(*MaxElementABS(std::begin(nVec), std::end(nVec)))); + const DWORD nHighest = *MaxElementABS(std::begin(nVec), std::end(nVec)); if (nHighest >= 1000000) { @@ -531,11 +544,11 @@ void CCrashHandler::CreateMessageProcess() //----------------------------------------------------------------------------- // Purpose: calls the crash callback //----------------------------------------------------------------------------- -void CCrashHandler::CrashCallback() +void CCrashHandler::CrashCallback() const { if (m_pCrashCallback) { - ((void(*)(void))m_pCrashCallback)(); + m_pCrashCallback(this); } } @@ -548,11 +561,9 @@ long __stdcall BottomLevelExceptionFilter(EXCEPTION_POINTERS* const pExceptionIn { g_CrashHandler.Start(); - // If the exception couldn't be handled, run the crash callback and - // terminate the process + // If the exception couldn't be handled, terminate the process if (g_CrashHandler.GetExit()) { - g_CrashHandler.CrashCallback(); ExitProcess(EXIT_FAILURE); } @@ -590,6 +601,9 @@ long __stdcall BottomLevelExceptionFilter(EXCEPTION_POINTERS* const pExceptionIn // Display the message to the user. g_CrashHandler.CreateMessageProcess(); + // Run the crash callback + g_CrashHandler.CrashCallback(); + // End it here, the next recursive call terminates the process. g_CrashHandler.End(); @@ -625,7 +639,6 @@ void CCrashHandler::Reset() m_Buffer.Clear(); m_CrashingModule.Clear(); m_MessageCmdLine.Clear(); - m_nCrashMsgFlags = 0; } diff --git a/src/tier2/CMakeLists.txt b/src/tier2/CMakeLists.txt index 2cca1d97..2c3b4b0b 100644 --- a/src/tier2/CMakeLists.txt +++ b/src/tier2/CMakeLists.txt @@ -4,6 +4,7 @@ add_module( "lib" "tier2" "vpc" ${FOLDER_CONTEXT} TRUE TRUE ) start_sources() add_sources( SOURCE_GROUP "Utility" + "crashreporter.cpp" "cryptutils.cpp" "curlutils.cpp" "fileutils.cpp" diff --git a/src/tier2/crashreporter.cpp b/src/tier2/crashreporter.cpp new file mode 100644 index 00000000..81a9b338 --- /dev/null +++ b/src/tier2/crashreporter.cpp @@ -0,0 +1,61 @@ +//=============================================================================// +// +// Purpose: Post-mortem crash reporter +// +//=============================================================================// +#include "tier0/crashhandler.h" +#include "tier0/cpu.h" +#include "tier2/curlutils.h" +#include "tier2/crashreporter.h" + +static ConVar backtrace_enabled("backtrace_enabled", "1", FCVAR_RELEASE, "Whether to report fatal errors to the collection server"); +static ConVar backtrace_hostname("backtrace_hostname", "submit.backtrace.io", FCVAR_RELEASE, "Holds the error collection server hostname"); +static ConVar backtrace_universe("backtrace_universe", "r5reloaded", FCVAR_RELEASE, "Holds the error collection server hosted instance"); +static ConVar backtrace_token("backtrace_token", "f178fd48d89c8fec7f8b6404ae6dae591c330fd3e2599cab888788033944ec98", FCVAR_RELEASE, "Holds the error collection server submission token"); + +static inline string CrashReporter_FormatAttributes(const CCrashHandler* const handler) +{ + const CPUInformation& pi = GetCPUInformation(); + const CrashHardWareInfo_s& hi = handler->GetHardwareInfo(); + + const char* const format = "uuid=%s&" "build_id=%lld&" + "cpu_model=%s&" "cpu_speed=%lf GHz&" "gpu_model=%s&" "gpu_flags=%lu&" + "ram_phys_total=%.2lf MiB&" "ram_phys_avail=%.2lf MiB&" + "ram_virt_total=%.2lf MiB&" "ram_virt_avail=%.2lf MiB&" + "disk_total=%.2lf MiB&" "disk_avail=%.2lf MiB"; + + const DISPLAY_DEVICE& dd = hi.displayDevice; + const MEMORYSTATUSEX& ms = hi.memoryStatus; + + return Format(format, g_LogSessionUUID.c_str(), g_SDKDll.GetNTHeaders()->FileHeader.TimeDateStamp, + pi.m_szProcessorBrand, (f64)(pi.m_Speed / 1000000000.0), dd.DeviceString, dd.StateFlags, + (f64)(ms.ullTotalPhys / (1024.0*1024.0)), (f64)(ms.ullAvailPhys / (1024.0*1024.0)), + (f64)(ms.ullTotalVirtual / (1024.0*1024.0)), (f64)(ms.ullAvailVirtual / (1024.0*1024.0)), + (f64)(hi.totalDiskSpace / (1024.0*1024.0)), (f64)(hi.availDiskSpace / (1024.0*1024.0))); +} + +void CrashReporter_SubmitToCollector(const CCrashHandler* const handler) +{ + if (!backtrace_enabled.GetBool()) + return; + + curl_slist* slist = nullptr; + slist = CURLSlistAppend(slist, "Expect:"); + + if (!slist) + return; // failure. + + const string attributes = CrashReporter_FormatAttributes(handler); + const string hostName = Format("%s%s/%s/%s/%s&%s", + "https://", backtrace_hostname.GetString(), backtrace_universe.GetString(), backtrace_token.GetString(), "minidump", attributes.c_str()); + const string miniDumpPath = Format("%s/%s.dmp", g_LogSessionDirectory.c_str(), "minidump"); + + CURLParams params; + params.readFunction = &CURLReadFileCallback; + params.writeFunction = &CURLWriteStringCallback; + params.timeout = curl_timeout.GetInt(); + params.verifyPeer = ssl_verify_peer.GetBool(); + params.verbose = curl_debug.GetBool(); + + CURLUploadFile(hostName.c_str(), miniDumpPath.c_str(), "rb", nullptr, true, slist, params); +} diff --git a/src/tier2/curlutils.cpp b/src/tier2/curlutils.cpp index fd6f08ca..0fa74a9b 100644 --- a/src/tier2/curlutils.cpp +++ b/src/tier2/curlutils.cpp @@ -6,40 +6,177 @@ #include "tier1/cvar.h" #include "tier2/curlutils.h" +size_t CURLReadFileCallback(void* data, const size_t size, const size_t nmemb, FILE* stream) +{ + const size_t numBytesRead = fread(data, size, nmemb, stream); + return numBytesRead; +} + +size_t CURLWriteFileCallback(void* data, const size_t size, const size_t nmemb, FILE* stream) +{ + const size_t numBytesWritten = fwrite(data, size, nmemb, stream); + return numBytesWritten; +} + size_t CURLWriteStringCallback(char* data, const size_t size, const size_t nmemb, string* userp) { userp->append(data, size * nmemb); return size * nmemb; } -size_t CURLWriteFileCallback(void* data, const size_t size, const size_t nmemb, FILE* userp) -{ - const size_t numBytesWritten = fwrite(data, size, nmemb, userp); - return numBytesWritten; -} - void CURLInitCommonOptions(CURL* curl, const char* remote, - const void* writeData, const CURLParams& params) + const void* readData, const void* writeData, + const CURLParams& params, const CURLProgress* progressData) { curl_easy_setopt(curl, CURLOPT_TIMEOUT, params.timeout); curl_easy_setopt(curl, CURLOPT_VERBOSE, params.verbose); + curl_easy_setopt(curl, CURLOPT_FAILONERROR, params.failOnError); curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, params.followRedirect); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, params.verifyPeer); curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L); curl_easy_setopt(curl, CURLOPT_USE_SSL, CURLUSESSL_ALL); curl_easy_setopt(curl, CURLOPT_URL, remote); curl_easy_setopt(curl, CURLOPT_WRITEDATA, writeData); + curl_easy_setopt(curl, CURLOPT_READDATA, readData); curl_easy_setopt(curl, CURLOPT_USERAGENT, "R5R HTTPS/1.0"); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, params.writeFunction); + curl_easy_setopt(curl, CURLOPT_READFUNCTION, params.readFunction); + + if (params.statusFunction) + { + Assert(progressData); + + curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0l); + curl_easy_setopt(curl, CURLOPT_XFERINFODATA, &progressData); + curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, params.statusFunction); + } } -bool CURLDownloadFile(const char* remote, const char* savePath, const char* fileName, - const char* options, curl_off_t dataSize, void* customPointer, const CURLParams& params) +static CURL* EasyInit() { CURL* curl = curl_easy_init(); if (!curl) { Error(eDLL_T::COMMON, NO_ERROR, "CURL: %s\n", "Easy init failed"); + } + + return curl; +} + +static FILE* OpenFile(const char* filePath, const char* options) +{ + FILE* file = fopen(filePath, options); + if (!file) + { + Error(eDLL_T::COMMON, NO_ERROR, "CURL: %s\n", "Open file failed"); + } + + return file; +} + +static bool FileStat(const char* fileName, struct _stat64& stat) +{ + if (_stat64(fileName, &stat) != 0) + { + Error(eDLL_T::COMMON, NO_ERROR, "CURL: %s\n", "File status query failed"); + return false; + } + + return true; +} + +curl_slist* CURLSlistAppend(curl_slist* slist, const char* string) +{ + slist = curl_slist_append(slist, string); + if (!slist) + { + Error(eDLL_T::COMMON, NO_ERROR, "CURL: %s\n", "Slist append failed"); + } + + return slist; +} + +bool CURLUploadFile(const char* remote, const char* filePath, + const char* options, void* customPointer, const bool usePost, + const curl_slist* slist, const CURLParams& params) +{ + CURL* curl = EasyInit(); + if (!curl) + { + return false; + } + + FILE* file = OpenFile(filePath, options); + if (!file) + { + return false; + } + + struct _stat64 fileStatus; + if (!FileStat(filePath, fileStatus)) + { + fclose(file); + return false; + } + + CURLProgress progressData; + + progressData.curl = curl; + progressData.name = filePath; + progressData.cust = customPointer; + progressData.size = fileStatus.st_size; + + string response; + CURLInitCommonOptions(curl, remote, file, &response, params, &progressData); + + if (slist) + { + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, slist); + } + + const bool largeFile = fileStatus.st_size > INT_MAX; + CURLoption fileSizeOption; + + if (usePost) + { + curl_easy_setopt(curl, CURLOPT_POST, 1L); + + fileSizeOption = largeFile + ? CURLOPT_POSTFIELDSIZE_LARGE + : CURLOPT_POSTFIELDSIZE; + } + else + { + curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L); + + fileSizeOption = largeFile + ? CURLOPT_INFILESIZE_LARGE + : CURLOPT_INFILESIZE; + } + + curl_easy_setopt(curl, fileSizeOption, (curl_off_t)fileStatus.st_size); + + CURLcode res = curl_easy_perform(curl); + const bool success = res == CURLE_OK; + + if (!success) + { + Error(eDLL_T::COMMON, NO_ERROR, "CURL: Upload of file '%s' failed; %s\n", + filePath, curl_easy_strerror(res)); + } + + curl_easy_cleanup(curl); + fclose(file); + + return success; +} + +bool CURLDownloadFile(const char* remote, const char* savePath, const char* fileName, + const char* options, curl_off_t dataSize, void* customPointer, const CURLParams& params) +{ + CURL* curl = EasyInit(); + if (!curl) + { return false; } @@ -48,12 +185,10 @@ bool CURLDownloadFile(const char* remote, const char* savePath, const char* file AppendSlash(filePath); filePath.append(fileName); - FILE* file = fopen(filePath.c_str(), options); + FILE* file = OpenFile(filePath.c_str(), options); if (!file) { - Error(eDLL_T::COMMON, NO_ERROR, "CURL: %s\n", "Open file failed"); curl_easy_cleanup(curl); - return false; } @@ -64,14 +199,7 @@ bool CURLDownloadFile(const char* remote, const char* savePath, const char* file progressData.cust = customPointer; progressData.size = dataSize; - CURLInitCommonOptions(curl, remote, file, params); - - if (params.statusFunction) - { - curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0l); - curl_easy_setopt(curl, CURLOPT_XFERINFODATA, &progressData); - curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, params.statusFunction); - } + CURLInitCommonOptions(curl, remote, nullptr, file, params, &progressData); CURLcode res = curl_easy_perform(curl); @@ -95,33 +223,25 @@ bool CURLDownloadFile(const char* remote, const char* savePath, const char* file CURL* CURLInitRequest(const char* remote, const char* request, string& outResponse, curl_slist*& slist, const CURLParams& params) { - std::function fnError = [&](const char* errorMsg) - { - Error(eDLL_T::COMMON, NO_ERROR, "CURL: %s\n", errorMsg); - curl_slist_free_all(slist); - }; - - slist = curl_slist_append(slist, "Content-Type: application/json"); + slist = CURLSlistAppend(slist, "Content-Type: application/json"); if (!slist) { - fnError("Slist init failed"); return nullptr; } - CURL* curl = curl_easy_init(); + CURL* curl = EasyInit(); if (!curl) { - fnError("Easy init failed"); return nullptr; } - CURLInitCommonOptions(curl, remote, &outResponse, params); + CURLInitCommonOptions(curl, remote, nullptr, &outResponse, params, nullptr); curl_easy_setopt(curl, CURLOPT_HTTPHEADER, slist); if (request) { - curl_easy_setopt(curl, CURLOPT_POSTFIELDS, request); curl_easy_setopt(curl, CURLOPT_POST, 1L); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, request); } return curl;