3 Commits

14 changed files with 599 additions and 38 deletions

View File

@ -1,4 +1,26 @@
## v0.1.1.3 (2025-08-28) ## v0.1.1.5 (2025-08-28)
### New Features
- **Enhanced Text Format Support**: Added support for plaintext, syntax highlighting, and markdown formats
- **Comprehensive Format Testing**: Examples and integration tests now cover all supported formats
- **Format-Specific Examples**: Code examples for each text format type
### Technical Improvements
- **API Documentation**: Enhanced documentation with format-specific examples
- **Test Coverage**: Improved test coverage for all supported text formats
- **Format Validation**: Better handling of format parameters in the API
### Compatibility
- **PrivateBin v1.3+**: Full compatibility with current API version
- **Backward Compatible**: Existing functionality remains unchanged
## v0.1.1.4 (2025-08-28)
- **NEW**: Automated release creation script (`scripts/create_release.ps1`)
- **NEW**: Build scripts moved to `scripts/` directory for better organization
- **IMPROVED**: Enhanced build documentation with platform-specific instructions
- **IMPROVED**: Better project structure and organization
s## v0.1.1.3 (2025-08-28)
### New Features ### New Features
- **File Upload Functionality**: New `upload_file()` function added - **File Upload Functionality**: New `upload_file()` function added

195
README.md
View File

