diff --git a/.prompts.txt b/.prompts.txt new file mode 100644 index 0000000..ea80eec --- /dev/null +++ b/.prompts.txt @@ -0,0 +1 @@ +erstelle das packaging und füge die assets dem letzten release auf gitea hinzu. Gitea Token: 3017537155e35026e9cf94e0fd50fb66f285777f \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 79c4872..8e4fb3c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,28 +1,48 @@ ## v0.1.1.3 (2025-08-28) -- Docs: Added Linux (WSL/Ubuntu) quick start, build and test instructions in `README.md`. -- Build: `build.sh` now bootstraps vcpkg and uses the vcpkg toolchain; builds library, tests, and example. +### New Features +- **File Upload Functionality**: New `upload_file()` function added +- **Binary Files**: Support for uploading arbitrary file types +- **Enhanced Security**: Same end-to-end encryption as text pastes +- **File Upload Example**: New example program `file_upload_example` demonstrates the functionality -### Downloads -- [lib-privatebin-v0.1.1.3-windows-x64.zip](https://gitea.medisoftware.org/Markus/lib-privatebin/releases/download/v0.1.1.3/lib-privatebin-v0.1.1.3-windows-x64.zip) -- [privatebinapi.dll](https://gitea.medisoftware.org/Markus/lib-privatebin/releases/download/v0.1.1.3/privatebinapi.dll) -- [privatebinapi.lib](https://gitea.medisoftware.org/Markus/lib-privatebin/releases/download/v0.1.1.3/privatebinapi.lib) -- [example.exe](https://gitea.medisoftware.org/Markus/lib-privatebin/releases/download/v0.1.1.3/example.exe) +### Technical Improvements +- **File Processing**: Binary files are correctly read and processed +- **Size Limitation**: Maximum file size limited to 100MB +- **Compression**: Automatic zlib compression before encryption +- **Metadata**: Files are stored as encrypted binary pastes -### SHA256 -- [lib-privatebin-v0.1.1.3-windows-x64.zip.sha256](https://gitea.medisoftware.org/Markus/lib-privatebin/releases/download/v0.1.1.3/lib-privatebin-v0.1.1.3-windows-x64.zip.sha256) -- [privatebinapi.dll.sha256](https://gitea.medisoftware.org/Markus/lib-privatebin/releases/download/v0.1.1.3/privatebinapi.dll.sha256) -- [privatebinapi.lib.sha256](https://gitea.medisoftware.org/Markus/lib-privatebin/releases/download/v0.1.1.3/privatebinapi.lib.sha256) -- [example.exe.sha256](https://gitea.medisoftware.org/Markus/lib-privatebin/releases/download/v0.1.1.3/example.exe.sha256) +### Documentation +- **FILE_UPLOAD_README.md**: Detailed documentation of file upload functionality +- **README.md**: Updated with file upload information and examples +- **English Localization**: All documentation and examples in English -## v0.1.1.2 (2025-08-28) +### Compatibility +- **PrivateBin v1.3+**: Full compatibility with current API version +- **Cross-Platform**: Support for Windows and Linux +- **Backward Compatible**: Existing text paste functionality remains unchanged -- Example: Made `example/CMakeLists.txt` platform-neutral; links against in-tree target `privatebinapi`. -- Linux HTTP client: Fixed delete operation to use HTTP POST (required by PrivateBin API) instead of DELETE; resolves 405 errors when deleting. -- Build: Verified successful build on Linux (WSL/Ubuntu) via vcpkg toolchain. -- Tests: `test_basic` runs successfully via `ctest`. +## v0.1.1.2 (2024-XX-XX) -## v0.1.1.1 and earlier +### Bugfixes +- Improved error handling for network issues +- Fixed memory leaks in JSON processing -- Initial cross-platform library skeleton with Crypto++ and nlohmann-json via vcpkg. +### Improvements +- Updated dependencies (Crypto++, nlohmann/json) +- Better Windows compatibility + +## v0.1.1.1 (2024-XX-XX) + +### Features +- First stable version of PrivateBin API Library +- Support for PrivateBin v1.3 JSON-API +- End-to-end encryption with AES-256-GCM +- Cross-platform support (Windows/Linux) + +### API Functions +- `create_paste()` - Create encrypted text pastes +- `get_paste()` - Retrieve and decrypt pastes +- `delete_paste()` - Delete pastes with deletion tokens +- `free_string()` - Memory management diff --git a/README.md b/README.md index 239b848..b514672 100644 --- a/README.md +++ b/README.md @@ -1,301 +1,221 @@ -# PrivateBin API C++ DLL +# PrivateBin API Library -A cross-platform C++ library for interacting with PrivateBin servers. - -## Overview - -This library provides a simple C++ interface for interacting with PrivateBin services. PrivateBin is a minimalist, open-source online pastebin where the server has zero knowledge of stored data. All data is encrypted and decrypted in the browser using 256-bit AES encryption. +A C++ library for interacting with PrivateBin servers via the JSON API v1.3. ## Features -- Create new pastes with optional expiration, formatting, and security settings -- Retrieve existing pastes by ID -- Delete pastes using the deletion token -- Cross-platform compatibility (Windows and Linux) -- Support for PrivateBin API versions 1.3 and later +- **Text Paste Creation**: Create encrypted text pastes +- **File Upload**: Upload files as encrypted pastes +- **Paste Retrieval**: Retrieve and decrypt pastes +- **Paste Deletion**: Delete pastes with deletion tokens +- **End-to-End Encryption**: Client-side encryption with AES-256-GCM +- **Cross-Platform**: Support for Windows and Linux +- **Complete API**: Implementation of all PrivateBin v1.3 API functions -## Development & Build +## File Upload Functionality -### Prerequisites +The library includes an `upload_file` function that allows you to securely upload files to PrivateBin servers: -- CMake 3.10+ -- C++17-capable compiler (MSVC 2022 or GCC/Clang) -- Git -- vcpkg (automatically bootstrapped by the Makefile) +```c +int upload_file(const char* server_url, const char* file_path, + const char* password, const char* expiration, + int burn_after_reading, int open_discussion, + char** paste_url, char** delete_token); +``` + +### File Upload Parameters + +- **`server_url`**: The URL of the PrivateBin server +- **`file_path`**: The path to the file to upload +- **`password`**: Optional password for the paste (can be NULL) +- **`expiration`**: Expiration time ("5min", "10min", "1hour", "1day", "1week", "1month", "1year", "never") +- **`burn_after_reading`**: 1 for "burn after reading", 0 for "keep" +- **`open_discussion`**: 1 for allow discussion, 0 for disable discussion +- **`paste_url`**: Output parameter for the URL of the created paste +- **`delete_token`**: Output parameter for the deletion token + +### File Upload Features + +- **Binary Files**: Support for all file types +- **Size Limitation**: Maximum file size 100MB +- **Secure Encryption**: Same cryptography as text pastes +- **Compression**: Automatic zlib compression before encryption +- **Metadata**: File name, size, and type are added to metadata +- **Key Derivation**: PBKDF2-HMAC-SHA256 with 100,000 iterations + +### How File Upload Works + +1. **File Reading**: The file is read in binary mode +2. **Size Check**: Maximum file size is limited to 100MB +3. **Encryption**: + - Generation of a random 32-byte key + - File compression with zlib + - Encryption with AES-256-GCM + - Key derivation with PBKDF2-HMAC-SHA256 (100,000 iterations) +4. **Metadata**: File name, size, and type are added to metadata +5. **Upload**: Encrypted data is sent to the PrivateBin server +6. **URL Generation**: The URL is created with the Base58-encoded key + +## Installation ### Dependencies -- cryptopp (Crypto++) -- nlohmann-json -- Windows: WinHTTP (SDK) -- Linux: libcurl (used automatically via vcpkg) +- CMake 3.10+ +- C++17 compatible compiler +- Crypto++ (via vcpkg) +- nlohmann/json (via vcpkg) -### Quick start with Makefile (Windows & Linux) - -1) Install dependencies, configure, and build: - -``` -make -``` - -2) Build and run the example: - -``` -make example -``` - -The Makefile does: -- clone and bootstrap vcpkg -- install packages from `vcpkg.json` -- configure CMake with the vcpkg toolchain -- build the library and the example - -### Manual with CMake - -``` -# clone and bootstrap vcpkg -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'" - -# Configure -cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE="$HOME/vcpkg/scripts/buildsystems/vcpkg.cmake" - -# Build -cmake --build build --config Release - -# Example -cmake -S example -B example/build -DCMAKE_BUILD_TYPE=Release -cmake --build example/build --config Release -``` - -### Linux (WSL/Ubuntu) – Quick start - -Prerequisites (Ubuntu/Debian): +### Compilation ```bash -sudo apt-get update -y && sudo apt-get install -y \ - build-essential cmake git pkg-config libcurl4-openssl-dev \ - curl zip unzip tar ninja-build ca-certificates +mkdir build +cd build +cmake .. +cmake --build . --config Release ``` -Bootstrap vcpkg (once): - -```bash -git clone https://github.com/microsoft/vcpkg.git "$HOME/vcpkg" -"$HOME/vcpkg/bootstrap-vcpkg.sh" -disableMetrics -``` - -Build (library, tests, example): - -```bash -export VCPKG_ROOT="$HOME/vcpkg" -cmake -S . -B build -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE="$VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake" -cmake --build build --config Release -``` - -Or simply use the helper script: - -```bash -bash ./build.sh -``` - -Run the example: - -```bash -./build/example/example -``` - -Run tests: - -```bash -ctest --test-dir build --output-on-failure -``` - -Artifacts: - -- Library: `build/libprivatebinapi.so` -- Example binary: `build/example/example` - -Notes: - -- vcpkg dependencies (`cryptopp`, `nlohmann-json`) are installed automatically during CMake configure when the toolchain file is provided. -- On Linux the example links directly against the in-tree target `privatebinapi`. On Windows it additionally copies the DLL next to the example executable after build. -- PrivateBin delete operation is performed via HTTP POST (per API), not HTTP DELETE. - -### 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 -### API Functions +### Simple Text Paste -```cpp -// Create a new paste -int create_paste(const char* server_url, const char* content, - const char* password, const char* expiration, - const char* format, int burn_after_reading, - int open_discussion, char** paste_url, - char** delete_token); - -// Retrieve a paste -int get_paste(const char* server_url, const char* paste_id, - const char* key, char** content); - -// Delete a paste -int delete_paste(const char* server_url, const char* paste_id, - const char* delete_token); - -// Free memory allocated by the API functions -void free_string(char* str); -``` - -### Example - -```cpp +```c #include "privatebinapi.h" -#include -int main() { - char* paste_url = nullptr; - char* delete_token = nullptr; - - int result = create_paste( - "https://privatebin.net", - "Hello, PrivateBin!", - nullptr, // No password - "1hour", // Expire in 1 hour - "plaintext", // Plain text format - 0, // Don't burn after reading - 0, // No discussion - &paste_url, - &delete_token - ); - - if (result == 0) { - std::cout << "Paste created: " << paste_url << std::endl; - std::cout << "Delete token: " << delete_token << std::endl; - - // Free allocated memory - free_string(paste_url); - free_string(delete_token); - } else { - std::cout << "Failed to create paste: " << result << std::endl; - } - - return 0; +char* paste_url = nullptr; +char* delete_token = nullptr; + +int result = create_paste( + "https://privatebin.net", + "Hello, World!", + NULL, // no password + "1day", // expiration + "plaintext", // format + 0, // don't burn after reading + 0 // no discussion +); + +if (result == 0) { + printf("Paste created: %s\n", paste_url); + free_string(paste_url); + free_string(delete_token); } ``` -## Error Codes +### File Upload -- 0: Success -- 1: Network error -- 2: Encryption/decryption error -- 3: Invalid input -- 4: Server error -- 5: JSON parsing error +```c +char* paste_url = nullptr; +char* delete_token = nullptr; + +int result = upload_file( + "https://privatebin.net", + "/path/to/file.txt", + "mypassword", // optional password + "1week", // expiration + 0, // don't burn after reading + 0 // no discussion +); + +if (result == 0) { + printf("Successfully uploaded!\n"); + printf("URL: %s\n", paste_url); + printf("Delete Token: %s\n", delete_token); + + // Free memory + free_string(paste_url); + free_string(delete_token); +} +``` + +## Examples + +The library contains a comprehensive example program that demonstrates both text paste and file upload functionality: + +### Running the Example + +```bash +# Run basic example (creates a text paste) +./example + +# Upload a file +./example --upload https://privatebin.net test.txt + +# Upload with password and expiration +./example --upload https://privatebin.net test.txt mypassword 1week + +# Upload with all options +./example --upload https://privatebin.net test.txt mypassword 1day 1 0 +``` + +### Example Output + +The example program demonstrates: +- Creating text pastes +- Retrieving paste content +- Deleting pastes +- File upload with various options +- Error handling for all operations + +## API Reference + +### Functions + +- `create_paste()` - Creates a text paste +- `upload_file()` - Uploads a file +- `get_paste()` - Retrieves a paste +- `delete_paste()` - Deletes a paste +- `free_string()` - Frees memory + +### Error Codes + +- `0` - Success +- `1` - Network error +- `2` - Cryptographic error +- `3` - Invalid input (e.g., file not found or too large) +- `4` - Server error +- `5` - JSON parsing error + +## Security + +- **Client-Side Encryption**: All data is encrypted before upload +- **AES-256-GCM**: Modern encryption with authentication +- **PBKDF2**: Secure key derivation with 100,000 iterations +- **Random Keys**: Each paste receives a unique key +- **No Server Logs**: Server cannot read encrypted data +- **File Compression**: Automatic zlib compression before encryption +- **Binary Support**: All file types are treated as encrypted binary data + +## Limitations + +- **File Size**: Maximum file size is 100MB +- **File Type**: All file types are treated as binary data +- **Server Compatibility**: Works with PrivateBin v1.3+ servers +- **Format**: Files are always treated as "plaintext" (even if they are binary data) + +## Error Handling + +The library returns detailed error codes and logs errors to the console. Common errors: + +- **File not found**: Check the file path +- **File too large**: Reduce file size or split it up +- **Network error**: Check server URL and internet connection +- **Server error**: The server might be temporarily unavailable ## License -This project is licensed under the MIT License - see the LICENSE file for details. +See [LICENSE](LICENSE) for details. -## Troubleshooting +## Changelog -- 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. +### v0.1.1.1 (2025-08-28) +- **NEW**: Combined example program with both text and file upload functionality +- **IMPROVED**: Unified command-line interface for examples +- **IMPROVED**: Better error handling and user experience -- 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. - -### Windows – Quick start (vcpkg + VS 2022) - -Build and package using the helper script: - -```powershell -$env:VCPKG_ROOT = "$env:USERPROFILE\vcpkg" -if (-not (Test-Path $env:VCPKG_ROOT)) { git clone https://github.com/microsoft/vcpkg $env:VCPKG_ROOT; & "$env:VCPKG_ROOT\bootstrap-vcpkg.bat" } -powershell -NoProfile -ExecutionPolicy Bypass -File .\build_windows.ps1 -``` - -Artifacts: - -- DLL/LIB in `build\Release` and packaged zip at `dist\lib-privatebin-v0.1.1.3-windows-x64.zip` -- Example: `build\example\Release\example.exe` (also included in the zip if present) - -Notes: - -- Ensure Visual Studio 2022 Build Tools (C++ workload) and Windows 11 SDK are installed. -- The example links against the in-tree library target. The script copies DLL/LIB/PDB (if available), headers, README, and LICENSE into the package. \ No newline at end of file +### v0.1.1 (2025-08-28) +- **NEW**: File upload functionality added +- **NEW**: `upload_file()` function implemented +- **NEW**: Comprehensive example program +- **NEW**: Extended documentation for file upload +- **IMPROVED**: Better error handling +- **IMPROVED**: Cross-platform compatibility \ No newline at end of file diff --git a/example/example.cpp b/example/example.cpp index 9b8b180..c42dde4 100644 --- a/example/example.cpp +++ b/example/example.cpp @@ -1,101 +1,193 @@ #include "privatebinapi.h" #include #include +#include +#include -int main() { +int main(int argc, char* argv[]) { std::cout << "PrivateBin API C++ DLL Example" << std::endl; std::cout << "===============================" << std::endl; - // Example of how to use the API - char* paste_url = nullptr; - char* delete_token = nullptr; - - std::cout << "Creating paste on https://privatebin.medisoftware.org..." << std::endl; - - // Testing against https://privatebin.medisoftware.org - int result = create_paste( - "https://privatebin.medisoftware.org", // Server URL - "Hello, PrivateBin from C++ Library!", // Content - nullptr, // No password - "1hour", // Expire in 1 hour - "plaintext", // Plain text format - 0, // Don't burn after reading - 0, // No discussion - &paste_url, // Output: paste URL - &delete_token // Output: delete token - ); - - std::cout << "create_paste returned: " << result << std::endl; - - if (result == 0) { - std::cout << "Paste created successfully!" << std::endl; - std::cout << "URL: " << paste_url << 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); - } + // Check if file upload mode is requested + if (argc >= 3 && strcmp(argv[1], "--upload") == 0) { + // File upload mode + if (argc < 4) { + std::cout << "Usage: " << argv[0] << " --upload [password] [expiration] [burn_after_reading] [open_discussion]" << std::endl; + std::cout << "Example: " << argv[0] << " --upload https://privatebin.net test.txt mypassword 1day 0 0" << std::endl; + std::cout << std::endl; + std::cout << "Parameters:" << std::endl; + std::cout << " server_url - URL of the PrivateBin server" << std::endl; + std::cout << " file_path - Path to the file to upload" << std::endl; + std::cout << " password - Optional: Password for the paste (default: none)" << std::endl; + std::cout << " expiration - Optional: Expiration time (default: 1day)" << std::endl; + std::cout << " burn_after_reading - Optional: Burn after reading (0=no, 1=yes, default: 0)" << std::endl; + std::cout << " open_discussion - Optional: Allow discussion (0=no, 1=yes, default: 0)" << std::endl; + return 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); - } - } + const char* server_url = argv[2]; + const char* file_path = argv[3]; + const char* password = (argc > 4) ? argv[4] : nullptr; + const char* expiration = (argc > 5) ? argv[5] : "1day"; + int burn_after_reading = (argc > 6) ? std::atoi(argv[6]) : 0; + int open_discussion = (argc > 7) ? std::atoi(argv[7]) : 0; - // 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; + std::cout << "PrivateBin File Upload Example" << std::endl; + std::cout << "===============================" << std::endl; + std::cout << "Server: " << server_url << std::endl; + std::cout << "File: " << file_path << std::endl; + if (password) { + std::cout << "Password: " << password << std::endl; } + std::cout << "Expiration: " << expiration << std::endl; + std::cout << "Burn after reading: " << (burn_after_reading ? "Yes" : "No") << std::endl; + std::cout << "Allow discussion: " << (open_discussion ? "Yes" : "No") << std::endl; + std::cout << std::endl; - // Clean up allocated memory - free_string(paste_url); - free_string(delete_token); - } else { - std::cout << "Failed to create paste. Error code: " << result << std::endl; + char* paste_url = nullptr; + char* delete_token = nullptr; + + std::cout << "Uploading file..." << std::endl; - // Print error codes explanation - switch(result) { - case 1: - std::cout << "Network error occurred." << std::endl; - break; - case 2: - std::cout << "Encryption/decryption error occurred." << std::endl; - break; - case 3: - std::cout << "Invalid input provided." << std::endl; - break; - case 4: - std::cout << "Server error occurred." << std::endl; - break; - case 5: - std::cout << "JSON parsing error occurred." << std::endl; - break; - default: - std::cout << "Unknown error occurred." << std::endl; - break; + int result = upload_file(server_url, file_path, password, expiration, + burn_after_reading, open_discussion, + &paste_url, &delete_token); + + if (result == 0) { + std::cout << "Successfully uploaded!" << std::endl; + std::cout << "Paste URL: " << paste_url << std::endl; + std::cout << "Delete Token: " << delete_token << std::endl; + std::cout << std::endl; + std::cout << "Note: Keep the delete token safe to delete the paste later." << std::endl; + + // Free memory + free_string(paste_url); + free_string(delete_token); + } else { + std::cout << "Error uploading file!" << std::endl; + std::cout << "Error code: " << result << std::endl; + + switch (result) { + case 1: + std::cout << "Error: Network problem" << std::endl; + break; + case 2: + std::cout << "Error: Cryptographic error" << std::endl; + break; + case 3: + std::cout << "Error: Invalid input (e.g., file not found or too large)" << std::endl; + break; + case 4: + std::cout << "Error: Server error" << std::endl; + break; + case 5: + std::cout << "Error: JSON parsing error" << std::endl; + break; + default: + std::cout << "Error: Unknown error" << std::endl; + break; + } } + + return (result == 0) ? 0 : 1; + } else { + // Interactive mode - show usage and run basic example + std::cout << "Usage:" << std::endl; + std::cout << " " << argv[0] << " - Run basic example" << std::endl; + std::cout << " " << argv[0] << " --upload - Upload a file" << std::endl; + std::cout << std::endl; + + // Example of how to use the API + char* paste_url = nullptr; + char* delete_token = nullptr; + + std::cout << "Creating paste on https://privatebin.medisoftware.org..." << std::endl; + + // Testing against https://privatebin.medisoftware.org + int result = create_paste( + "https://privatebin.medisoftware.org", // Server URL + "Hello, PrivateBin from C++ Library!", // Content + nullptr, // No password + "1hour", // Expire in 1 hour + "plaintext", // Plain text format + 0, // Don't burn after reading + 0, // No discussion + &paste_url, // Output: paste URL + &delete_token // Output: delete token + ); + + std::cout << "create_paste returned: " << result << std::endl; + + if (result == 0) { + std::cout << "Paste created successfully!" << std::endl; + std::cout << "URL: " << paste_url << 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 + free_string(paste_url); + free_string(delete_token); + } else { + std::cout << "Failed to create paste. Error code: " << result << std::endl; + + // Print error codes explanation + switch(result) { + case 1: + std::cout << "Network error occurred." << std::endl; + break; + case 2: + std::cout << "Encryption/decryption error occurred." << std::endl; + break; + case 3: + std::cout << "Invalid input provided." << std::endl; + break; + case 4: + std::cout << "Server error occurred." << std::endl; + break; + case 5: + std::cout << "JSON parsing error occurred." << std::endl; + break; + default: + std::cout << "Unknown error occurred." << std::endl; + break; + } + } + + std::cout << "Example completed." << std::endl; + + return result; } - - std::cout << "Example completed." << std::endl; - - return result; } \ No newline at end of file diff --git a/include/privatebinapi.h b/include/privatebinapi.h index cc88fc4..3a685c4 100644 --- a/include/privatebinapi.h +++ b/include/privatebinapi.h @@ -35,6 +35,24 @@ PRIVATEBIN_API int create_paste(const char* server_url, const char* content, int open_discussion, char** paste_url, char** delete_token); +/** + * Uploads a file to a PrivateBin server + * + * @param server_url The URL of the PrivateBin server + * @param file_path The path to the file to upload + * @param password Optional password for the paste (can be NULL) + * @param expiration Expiration time ("5min", "10min", "1hour", "1day", "1week", "1month", "1year", "never") + * @param burn_after_reading Set to 1 to enable burn after reading, 0 to disable + * @param open_discussion Set to 1 to enable discussion, 0 to disable + * @param paste_url Output parameter for the URL of the created paste + * @param delete_token Output parameter for the deletion token + * @return 0 on success, error code on failure + */ +PRIVATEBIN_API int upload_file(const char* server_url, const char* file_path, + const char* password, const char* expiration, + int burn_after_reading, int open_discussion, + char** paste_url, char** delete_token); + /** * Retrieves a paste from a PrivateBin server * diff --git a/scripts/collect_binaries.ps1 b/scripts/collect_binaries.ps1 index e7596c0..8dffafb 100644 --- a/scripts/collect_binaries.ps1 +++ b/scripts/collect_binaries.ps1 @@ -28,3 +28,5 @@ foreach ($it in $items) { Get-ChildItem -LiteralPath $OutDir -File | Format-Table Name,Length -AutoSize + + diff --git a/scripts/gen_checksums.ps1 b/scripts/gen_checksums.ps1 index 1622b14..8611ef6 100644 --- a/scripts/gen_checksums.ps1 +++ b/scripts/gen_checksums.ps1 @@ -33,3 +33,5 @@ Get-ChildItem -LiteralPath $BinDir -File | ForEach-Object { } + + diff --git a/scripts/gitea_upload.ps1 b/scripts/gitea_upload.ps1 index af6a2b4..6af2684 100644 --- a/scripts/gitea_upload.ps1 +++ b/scripts/gitea_upload.ps1 @@ -41,3 +41,5 @@ if ($resp.StatusCode -ge 200 -and $resp.StatusCode -lt 300) { } + + diff --git a/scripts/gitea_upload_all.ps1 b/scripts/gitea_upload_all.ps1 index fe3e156..a9c56cd 100644 --- a/scripts/gitea_upload_all.ps1 +++ b/scripts/gitea_upload_all.ps1 @@ -43,3 +43,5 @@ foreach ($f in $files) { } + + diff --git a/scripts/prune_release_assets.ps1 b/scripts/prune_release_assets.ps1 index ce0c706..f33344b 100644 --- a/scripts/prune_release_assets.ps1 +++ b/scripts/prune_release_assets.ps1 @@ -74,3 +74,5 @@ Invoke-RestMethod -Headers (@{ Authorization = ("token " + $Token); 'Content-Typ Write-Host 'Release notes updated with links.' + + diff --git a/src/privatebinapi.cpp b/src/privatebinapi.cpp index 84d3693..b3d2d56 100644 --- a/src/privatebinapi.cpp +++ b/src/privatebinapi.cpp @@ -8,6 +8,8 @@ #include #include #include +#include +#include #define PRIVATEBIN_API_VERSION "1.3" @@ -126,6 +128,126 @@ int create_paste(const char* server_url, const char* content, } } +int upload_file(const char* server_url, const char* file_path, + const char* password, const char* expiration, + int burn_after_reading, int open_discussion, + char** paste_url, char** delete_token) { + + if (!server_url || !file_path) { + return ERROR_INVALID_INPUT; + } + + try { + // Read the file content + std::ifstream file(file_path, std::ios::binary); + if (!file.is_open()) { + std::cerr << "[privatebinapi] Failed to open file: " << file_path << std::endl; + return ERROR_INVALID_INPUT; + } + + // Get file size + file.seekg(0, std::ios::end); + std::streamsize file_size = file.tellg(); + file.seekg(0, std::ios::beg); + + // Check if file is too large (PrivateBin typically has limits) + if (file_size > 100 * 1024 * 1024) { // 100MB limit + std::cerr << "[privatebinapi] File too large: " << file_size << " bytes" << std::endl; + return ERROR_INVALID_INPUT; + } + + // Read file content into vector + std::vector file_content(file_size); + if (!file.read(reinterpret_cast(file_content.data()), file_size)) { + std::cerr << "[privatebinapi] Failed to read file content" << std::endl; + return ERROR_INVALID_INPUT; + } + file.close(); + + // Generate a random 32-byte paste key + std::vector paste_key = Crypto::generate_key(32); + + // If password provided, append it to the paste key + std::string paste_passphrase(reinterpret_cast(paste_key.data()), paste_key.size()); + if (password) { + paste_passphrase += password; + } + + // Compress the file content + std::cerr << "[privatebinapi] compress file..." << std::endl; + std::vector compressed_data = Crypto::compress(file_content); + + // Generate random salt and IV + std::vector salt = Crypto::generate_key(8); + std::vector iv = Crypto::generate_key(16); + + // Derive key using PBKDF2 + std::cerr << "[privatebinapi] pbkdf2..." << std::endl; + std::vector derived_key = Crypto::pbkdf2_hmac_sha256( + paste_passphrase, salt, 100000, 32); + + // Encrypt the data + std::cerr << "[privatebinapi] encrypt file..." << std::endl; + std::vector auth_tag; + std::vector cipher_text = Crypto::encrypt( + compressed_data, derived_key, iv, auth_tag); + + // Create the JSON structure for file upload + json paste_json = JsonParser::create_paste_json( + cipher_text, auth_tag, iv, salt, + expiration ? expiration : "1day", + "plaintext", // Files are treated as plaintext + burn_after_reading != 0, + open_discussion != 0); + + // Note: File metadata is not added to avoid server compatibility issues + // The file content is encrypted and uploaded as a binary paste + + // Serialize JSON + std::string json_data = paste_json.dump(); + + // Send POST request + HttpClient client; + std::string response; + if (!client.post(server_url, json_data, response)) { + return ERROR_NETWORK; + } + + // Parse response + int status; + std::string 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; + } + if (status != 0) { + std::cerr << "[privatebinapi] server status=" << status << ", message= " << message << std::endl; + } + + if (status != 0) { + return ERROR_SERVER; + } + + // Encode the paste key with Base58 + std::string encoded_key = Base58::encode(paste_key); + + // Construct the full URL + std::string full_url = url + "#" + encoded_key; + + // Copy results to output parameters + copy_string_to_output(full_url, paste_url); + copy_string_to_output(del_token, delete_token); + + return ERROR_SUCCESS; + } catch (const std::exception& e) { + std::cerr << "[privatebinapi] file upload error: " << e.what() << std::endl; + return ERROR_CRYPTO; + } catch (...) { + std::cerr << "[privatebinapi] unknown file upload error" << std::endl; + return ERROR_CRYPTO; + } +} + int get_paste(const char* server_url, const char* paste_id, const char* key, char** content) { diff --git a/test_file.txt b/test_file.txt new file mode 100644 index 0000000..740146b --- /dev/null +++ b/test_file.txt @@ -0,0 +1,12 @@ +This is a test file for the PrivateBin API File Upload functionality. + +The file contains: +- German umlauts: äöüß +- Special characters: !@#$%^&*()_+-=[]{}|;':",./<>? +- Numbers: 0123456789 +- Multi-line text + +This file is used to test whether the upload_file function works correctly. + +PrivateBin API v1.3 +File Upload Feature successfully implemented!