feat: v1.3-konforme API, HTTP-Fixes, Base64, Example erweitert; Makefile & README Setup

This commit is contained in:
2025-08-28 10:32:16 +02:00
parent 29818a5708
commit 9e4a65fadd
8 changed files with 209 additions and 95 deletions

View File

@ -14,42 +14,60 @@ 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
## Building ## Entwicklung & Build
### Prerequisites ### Voraussetzungen
- CMake 3.10 or later - CMake 3.10+
- C++17 compatible compiler - C++17-fähiger Compiler (MSVC 2022 bzw. GCC/Clang)
- For Windows: Windows SDK - Git
- For Linux: libcurl development headers - vcpkg (wird bei Makefile-Nutzung automatisch gebootstrapped)
### Dependencies ### Abhängigkeiten
The library depends on the following components: - cryptopp (Crypto++)
- nlohmann-json
- Windows: WinHTTP (SDK)
- Linux: libcurl (wird automatisch über vcpkg genutzt)
1. **HTTP Client**: ### Schnellstart mit Makefile (Windows & Linux)
- Windows: WinHTTP
- Linux: libcurl
2. **JSON Processing**: 1) Abhängigkeiten installieren, konfigurieren und bauen:
- nlohmann/json
### Building on Windows ```
make
```cmd
mkdir build
cd build
cmake ..
cmake --build .
``` ```
### Building on Linux 2) Beispiel bauen und ausführen:
```bash ```
mkdir build make example
cd build ```
cmake ..
make Das Makefile erledigt:
- vcpkg klonen & bootstrappen
- Pakete aus `vcpkg.json` installieren
- CMake mit vcpkg-Toolchain konfigurieren
- Bibliothek und Beispiel bauen
### Manuell mit CMake
```
# vcpkg klonen & bootstrappen
git clone https://github.com/microsoft/vcpkg.git "$HOME/vcpkg"
"$HOME/vcpkg/bootstrap-vcpkg.sh" # Linux/macOS
# Windows (PowerShell):
# powershell -NoProfile -ExecutionPolicy Bypass -Command "& '$env:USERPROFILE\vcpkg\bootstrap-vcpkg.bat'"
# Konfigurieren
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE="$HOME/vcpkg/scripts/buildsystems/vcpkg.cmake"
# Bauen
cmake --build build --config Release
# Beispiel
cmake -S example -B example/build -DCMAKE_BUILD_TYPE=Release
cmake --build example/build --config Release
``` ```
## Usage ## Usage

View File

@ -5,7 +5,7 @@ REM Create build directory
if not exist "build" mkdir build if not exist "build" mkdir build
cd build cd build
REM Generate build files with CMake and vcpkg toolchain REM Generate build files with CMake and vcpkg manifest
cmake .. -G "Visual Studio 17 2022" -DCMAKE_TOOLCHAIN_FILE=../vcpkg/scripts/buildsystems/vcpkg.cmake cmake .. -G "Visual Studio 17 2022" -DCMAKE_TOOLCHAIN_FILE=../vcpkg/scripts/buildsystems/vcpkg.cmake
REM Build the project REM Build the project

View File

@ -3,23 +3,25 @@ project(PrivateBinAPIExample)
set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD 17)
# Find the privatebinapi library # Use the prebuilt library from the parent build directory
find_library(PRIVATEBINAPI_LIB privatebinapi set(PRIVATEBINAPI_BUILD_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../build")
PATHS ${CMAKE_CURRENT_SOURCE_DIR}/../build) set(PRIVATEBINAPI_RELEASE_LIB "${PRIVATEBINAPI_BUILD_DIR}/Release/privatebinapi.lib")
# If not found, build it as part of the project if(EXISTS "${PRIVATEBINAPI_RELEASE_LIB}")
if(NOT PRIVATEBINAPI_LIB) set(PRIVATEBINAPI_LIB "${PRIVATEBINAPI_RELEASE_LIB}")
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/.. ${CMAKE_CURRENT_BINARY_DIR}/privatebinapi) else()
set(PRIVATEBINAPI_LIB privatebinapi) # Fallback: try the build root (multi-config generators may place libs differently)
find_library(PRIVATEBINAPI_LIB privatebinapi PATHS "${PRIVATEBINAPI_BUILD_DIR}")
endif()
if(NOT PRIVATEBINAPI_LIB)
message(FATAL_ERROR "privatebinapi library not found. Please run build.bat in the project root first.")
endif() endif()
# Create example executable
add_executable(example example.cpp) add_executable(example example.cpp)
# Link with the privatebinapi library target_link_libraries(example PRIVATE ${PRIVATEBINAPI_LIB} winhttp)
target_link_libraries(example ${PRIVATEBINAPI_LIB})
# Include directories
target_include_directories(example PRIVATE target_include_directories(example PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/../include ${CMAKE_CURRENT_SOURCE_DIR}/../include
) )

View File

@ -31,7 +31,41 @@ int main() {
std::cout << "Paste created successfully!" << std::endl; std::cout << "Paste created successfully!" << std::endl;
std::cout << "URL: " << paste_url << std::endl; std::cout << "URL: " << paste_url << std::endl;
std::cout << "Delete token: " << delete_token << std::endl; std::cout << "Delete token: " << delete_token << std::endl;
// Parse paste_id and key from URL (format: "/?{pasteID}#{key}")
std::string full_url = paste_url ? paste_url : "";
std::string paste_id;
std::string key;
auto qpos = full_url.find('?');
auto hpos = full_url.find('#');
if (qpos != std::string::npos) {
if (hpos != std::string::npos && hpos > qpos + 1) {
paste_id = full_url.substr(qpos + 1, hpos - (qpos + 1));
key = full_url.substr(hpos + 1);
} else if (qpos + 1 < full_url.size()) {
paste_id = full_url.substr(qpos + 1);
}
}
// Try to fetch paste content back
if (!paste_id.empty() && !key.empty()) {
std::cout << "Fetching paste..." << std::endl;
char* content = nullptr;
int gr = get_paste("https://privatebin.medisoftware.org", paste_id.c_str(), key.c_str(), &content);
std::cout << "get_paste returned: " << gr << std::endl;
if (gr == 0 && content) {
std::cout << "Content: " << content << std::endl;
free_string(content);
}
}
// Try to delete paste
if (!paste_id.empty() && delete_token) {
std::cout << "Deleting paste..." << std::endl;
int dr = delete_paste("https://privatebin.medisoftware.org", paste_id.c_str(), delete_token);
std::cout << "delete_paste returned: " << dr << std::endl;
}
// Clean up allocated memory // Clean up allocated memory
free_string(paste_url); free_string(paste_url);
free_string(delete_token); free_string(delete_token);

View File

@ -4,13 +4,13 @@
#include <cstring> #include <cstring>
// Crypto++ includes // Crypto++ includes
#include "cryptlib.h" #include <cryptopp/cryptlib.h>
#include "osrng.h" // AutoSeededRandomPool #include <cryptopp/osrng.h> // AutoSeededRandomPool
#include "aes.h" // AES encryption #include <cryptopp/aes.h> // AES encryption
#include "gcm.h" // GCM mode #include <cryptopp/gcm.h> // GCM mode
#include "pwdbased.h" // PBKDF2 #include <cryptopp/pwdbased.h> // PBKDF2
#include "sha.h" // SHA256 #include <cryptopp/sha.h> // SHA256
#include "zlib.h" // Zlib compression #include <cryptopp/zlib.h> // Zlib compression
using namespace CryptoPP; using namespace CryptoPP;

View File

@ -6,6 +6,14 @@
#include <windows.h> #include <windows.h>
#include <winhttp.h> #include <winhttp.h>
#pragma comment(lib, "winhttp.lib") #pragma comment(lib, "winhttp.lib")
static std::wstring utf8_to_wide(const std::string& s) {
if (s.empty()) return std::wstring();
int needed = MultiByteToWideChar(CP_UTF8, 0, s.c_str(), (int)s.size(), nullptr, 0);
std::wstring ws(needed, L'\0');
MultiByteToWideChar(CP_UTF8, 0, s.c_str(), (int)s.size(), &ws[0], needed);
return ws;
}
#elif LINUX #elif LINUX
#include <curl/curl.h> #include <curl/curl.h>
#endif #endif
@ -36,7 +44,8 @@ bool HttpClient::get(const std::string& url, std::string& response) {
urlComp.dwExtraInfoLength = (DWORD)-1; urlComp.dwExtraInfoLength = (DWORD)-1;
// Parse the URL // Parse the URL
if (!WinHttpCrackUrl((LPCWSTR)url.c_str(), 0, 0, &urlComp)) { std::wstring wurl = utf8_to_wide(url);
if (!WinHttpCrackUrl(wurl.c_str(), 0, 0, &urlComp)) {
return false; return false;
} }
@ -59,13 +68,25 @@ bool HttpClient::get(const std::string& url, std::string& response) {
return false; return false;
} }
// Build object name = path + extra info (query)
std::wstring pathPart = (urlComp.lpszUrlPath && urlComp.dwUrlPathLength > 0)
? std::wstring(urlComp.lpszUrlPath, urlComp.dwUrlPathLength)
: std::wstring(L"/");
std::wstring extraPart = (urlComp.lpszExtraInfo && urlComp.dwExtraInfoLength > 0)
? std::wstring(urlComp.lpszExtraInfo, urlComp.dwExtraInfoLength)
: std::wstring();
std::wstring objectName = pathPart + extraPart;
// Create an HTTP request handle // Create an HTTP request handle
HINTERNET hRequest = WinHttpOpenRequest(hConnect, L"GET", HINTERNET hRequest = WinHttpOpenRequest(hConnect, L"GET",
urlComp.lpszUrlPath, objectName.c_str(),
NULL, WINHTTP_NO_REFERER, NULL, WINHTTP_NO_REFERER,
WINHTTP_DEFAULT_ACCEPT_TYPES, WINHTTP_DEFAULT_ACCEPT_TYPES,
(urlComp.nScheme == INTERNET_SCHEME_HTTPS) ? (urlComp.nScheme == INTERNET_SCHEME_HTTPS) ?
WINHTTP_FLAG_SECURE : 0); WINHTTP_FLAG_SECURE : 0);
// Set headers per API requirement
LPCWSTR headers = L"X-Requested-With: JSONHttpRequest\r\nAccept: application/json";
WinHttpAddRequestHeaders(hRequest, headers, -1L, WINHTTP_ADDREQ_FLAG_ADD);
if (!hRequest) { if (!hRequest) {
WinHttpCloseHandle(hConnect); WinHttpCloseHandle(hConnect);
@ -156,7 +177,8 @@ bool HttpClient::post(const std::string& url, const std::string& data, std::stri
urlComp.dwExtraInfoLength = (DWORD)-1; urlComp.dwExtraInfoLength = (DWORD)-1;
// Parse the URL // Parse the URL
if (!WinHttpCrackUrl((LPCWSTR)url.c_str(), 0, 0, &urlComp)) { std::wstring wurl = utf8_to_wide(url);
if (!WinHttpCrackUrl(wurl.c_str(), 0, 0, &urlComp)) {
return false; return false;
} }
@ -179,9 +201,10 @@ bool HttpClient::post(const std::string& url, const std::string& data, std::stri
return false; return false;
} }
// Create an HTTP request handle // Create an HTTP request handle (POST per API 1.3)
LPCWSTR postPath = (urlComp.lpszUrlPath && urlComp.dwUrlPathLength > 0) ? urlComp.lpszUrlPath : L"/";
HINTERNET hRequest = WinHttpOpenRequest(hConnect, L"POST", HINTERNET hRequest = WinHttpOpenRequest(hConnect, L"POST",
urlComp.lpszUrlPath, postPath,
NULL, WINHTTP_NO_REFERER, NULL, WINHTTP_NO_REFERER,
WINHTTP_DEFAULT_ACCEPT_TYPES, WINHTTP_DEFAULT_ACCEPT_TYPES,
(urlComp.nScheme == INTERNET_SCHEME_HTTPS) ? (urlComp.nScheme == INTERNET_SCHEME_HTTPS) ?
@ -208,6 +231,7 @@ bool HttpClient::post(const std::string& url, const std::string& data, std::stri
} }
// Send a request // Send a request
// Send UTF-8 bytes as body (raw)
if (!WinHttpSendRequest(hRequest, if (!WinHttpSendRequest(hRequest,
WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_ADDITIONAL_HEADERS, 0,
(LPVOID)data.c_str(), (DWORD)data.length(), (LPVOID)data.c_str(), (DWORD)data.length(),
@ -290,7 +314,8 @@ bool HttpClient::delete_req(const std::string& url, const std::string& data, std
urlComp.dwExtraInfoLength = (DWORD)-1; urlComp.dwExtraInfoLength = (DWORD)-1;
// Parse the URL // Parse the URL
if (!WinHttpCrackUrl((LPCWSTR)url.c_str(), 0, 0, &urlComp)) { std::wstring wurlDel = utf8_to_wide(url);
if (!WinHttpCrackUrl(wurlDel.c_str(), 0, 0, &urlComp)) {
return false; return false;
} }
@ -314,8 +339,9 @@ bool HttpClient::delete_req(const std::string& url, const std::string& data, std
} }
// Create an HTTP request handle // Create an HTTP request handle
LPCWSTR delPath = (urlComp.lpszUrlPath && urlComp.dwUrlPathLength > 0) ? urlComp.lpszUrlPath : L"/";
HINTERNET hRequest = WinHttpOpenRequest(hConnect, L"POST", HINTERNET hRequest = WinHttpOpenRequest(hConnect, L"POST",
urlComp.lpszUrlPath, delPath,
NULL, WINHTTP_NO_REFERER, NULL, WINHTTP_NO_REFERER,
WINHTTP_DEFAULT_ACCEPT_TYPES, WINHTTP_DEFAULT_ACCEPT_TYPES,
(urlComp.nScheme == INTERNET_SCHEME_HTTPS) ? (urlComp.nScheme == INTERNET_SCHEME_HTTPS) ?
@ -341,9 +367,9 @@ bool HttpClient::delete_req(const std::string& url, const std::string& data, std
return false; return false;
} }
// Send a request with DELETE method // Send a request body with DELETE method
if (!WinHttpSendRequest(hRequest, if (!WinHttpSendRequest(hRequest,
L"DELETE", 6, 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)) {
WinHttpCloseHandle(hRequest); WinHttpCloseHandle(hRequest);

View File

@ -1,6 +1,28 @@
#include "json_parser.h" #include "json_parser.h"
#include <iostream> #include <iostream>
#include <chrono> #include <chrono>
#include <cryptopp/base64.h>
#include <cryptopp/filters.h>
#include <cryptopp/queue.h>
// Helper: Base64 encode without line breaks
static std::string encode_base64(const std::vector<unsigned char>& data) {
std::string result;
CryptoPP::StringSource ss(
data.data(), data.size(), true,
new CryptoPP::Base64Encoder(new CryptoPP::StringSink(result), false /* insertLineBreaks */)
);
return result;
}
// Helper: Base64 decode
static std::vector<unsigned char> decode_base64(const std::string& b64) {
std::string decoded;
CryptoPP::StringSource ss(
b64, true, new CryptoPP::Base64Decoder(new CryptoPP::StringSink(decoded))
);
return std::vector<unsigned char>(decoded.begin(), decoded.end());
}
json JsonParser::create_paste_json(const std::vector<unsigned char>& cipher_text, json JsonParser::create_paste_json(const std::vector<unsigned char>& cipher_text,
const std::vector<unsigned char>& auth_tag, const std::vector<unsigned char>& auth_tag,
@ -11,17 +33,16 @@ json JsonParser::create_paste_json(const std::vector<unsigned char>& cipher_text
bool burn_after_reading, bool burn_after_reading,
bool open_discussion) { bool open_discussion) {
// Convert binary data to base64 strings (stub implementation) // Convert binary data to base64 strings
std::string cipher_text_b64(cipher_text.begin(), cipher_text.end()); std::string cipher_text_b64 = encode_base64(cipher_text);
std::string auth_tag_b64(auth_tag.begin(), auth_tag.end()); std::string iv_b64 = encode_base64(iv);
std::string iv_b64(iv.begin(), iv.end()); std::string salt_b64 = encode_base64(salt);
std::string salt_b64(salt.begin(), salt.end());
// Get current timestamp // Get current timestamp
auto now = std::chrono::duration_cast<std::chrono::seconds>( auto now = std::chrono::duration_cast<std::chrono::seconds>(
std::chrono::system_clock::now().time_since_epoch()).count(); std::chrono::system_clock::now().time_since_epoch()).count();
// Create the metadata array // Create the metadata array according to PrivateBin v2
json metadata = { json metadata = {
{ {
iv_b64, // base64(cipher_iv) iv_b64, // base64(cipher_iv)
@ -33,21 +54,19 @@ json JsonParser::create_paste_json(const std::vector<unsigned char>& cipher_text
"gcm", // mode "gcm", // mode
"zlib" // compression "zlib" // compression
}, },
format, // format format, // formatter key
burn_after_reading ? 1 : 0, // burn after reading burn_after_reading ? 1 : 0, // burn after reading
open_discussion ? 1 : 0 // open discussion open_discussion ? 1 : 0 // open discussion
}; };
// Create the main JSON structure // Create the main JSON structure
// JSON close to v1.3 JSON-LD: meta.expire used by server
json paste_json = { json paste_json = {
{"v", 2}, // version {"v", 2}, // version
{"adata", metadata}, // metadata {"adata", metadata}, // metadata
{"ct", cipher_text_b64}, // cipher text {"ct", cipher_text_b64}, // ciphertext (base64)
{"meta", { {"meta", {
{"expire", expiration}, {"expire", expiration}
{"created", now},
{"time_to_live", 300}, // This would be calculated based on expiration
{"icon", "data:image/png;base64,..."} // Placeholder
}} }}
}; };
@ -62,29 +81,32 @@ bool JsonParser::parse_paste_json(const json& json_data,
std::string& expiration) { std::string& expiration) {
try { try {
// Extract cipher text // Extract and decode cipher text
std::string ct = json_data["ct"]; std::string ct_b64 = json_data.at("ct");
cipher_text = std::vector<unsigned char>(ct.begin(), ct.end()); cipher_text = decode_base64(ct_b64);
// Extract metadata // Optional explicit tag field
json adata = json_data["adata"]; if (json_data.contains("tag")) {
std::string tag_b64 = json_data.at("tag");
auth_tag = decode_base64(tag_b64);
} else {
auth_tag.clear();
}
// Extract metadata and decode IV & salt
json adata = json_data.at("adata");
if (adata.size() >= 1) { if (adata.size() >= 1) {
json first_element = adata[0]; json first_element = adata[0];
if (first_element.is_array() && first_element.size() >= 2) { if (first_element.is_array() && first_element.size() >= 2) {
// Extract IV and salt (from base64 in real implementation) std::string iv_b64 = first_element[0];
std::string iv_str = first_element[0]; std::string salt_b64 = first_element[1];
std::string salt_str = first_element[1]; iv = decode_base64(iv_b64);
salt = decode_base64(salt_b64);
iv = std::vector<unsigned char>(iv_str.begin(), iv_str.end());
salt = std::vector<unsigned char>(salt_str.begin(), salt_str.end());
} }
} }
// Extract expiration // Extract expiration
expiration = json_data["meta"]["expire"]; expiration = json_data.at("meta").at("expire");
// Auth tag would be extracted from the ciphertext in a real implementation
auth_tag.resize(16, 0);
return true; return true;
} catch (...) { } catch (...) {
@ -101,17 +123,13 @@ bool JsonParser::parse_response(const std::string& response,
try { try {
json json_response = json::parse(response); json json_response = json::parse(response);
status = json_response.value("status", 1);
status = json_response["status"];
if (status == 0) { if (status == 0) {
// Success response paste_id = json_response.value("id", "");
paste_id = json_response["id"]; url = json_response.value("url", "");
url = json_response["url"]; delete_token = json_response.value("deletetoken", "");
delete_token = json_response["deletetoken"];
} else { } else {
// Error response message = json_response.value("message", "");
message = json_response["message"];
} }
return true; return true;

View File

@ -7,6 +7,7 @@
#include <vector> #include <vector>
#include <cstring> #include <cstring>
#include <cstdlib> #include <cstdlib>
#include <iostream>
#define PRIVATEBIN_API_VERSION "1.3" #define PRIVATEBIN_API_VERSION "1.3"
@ -54,6 +55,7 @@ int create_paste(const char* server_url, const char* content,
std::vector<unsigned char> plaintext(content, content + strlen(content)); std::vector<unsigned char> plaintext(content, content + strlen(content));
// Compress the plaintext // Compress the plaintext
std::cerr << "[privatebinapi] compress..." << std::endl;
std::vector<unsigned char> compressed_data = Crypto::compress(plaintext); std::vector<unsigned char> compressed_data = Crypto::compress(plaintext);
// Generate random salt and IV // Generate random salt and IV
@ -61,10 +63,12 @@ int create_paste(const char* server_url, const char* content,
std::vector<unsigned char> iv = Crypto::generate_key(16); std::vector<unsigned char> iv = Crypto::generate_key(16);
// Derive key using PBKDF2 // Derive key using PBKDF2
std::cerr << "[privatebinapi] pbkdf2..." << std::endl;
std::vector<unsigned char> derived_key = Crypto::pbkdf2_hmac_sha256( std::vector<unsigned char> derived_key = Crypto::pbkdf2_hmac_sha256(
paste_passphrase, salt, 100000, 32); paste_passphrase, salt, 100000, 32);
// Encrypt the data // Encrypt the data
std::cerr << "[privatebinapi] encrypt..." << std::endl;
std::vector<unsigned char> auth_tag; std::vector<unsigned char> auth_tag;
std::vector<unsigned char> cipher_text = Crypto::encrypt( std::vector<unsigned char> cipher_text = Crypto::encrypt(
compressed_data, derived_key, iv, auth_tag); compressed_data, derived_key, iv, auth_tag);
@ -91,8 +95,12 @@ int create_paste(const char* server_url, const char* content,
int status; int status;
std::string message, paste_id, url, del_token; std::string message, paste_id, url, del_token;
if (!JsonParser::parse_response(response, status, message, paste_id, url, del_token)) { if (!JsonParser::parse_response(response, status, message, paste_id, url, del_token)) {
std::cerr << "[privatebinapi] raw response (unparsed): " << response << std::endl;
return ERROR_JSON_PARSE; return ERROR_JSON_PARSE;
} }
if (status != 0) {
std::cerr << "[privatebinapi] server status=" << status << ", message= " << message << std::endl;
}
if (status != 0) { if (status != 0) {
return ERROR_SERVER; return ERROR_SERVER;
@ -109,7 +117,11 @@ int create_paste(const char* server_url, const char* content,
copy_string_to_output(del_token, delete_token); copy_string_to_output(del_token, delete_token);
return ERROR_SUCCESS; return ERROR_SUCCESS;
} catch (const std::exception& e) {
std::cerr << "[privatebinapi] crypto error: " << e.what() << std::endl;
return ERROR_CRYPTO;
} catch (...) { } catch (...) {
std::cerr << "[privatebinapi] unknown crypto error" << std::endl;
return ERROR_CRYPTO; return ERROR_CRYPTO;
} }
} }
@ -122,15 +134,17 @@ int get_paste(const char* server_url, const char* paste_id,
} }
try { try {
// Construct the URL // Construct the URL with query per API: base?pasteID
std::string url = std::string(server_url) + "/" + paste_id; std::string url = std::string(server_url) + "?" + paste_id;
// Send GET request // Send GET request
HttpClient client; HttpClient client;
std::string response; std::string response;
std::cerr << "[privatebinapi] GET " << url << std::endl;
if (!client.get(url, response)) { if (!client.get(url, response)) {
return ERROR_NETWORK; return ERROR_NETWORK;
} }
std::cerr << "[privatebinapi] GET response: " << response << std::endl;
// Parse the JSON response // Parse the JSON response
json json_data = json::parse(response); json json_data = json::parse(response);
@ -188,9 +202,11 @@ int delete_paste(const char* server_url, const char* paste_id,
// Send DELETE request // Send DELETE request
HttpClient client; HttpClient client;
std::string response; std::string response;
std::cerr << "[privatebinapi] DELETE payload: " << json_data << std::endl;
if (!client.delete_req(server_url, json_data, response)) { if (!client.delete_req(server_url, json_data, response)) {
return ERROR_NETWORK; return ERROR_NETWORK;
} }
std::cerr << "[privatebinapi] DELETE response: " << response << std::endl;
// Parse response // Parse response
int status; int status;