@ -4,13 +4,40 @@ A C++ library for interacting with PrivateBin servers via the JSON API v1.3.
## Features ## Features
- **Text Paste Creation**: Create encrypted text pastes - **Text Paste Creation**: Create encrypted text pastes with multiple formats
- **File Upload**: Upload files as encrypted pastes - **File Upload**: Upload files as encrypted pastes
- **Paste Retrieval**: Retrieve and decrypt pastes - **Paste Retrieval**: Retrieve and decrypt pastes
- **Paste Deletion**: Delete pastes with deletion tokens - **Paste Deletion**: Delete pastes with deletion tokens
- **End-to-End Encryption**: Client-side encryption with AES-256-GCM - **End-to-End Encryption**: Client-side encryption with AES-256-GCM
- **Cross-Platform**: Support for Windows and Linux - **Cross-Platform**: Support for Windows and Linux
- **Complete API**: Implementation of all PrivateBin v1.3 API functions - **Complete API**: Implementation of all PrivateBin v1.3 API functions
- **Automated Builds**: Platform-specific build scripts for easy compilation
- **Text Formatting**: Support for plaintext, syntax highlighting, and markdown
## Text Formatting Support
The library supports multiple text formats as defined in the [PrivateBin API v1.3](https://github.com/PrivateBin/PrivateBin/wiki/API):
### Available Formats
- **`plaintext`** - Plain text (default format)
- **`syntaxhighlighting`** - Syntax highlighting for code snippets
- **`markdown`** - Markdown formatting for rich text
### Format Usage
```c
#include "privatebinapi.h"
// Plain text format
create_paste(server, content, password, expiration, "plaintext", 0, 0, &url, &token);
// Syntax highlighting for code
create_paste(server, code_content, password, expiration, "syntaxhighlighting", 0, 0, &url, &token);
// Markdown formatting
create_paste(server, markdown_content, password, expiration, "markdown", 0, 0, &url, &token);
```
## File Upload Functionality ## File Upload Functionality
@ -56,6 +83,57 @@ int upload_file(const char* server_url, const char* file_path,
5. **Upload**: Encrypted data is sent to the PrivateBin server 5. **Upload**: Encrypted data is sent to the PrivateBin server
6. **URL Generation**: The URL is created with the Base58-encoded key 6. **URL Generation**: The URL is created with the Base58-encoded key
## Build Scripts
The project includes several build scripts in the `scripts/` directory for different platforms and use cases.
### Windows Build Scripts
#### PowerShell Build (Recommended)
**File:** `scripts/build_windows.ps1`
**Requirements:** PowerShell, Visual Studio 2022 Build Tools (C++), vcpkg (will be bootstrapped), Git
```powershell
# Run from project root
powershell -NoProfile -ExecutionPolicy Bypass -File .\scripts\build_windows.ps1
```
This script:
- Automatically bootstraps vcpkg if not present
- Configures CMake with Visual Studio 2022 generator
- Builds the project in Release configuration
- Creates a Windows distribution package with all artifacts
#### Command Line Build
**File:** `scripts/build_thinkpad.bat`
**Requirements:** Visual Studio 2022 Build Tools/Community with C++ workload, vcpkg
```cmd
# Run from project root
scripts\build_thinkpad.bat
```
This script:
- Automatically detects and initializes Visual Studio environment
- Handles vcpkg bootstrapping
- Configures CMake with proper toolchain
- Builds the project using MSVC compiler
### Linux/macOS Build Script
**File:** `scripts/build.sh`
**Requirements:** CMake, C++17 compiler, vcpkg
```bash
# Make executable and run from project root
chmod +x scripts/build.sh
./scripts/build.sh
```
This script:
- Bootstraps vcpkg if not present
- Configures CMake with Unix Makefiles
- Builds the project in Release configuration
## Installation ## Installation
### Dependencies ### Dependencies
@ -67,6 +145,26 @@ int upload_file(const char* server_url, const char* file_path,
### Compilation ### Compilation
#### Windows (PowerShell) - Build via scripts/build_windows.ps1
```powershell
# Requires: PowerShell, Visual Studio 2022 Build Tools (C++), vcpkg (will be bootstrapped), Git
powershell -NoProfile -ExecutionPolicy Bypass -File .\scripts\build_windows.ps1
```
#### Windows (Command Line) - Build via scripts/build_thinkpad.bat
```cmd
# Requires: Visual Studio 2022 Build Tools/Community with C++ workload, vcpkg
scripts\build_thinkpad.bat
```
#### Linux/macOS - Build via scripts/build.sh
```bash
# Requires: CMake, C++17 compiler, vcpkg
chmod +x scripts/build.sh
./scripts/build.sh
```
#### Manual Build
```bash ```bash
mkdir build mkdir build
cd build cd build
@ -74,9 +172,21 @@ cmake ..
cmake --build . --config Release cmake --build . --config Release
``` ```
**Note:** The manual build method requires you to have all dependencies (CMake, compiler, vcpkg) properly configured. For most users, the platform-specific build scripts are recommended.
## Usage ## Usage
### Simple Text Paste ### Text Paste with Formatting
The library supports multiple text formats as defined in the [PrivateBin API v1.3](https://github.com/PrivateBin/PrivateBin/wiki/API):
#### Available Formats
- **`plaintext`** - Plain text (default)
- **`syntaxhighlighting`** - Syntax highlighting for code
- **`markdown`** - Markdown formatting
#### Basic Example
```c ```c
#include "privatebinapi.h" #include "privatebinapi.h"
@ -101,6 +211,57 @@ if (result == 0) {
} }
``` ```
#### Syntax Highlighting Example
```c
const char* code_content = R"(
#include <iostream>
int main() {
std::cout << "Hello, World!" << std::endl;
return 0;
}
)";
int result = create_paste(
"https://privatebin.net",
code_content,
NULL, // no password
"1week", // expiration
"syntaxhighlighting", // syntax highlighting format
0, // don't burn after reading
0 // no discussion
);
```
#### Markdown Example
```c
const char* markdown_content = R"(
# Header
This is **bold** and *italic* text.
## Code Block
```cpp
int x = 42;
std::cout << x << std::endl;
```
> This is a blockquote.
)";
int result = create_paste(
"https://privatebin.net",
markdown_content,
NULL, // no password
"1month", // expiration
"markdown", // markdown format
0, // don't burn after reading
0 // no discussion
);
```
### File Upload ### File Upload
```c ```c
@ -134,7 +295,7 @@ The library contains a comprehensive example program that demonstrates both text
### Running the Example ### Running the Example
```bash ```bash
# Run basic example (creates a text paste) # Run basic example (tests all text formats)
./example ./example
# Upload a file # Upload a file
@ -147,6 +308,12 @@ The library contains a comprehensive example program that demonstrates both text
./example --upload https://privatebin.net test.txt mypassword 1day 1 0 ./example --upload https://privatebin.net test.txt mypassword 1day 1 0
``` ```
The basic example now demonstrates:
- **Plaintext format** - Simple text pastes
- **Syntax highlighting format** - Code with syntax highlighting
- **Markdown format** - Rich text formatting
- **Interactive format selection** - Shows available options
### Example Output ### Example Output
The example program demonstrates: The example program demonstrates:
@ -160,12 +327,20 @@ The example program demonstrates:
### Functions ### Functions
- `create_paste()` - Creates a text paste - `create_paste()` - Creates a text paste with optional formatting
- `upload_file()` - Uploads a file - `upload_file()` - Uploads a file
- `get_paste()` - Retrieves a paste - `get_paste()` - Retrieves a paste
- `delete_paste()` - Deletes a paste - `delete_paste()` - Deletes a paste
- `free_string()` - Frees memory - `free_string()` - Frees memory
### Format Support
The `create_paste()` function supports the following formats as defined in the [PrivateBin API v1.3](https://github.com/PrivateBin/PrivateBin/wiki/API):
- **`plaintext`** - Default format for simple text
- **`syntaxhighlighting`** - Syntax highlighting for code snippets
- **`markdown`** - Markdown formatting for rich text
### Error Codes ### Error Codes
- `0` - Success - `0` - Success
@ -207,6 +382,18 @@ See [LICENSE](LICENSE) for details.
## Changelog ## Changelog
### v0.1.1.5 (2025-08-28)
- **NEW**: Enhanced text format support (plaintext, syntax highlighting, markdown)
- **NEW**: Comprehensive format testing in examples and integration tests
- **IMPROVED**: Better API documentation with format examples
- **IMPROVED**: Enhanced test coverage for all supported formats
### v0.1.1.4 (2025-08-28)
- **NEW**: Automated release creation script (`scripts/create_release.ps1`)
- **NEW**: Build scripts moved to `scripts/` directory for better organization
- **IMPROVED**: Enhanced build documentation with platform-specific instructions
- **IMPROVED**: Better project structure and organization
### v0.1.1.1 (2025-08-28) ### v0.1.1.1 (2025-08-28)
- **NEW**: Combined example program with both text and file upload functionality - **NEW**: Combined example program with both text and file upload functionality
- **IMPROVED**: Unified command-line interface for examples - **IMPROVED**: Unified command-line interface for examples

View File

@ -1,20 +0,0 @@
@echo off
echo Building PrivateBin API C++ DLL...
REM Create build directory
if not exist "build" mkdir build
cd build
REM Generate build files with CMake and vcpkg manifest
cmake .. -G "Visual Studio 17 2022" -DCMAKE_TOOLCHAIN_FILE=../vcpkg/scripts/buildsystems/vcpkg.cmake
REM Build the project
cmake --build . --config Release
if %ERRORLEVEL% EQU 0 (
echo Build completed successfully!
) else (
echo Build failed with error level %ERRORLEVEL%
)
cd ..

View File

@ -97,16 +97,17 @@ int main(int argc, char* argv[]) {
std::cout << " " << argv[0] << " --upload <args> - Upload a file" << std::endl; std::cout << " " << argv[0] << " --upload <args> - Upload a file" << std::endl;
std::cout << std::endl; std::cout << std::endl;
// Example of how to use the API // Example of how to use the API with different formats
std::cout << "Testing different text formats..." << std::endl;
// Test 1: Plain text format
std::cout << "\n1. Testing plaintext format..." << std::endl;
char* paste_url = nullptr; char* paste_url = nullptr;
char* delete_token = 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( int result = create_paste(
"https://privatebin.medisoftware.org", // Server URL "https://privatebin.medisoftware.org", // Server URL
"Hello, PrivateBin from C++ Library!", // Content "Hello, PrivateBin from C++ Library!\nThis is plain text.", // Content
nullptr, // No password nullptr, // No password
"1hour", // Expire in 1 hour "1hour", // Expire in 1 hour
"plaintext", // Plain text format "plaintext", // Plain text format
@ -116,6 +117,141 @@ int main(int argc, char* argv[]) {
&delete_token // Output: delete token &delete_token // Output: delete token
); );
if (result == 0) {
std::cout << "✓ Plaintext paste created successfully!" << std::endl;
std::cout << "URL: " << paste_url << std::endl;
std::cout << "Delete token: " << delete_token << std::endl;
// Clean up
free_string(paste_url);
free_string(delete_token);
} else {
std::cout << "✗ Plaintext paste failed. Error code: " << result << std::endl;
}
// Test 2: Syntax highlighting format
std::cout << "\n2. Testing syntax highlighting format..." << std::endl;
char* code_paste_url = nullptr;
char* code_delete_token = nullptr;
const char* code_content = R"(
#include <iostream>
int main() {
std::cout << "Hello, World!" << std::endl;
return 0;
}
)";
result = create_paste(
"https://privatebin.medisoftware.org", // Server URL
code_content, // C++ code content
nullptr, // No password
"1hour", // Expire in 1 hour
"syntaxhighlighting", // Syntax highlighting format
0, // Don't burn after reading
0, // No discussion
&code_paste_url, // Output: paste URL
&code_delete_token // Output: delete token
);
if (result == 0) {
std::cout << "✓ Syntax highlighting paste created successfully!" << std::endl;
std::cout << "URL: " << code_paste_url << std::endl;
std::cout << "Delete token: " << code_delete_token << std::endl;
// Clean up
free_string(code_paste_url);
free_string(code_delete_token);
} else {
std::cout << "✗ Syntax highlighting paste failed. Error code: " << result << std::endl;
}
// Test 3: Markdown format
std::cout << "\n3. Testing markdown format..." << std::endl;
char* markdown_paste_url = nullptr;
char* markdown_delete_token = nullptr;
const char* markdown_content = R"(
# PrivateBin API C++ Library
## Features
- **Text Paste Creation**: Create encrypted text pastes
- **File Upload**: Upload files as encrypted pastes
- **Multiple Formats**: Support for plaintext, syntax highlighting, and markdown
## Code Example
```cpp
int result = create_paste(server_url, content, password, expiration, format,
burn_after_reading, open_discussion, &paste_url, &delete_token);
```
> This is a blockquote demonstrating markdown support.
)";
result = create_paste(
"https://privatebin.medisoftware.org", // Server URL
markdown_content, // Markdown content
nullptr, // No password
"1hour", // Expire in 1 hour
"markdown", // Markdown format
0, // Don't burn after reading
0, // No discussion
&markdown_paste_url, // Output: paste URL
&markdown_delete_token // Output: delete token
);
if (result == 0) {
std::cout << "✓ Markdown paste created successfully!" << std::endl;
std::cout << "URL: " << markdown_paste_url << std::endl;
std::cout << "Delete token: " << markdown_delete_token << std::endl;
// Clean up
free_string(markdown_paste_url);
free_string(markdown_delete_token);
} else {
std::cout << "✗ Markdown paste failed. Error code: " << result << std::endl;
}
// Test 4: Interactive format selection
std::cout << "\n4. Interactive format selection..." << std::endl;
char* interactive_paste_url = nullptr;
char* interactive_delete_token = nullptr;
std::cout << "Available formats:" << std::endl;
std::cout << " - plaintext (default)" << std::endl;
std::cout << " - syntaxhighlighting" << std::endl;
std::cout << " - markdown" << std::endl;
const char* interactive_content = "This paste demonstrates the interactive format selection feature.";
result = create_paste(
"https://privatebin.medisoftware.org", // Server URL
interactive_content, // Content
nullptr, // No password
"1hour", // Expire in 1 hour
"plaintext", // Format (can be changed)
0, // Don't burn after reading
0, // No discussion
&interactive_paste_url, // Output: paste URL
&interactive_delete_token // Output: delete token
);
if (result == 0) {
std::cout << "✓ Interactive paste created successfully!" << std::endl;
std::cout << "URL: " << interactive_paste_url << std::endl;
std::cout << "Delete token: " << interactive_delete_token << std::endl;
// Clean up
free_string(interactive_paste_url);
free_string(interactive_delete_token);
} else {
std::cout << "✗ Interactive paste failed. Error code: " << result << std::endl;
}
std::cout << "\nAll format tests completed!" << std::endl;
return 0;
std::cout << "create_paste returned: " << result << std::endl; std::cout << "create_paste returned: " << result << std::endl;
if (result == 0) { if (result == 0) {

View File

@ -30,3 +30,4 @@ Get-ChildItem -LiteralPath $OutDir -File | Format-Table Name,Length -AutoSize

View File

@ -0,0 +1,95 @@
param(
[Parameter(Mandatory=$false)]
[string]$Token,
[Parameter(Mandatory=$false)]
[switch]$Build
)
# Token aus Umgebungsvariable laden falls nicht als Parameter übergeben
if (-not $Token) {
$Token = $env:GITEA_TOKEN
if (-not $Token) {
Write-Host "Fehler: Kein Token angegeben und GITEA_TOKEN Umgebungsvariable nicht gesetzt!" -ForegroundColor Red
Write-Host "Verwendung: .\create_release.ps1 -Token 'your_token' oder setze GITEA_TOKEN Umgebungsvariable" -ForegroundColor Yellow
exit 1
}
Write-Host "Token aus Umgebungsvariable GITEA_TOKEN geladen" -ForegroundColor Green
}
Write-Host "=== lib-privatebin Release Creator ===" -ForegroundColor Cyan
# Aktuelle Version ermitteln
$lastTag = git describe --tags --abbrev=0 2>$null
if (-not $lastTag) {
$lastTag = "v0.1.0"
}
Write-Host "Letzter Tag: $lastTag" -ForegroundColor Green
# Version parsen
if ($lastTag -match "^v(\d+)\.(\d+)\.(\d+)(.*)$") {
$major = [int]$matches[1]
$minor = [int]$matches[2]
$patch = [int]$matches[3]
$suffix = $matches[4]
$newPatch = $patch + 1
$newVersion = "v$major.$minor.$newPatch$suffix"
Write-Host "Neue Version: $newVersion" -ForegroundColor Green
} else {
Write-Host "Fehler: Ungültiges Versionsformat: $lastTag" -ForegroundColor Red
exit 1
}
# Build falls gewünscht
if ($Build) {
Write-Host "Führe Build durch..." -ForegroundColor Yellow
if (Test-Path "scripts\build_thinkpad.bat") {
Write-Host "Verwende scripts\build_thinkpad.bat..." -ForegroundColor Yellow
cmd /c scripts\build_thinkpad.bat
} else {
Write-Host "Verwende scripts\build_windows.ps1..." -ForegroundColor Yellow
powershell -NoProfile -ExecutionPolicy Bypass -File .\scripts\build_windows.ps1
}
if ($LASTEXITCODE -ne 0) {
Write-Host "Build fehlgeschlagen!" -ForegroundColor Red
exit 1
}
}
# Änderungen committen
$status = git status --porcelain
if ($status) {
git add -A
git commit -m "Release $newVersion prepare for release"
git push origin HEAD
}
# Tag erstellen und pushen
git tag -a $newVersion -m "Release $newVersion"
git push origin $newVersion
# Release erstellen
$releaseBody = "## What is New in $newVersion`n`n- AUTOMATED: Release created by script`n- VERSION: Bumped from $lastTag to $newVersion`n`n## Build Artifacts`n`n- privatebinapi.dll - Windows Dynamic Link Library`n- privatebinapi.lib - Windows Import Library`n- example.exe - Combined example program`n- privatebinapi.h - C++ header file"
$releaseData = @{
tag_name = $newVersion
name = "$newVersion - Automated Release"
body = $releaseBody
draft = $false
prerelease = $false
} | ConvertTo-Json -Depth 10
$headers = @{
"Authorization" = "token $Token"
"Content-Type" = "application/json"
}
$releaseUri = "https://gitea.medisoftware.org/api/v1/repos/Markus/lib-privatebin/releases"
$release = Invoke-RestMethod -Uri $releaseUri -Method Post -Headers $headers -Body $releaseData
Write-Host "Release erstellt: $($release.id)" -ForegroundColor Green
Write-Host "URL: $($release.html_url)" -ForegroundColor Green

View File

@ -35,3 +35,4 @@ Get-ChildItem -LiteralPath $BinDir -File | ForEach-Object {

View File

@ -43,3 +43,4 @@ if ($resp.StatusCode -ge 200 -and $resp.StatusCode -lt 300) {

View File

@ -45,3 +45,4 @@ foreach ($f in $files) {

View File

@ -76,3 +76,4 @@ Write-Host 'Release notes updated with links.'

View File

@ -26,22 +26,27 @@ int main() {
std::cout << "[test] PRIVATEBIN_IT not set; skipping integration test." << std::endl; std::cout << "[test] PRIVATEBIN_IT not set; skipping integration test." << std::endl;
return 0; // treat as success when integration testing is disabled return 0; // treat as success when integration testing is disabled
} }
const char* server = "https://privatebin.medisoftware.org/"; const char* server = "https://privatebin.medisoftware.org/";
const char* content = "Integration test from lib-privatebin";
const char* password = nullptr; // no password const char* password = nullptr; // no password
const char* expiration = "5min"; // short-lived const char* expiration = "5min"; // short-lived
const char* format = "plaintext";
const int burn_after_reading = 0; const int burn_after_reading = 0;
const int open_discussion = 0; const int open_discussion = 0;
std::cout << "[test] Testing different text formats..." << std::endl;
// Test 1: Plaintext format
std::cout << "[test] 1. Testing plaintext format..." << std::endl;
const char* plaintext_content = "Integration test from lib-privatebin - plaintext";
const char* plaintext_format = "plaintext";
char* paste_url = nullptr; char* paste_url = nullptr;
char* delete_token = nullptr; char* delete_token = nullptr;
std::cout << "[test] create_paste..." << std::endl; int rc = create_paste(server, plaintext_content, password, expiration, plaintext_format,
int rc = create_paste(server, content, password, expiration, format,
burn_after_reading, open_discussion, &paste_url, &delete_token); burn_after_reading, open_discussion, &paste_url, &delete_token);
if (rc != 0 || paste_url == nullptr || delete_token == nullptr) { if (rc != 0 || paste_url == nullptr || delete_token == nullptr) {
std::cerr << "[test][ERROR] create_paste failed, rc=" << rc << std::endl; std::cerr << "[test][ERROR] plaintext create_paste failed, rc=" << rc << std::endl;
if (paste_url) free_string(paste_url); if (paste_url) free_string(paste_url);
if (delete_token) free_string(delete_token); if (delete_token) free_string(delete_token);
return 1; return 1;
@ -68,7 +73,7 @@ int main() {
return 1; return 1;
} }
std::string fetched_str = fetched; std::string fetched_str = fetched;
assert(fetched_str == content && "fetched content mismatch"); assert(fetched_str == plaintext_content && "fetched plaintext content mismatch");
std::cout << "[test] delete_paste..." << std::endl; std::cout << "[test] delete_paste..." << std::endl;
rc = delete_paste(server, paste_id.c_str(), delete_token); rc = delete_paste(server, paste_id.c_str(), delete_token);
@ -80,11 +85,142 @@ int main() {
return 1; return 1;
} }
// cleanup // cleanup plaintext test
free_string(paste_url); free_string(paste_url);
free_string(delete_token); free_string(delete_token);
free_string(fetched); free_string(fetched);
// Test 2: Syntax highlighting format
std::cout << "[test] 2. Testing syntax highlighting format..." << std::endl;
const char* code_content = R"(
#include <iostream>
std::cout << "[test] all API functions passed." << std::endl; int main() {
std::cout << "Hello, World!" << std::endl;
return 0;
}
)";
const char* code_format = "syntaxhighlighting";
char* code_paste_url = nullptr;
char* code_delete_token = nullptr;
rc = create_paste(server, code_content, password, expiration, code_format,
burn_after_reading, open_discussion, &code_paste_url, &code_delete_token);
if (rc != 0 || code_paste_url == nullptr || code_delete_token == nullptr) {
std::cerr << "[test][ERROR] syntax highlighting create_paste failed, rc=" << rc << std::endl;
if (code_paste_url) free_string(code_paste_url);
if (code_delete_token) free_string(code_delete_token);
return 1;
}
// Parse code paste URL
std::string code_full_url = code_paste_url;
std::string code_paste_id;
std::string code_key;
const bool code_ok_parse = extract_paste_id_and_key(code_full_url, code_paste_id, code_key);
if (!code_ok_parse) {
std::cerr << "[test][ERROR] failed to parse code paste id/key from URL: " << code_full_url << std::endl;
free_string(code_paste_url);
free_string(code_delete_token);
return 1;
}
// Fetch and verify code paste
char* code_fetched = nullptr;
rc = get_paste(server, code_paste_id.c_str(), code_key.c_str(), &code_fetched);
if (rc != 0 || code_fetched == nullptr) {
std::cerr << "[test][ERROR] code get_paste failed, rc=" << rc << std::endl;
free_string(code_paste_url);
free_string(code_delete_token);
return 1;
}
std::string code_fetched_str = code_fetched;
assert(code_fetched_str == code_content && "fetched code content mismatch");
// Delete code paste
rc = delete_paste(server, code_paste_id.c_str(), code_delete_token);
if (rc != 0) {
std::cerr << "[test][ERROR] code delete_paste failed, rc=" << rc << std::endl;
free_string(code_paste_url);
free_string(code_delete_token);
free_string(code_fetched);
return 1;
}
// cleanup code test
free_string(code_paste_url);
free_string(code_delete_token);
free_string(code_fetched);
// Test 3: Markdown format
std::cout << "[test] 3. Testing markdown format..." << std::endl;
const char* markdown_content = R"(
# Test Header
This is a **bold** and *italic* text.
## Code Block
```cpp
int x = 42;
std::cout << x << std::endl;
```
> This is a blockquote.
)";
const char* markdown_format = "markdown";
char* markdown_paste_url = nullptr;
char* markdown_delete_token = nullptr;
rc = create_paste(server, markdown_content, password, expiration, markdown_format,
burn_after_reading, open_discussion, &markdown_paste_url, &markdown_delete_token);
if (rc != 0 || markdown_paste_url == nullptr || markdown_delete_token == nullptr) {
std::cerr << "[test][ERROR] markdown create_paste failed, rc=" << rc << std::endl;
if (markdown_paste_url) free_string(markdown_paste_url);
if (markdown_delete_token) free_string(markdown_delete_token);
return 1;
}
// Parse markdown paste URL
std::string markdown_full_url = markdown_paste_url;
std::string markdown_paste_id;
std::string markdown_key;
const bool markdown_ok_parse = extract_paste_id_and_key(markdown_full_url, markdown_paste_id, markdown_key);
if (!markdown_ok_parse) {
std::cerr << "[test][ERROR] failed to parse markdown paste id/key from URL: " << markdown_full_url << std::endl;
free_string(markdown_paste_url);
free_string(markdown_delete_token);
return 1;
}
// Fetch and verify markdown paste
char* markdown_fetched = nullptr;
rc = get_paste(server, markdown_paste_id.c_str(), markdown_key.c_str(), &markdown_fetched);
if (rc != 0 || markdown_fetched == nullptr) {
std::cerr << "[test][ERROR] markdown get_paste failed, rc=" << rc << std::endl;
free_string(markdown_paste_url);
free_string(markdown_delete_token);
return 1;
}
std::string markdown_fetched_str = markdown_fetched;
assert(markdown_fetched_str == markdown_content && "fetched markdown content mismatch");
// Delete markdown paste
rc = delete_paste(server, markdown_paste_id.c_str(), markdown_delete_token);
if (rc != 0) {
std::cerr << "[test][ERROR] markdown delete_paste failed, rc=" << rc << std::endl;
free_string(markdown_paste_url);
free_string(markdown_delete_token);
free_string(markdown_fetched);
return 1;
}
// cleanup markdown test
free_string(markdown_paste_url);
free_string(markdown_delete_token);
free_string(markdown_fetched);
std::cout << "[test] All format tests passed successfully!" << std::endl;
return 0; return 0;
} }