Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| df74c8a1af | |||
| 0f58d40f52 | |||
| 7a125a4c9c |
@ -64,4 +64,50 @@ install(TARGETS privatebinapi
|
|||||||
ARCHIVE DESTINATION lib
|
ARCHIVE DESTINATION lib
|
||||||
)
|
)
|
||||||
|
|
||||||
install(FILES ${HEADERS} DESTINATION include/privatebinapi)
|
install(FILES ${HEADERS} DESTINATION include/privatebinapi)
|
||||||
|
|
||||||
|
# Tests
|
||||||
|
include(CTest)
|
||||||
|
enable_testing()
|
||||||
|
add_subdirectory(tests)
|
||||||
|
add_subdirectory(example)
|
||||||
|
|
||||||
|
# ===================== LLVM/clang-cl Coverage (optional) =====================
|
||||||
|
option(ENABLE_LLVM_COVERAGE "Enable LLVM/clang-cl coverage instrumentation" OFF)
|
||||||
|
|
||||||
|
if(ENABLE_LLVM_COVERAGE)
|
||||||
|
if(NOT CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||||
|
message(FATAL_ERROR "ENABLE_LLVM_COVERAGE requires clang/clang-cl as compiler (CMAKE_CXX_COMPILER_ID=Clang)")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Instrumentation flags
|
||||||
|
add_compile_options(-fprofile-instr-generate -fcoverage-mapping)
|
||||||
|
add_link_options(-fprofile-instr-generate)
|
||||||
|
|
||||||
|
# Helper variables for report tools (can be overridden from environment)
|
||||||
|
set(LLVM_PROFDATA "llvm-profdata" CACHE STRING "Path to llvm-profdata")
|
||||||
|
set(LLVM_COV "llvm-cov" CACHE STRING "Path to llvm-cov")
|
||||||
|
|
||||||
|
# Custom target to run tests and produce coverage report
|
||||||
|
# Usage: cmake --build build --target coverage_llvm --config Release
|
||||||
|
add_custom_target(
|
||||||
|
coverage_llvm
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E env
|
||||||
|
LLVM_PROFILE_FILE=${CMAKE_BINARY_DIR}/coverage/%p-%m.profraw
|
||||||
|
ctest -C $<IF:$<CONFIG:>,${CMAKE_BUILD_TYPE},$<CONFIG>> --output-on-failure
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_BINARY_DIR}/coverage
|
||||||
|
COMMAND ${LLVM_PROFDATA} merge -sparse ${CMAKE_BINARY_DIR}/coverage/*.profraw -o ${CMAKE_BINARY_DIR}/coverage/merged.profdata
|
||||||
|
COMMAND ${LLVM_COV} report
|
||||||
|
$<TARGET_FILE:privatebinapi>
|
||||||
|
--instr-profile=${CMAKE_BINARY_DIR}/coverage/merged.profdata
|
||||||
|
--ignore-filename-regex="(vcpkg|external|CMakeFiles)"
|
||||||
|
COMMAND ${LLVM_COV} show
|
||||||
|
$<TARGET_FILE:privatebinapi>
|
||||||
|
--instr-profile=${CMAKE_BINARY_DIR}/coverage/merged.profdata
|
||||||
|
--ignore-filename-regex="(vcpkg|external|CMakeFiles)"
|
||||||
|
--format=html
|
||||||
|
--output-dir=${CMAKE_BINARY_DIR}/coverage/html
|
||||||
|
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||||
|
DEPENDS privatebinapi
|
||||||
|
)
|
||||||
|
endif()
|
||||||
122
README.md
122
README.md
@ -14,62 +14,85 @@ This library provides a simple C++ interface for interacting with PrivateBin ser
|
|||||||
- Cross-platform compatibility (Windows and Linux)
|
- Cross-platform compatibility (Windows and Linux)
|
||||||
- Support for PrivateBin API versions 1.3 and later
|
- Support for PrivateBin API versions 1.3 and later
|
||||||
|
|
||||||
## Entwicklung & Build
|
## Development & Build
|
||||||
|
|
||||||
### Voraussetzungen
|
### Prerequisites
|
||||||
|
|
||||||
- CMake 3.10+
|
- CMake 3.10+
|
||||||
- C++17-fähiger Compiler (MSVC 2022 bzw. GCC/Clang)
|
- C++17-capable compiler (MSVC 2022 or GCC/Clang)
|
||||||
- Git
|
- Git
|
||||||
- vcpkg (wird bei Makefile-Nutzung automatisch gebootstrapped)
|
- vcpkg (automatically bootstrapped by the Makefile)
|
||||||
|
|
||||||
### Abhängigkeiten
|
### Dependencies
|
||||||
|
|
||||||
- cryptopp (Crypto++)
|
- cryptopp (Crypto++)
|
||||||
- nlohmann-json
|
- nlohmann-json
|
||||||
- Windows: WinHTTP (SDK)
|
- Windows: WinHTTP (SDK)
|
||||||
- Linux: libcurl (wird automatisch über vcpkg genutzt)
|
- Linux: libcurl (used automatically via vcpkg)
|
||||||
|
|
||||||
### Schnellstart mit Makefile (Windows & Linux)
|
### Quick start with Makefile (Windows & Linux)
|
||||||
|
|
||||||
1) Abhängigkeiten installieren, konfigurieren und bauen:
|
1) Install dependencies, configure, and build:
|
||||||
|
|
||||||
```
|
```
|
||||||
make
|
make
|
||||||
```
|
```
|
||||||
|
|
||||||
2) Beispiel bauen und ausführen:
|
2) Build and run the example:
|
||||||
|
|
||||||
```
|
```
|
||||||
make example
|
make example
|
||||||
```
|
```
|
||||||
|
|
||||||
Das Makefile erledigt:
|
The Makefile does:
|
||||||
- vcpkg klonen & bootstrappen
|
- clone and bootstrap vcpkg
|
||||||
- Pakete aus `vcpkg.json` installieren
|
- install packages from `vcpkg.json`
|
||||||
- CMake mit vcpkg-Toolchain konfigurieren
|
- configure CMake with the vcpkg toolchain
|
||||||
- Bibliothek und Beispiel bauen
|
- build the library and the example
|
||||||
|
|
||||||
### Manuell mit CMake
|
### Manual with CMake
|
||||||
|
|
||||||
```
|
```
|
||||||
# vcpkg klonen & bootstrappen
|
# clone and bootstrap vcpkg
|
||||||
git clone https://github.com/microsoft/vcpkg.git "$HOME/vcpkg"
|
git clone https://github.com/microsoft/vcpkg.git "$HOME/vcpkg"
|
||||||
"$HOME/vcpkg/bootstrap-vcpkg.sh" # Linux/macOS
|
"$HOME/vcpkg/bootstrap-vcpkg.sh" # Linux/macOS
|
||||||
# Windows (PowerShell):
|
# Windows (PowerShell):
|
||||||
# powershell -NoProfile -ExecutionPolicy Bypass -Command "& '$env:USERPROFILE\vcpkg\bootstrap-vcpkg.bat'"
|
# powershell -NoProfile -ExecutionPolicy Bypass -Command "& '$env:USERPROFILE\vcpkg\bootstrap-vcpkg.bat'"
|
||||||
|
|
||||||
# Konfigurieren
|
# Configure
|
||||||
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE="$HOME/vcpkg/scripts/buildsystems/vcpkg.cmake"
|
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE="$HOME/vcpkg/scripts/buildsystems/vcpkg.cmake"
|
||||||
|
|
||||||
# Bauen
|
# Build
|
||||||
cmake --build build --config Release
|
cmake --build build --config Release
|
||||||
|
|
||||||
# Beispiel
|
# Example
|
||||||
cmake -S example -B example/build -DCMAKE_BUILD_TYPE=Release
|
cmake -S example -B example/build -DCMAKE_BUILD_TYPE=Release
|
||||||
cmake --build example/build --config Release
|
cmake --build example/build --config Release
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Windows (PowerShell) – Build via build_thinkpad.bat
|
||||||
|
|
||||||
|
For systems with Visual Studio 2022 Build Tools (C++ workload) and vcpkg in the user profile, there is a robust build script:
|
||||||
|
|
||||||
|
```
|
||||||
|
cd C:\Users\mbusc\source\repos\lib-privatebin
|
||||||
|
./build_thinkpad.bat
|
||||||
|
```
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
- Requires Visual Studio 2022 Build Tools with C++ tools and the Windows 11 SDK. If `VsDevCmd.bat` is found, the script automatically initializes the MSVC environment.
|
||||||
|
- vcpkg is bootstrapped if needed; missing dependencies (`cryptopp`, `nlohmann-json`) are installed.
|
||||||
|
- If vcpkg requires a baseline, the script sets it automatically.
|
||||||
|
|
||||||
|
Edition/Path notes:
|
||||||
|
- If you use another VS edition (e.g., Professional/Enterprise), adjust the paths in `build_thinkpad.bat`:
|
||||||
|
- `C:\Program Files\Microsoft Visual Studio\2022\Professional\Common7\Tools\VsDevCmd.bat`
|
||||||
|
- `C:\Program Files\Microsoft Visual Studio\2022\Enterprise\Common7\Tools\VsDevCmd.bat`
|
||||||
|
- Alternatively, you can use `vswhere.exe` to discover the installation path:
|
||||||
|
```powershell
|
||||||
|
& 'C:\Program Files\Microsoft Visual Studio\Installer\vswhere.exe' -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath
|
||||||
|
```
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
### API Functions
|
### API Functions
|
||||||
@ -142,4 +165,63 @@ int main() {
|
|||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
This project is licensed under the MIT License - see the LICENSE file for details.
|
This project is licensed under the MIT License - see the LICENSE file for details.
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
- vcpkg requires a baseline / "this vcpkg instance requires a manifest with a specified baseline"
|
||||||
|
- Run in the repo root to add an initial builtin baseline to `vcpkg.json`:
|
||||||
|
```powershell
|
||||||
|
$env:VCPKG_ROOT = "$env:USERPROFILE\vcpkg"
|
||||||
|
& "$env:VCPKG_ROOT\vcpkg.exe" x-update-baseline --add-initial-baseline
|
||||||
|
```
|
||||||
|
- Then configure/build again.
|
||||||
|
|
||||||
|
- Visual Studio instance not found / "could not find specified instance of Visual Studio"
|
||||||
|
- Ensure VS 2022 Build Tools or Community with C++ tools and Windows 11 SDK are installed.
|
||||||
|
- Use the Developer Command Prompt (VsDevCmd):
|
||||||
|
```powershell
|
||||||
|
cmd /c "call `"C:\\Program Files (x86)\\Microsoft Visual Studio\\2022\\BuildTools\\Common7\\Tools\\VsDevCmd.bat`" -arch=x64 && build.bat"
|
||||||
|
```
|
||||||
|
- Or delete the build folder to clear stale CMake cache and reconfigure:
|
||||||
|
```powershell
|
||||||
|
Remove-Item -Recurse -Force build
|
||||||
|
```
|
||||||
|
|
||||||
|
- `vswhere.exe` not found
|
||||||
|
- Add the VS Installer directory to PATH for the current session:
|
||||||
|
```powershell
|
||||||
|
$env:PATH = 'C:\\Program Files\\Microsoft Visual Studio\\Installer;' + $env:PATH
|
||||||
|
```
|
||||||
|
- Or install/download `vswhere` from Microsoft and place it under the Installer folder.
|
||||||
|
|
||||||
|
- `cryptoppConfig.cmake` / `cryptopp-config.cmake` not found during CMake configure
|
||||||
|
- Make sure CMake uses vcpkg's toolchain file and that the ports are installed for the active triplet:
|
||||||
|
```powershell
|
||||||
|
$env:VCPKG_ROOT = "$env:USERPROFILE\vcpkg"
|
||||||
|
cmake -S . -B build -DCMAKE_TOOLCHAIN_FILE="$env:VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake"
|
||||||
|
& "$env:VCPKG_ROOT\vcpkg.exe" install cryptopp nlohmann-json --triplet x64-windows
|
||||||
|
```
|
||||||
|
|
||||||
|
- PowerShell line continuation issues / parser errors
|
||||||
|
- Prefer single-line commands in PowerShell (avoid backticks if unsure). The README uses single-line examples for reliability.
|
||||||
|
|
||||||
|
## LLVM/clang-cl Coverage (Windows)
|
||||||
|
|
||||||
|
Requirements:
|
||||||
|
- clang/clang-cl toolchain installed
|
||||||
|
- LLVM tools on PATH (`llvm-profdata`, `llvm-cov`)
|
||||||
|
|
||||||
|
Configure with coverage:
|
||||||
|
```powershell
|
||||||
|
cmd /c "call ""C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\Common7\Tools\VsDevCmd.bat"" -arch=x64 && cmake -S . -B build-llvm -G "Ninja" -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang-cl -DENABLE_LLVM_COVERAGE=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE=""%USERPROFILE%\vcpkg\scripts\buildsystems\vcpkg.cmake"" && cmake --build build-llvm --config Release"
|
||||||
|
```
|
||||||
|
|
||||||
|
Run coverage target (executes tests, merges profiles, generates HTML report):
|
||||||
|
```powershell
|
||||||
|
cmd /c "call ""C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\Common7\Tools\VsDevCmd.bat"" -arch=x64 && cmake --build build-llvm --target coverage_llvm --config Release && start build-llvm\coverage\html\index.html"
|
||||||
|
```
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
- If Ninja is not available, you can use the VS generator, but Ninja is recommended for clang.
|
||||||
|
- You can override `LLVM_PROFDATA` and `LLVM_COV` cache variables to absolute tool paths if needed.
|
||||||
1
Testing/Temporary/CTestCostData.txt
Normal file
1
Testing/Temporary/CTestCostData.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
---
|
||||||
1
build_tests/Testing/Temporary/CTestCostData.txt
Normal file
1
build_tests/Testing/Temporary/CTestCostData.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
---
|
||||||
128
build_thinkpad.bat
Normal file
128
build_thinkpad.bat
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
@echo off
|
||||||
|
setlocal ENABLEDELAYEDEXPANSION
|
||||||
|
echo Building PrivateBin API C++ DLL...
|
||||||
|
|
||||||
|
REM Proaktiv: VS-Entwicklungsumgebung initialisieren, wenn bekannt
|
||||||
|
set "VSINSTALL_HINT=C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools"
|
||||||
|
set "VSDEVCMD_HINT=%VSINSTALL_HINT%\Common7\Tools\VsDevCmd.bat"
|
||||||
|
if not exist "%VSDEVCMD_HINT%" (
|
||||||
|
set "VSINSTALL_HINT=C:\Program Files\Microsoft Visual Studio\2022\Community"
|
||||||
|
set "VSDEVCMD_HINT=%VSINSTALL_HINT%\Common7\Tools\VsDevCmd.bat"
|
||||||
|
)
|
||||||
|
if exist "%VSDEVCMD_HINT%" (
|
||||||
|
echo Initializing MSVC environment from "%VSDEVCMD_HINT%" for x64...
|
||||||
|
call "%VSDEVCMD_HINT%" -arch=x64 >nul
|
||||||
|
set "VCPKG_VISUAL_STUDIO_PATH=%VSINSTALL_HINT%"
|
||||||
|
)
|
||||||
|
|
||||||
|
REM Ensure MSVC environment is loaded (cl.exe). Try via vswhere if not present.
|
||||||
|
where cl >nul 2>&1
|
||||||
|
if errorlevel 1 (
|
||||||
|
REM Ensure vswhere is on PATH (both PF and PF(x86))
|
||||||
|
set "PATH=%ProgramFiles%\Microsoft Visual Studio\Installer;%ProgramFiles(x86)%\Microsoft Visual Studio\Installer;%PATH%"
|
||||||
|
REM First try well-known VsDevCmd from BuildTools/Community to avoid vswhere dependency
|
||||||
|
set "VSINSTALL=C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools"
|
||||||
|
set "VSDEVCMD_CANDIDATE=%VSINSTALL%\Common7\Tools\VsDevCmd.bat"
|
||||||
|
if not exist "%VSDEVCMD_CANDIDATE%" (
|
||||||
|
set "VSINSTALL=C:\Program Files\Microsoft Visual Studio\2022\Community"
|
||||||
|
set "VSDEVCMD_CANDIDATE=%VSINSTALL%\Common7\Tools\VsDevCmd.bat"
|
||||||
|
)
|
||||||
|
if exist "%VSDEVCMD_CANDIDATE%" (
|
||||||
|
echo Initializing MSVC environment from "%VSDEVCMD_CANDIDATE%" for x64...
|
||||||
|
call "%VSDEVCMD_CANDIDATE%" -arch=x64 >nul
|
||||||
|
REM Provide VS path hint for vcpkg
|
||||||
|
set "VCPKG_VISUAL_STUDIO_PATH=%VSINSTALL%"
|
||||||
|
REM Re-check if cl is now available; if so, skip vswhere path
|
||||||
|
where cl >nul 2>&1
|
||||||
|
if not errorlevel 1 goto :after_vs_env
|
||||||
|
)
|
||||||
|
|
||||||
|
set "VSWHERE=%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe"
|
||||||
|
if not exist "%VSWHERE%" (
|
||||||
|
set "VSWHERE=%ProgramFiles%\Microsoft Visual Studio\Installer\vswhere.exe"
|
||||||
|
)
|
||||||
|
if not exist "%VSWHERE%" (
|
||||||
|
set "VSWHERE=c:\tools\vswhere.exe"
|
||||||
|
)
|
||||||
|
if exist "%VSWHERE%" (
|
||||||
|
for /f "usebackq tokens=*" %%i in (`"%VSWHERE%" -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath`) do set "VSINSTALL=%%i"
|
||||||
|
if defined VSINSTALL (
|
||||||
|
set "VSDEVCMD=%VSINSTALL%\Common7\Tools\VsDevCmd.bat"
|
||||||
|
if exist "%VSDEVCMD%" (
|
||||||
|
echo Initializing MSVC environment from "%VSDEVCMD%" for x64...
|
||||||
|
call "%VSDEVCMD%" -arch=x64 >nul
|
||||||
|
set "VCPKG_VISUAL_STUDIO_PATH=%VSINSTALL%"
|
||||||
|
) else (
|
||||||
|
echo VsDevCmd.bat not found under "%VSINSTALL%". cl.exe may be unavailable.
|
||||||
|
)
|
||||||
|
) else (
|
||||||
|
echo Visual Studio with C++ tools not found. Install "Desktop development with C++" workload.
|
||||||
|
)
|
||||||
|
) else (
|
||||||
|
echo vswhere.exe not found. Consider installing Visual Studio 2022 with C++ tools.
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
:after_vs_env
|
||||||
|
|
||||||
|
REM Detect or bootstrap vcpkg
|
||||||
|
if not defined VCPKG_ROOT (
|
||||||
|
set "VCPKG_ROOT=%USERPROFILE%\vcpkg"
|
||||||
|
)
|
||||||
|
|
||||||
|
if not exist "%VCPKG_ROOT%\scripts\buildsystems\vcpkg.cmake" (
|
||||||
|
echo vcpkg not found at "%VCPKG_ROOT%". Cloning and bootstrapping...
|
||||||
|
if not exist "%VCPKG_ROOT%" (
|
||||||
|
git clone https://github.com/microsoft/vcpkg.git "%VCPKG_ROOT%"
|
||||||
|
if errorlevel 1 (
|
||||||
|
echo Failed to clone vcpkg
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
)
|
||||||
|
call "%VCPKG_ROOT%\bootstrap-vcpkg.bat"
|
||||||
|
if errorlevel 1 (
|
||||||
|
echo Failed to bootstrap vcpkg
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
REM Ensure vcpkg manifest/config has a baseline; create vcpkg-configuration.json if missing
|
||||||
|
REM Baseline wird außerhalb des Skripts gesetzt (vcpkg x-update-baseline)
|
||||||
|
|
||||||
|
REM Reset build directory to avoid stale CMake cache (e.g., CMAKE_GENERATOR_INSTANCE)
|
||||||
|
if exist "build" rd /s /q build
|
||||||
|
mkdir build
|
||||||
|
pushd build
|
||||||
|
|
||||||
|
REM Generate build files with CMake and vcpkg (manifest mode with auto-baseline)
|
||||||
|
set "VCPKG_DEFAULT_TRIPLET=x64-windows"
|
||||||
|
REM Ensure no stale generator instance is leaking from environment
|
||||||
|
set "CMAKE_GENERATOR_INSTANCE="
|
||||||
|
|
||||||
|
REM Ensure vcpkg manifest has a builtin-baseline (adds if missing)
|
||||||
|
if exist "vcpkg.json" (
|
||||||
|
"%VCPKG_ROOT%\vcpkg.exe" x-update-baseline --add-initial-baseline 1>nul 2>nul
|
||||||
|
)
|
||||||
|
|
||||||
|
REM Use user vcpkg toolchain
|
||||||
|
set "TOOLCHAIN_FILE=%VCPKG_ROOT%/scripts/buildsystems/vcpkg.cmake"
|
||||||
|
cmake .. -G "Visual Studio 17 2022" -A x64 -DCMAKE_TOOLCHAIN_FILE="%TOOLCHAIN_FILE%"
|
||||||
|
if errorlevel 1 (
|
||||||
|
echo CMake configuration failed
|
||||||
|
popd
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
REM Build the project
|
||||||
|
cmake --build . --config Release
|
||||||
|
set BUILD_ERROR=%ERRORLEVEL%
|
||||||
|
|
||||||
|
if %BUILD_ERROR% EQU 0 (
|
||||||
|
echo Build completed successfully!
|
||||||
|
) else (
|
||||||
|
echo Build failed with error level %BUILD_ERROR%
|
||||||
|
)
|
||||||
|
|
||||||
|
popd
|
||||||
|
exit /b %BUILD_ERROR%
|
||||||
|
|
||||||
@ -24,4 +24,13 @@ target_link_libraries(example PRIVATE ${PRIVATEBINAPI_LIB} winhttp)
|
|||||||
|
|
||||||
target_include_directories(example PRIVATE
|
target_include_directories(example PRIVATE
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../include
|
${CMAKE_CURRENT_SOURCE_DIR}/../include
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Install/run helpers for tests: copy DLL next to example on Windows
|
||||||
|
if(WIN32)
|
||||||
|
add_custom_command(TARGET example POST_BUILD
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||||
|
${PRIVATEBINAPI_BUILD_DIR}/Release/privatebinapi.dll
|
||||||
|
$<TARGET_FILE_DIR:example>
|
||||||
|
)
|
||||||
|
endif()
|
||||||
@ -1,11 +1,30 @@
|
|||||||
#include "http_client.h"
|
#include "http_client.h"
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
#ifdef WINDOWS
|
#ifdef WINDOWS
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <winhttp.h>
|
#include <winhttp.h>
|
||||||
#pragma comment(lib, "winhttp.lib")
|
#pragma comment(lib, "winhttp.lib")
|
||||||
|
static std::string last_winhttp_error(const char* where) {
|
||||||
|
DWORD err = GetLastError();
|
||||||
|
LPVOID lpMsgBuf = nullptr;
|
||||||
|
FormatMessageA(
|
||||||
|
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||||
|
NULL,
|
||||||
|
err,
|
||||||
|
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||||
|
(LPSTR)&lpMsgBuf,
|
||||||
|
0, NULL);
|
||||||
|
std::ostringstream os;
|
||||||
|
os << "[WinHTTP] " << where << " failed, error=" << err;
|
||||||
|
if (lpMsgBuf) {
|
||||||
|
os << ": " << (char*)lpMsgBuf;
|
||||||
|
LocalFree(lpMsgBuf);
|
||||||
|
}
|
||||||
|
return os.str();
|
||||||
|
}
|
||||||
|
|
||||||
static std::wstring utf8_to_wide(const std::string& s) {
|
static std::wstring utf8_to_wide(const std::string& s) {
|
||||||
if (s.empty()) return std::wstring();
|
if (s.empty()) return std::wstring();
|
||||||
@ -56,14 +75,28 @@ bool HttpClient::get(const std::string& url, std::string& response) {
|
|||||||
WINHTTP_NO_PROXY_BYPASS, 0);
|
WINHTTP_NO_PROXY_BYPASS, 0);
|
||||||
|
|
||||||
if (!hSession) {
|
if (!hSession) {
|
||||||
|
std::cerr << last_winhttp_error("WinHttpOpen(GET)") << std::endl;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
// Force modern TLS versions to avoid handshake failures on some hosts
|
||||||
|
DWORD protocols = WINHTTP_FLAG_SECURE_PROTOCOL_TLS1 | WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_1 |
|
||||||
|
WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2 | 0x00002000 /* TLS1_3 if available */;
|
||||||
|
WinHttpSetOption(hSession, WINHTTP_OPTION_SECURE_PROTOCOLS, &protocols, sizeof(protocols));
|
||||||
|
// Disable HTTP/2 if it causes issues
|
||||||
|
#ifdef WINHTTP_DISABLE_FEATURE_HTTP2
|
||||||
|
DWORD features = WINHTTP_DISABLE_FEATURE_HTTP2;
|
||||||
|
WinHttpSetOption(hSession, WINHTTP_OPTION_DISABLE_FEATURE, &features, sizeof(features));
|
||||||
|
#endif
|
||||||
|
|
||||||
// Specify an HTTP server
|
// Specify an HTTP server (host must be null-terminated; urlComp provides length)
|
||||||
HINTERNET hConnect = WinHttpConnect(hSession, urlComp.lpszHostName,
|
std::wstring host = (urlComp.lpszHostName && urlComp.dwHostNameLength > 0)
|
||||||
urlComp.nPort, 0);
|
? std::wstring(urlComp.lpszHostName, urlComp.dwHostNameLength)
|
||||||
|
: std::wstring();
|
||||||
|
INTERNET_PORT port = urlComp.nPort ? urlComp.nPort : ((urlComp.nScheme == INTERNET_SCHEME_HTTPS) ? INTERNET_DEFAULT_HTTPS_PORT : INTERNET_DEFAULT_HTTP_PORT);
|
||||||
|
HINTERNET hConnect = WinHttpConnect(hSession, host.c_str(), port, 0);
|
||||||
|
|
||||||
if (!hConnect) {
|
if (!hConnect) {
|
||||||
|
std::cerr << last_winhttp_error("WinHttpConnect(GET)") << std::endl;
|
||||||
WinHttpCloseHandle(hSession);
|
WinHttpCloseHandle(hSession);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -86,7 +119,9 @@ bool HttpClient::get(const std::string& url, std::string& response) {
|
|||||||
WINHTTP_FLAG_SECURE : 0);
|
WINHTTP_FLAG_SECURE : 0);
|
||||||
// Set headers per API requirement
|
// Set headers per API requirement
|
||||||
LPCWSTR headers = L"X-Requested-With: JSONHttpRequest\r\nAccept: application/json";
|
LPCWSTR headers = L"X-Requested-With: JSONHttpRequest\r\nAccept: application/json";
|
||||||
WinHttpAddRequestHeaders(hRequest, headers, -1L, WINHTTP_ADDREQ_FLAG_ADD);
|
if (!WinHttpAddRequestHeaders(hRequest, headers, -1L, WINHTTP_ADDREQ_FLAG_ADD)) {
|
||||||
|
std::cerr << last_winhttp_error("WinHttpAddRequestHeaders(GET)") << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
if (!hRequest) {
|
if (!hRequest) {
|
||||||
WinHttpCloseHandle(hConnect);
|
WinHttpCloseHandle(hConnect);
|
||||||
@ -99,6 +134,7 @@ bool HttpClient::get(const std::string& url, std::string& response) {
|
|||||||
WINHTTP_NO_ADDITIONAL_HEADERS, 0,
|
WINHTTP_NO_ADDITIONAL_HEADERS, 0,
|
||||||
WINHTTP_NO_REQUEST_DATA, 0,
|
WINHTTP_NO_REQUEST_DATA, 0,
|
||||||
0, 0)) {
|
0, 0)) {
|
||||||
|
std::cerr << last_winhttp_error("WinHttpSendRequest(GET)") << std::endl;
|
||||||
WinHttpCloseHandle(hRequest);
|
WinHttpCloseHandle(hRequest);
|
||||||
WinHttpCloseHandle(hConnect);
|
WinHttpCloseHandle(hConnect);
|
||||||
WinHttpCloseHandle(hSession);
|
WinHttpCloseHandle(hSession);
|
||||||
@ -107,6 +143,7 @@ bool HttpClient::get(const std::string& url, std::string& response) {
|
|||||||
|
|
||||||
// End the request
|
// End the request
|
||||||
if (!WinHttpReceiveResponse(hRequest, NULL)) {
|
if (!WinHttpReceiveResponse(hRequest, NULL)) {
|
||||||
|
std::cerr << last_winhttp_error("WinHttpReceiveResponse(GET)") << std::endl;
|
||||||
WinHttpCloseHandle(hRequest);
|
WinHttpCloseHandle(hRequest);
|
||||||
WinHttpCloseHandle(hConnect);
|
WinHttpCloseHandle(hConnect);
|
||||||
WinHttpCloseHandle(hSession);
|
WinHttpCloseHandle(hSession);
|
||||||
@ -122,6 +159,7 @@ bool HttpClient::get(const std::string& url, std::string& response) {
|
|||||||
// Check for available data
|
// Check for available data
|
||||||
dwSize = 0;
|
dwSize = 0;
|
||||||
if (!WinHttpQueryDataAvailable(hRequest, &dwSize)) {
|
if (!WinHttpQueryDataAvailable(hRequest, &dwSize)) {
|
||||||
|
std::cerr << last_winhttp_error("WinHttpQueryDataAvailable(GET)") << std::endl;
|
||||||
WinHttpCloseHandle(hRequest);
|
WinHttpCloseHandle(hRequest);
|
||||||
WinHttpCloseHandle(hConnect);
|
WinHttpCloseHandle(hConnect);
|
||||||
WinHttpCloseHandle(hSession);
|
WinHttpCloseHandle(hSession);
|
||||||
@ -140,6 +178,7 @@ bool HttpClient::get(const std::string& url, std::string& response) {
|
|||||||
// Read the data
|
// Read the data
|
||||||
ZeroMemory(pszOutBuffer, dwSize + 1);
|
ZeroMemory(pszOutBuffer, dwSize + 1);
|
||||||
if (!WinHttpReadData(hRequest, (LPVOID)pszOutBuffer, dwSize, &dwDownloaded)) {
|
if (!WinHttpReadData(hRequest, (LPVOID)pszOutBuffer, dwSize, &dwDownloaded)) {
|
||||||
|
std::cerr << last_winhttp_error("WinHttpReadData(GET)") << std::endl;
|
||||||
delete[] pszOutBuffer;
|
delete[] pszOutBuffer;
|
||||||
WinHttpCloseHandle(hRequest);
|
WinHttpCloseHandle(hRequest);
|
||||||
WinHttpCloseHandle(hConnect);
|
WinHttpCloseHandle(hConnect);
|
||||||
@ -189,14 +228,26 @@ bool HttpClient::post(const std::string& url, const std::string& data, std::stri
|
|||||||
WINHTTP_NO_PROXY_BYPASS, 0);
|
WINHTTP_NO_PROXY_BYPASS, 0);
|
||||||
|
|
||||||
if (!hSession) {
|
if (!hSession) {
|
||||||
|
std::cerr << last_winhttp_error("WinHttpOpen(POST)") << std::endl;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
DWORD protocols2 = WINHTTP_FLAG_SECURE_PROTOCOL_TLS1 | WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_1 |
|
||||||
|
WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2 | 0x00002000;
|
||||||
|
WinHttpSetOption(hSession, WINHTTP_OPTION_SECURE_PROTOCOLS, &protocols2, sizeof(protocols2));
|
||||||
|
#ifdef WINHTTP_DISABLE_FEATURE_HTTP2
|
||||||
|
DWORD features2 = WINHTTP_DISABLE_FEATURE_HTTP2;
|
||||||
|
WinHttpSetOption(hSession, WINHTTP_OPTION_DISABLE_FEATURE, &features2, sizeof(features2));
|
||||||
|
#endif
|
||||||
|
|
||||||
// Specify an HTTP server
|
// Specify an HTTP server (ensure host is null-terminated)
|
||||||
HINTERNET hConnect = WinHttpConnect(hSession, urlComp.lpszHostName,
|
std::wstring host2 = (urlComp.lpszHostName && urlComp.dwHostNameLength > 0)
|
||||||
urlComp.nPort, 0);
|
? std::wstring(urlComp.lpszHostName, urlComp.dwHostNameLength)
|
||||||
|
: std::wstring();
|
||||||
|
INTERNET_PORT port2 = urlComp.nPort ? urlComp.nPort : ((urlComp.nScheme == INTERNET_SCHEME_HTTPS) ? INTERNET_DEFAULT_HTTPS_PORT : INTERNET_DEFAULT_HTTP_PORT);
|
||||||
|
HINTERNET hConnect = WinHttpConnect(hSession, host2.c_str(), port2, 0);
|
||||||
|
|
||||||
if (!hConnect) {
|
if (!hConnect) {
|
||||||
|
std::cerr << last_winhttp_error("WinHttpConnect(POST)") << std::endl;
|
||||||
WinHttpCloseHandle(hSession);
|
WinHttpCloseHandle(hSession);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -211,6 +262,7 @@ bool HttpClient::post(const std::string& url, const std::string& data, std::stri
|
|||||||
WINHTTP_FLAG_SECURE : 0);
|
WINHTTP_FLAG_SECURE : 0);
|
||||||
|
|
||||||
if (!hRequest) {
|
if (!hRequest) {
|
||||||
|
std::cerr << "[WinHTTP] WinHttpOpenRequest(POST) failed" << std::endl;
|
||||||
WinHttpCloseHandle(hConnect);
|
WinHttpCloseHandle(hConnect);
|
||||||
WinHttpCloseHandle(hSession);
|
WinHttpCloseHandle(hSession);
|
||||||
return false;
|
return false;
|
||||||
@ -224,6 +276,7 @@ bool HttpClient::post(const std::string& url, const std::string& data, std::stri
|
|||||||
WINHTTP_ADDREQ_FLAG_ADD);
|
WINHTTP_ADDREQ_FLAG_ADD);
|
||||||
|
|
||||||
if (!bResults) {
|
if (!bResults) {
|
||||||
|
std::cerr << last_winhttp_error("WinHttpAddRequestHeaders(POST)") << std::endl;
|
||||||
WinHttpCloseHandle(hRequest);
|
WinHttpCloseHandle(hRequest);
|
||||||
WinHttpCloseHandle(hConnect);
|
WinHttpCloseHandle(hConnect);
|
||||||
WinHttpCloseHandle(hSession);
|
WinHttpCloseHandle(hSession);
|
||||||
@ -236,6 +289,7 @@ bool HttpClient::post(const std::string& url, const std::string& data, std::stri
|
|||||||
WINHTTP_NO_ADDITIONAL_HEADERS, 0,
|
WINHTTP_NO_ADDITIONAL_HEADERS, 0,
|
||||||
(LPVOID)data.c_str(), (DWORD)data.length(),
|
(LPVOID)data.c_str(), (DWORD)data.length(),
|
||||||
(DWORD)data.length(), 0)) {
|
(DWORD)data.length(), 0)) {
|
||||||
|
std::cerr << last_winhttp_error("WinHttpSendRequest(POST)") << std::endl;
|
||||||
WinHttpCloseHandle(hRequest);
|
WinHttpCloseHandle(hRequest);
|
||||||
WinHttpCloseHandle(hConnect);
|
WinHttpCloseHandle(hConnect);
|
||||||
WinHttpCloseHandle(hSession);
|
WinHttpCloseHandle(hSession);
|
||||||
@ -244,6 +298,7 @@ bool HttpClient::post(const std::string& url, const std::string& data, std::stri
|
|||||||
|
|
||||||
// End the request
|
// End the request
|
||||||
if (!WinHttpReceiveResponse(hRequest, NULL)) {
|
if (!WinHttpReceiveResponse(hRequest, NULL)) {
|
||||||
|
std::cerr << last_winhttp_error("WinHttpReceiveResponse(POST)") << std::endl;
|
||||||
WinHttpCloseHandle(hRequest);
|
WinHttpCloseHandle(hRequest);
|
||||||
WinHttpCloseHandle(hConnect);
|
WinHttpCloseHandle(hConnect);
|
||||||
WinHttpCloseHandle(hSession);
|
WinHttpCloseHandle(hSession);
|
||||||
@ -259,6 +314,7 @@ bool HttpClient::post(const std::string& url, const std::string& data, std::stri
|
|||||||
// Check for available data
|
// Check for available data
|
||||||
dwSize = 0;
|
dwSize = 0;
|
||||||
if (!WinHttpQueryDataAvailable(hRequest, &dwSize)) {
|
if (!WinHttpQueryDataAvailable(hRequest, &dwSize)) {
|
||||||
|
std::cerr << last_winhttp_error("WinHttpQueryDataAvailable(POST)") << std::endl;
|
||||||
WinHttpCloseHandle(hRequest);
|
WinHttpCloseHandle(hRequest);
|
||||||
WinHttpCloseHandle(hConnect);
|
WinHttpCloseHandle(hConnect);
|
||||||
WinHttpCloseHandle(hSession);
|
WinHttpCloseHandle(hSession);
|
||||||
@ -277,6 +333,7 @@ bool HttpClient::post(const std::string& url, const std::string& data, std::stri
|
|||||||
// Read the data
|
// Read the data
|
||||||
ZeroMemory(pszOutBuffer, dwSize + 1);
|
ZeroMemory(pszOutBuffer, dwSize + 1);
|
||||||
if (!WinHttpReadData(hRequest, (LPVOID)pszOutBuffer, dwSize, &dwDownloaded)) {
|
if (!WinHttpReadData(hRequest, (LPVOID)pszOutBuffer, dwSize, &dwDownloaded)) {
|
||||||
|
std::cerr << last_winhttp_error("WinHttpReadData(POST)") << std::endl;
|
||||||
delete[] pszOutBuffer;
|
delete[] pszOutBuffer;
|
||||||
WinHttpCloseHandle(hRequest);
|
WinHttpCloseHandle(hRequest);
|
||||||
WinHttpCloseHandle(hConnect);
|
WinHttpCloseHandle(hConnect);
|
||||||
@ -326,14 +383,26 @@ bool HttpClient::delete_req(const std::string& url, const std::string& data, std
|
|||||||
WINHTTP_NO_PROXY_BYPASS, 0);
|
WINHTTP_NO_PROXY_BYPASS, 0);
|
||||||
|
|
||||||
if (!hSession) {
|
if (!hSession) {
|
||||||
|
std::cerr << last_winhttp_error("WinHttpOpen(DEL)") << std::endl;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
DWORD protocols3 = WINHTTP_FLAG_SECURE_PROTOCOL_TLS1 | WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_1 |
|
||||||
|
WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2 | 0x00002000;
|
||||||
|
WinHttpSetOption(hSession, WINHTTP_OPTION_SECURE_PROTOCOLS, &protocols3, sizeof(protocols3));
|
||||||
|
#ifdef WINHTTP_DISABLE_FEATURE_HTTP2
|
||||||
|
DWORD features3 = WINHTTP_DISABLE_FEATURE_HTTP2;
|
||||||
|
WinHttpSetOption(hSession, WINHTTP_OPTION_DISABLE_FEATURE, &features3, sizeof(features3));
|
||||||
|
#endif
|
||||||
|
|
||||||
// Specify an HTTP server
|
// Specify an HTTP server (ensure host is null-terminated)
|
||||||
HINTERNET hConnect = WinHttpConnect(hSession, urlComp.lpszHostName,
|
std::wstring host3 = (urlComp.lpszHostName && urlComp.dwHostNameLength > 0)
|
||||||
urlComp.nPort, 0);
|
? std::wstring(urlComp.lpszHostName, urlComp.dwHostNameLength)
|
||||||
|
: std::wstring();
|
||||||
|
INTERNET_PORT port3 = urlComp.nPort ? urlComp.nPort : ((urlComp.nScheme == INTERNET_SCHEME_HTTPS) ? INTERNET_DEFAULT_HTTPS_PORT : INTERNET_DEFAULT_HTTP_PORT);
|
||||||
|
HINTERNET hConnect = WinHttpConnect(hSession, host3.c_str(), port3, 0);
|
||||||
|
|
||||||
if (!hConnect) {
|
if (!hConnect) {
|
||||||
|
std::cerr << last_winhttp_error("WinHttpConnect(DEL)") << std::endl;
|
||||||
WinHttpCloseHandle(hSession);
|
WinHttpCloseHandle(hSession);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -348,6 +417,7 @@ bool HttpClient::delete_req(const std::string& url, const std::string& data, std
|
|||||||
WINHTTP_FLAG_SECURE : 0);
|
WINHTTP_FLAG_SECURE : 0);
|
||||||
|
|
||||||
if (!hRequest) {
|
if (!hRequest) {
|
||||||
|
std::cerr << "[WinHTTP] WinHttpOpenRequest(DEL) failed" << std::endl;
|
||||||
WinHttpCloseHandle(hConnect);
|
WinHttpCloseHandle(hConnect);
|
||||||
WinHttpCloseHandle(hSession);
|
WinHttpCloseHandle(hSession);
|
||||||
return false;
|
return false;
|
||||||
@ -361,6 +431,7 @@ bool HttpClient::delete_req(const std::string& url, const std::string& data, std
|
|||||||
WINHTTP_ADDREQ_FLAG_ADD);
|
WINHTTP_ADDREQ_FLAG_ADD);
|
||||||
|
|
||||||
if (!bResults) {
|
if (!bResults) {
|
||||||
|
std::cerr << last_winhttp_error("WinHttpAddRequestHeaders(DEL)") << std::endl;
|
||||||
WinHttpCloseHandle(hRequest);
|
WinHttpCloseHandle(hRequest);
|
||||||
WinHttpCloseHandle(hConnect);
|
WinHttpCloseHandle(hConnect);
|
||||||
WinHttpCloseHandle(hSession);
|
WinHttpCloseHandle(hSession);
|
||||||
@ -372,6 +443,7 @@ bool HttpClient::delete_req(const std::string& url, const std::string& data, std
|
|||||||
WINHTTP_NO_ADDITIONAL_HEADERS, 0,
|
WINHTTP_NO_ADDITIONAL_HEADERS, 0,
|
||||||
(LPVOID)data.c_str(), (DWORD)data.length(),
|
(LPVOID)data.c_str(), (DWORD)data.length(),
|
||||||
(DWORD)data.length(), 0)) {
|
(DWORD)data.length(), 0)) {
|
||||||
|
std::cerr << last_winhttp_error("WinHttpSendRequest(DEL)") << std::endl;
|
||||||
WinHttpCloseHandle(hRequest);
|
WinHttpCloseHandle(hRequest);
|
||||||
WinHttpCloseHandle(hConnect);
|
WinHttpCloseHandle(hConnect);
|
||||||
WinHttpCloseHandle(hSession);
|
WinHttpCloseHandle(hSession);
|
||||||
@ -380,6 +452,7 @@ bool HttpClient::delete_req(const std::string& url, const std::string& data, std
|
|||||||
|
|
||||||
// End the request
|
// End the request
|
||||||
if (!WinHttpReceiveResponse(hRequest, NULL)) {
|
if (!WinHttpReceiveResponse(hRequest, NULL)) {
|
||||||
|
std::cerr << last_winhttp_error("WinHttpReceiveResponse(DEL)") << std::endl;
|
||||||
WinHttpCloseHandle(hRequest);
|
WinHttpCloseHandle(hRequest);
|
||||||
WinHttpCloseHandle(hConnect);
|
WinHttpCloseHandle(hConnect);
|
||||||
WinHttpCloseHandle(hSession);
|
WinHttpCloseHandle(hSession);
|
||||||
@ -395,6 +468,7 @@ bool HttpClient::delete_req(const std::string& url, const std::string& data, std
|
|||||||
// Check for available data
|
// Check for available data
|
||||||
dwSize = 0;
|
dwSize = 0;
|
||||||
if (!WinHttpQueryDataAvailable(hRequest, &dwSize)) {
|
if (!WinHttpQueryDataAvailable(hRequest, &dwSize)) {
|
||||||
|
std::cerr << last_winhttp_error("WinHttpQueryDataAvailable(DEL)") << std::endl;
|
||||||
WinHttpCloseHandle(hRequest);
|
WinHttpCloseHandle(hRequest);
|
||||||
WinHttpCloseHandle(hConnect);
|
WinHttpCloseHandle(hConnect);
|
||||||
WinHttpCloseHandle(hSession);
|
WinHttpCloseHandle(hSession);
|
||||||
@ -413,6 +487,7 @@ bool HttpClient::delete_req(const std::string& url, const std::string& data, std
|
|||||||
// Read the data
|
// Read the data
|
||||||
ZeroMemory(pszOutBuffer, dwSize + 1);
|
ZeroMemory(pszOutBuffer, dwSize + 1);
|
||||||
if (!WinHttpReadData(hRequest, (LPVOID)pszOutBuffer, dwSize, &dwDownloaded)) {
|
if (!WinHttpReadData(hRequest, (LPVOID)pszOutBuffer, dwSize, &dwDownloaded)) {
|
||||||
|
std::cerr << last_winhttp_error("WinHttpReadData(DEL)") << std::endl;
|
||||||
delete[] pszOutBuffer;
|
delete[] pszOutBuffer;
|
||||||
WinHttpCloseHandle(hRequest);
|
WinHttpCloseHandle(hRequest);
|
||||||
WinHttpCloseHandle(hConnect);
|
WinHttpCloseHandle(hConnect);
|
||||||
|
|||||||
@ -105,8 +105,18 @@ bool JsonParser::parse_paste_json(const json& json_data,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract expiration
|
// Extract expiration; servers may return either meta.expire (string)
|
||||||
expiration = json_data.at("meta").at("expire");
|
// or meta.time_to_live (integer seconds). Both are optional for our use.
|
||||||
|
try {
|
||||||
|
expiration = json_data.at("meta").at("expire");
|
||||||
|
} catch (...) {
|
||||||
|
try {
|
||||||
|
auto ttl = json_data.at("meta").at("time_to_live").get<int>();
|
||||||
|
expiration = std::to_string(ttl);
|
||||||
|
} catch (...) {
|
||||||
|
expiration.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
|
|||||||
@ -3,23 +3,35 @@ project(PrivateBinAPITests)
|
|||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 17)
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
|
|
||||||
# Find the privatebinapi library
|
|
||||||
find_library(PRIVATEBINAPI_LIB privatebinapi
|
|
||||||
PATHS ${CMAKE_CURRENT_SOURCE_DIR}/../build)
|
|
||||||
|
|
||||||
# If not found, build it as part of the project
|
|
||||||
if(NOT PRIVATEBINAPI_LIB)
|
|
||||||
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/.. ${CMAKE_CURRENT_BINARY_DIR}/privatebinapi)
|
|
||||||
set(PRIVATEBINAPI_LIB privatebinapi)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Create test executable
|
# Create test executable
|
||||||
add_executable(test_basic test_basic.cpp)
|
add_executable(test_basic test_basic.cpp)
|
||||||
|
|
||||||
# Link with the privatebinapi library
|
# Link with the already-defined privatebinapi target from the root project
|
||||||
target_link_libraries(test_basic ${PRIVATEBINAPI_LIB})
|
target_link_libraries(test_basic PRIVATE privatebinapi)
|
||||||
|
|
||||||
|
# Ensure the DLL is available next to the test executable on Windows
|
||||||
|
if(WIN32)
|
||||||
|
add_dependencies(test_basic privatebinapi)
|
||||||
|
add_custom_command(TARGET test_basic POST_BUILD
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||||
|
$<TARGET_FILE:privatebinapi>
|
||||||
|
$<TARGET_FILE_DIR:test_basic>
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
# Include directories
|
# Include directories
|
||||||
target_include_directories(test_basic PRIVATE
|
target_include_directories(test_basic PRIVATE
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../include
|
${CMAKE_CURRENT_SOURCE_DIR}/../include
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Register the test with CTest
|
||||||
|
add_test(NAME test_basic COMMAND test_basic)
|
||||||
|
|
||||||
|
# Optional example run as a test (requires network and live server)
|
||||||
|
# Example-Test nur aktiv, wenn PRIVATEBIN_IT=1 gesetzt ist
|
||||||
|
add_test(NAME example_run COMMAND $<TARGET_FILE:example>)
|
||||||
|
if(DEFINED ENV{PRIVATEBIN_IT} AND NOT "$ENV{PRIVATEBIN_IT}" STREQUAL "0")
|
||||||
|
set_tests_properties(example_run PROPERTIES DISABLED FALSE)
|
||||||
|
else()
|
||||||
|
set_tests_properties(example_run PROPERTIES DISABLED TRUE)
|
||||||
|
endif()
|
||||||
@ -1,15 +1,90 @@
|
|||||||
|
// Network integration test hitting a live PrivateBin instance (optional)
|
||||||
|
// Server under test: https://privatebin.medisoftware.org/
|
||||||
|
// Enable by setting environment variable PRIVATEBIN_IT=1
|
||||||
|
|
||||||
#include "privatebinapi.h"
|
#include "privatebinapi.h"
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
#include <cstdlib>
|
||||||
|
|
||||||
|
static bool extract_paste_id_and_key(const std::string& full_url, std::string& paste_id, std::string& key) {
|
||||||
|
// Expected format: https://host/path?PASTEID#BASE58_KEY
|
||||||
|
const std::size_t qpos = full_url.find('?');
|
||||||
|
const std::size_t hpos = full_url.find('#');
|
||||||
|
if (qpos == std::string::npos || hpos == std::string::npos || hpos <= qpos + 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
paste_id = full_url.substr(qpos + 1, hpos - (qpos + 1));
|
||||||
|
key = full_url.substr(hpos + 1);
|
||||||
|
return !paste_id.empty() && !key.empty();
|
||||||
|
}
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
// Test Base58 encoding/decoding
|
const char* it = std::getenv("PRIVATEBIN_IT");
|
||||||
std::cout << "Testing Base58 encoding/decoding..." << std::endl;
|
if (!it || std::string(it) == "0") {
|
||||||
|
std::cout << "[test] PRIVATEBIN_IT not set; skipping integration test." << std::endl;
|
||||||
// This is just a basic test to verify the API compiles and links
|
return 0; // treat as success when integration testing is disabled
|
||||||
// In a real test, we would test actual functionality
|
}
|
||||||
|
const char* server = "https://privatebin.medisoftware.org/";
|
||||||
std::cout << "API test completed successfully!" << std::endl;
|
const char* content = "Integration test from lib-privatebin";
|
||||||
|
const char* password = nullptr; // no password
|
||||||
|
const char* expiration = "5min"; // short-lived
|
||||||
|
const char* format = "plaintext";
|
||||||
|
const int burn_after_reading = 0;
|
||||||
|
const int open_discussion = 0;
|
||||||
|
|
||||||
|
char* paste_url = nullptr;
|
||||||
|
char* delete_token = nullptr;
|
||||||
|
|
||||||
|
std::cout << "[test] create_paste..." << std::endl;
|
||||||
|
int rc = create_paste(server, content, password, expiration, format,
|
||||||
|
burn_after_reading, open_discussion, &paste_url, &delete_token);
|
||||||
|
if (rc != 0 || paste_url == nullptr || delete_token == nullptr) {
|
||||||
|
std::cerr << "[test][ERROR] create_paste failed, rc=" << rc << std::endl;
|
||||||
|
if (paste_url) free_string(paste_url);
|
||||||
|
if (delete_token) free_string(delete_token);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string full_url = paste_url;
|
||||||
|
std::string paste_id;
|
||||||
|
std::string key;
|
||||||
|
const bool ok_parse = extract_paste_id_and_key(full_url, paste_id, key);
|
||||||
|
if (!ok_parse) {
|
||||||
|
std::cerr << "[test][ERROR] failed to parse paste id/key from URL: " << full_url << std::endl;
|
||||||
|
free_string(paste_url);
|
||||||
|
free_string(delete_token);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "[test] get_paste... id=" << paste_id << std::endl;
|
||||||
|
char* fetched = nullptr;
|
||||||
|
rc = get_paste(server, paste_id.c_str(), key.c_str(), &fetched);
|
||||||
|
if (rc != 0 || fetched == nullptr) {
|
||||||
|
std::cerr << "[test][ERROR] get_paste failed, rc=" << rc << std::endl;
|
||||||
|
free_string(paste_url);
|
||||||
|
free_string(delete_token);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
std::string fetched_str = fetched;
|
||||||
|
assert(fetched_str == content && "fetched content mismatch");
|
||||||
|
|
||||||
|
std::cout << "[test] delete_paste..." << std::endl;
|
||||||
|
rc = delete_paste(server, paste_id.c_str(), delete_token);
|
||||||
|
if (rc != 0) {
|
||||||
|
std::cerr << "[test][ERROR] delete_paste failed, rc=" << rc << std::endl;
|
||||||
|
free_string(paste_url);
|
||||||
|
free_string(delete_token);
|
||||||
|
free_string(fetched);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// cleanup
|
||||||
|
free_string(paste_url);
|
||||||
|
free_string(delete_token);
|
||||||
|
free_string(fetched);
|
||||||
|
|
||||||
|
std::cout << "[test] all API functions passed." << std::endl;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
10
vcpkg.json
10
vcpkg.json
@ -1 +1,9 @@
|
|||||||
{"name": "privatebin-api", "version-string": "1.0.0", "dependencies": ["cryptopp", "nlohmann-json"]}
|
{
|
||||||
|
"name": "privatebin-api",
|
||||||
|
"version-string": "1.0.0",
|
||||||
|
"dependencies": [
|
||||||
|
"cryptopp",
|
||||||
|
"nlohmann-json"
|
||||||
|
],
|
||||||
|
"builtin-baseline": "120deac3062162151622ca4860575a33844ba10b"
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user