From 84c7b9962ad199e2ac39df517ec1aa5ab8a4c5b6 Mon Sep 17 00:00:00 2001 From: elpatron Date: Fri, 29 Aug 2025 12:48:39 +0200 Subject: [PATCH] Release v0.1.6.4 prepare for release --- .../Testing/Temporary/CTestCostData.txt | 2 +- .../repos/privatebin-cpp/src/base58.cpp.html | 2 +- .../repos/privatebin-cpp/src/crypto.cpp.html | 2 +- .../privatebin-cpp/src/http_client.cpp.html | 2 +- .../privatebin-cpp/src/json_parser.cpp.html | 2 +- .../privatebin-cpp/src/privatebinapi.cpp.html | 2 +- build-clang/coverage/html/index.html | 2 +- build-clang/coverage/m.profraw | Bin 9848 -> 9848 bytes scripts/create_release.ps1 | 15 ++++++++++----- 9 files changed, 17 insertions(+), 12 deletions(-) diff --git a/build-clang/Testing/Temporary/CTestCostData.txt b/build-clang/Testing/Temporary/CTestCostData.txt index b5cbb2c..0d905e3 100644 --- a/build-clang/Testing/Temporary/CTestCostData.txt +++ b/build-clang/Testing/Temporary/CTestCostData.txt @@ -1,3 +1,3 @@ -test_basic 14 0.474679 +test_basic 15 0.446662 example_run 0 0 --- diff --git a/build-clang/coverage/html/coverage/Users/mbusc/source/repos/privatebin-cpp/src/base58.cpp.html b/build-clang/coverage/html/coverage/Users/mbusc/source/repos/privatebin-cpp/src/base58.cpp.html index f164bca..db27bab 100644 --- a/build-clang/coverage/html/coverage/Users/mbusc/source/repos/privatebin-cpp/src/base58.cpp.html +++ b/build-clang/coverage/html/coverage/Users/mbusc/source/repos/privatebin-cpp/src/base58.cpp.html @@ -1 +1 @@ -

Coverage Report

Created: 2025-08-29 12:44

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
C:\Users\mbusc\source\repos\privatebin-cpp\src\base58.cpp
Line
Count
Source
1
#include "base58.h"
2
#include <algorithm>
3
#include <stdexcept>
4
5
const std::string Base58::ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
6
7
0
std::string Base58::encode(const std::vector<unsigned char>& data) {
8
0
    if (data.empty()) {
9
0
        return "";
10
0
    }
11
    
12
    // Skip leading zeros
13
0
    size_t leading_zeros = 0;
14
0
    while (leading_zeros < data.size() && data[leading_zeros] == 0) {
15
0
        leading_zeros++;
16
0
    }
17
    
18
    // Convert to base58
19
0
    size_t digits_size = (data.size() - leading_zeros) * 138 / 100 + 1;
20
0
    std::vector<unsigned char> digits(digits_size);
21
0
    size_t digitslen = 1;
22
    
23
0
    for (size_t i = leading_zeros; i < data.size(); i++) {
24
0
        size_t carry = static_cast<size_t>(data[i]);
25
0
        for (size_t j = 0; j < digitslen; j++) {
26
0
            carry += static_cast<size_t>(digits[j]) << 8;
27
0
            digits[j] = static_cast<unsigned char>(carry % 58);
28
0
            carry /= 58;
29
0
        }
30
0
        while (carry > 0) {
31
0
            digits[digitslen++] = static_cast<unsigned char>(carry % 58);
32
0
            carry /= 58;
33
0
        }
34
0
    }
35
    
36
    // Convert to string
37
0
    std::string result;
38
0
    for (size_t i = 0; i < leading_zeros; i++) {
39
0
        result += ALPHABET[0];
40
0
    }
41
0
    for (size_t i = 0; i < digitslen; i++) {
42
0
        result += ALPHABET[digits[digitslen - 1 - i]];
43
0
    }
44
    
45
0
    return result;
46
0
}
47
48
0
std::vector<unsigned char> Base58::decode(const std::string& encoded) {
49
0
    if (encoded.empty()) {
50
0
        return std::vector<unsigned char>();
51
0
    }
52
    
53
    // Skip leading '1's (which represent leading zeros)
54
0
    size_t leading_ones = 0;
55
0
    while (leading_ones < encoded.length() && encoded[leading_ones] == '1') {
56
0
        leading_ones++;
57
0
    }
58
    
59
    // Convert from base58
60
0
    size_t bytes_size = (encoded.length() - leading_ones) * 733 / 1000 + 1;
61
0
    std::vector<unsigned char> bytes(bytes_size);
62
0
    size_t byteslen = 1;
63
    
64
0
    for (size_t i = leading_ones; i < encoded.length(); i++) {
65
0
        size_t carry = static_cast<size_t>(ALPHABET.find(encoded[i]));
66
0
        if (carry == std::string::npos) {
67
0
            throw std::invalid_argument("Invalid character in Base58 string");
68
0
        }
69
        
70
0
        for (size_t j = 0; j < byteslen; j++) {
71
0
            carry += static_cast<size_t>(bytes[j]) * 58;
72
0
            bytes[j] = static_cast<unsigned char>(carry & 0xff);
73
0
            carry >>= 8;
74
0
        }
75
0
        while (carry > 0) {
76
0
            bytes[byteslen++] = static_cast<unsigned char>(carry & 0xff);
77
0
            carry >>= 8;
78
0
        }
79
0
    }
80
    
81
    // Add leading zeros
82
0
    std::vector<unsigned char> result(leading_ones + byteslen);
83
0
    for (size_t i = 0; i < leading_ones; i++) {
84
0
        result[i] = 0;
85
0
    }
86
0
    for (size_t i = 0; i < byteslen; i++) {
87
0
        result[leading_ones + i] = bytes[byteslen - 1 - i];
88
0
    }
89
    
90
0
    return result;
91
0
}
\ No newline at end of file +

Coverage Report

Created: 2025-08-29 12:48

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
C:\Users\mbusc\source\repos\privatebin-cpp\src\base58.cpp
Line
Count
Source
1
#include "base58.h"
2
#include <algorithm>
3
#include <stdexcept>
4
5
const std::string Base58::ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
6
7
0
std::string Base58::encode(const std::vector<unsigned char>& data) {
8
0
    if (data.empty()) {
9
0
        return "";
10
0
    }
11
    
12
    // Skip leading zeros
13
0
    size_t leading_zeros = 0;
14
0
    while (leading_zeros < data.size() && data[leading_zeros] == 0) {
15
0
        leading_zeros++;
16
0
    }
17
    
18
    // Convert to base58
19
0
    size_t digits_size = (data.size() - leading_zeros) * 138 / 100 + 1;
20
0
    std::vector<unsigned char> digits(digits_size);
21
0
    size_t digitslen = 1;
22
    
23
0
    for (size_t i = leading_zeros; i < data.size(); i++) {
24
0
        size_t carry = static_cast<size_t>(data[i]);
25
0
        for (size_t j = 0; j < digitslen; j++) {
26
0
            carry += static_cast<size_t>(digits[j]) << 8;
27
0
            digits[j] = static_cast<unsigned char>(carry % 58);
28
0
            carry /= 58;
29
0
        }
30
0
        while (carry > 0) {
31
0
            digits[digitslen++] = static_cast<unsigned char>(carry % 58);
32
0
            carry /= 58;
33
0
        }
34
0
    }
35
    
36
    // Convert to string
37
0
    std::string result;
38
0
    for (size_t i = 0; i < leading_zeros; i++) {
39
0
        result += ALPHABET[0];
40
0
    }
41
0
    for (size_t i = 0; i < digitslen; i++) {
42
0
        result += ALPHABET[digits[digitslen - 1 - i]];
43
0
    }
44
    
45
0
    return result;
46
0
}
47
48
0
std::vector<unsigned char> Base58::decode(const std::string& encoded) {
49
0
    if (encoded.empty()) {
50
0
        return std::vector<unsigned char>();
51
0
    }
52
    
53
    // Skip leading '1's (which represent leading zeros)
54
0
    size_t leading_ones = 0;
55
0
    while (leading_ones < encoded.length() && encoded[leading_ones] == '1') {
56
0
        leading_ones++;
57
0
    }
58
    
59
    // Convert from base58
60
0
    size_t bytes_size = (encoded.length() - leading_ones) * 733 / 1000 + 1;
61
0
    std::vector<unsigned char> bytes(bytes_size);
62
0
    size_t byteslen = 1;
63
    
64
0
    for (size_t i = leading_ones; i < encoded.length(); i++) {
65
0
        size_t carry = static_cast<size_t>(ALPHABET.find(encoded[i]));
66
0
        if (carry == std::string::npos) {
67
0
            throw std::invalid_argument("Invalid character in Base58 string");
68
0
        }
69
        
70
0
        for (size_t j = 0; j < byteslen; j++) {
71
0
            carry += static_cast<size_t>(bytes[j]) * 58;
72
0
            bytes[j] = static_cast<unsigned char>(carry & 0xff);
73
0
            carry >>= 8;
74
0
        }
75
0
        while (carry > 0) {
76
0
            bytes[byteslen++] = static_cast<unsigned char>(carry & 0xff);
77
0
            carry >>= 8;
78
0
        }
79
0
    }
80
    
81
    // Add leading zeros
82
0
    std::vector<unsigned char> result(leading_ones + byteslen);
83
0
    for (size_t i = 0; i < leading_ones; i++) {
84
0
        result[i] = 0;
85
0
    }
86
0
    for (size_t i = 0; i < byteslen; i++) {
87
0
        result[leading_ones + i] = bytes[byteslen - 1 - i];
88
0
    }
89
    
90
0
    return result;
91
0
}
\ No newline at end of file diff --git a/build-clang/coverage/html/coverage/Users/mbusc/source/repos/privatebin-cpp/src/crypto.cpp.html b/build-clang/coverage/html/coverage/Users/mbusc/source/repos/privatebin-cpp/src/crypto.cpp.html index 29b19ee..68c5790 100644 --- a/build-clang/coverage/html/coverage/Users/mbusc/source/repos/privatebin-cpp/src/crypto.cpp.html +++ b/build-clang/coverage/html/coverage/Users/mbusc/source/repos/privatebin-cpp/src/crypto.cpp.html @@ -1 +1 @@ -

Coverage Report

Created: 2025-08-29 12:44

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
C:\Users\mbusc\source\repos\privatebin-cpp\src\crypto.cpp
Line
Count
Source
1
#include "crypto.h"
2
#include <random>
3
#include <stdexcept>
4
#include <cstring>
5
6
// Crypto++ includes
7
#include <cryptopp/cryptlib.h>
8
#include <cryptopp/osrng.h>      // AutoSeededRandomPool
9
#include <cryptopp/aes.h>        // AES encryption
10
#include <cryptopp/gcm.h>        // GCM mode
11
#include <cryptopp/pwdbased.h>   // PBKDF2
12
#include <cryptopp/sha.h>        // SHA256
13
#include <cryptopp/zlib.h>       // Zlib compression
14
15
using namespace CryptoPP;
16
17
3
std::vector<unsigned char> Crypto::generate_key(size_t length) {
18
3
    std::vector<unsigned char> key(length);
19
    
20
    // Use Crypto++ AutoSeededRandomPool for cryptographically secure random numbers
21
3
    AutoSeededRandomPool rng;
22
3
    rng.GenerateBlock(key.data(), length);
23
    
24
3
    return key;
25
3
}
26
27
std::vector<unsigned char> Crypto::encrypt(const std::vector<unsigned char>& plaintext,
28
                                         const std::vector<unsigned char>& key,
29
                                         const std::vector<unsigned char>& iv,
30
1
                                         std::vector<unsigned char>& auth_tag) {
31
1
    try {
32
        // Create GCM mode encryption object
33
1
        GCM<AES>::Encryption encryption;
34
        
35
        // Set key and IV
36
1
        encryption.SetKeyWithIV(key.data(), key.size(), iv.data(), iv.size());
37
        
38
        // Prepare output vector
39
1
        std::vector<unsigned char> ciphertext(plaintext.size());
40
        
41
        // Prepare authentication tag (16 bytes for GCM)
42
1
        auth_tag.resize(16);
43
        
44
        // Perform encryption
45
1
        encryption.EncryptAndAuthenticate(
46
1
            ciphertext.data(), 
47
1
            auth_tag.data(),
48
1
            static_cast<int>(auth_tag.size()),
49
1
            iv.data(), 
50
1
            static_cast<int>(iv.size()),
51
1
            nullptr, 
52
1
            0,  // Additional authenticated data
53
1
            plaintext.data(), 
54
1
            static_cast<int>(plaintext.size())
55
1
        );
56
        
57
1
        return ciphertext;
58
1
    }
59
1
    catch(const CryptoPP::Exception& e) {
60
0
        throw std::runtime_error("Encryption failed: " + std::string(e.what()));
61
0
    }
62
1
}
63
64
std::vector<unsigned char> Crypto::decrypt(const std::vector<unsigned char>& ciphertext,
65
                                         const std::vector<unsigned char>& key,
66
                                         const std::vector<unsigned char>& iv,
67
0
                                         const std::vector<unsigned char>& auth_tag) {
68
0
    try {
69
        // Create GCM mode decryption object
70
0
        GCM<AES>::Decryption decryption;
71
        
72
        // Set key and IV
73
0
        decryption.SetKeyWithIV(key.data(), key.size(), iv.data(), iv.size());
74
        
75
        // Prepare output vector
76
0
        std::vector<unsigned char> plaintext(ciphertext.size());
77
        
78
        // Perform decryption and authentication
79
0
        bool valid = decryption.DecryptAndVerify(
80
0
            plaintext.data(),
81
0
            auth_tag.data(),
82
0
            static_cast<int>(auth_tag.size()),
83
0
            iv.data(),
84
0
            static_cast<int>(iv.size()),
85
0
            nullptr,
86
0
            0,  // Additional authenticated data
87
0
            ciphertext.data(),
88
0
            static_cast<int>(ciphertext.size())
89
0
        );
90
        
91
0
        if(!valid) {
92
0
            throw std::runtime_error("Authentication failed during decryption");
93
0
        }
94
        
95
0
        return plaintext;
96
0
    }
97
0
    catch(const CryptoPP::Exception& e) {
98
0
        throw std::runtime_error("Decryption failed: " + std::string(e.what()));
99
0
    }
100
0
}
101
102
std::vector<unsigned char> Crypto::pbkdf2_hmac_sha256(const std::string& password,
103
                                                    const std::vector<unsigned char>& salt,
104
                                                    int iterations,
105
1
                                                    size_t key_length) {
106
1
    try {
107
1
        std::vector<unsigned char> derived_key(key_length);
108
        
109
        // Use Crypto++ PKCS5_PBKDF2_HMAC for key derivation
110
1
        PKCS5_PBKDF2_HMAC<SHA256> pbkdf;
111
1
        pbkdf.DeriveKey(
112
1
            derived_key.data(), 
113
1
            derived_key.size(),
114
1
            0x00,  // Purpose byte
115
1
            reinterpret_cast<const byte*>(password.data()), 
116
1
            password.length(),
117
1
            salt.data(), 
118
1
            salt.size(),
119
1
            iterations,
120
1
            0.0f  // Timeout (0 = no timeout)
121
1
        );
122
        
123
1
        return derived_key;
124
1
    }
125
1
    catch(const CryptoPP::Exception& e) {
126
0
        throw std::runtime_error("PBKDF2 key derivation failed: " + std::string(e.what()));
127
0
    }
128
1
}
129
130
1
std::vector<unsigned char> Crypto::compress(const std::vector<unsigned char>& data) {
131
1
    try {
132
1
        std::string compressed;
133
        
134
        // Create zlib compressor
135
1
        ZlibCompressor compressor;
136
1
        compressor.Put(data.data(), data.size());
137
1
        compressor.MessageEnd();
138
        
139
        // Retrieve compressed data
140
1
        size_t size = compressor.MaxRetrievable();
141
1
        compressed.resize(size);
142
1
        compressor.Get(reinterpret_cast<byte*>(&compressed[0]), compressed.size());
143
        
144
        // Convert to vector
145
1
        return std::vector<unsigned char>(compressed.begin(), compressed.end());
146
1
    }
147
1
    catch(const CryptoPP::Exception& e) {
148
0
        throw std::runtime_error("Compression failed: " + std::string(e.what()));
149
0
    }
150
1
}
151
152
0
std::vector<unsigned char> Crypto::decompress(const std::vector<unsigned char>& data) {
153
0
    try {
154
0
        std::string decompressed;
155
        
156
        // Create zlib decompressor
157
0
        ZlibDecompressor decompressor;
158
0
        decompressor.Put(data.data(), data.size());
159
0
        decompressor.MessageEnd();
160
        
161
        // Retrieve decompressed data
162
0
        size_t size = decompressor.MaxRetrievable();
163
0
        decompressed.resize(size);
164
0
        decompressor.Get(reinterpret_cast<byte*>(&decompressed[0]), decompressed.size());
165
        
166
        // Convert to vector
167
0
        return std::vector<unsigned char>(decompressed.begin(), decompressed.end());
168
0
    }
169
0
    catch(const CryptoPP::Exception& e) {
170
0
        throw std::runtime_error("Decompression failed: " + std::string(e.what()));
171
0
    }
172
0
}
\ No newline at end of file +

Coverage Report

Created: 2025-08-29 12:48

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
C:\Users\mbusc\source\repos\privatebin-cpp\src\crypto.cpp
Line
Count
Source
1
#include "crypto.h"
2
#include <random>
3
#include <stdexcept>
4
#include <cstring>
5
6
// Crypto++ includes
7
#include <cryptopp/cryptlib.h>
8
#include <cryptopp/osrng.h>      // AutoSeededRandomPool
9
#include <cryptopp/aes.h>        // AES encryption
10
#include <cryptopp/gcm.h>        // GCM mode
11
#include <cryptopp/pwdbased.h>   // PBKDF2
12
#include <cryptopp/sha.h>        // SHA256
13
#include <cryptopp/zlib.h>       // Zlib compression
14
15
using namespace CryptoPP;
16
17
3
std::vector<unsigned char> Crypto::generate_key(size_t length) {
18
3
    std::vector<unsigned char> key(length);
19
    
20
    // Use Crypto++ AutoSeededRandomPool for cryptographically secure random numbers
21
3
    AutoSeededRandomPool rng;
22
3
    rng.GenerateBlock(key.data(), length);
23
    
24
3
    return key;
25
3
}
26
27
std::vector<unsigned char> Crypto::encrypt(const std::vector<unsigned char>& plaintext,
28
                                         const std::vector<unsigned char>& key,
29
                                         const std::vector<unsigned char>& iv,
30
1
                                         std::vector<unsigned char>& auth_tag) {
31
1
    try {
32
        // Create GCM mode encryption object
33
1
        GCM<AES>::Encryption encryption;
34
        
35
        // Set key and IV
36
1
        encryption.SetKeyWithIV(key.data(), key.size(), iv.data(), iv.size());
37
        
38
        // Prepare output vector
39
1
        std::vector<unsigned char> ciphertext(plaintext.size());
40
        
41
        // Prepare authentication tag (16 bytes for GCM)
42
1
        auth_tag.resize(16);
43
        
44
        // Perform encryption
45
1
        encryption.EncryptAndAuthenticate(
46
1
            ciphertext.data(), 
47
1
            auth_tag.data(),
48
1
            static_cast<int>(auth_tag.size()),
49
1
            iv.data(), 
50
1
            static_cast<int>(iv.size()),
51
1
            nullptr, 
52
1
            0,  // Additional authenticated data
53
1
            plaintext.data(), 
54
1
            static_cast<int>(plaintext.size())
55
1
        );
56
        
57
1
        return ciphertext;
58
1
    }
59
1
    catch(const CryptoPP::Exception& e) {
60
0
        throw std::runtime_error("Encryption failed: " + std::string(e.what()));
61
0
    }
62
1
}
63
64
std::vector<unsigned char> Crypto::decrypt(const std::vector<unsigned char>& ciphertext,
65
                                         const std::vector<unsigned char>& key,
66
                                         const std::vector<unsigned char>& iv,
67
0
                                         const std::vector<unsigned char>& auth_tag) {
68
0
    try {
69
        // Create GCM mode decryption object
70
0
        GCM<AES>::Decryption decryption;
71
        
72
        // Set key and IV
73
0
        decryption.SetKeyWithIV(key.data(), key.size(), iv.data(), iv.size());
74
        
75
        // Prepare output vector
76
0
        std::vector<unsigned char> plaintext(ciphertext.size());
77
        
78
        // Perform decryption and authentication
79
0
        bool valid = decryption.DecryptAndVerify(
80
0
            plaintext.data(),
81
0
            auth_tag.data(),
82
0
            static_cast<int>(auth_tag.size()),
83
0
            iv.data(),
84
0
            static_cast<int>(iv.size()),
85
0
            nullptr,
86
0
            0,  // Additional authenticated data
87
0
            ciphertext.data(),
88
0
            static_cast<int>(ciphertext.size())
89
0
        );
90
        
91
0
        if(!valid) {
92
0
            throw std::runtime_error("Authentication failed during decryption");
93
0
        }
94
        
95
0
        return plaintext;
96
0
    }
97
0
    catch(const CryptoPP::Exception& e) {
98
0
        throw std::runtime_error("Decryption failed: " + std::string(e.what()));
99
0
    }
100
0
}
101
102
std::vector<unsigned char> Crypto::pbkdf2_hmac_sha256(const std::string& password,
103
                                                    const std::vector<unsigned char>& salt,
104
                                                    int iterations,
105
1
                                                    size_t key_length) {
106
1
    try {
107
1
        std::vector<unsigned char> derived_key(key_length);
108
        
109
        // Use Crypto++ PKCS5_PBKDF2_HMAC for key derivation
110
1
        PKCS5_PBKDF2_HMAC<SHA256> pbkdf;
111
1
        pbkdf.DeriveKey(
112
1
            derived_key.data(), 
113
1
            derived_key.size(),
114
1
            0x00,  // Purpose byte
115
1
            reinterpret_cast<const byte*>(password.data()), 
116
1
            password.length(),
117
1
            salt.data(), 
118
1
            salt.size(),
119
1
            iterations,
120
1
            0.0f  // Timeout (0 = no timeout)
121
1
        );
122
        
123
1
        return derived_key;
124
1
    }
125
1
    catch(const CryptoPP::Exception& e) {
126
0
        throw std::runtime_error("PBKDF2 key derivation failed: " + std::string(e.what()));
127
0
    }
128
1
}
129
130
1
std::vector<unsigned char> Crypto::compress(const std::vector<unsigned char>& data) {
131
1
    try {
132
1
        std::string compressed;
133
        
134
        // Create zlib compressor
135
1
        ZlibCompressor compressor;
136
1
        compressor.Put(data.data(), data.size());
137
1
        compressor.MessageEnd();
138
        
139
        // Retrieve compressed data
140
1
        size_t size = compressor.MaxRetrievable();
141
1
        compressed.resize(size);
142
1
        compressor.Get(reinterpret_cast<byte*>(&compressed[0]), compressed.size());
143
        
144
        // Convert to vector
145
1
        return std::vector<unsigned char>(compressed.begin(), compressed.end());
146
1
    }
147
1
    catch(const CryptoPP::Exception& e) {
148
0
        throw std::runtime_error("Compression failed: " + std::string(e.what()));
149
0
    }
150
1
}
151
152
0
std::vector<unsigned char> Crypto::decompress(const std::vector<unsigned char>& data) {
153
0
    try {
154
0
        std::string decompressed;
155
        
156
        // Create zlib decompressor
157
0
        ZlibDecompressor decompressor;
158
0
        decompressor.Put(data.data(), data.size());
159
0
        decompressor.MessageEnd();
160
        
161
        // Retrieve decompressed data
162
0
        size_t size = decompressor.MaxRetrievable();
163
0
        decompressed.resize(size);
164
0
        decompressor.Get(reinterpret_cast<byte*>(&decompressed[0]), decompressed.size());
165
        
166
        // Convert to vector
167
0
        return std::vector<unsigned char>(decompressed.begin(), decompressed.end());
168
0
    }
169
0
    catch(const CryptoPP::Exception& e) {
170
0
        throw std::runtime_error("Decompression failed: " + std::string(e.what()));
171
0
    }
172
0
}
\ No newline at end of file diff --git a/build-clang/coverage/html/coverage/Users/mbusc/source/repos/privatebin-cpp/src/http_client.cpp.html b/build-clang/coverage/html/coverage/Users/mbusc/source/repos/privatebin-cpp/src/http_client.cpp.html index 0814afc..830d9f1 100644 --- a/build-clang/coverage/html/coverage/Users/mbusc/source/repos/privatebin-cpp/src/http_client.cpp.html +++ b/build-clang/coverage/html/coverage/Users/mbusc/source/repos/privatebin-cpp/src/http_client.cpp.html @@ -1 +1 @@ -

Coverage Report

Created: 2025-08-29 12:44

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
C:\Users\mbusc\source\repos\privatebin-cpp\src\http_client.cpp
Line
Count
Source
1
#include "http_client.h"
2
#include <iostream>
3
#include <string>
4
#include <sstream>
5
6
#ifdef WINDOWS
7
#include <windows.h>
8
#include <winhttp.h>
9
#pragma comment(lib, "winhttp.lib")
10
3
static std::string last_winhttp_error(const char* where) {
11
3
    DWORD err = GetLastError();
12
3
    LPVOID lpMsgBuf = nullptr;
13
3
    FormatMessageA(
14
3
        FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
15
3
        NULL,
16
3
        err,
17
3
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
18
3
        (LPSTR)&lpMsgBuf,
19
3
        0, NULL);
20
3
    std::ostringstream os;
21
3
    os << "[WinHTTP] " << where << " failed, error=" << err;
22
3
    if (lpMsgBuf) {
23
0
        os << ": " << (char*)lpMsgBuf;
24
0
        LocalFree(lpMsgBuf);
25
0
    }
26
3
    return os.str();
27
3
}
28
29
4
static std::wstring utf8_to_wide(const std::string& s) {
30
4
    if (s.empty()) return std::wstring();
31
4
    int needed = MultiByteToWideChar(CP_UTF8, 0, s.c_str(), (int)s.size(), nullptr, 0);
32
4
    std::wstring ws(needed, L'\0');
33
4
    MultiByteToWideChar(CP_UTF8, 0, s.c_str(), (int)s.size(), &ws[0], needed);
34
4
    return ws;
35
4
}
36
#elif LINUX
37
#include <curl/curl.h>
38
#endif
39
40
#ifdef WINDOWS
41
42
/* Entfernt: ungenutzte WinHttpData/Callback, um Clang-Warnungen zu vermeiden */
43
44
2
bool HttpClient::get(const std::string& url, std::string& response) {
45
    // Parse URL
46
2
    URL_COMPONENTS urlComp;
47
2
    ZeroMemory(&urlComp, sizeof(urlComp));
48
2
    urlComp.dwStructSize = sizeof(urlComp);
49
    
50
    // Set required component lengths to non-zero to indicate they exist
51
2
    urlComp.dwHostNameLength = (DWORD)-1;
52
2
    urlComp.dwUrlPathLength = (DWORD)-1;
53
2
    urlComp.dwExtraInfoLength = (DWORD)-1;
54
    
55
    // Parse the URL
56
2
    std::wstring wurl = utf8_to_wide(url);
57
2
    if (!WinHttpCrackUrl(wurl.c_str(), 0, 0, &urlComp)) {
58
0
        return false;
59
0
    }
60
    
61
    // Use WinHttpOpen to obtain a session handle
62
2
    HINTERNET hSession = WinHttpOpen(L"PrivateBin API Client/1.0",
63
2
                                    WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
64
2
                                    WINHTTP_NO_PROXY_NAME,
65
2
                                    WINHTTP_NO_PROXY_BYPASS, 0);
66
    
67
2
    if (!hSession) {
68
0
        std::cerr << last_winhttp_error("WinHttpOpen(GET)") << std::endl;
69
0
        return false;
70
0
    }
71
    // Force modern TLS versions to avoid handshake failures on some hosts
72
2
    DWORD protocols = WINHTTP_FLAG_SECURE_PROTOCOL_TLS1 | WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_1 |
73
2
                      WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2 | 0x00002000 /* TLS1_3 if available */;
74
2
    WinHttpSetOption(hSession, WINHTTP_OPTION_SECURE_PROTOCOLS, &protocols, sizeof(protocols));
75
    // Disable HTTP/2 if it causes issues
76
    #ifdef WINHTTP_DISABLE_FEATURE_HTTP2
77
    DWORD features = WINHTTP_DISABLE_FEATURE_HTTP2;
78
    WinHttpSetOption(hSession, WINHTTP_OPTION_DISABLE_FEATURE, &features, sizeof(features));
79
    #endif
80
    
81
    // Specify an HTTP server (host must be null-terminated; urlComp provides length)
82
2
    std::wstring host = (urlComp.lpszHostName && urlComp.dwHostNameLength > 0)
83
2
        ? std::wstring(urlComp.lpszHostName, urlComp.dwHostNameLength)
84
2
        : std::wstring();
85
2
    INTERNET_PORT port = urlComp.nPort ? urlComp.nPort : ((urlComp.nScheme == INTERNET_SCHEME_HTTPS) ? INTERNET_DEFAULT_HTTPS_PORT : INTERNET_DEFAULT_HTTP_PORT);
86
2
    HINTERNET hConnect = WinHttpConnect(hSession, host.c_str(), port, 0);
87
    
88
2
    if (!hConnect) {
89
0
        std::cerr << last_winhttp_error("WinHttpConnect(GET)") << std::endl;
90
0
        WinHttpCloseHandle(hSession);
91
0
        return false;
92
0
    }
93
    
94
    // Build object name = path + extra info (query)
95
2
    std::wstring pathPart = (urlComp.lpszUrlPath && urlComp.dwUrlPathLength > 0)
96
2
        ? std::wstring(urlComp.lpszUrlPath, urlComp.dwUrlPathLength)
97
2
        : std::wstring(L"/");
98
2
    std::wstring extraPart = (urlComp.lpszExtraInfo && urlComp.dwExtraInfoLength > 0)
99
2
        ? std::wstring(urlComp.lpszExtraInfo, urlComp.dwExtraInfoLength)
100
2
        : std::wstring();
101
2
    std::wstring objectName = pathPart + extraPart;
102
103
    // Create an HTTP request handle
104
2
    HINTERNET hRequest = WinHttpOpenRequest(hConnect, L"GET",
105
2
                                           objectName.c_str(),
106
2
                                           NULL, WINHTTP_NO_REFERER,
107
2
                                           WINHTTP_DEFAULT_ACCEPT_TYPES,
108
2
                                           (urlComp.nScheme == INTERNET_SCHEME_HTTPS) ? 
109
2
                                           WINHTTP_FLAG_SECURE : 0);
110
    // Set headers per API requirement
111
2
    LPCWSTR headers = L"X-Requested-With: JSONHttpRequest\r\nAccept: application/json";
112
2
    if (!WinHttpAddRequestHeaders(hRequest, headers, -1L, WINHTTP_ADDREQ_FLAG_ADD)) {
113
0
        std::cerr << last_winhttp_error("WinHttpAddRequestHeaders(GET)") << std::endl;
114
0
    }
115
    
116
2
    if (!hRequest) {
117
0
        WinHttpCloseHandle(hConnect);
118
0
        WinHttpCloseHandle(hSession);
119
0
        return false;
120
0
    }
121
    
122
    // Send a request
123
2
    if (!WinHttpSendRequest(hRequest,
124
2
                           WINHTTP_NO_ADDITIONAL_HEADERS, 0,
125
2
                           WINHTTP_NO_REQUEST_DATA, 0,
126
2
                           0, 0)) {
127
1
        std::cerr << last_winhttp_error("WinHttpSendRequest(GET)") << std::endl;
128
1
        WinHttpCloseHandle(hRequest);
129
1
        WinHttpCloseHandle(hConnect);
130
1
        WinHttpCloseHandle(hSession);
131
1
        return false;
132
1
    }
133
    
134
    // End the request
135
1
    if (!WinHttpReceiveResponse(hRequest, NULL)) {
136
0
        std::cerr << last_winhttp_error("WinHttpReceiveResponse(GET)") << std::endl;
137
0
        WinHttpCloseHandle(hRequest);
138
0
        WinHttpCloseHandle(hConnect);
139
0
        WinHttpCloseHandle(hSession);
140
0
        return false;
141
0
    }
142
    
143
    // Keep checking for data until there is nothing left
144
1
    DWORD dwSize = 0;
145
1
    DWORD dwDownloaded = 0;
146
1
    std::string result;
147
    
148
2
    do {
149
        // Check for available data
150
2
        dwSize = 0;
151
2
        if (!WinHttpQueryDataAvailable(hRequest, &dwSize)) {
152
0
            std::cerr << last_winhttp_error("WinHttpQueryDataAvailable(GET)") << std::endl;
153
0
            WinHttpCloseHandle(hRequest);
154
0
            WinHttpCloseHandle(hConnect);
155
0
            WinHttpCloseHandle(hSession);
156
0
            return false;
157
0
        }
158
        
159
        // Allocate space for the buffer
160
2
        char* pszOutBuffer = new char[dwSize + 1];
161
2
        if (!pszOutBuffer) {
162
0
            WinHttpCloseHandle(hRequest);
163
0
            WinHttpCloseHandle(hConnect);
164
0
            WinHttpCloseHandle(hSession);
165
0
            return false;
166
0
        }
167
        
168
        // Read the data
169
2
        ZeroMemory(pszOutBuffer, dwSize + 1);
170
2
        if (!WinHttpReadData(hRequest, (LPVOID)pszOutBuffer, dwSize, &dwDownloaded)) {
171
0
            std::cerr << last_winhttp_error("WinHttpReadData(GET)") << std::endl;
172
0
            delete[] pszOutBuffer;
173
0
            WinHttpCloseHandle(hRequest);
174
0
            WinHttpCloseHandle(hConnect);
175
0
            WinHttpCloseHandle(hSession);
176
0
            return false;
177
0
        }
178
2
        else {
179
2
            result.append(pszOutBuffer);
180
2
        }
181
        
182
        // Free the memory
183
2
        delete[] pszOutBuffer;
184
        
185
2
    } while (dwSize > 0);
186
    
187
1
    response = result;
188
    
189
    // Close any open handles
190
1
    WinHttpCloseHandle(hRequest);
191
1
    WinHttpCloseHandle(hConnect);
192
1
    WinHttpCloseHandle(hSession);
193
    
194
1
    return true;
195
1
}
196
197
1
bool HttpClient::post(const std::string& url, const std::string& data, std::string& response) {
198
    // Parse URL
199
1
    URL_COMPONENTS urlComp;
200
1
    ZeroMemory(&urlComp, sizeof(urlComp));
201
1
    urlComp.dwStructSize = sizeof(urlComp);
202
    
203
    // Set required component lengths to non-zero to indicate they exist
204
1
    urlComp.dwHostNameLength = (DWORD)-1;
205
1
    urlComp.dwUrlPathLength = (DWORD)-1;
206
1
    urlComp.dwExtraInfoLength = (DWORD)-1;
207
    
208
    // Parse the URL
209
1
    std::wstring wurl = utf8_to_wide(url);
210
1
    if (!WinHttpCrackUrl(wurl.c_str(), 0, 0, &urlComp)) {
211
0
        return false;
212
0
    }
213
    
214
    // Use WinHttpOpen to obtain a session handle
215
1
    HINTERNET hSession = WinHttpOpen(L"PrivateBin API Client/1.0",
216
1
                                    WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
217
1
                                    WINHTTP_NO_PROXY_NAME,
218
1
                                    WINHTTP_NO_PROXY_BYPASS, 0);
219
    
220
1
    if (!hSession) {
221
0
        std::cerr << last_winhttp_error("WinHttpOpen(POST)") << std::endl;
222
0
        return false;
223
0
    }
224
1
    DWORD protocols2 = WINHTTP_FLAG_SECURE_PROTOCOL_TLS1 | WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_1 |
225
1
                       WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2 | 0x00002000;
226
1
    WinHttpSetOption(hSession, WINHTTP_OPTION_SECURE_PROTOCOLS, &protocols2, sizeof(protocols2));
227
    #ifdef WINHTTP_DISABLE_FEATURE_HTTP2
228
    DWORD features2 = WINHTTP_DISABLE_FEATURE_HTTP2;
229
    WinHttpSetOption(hSession, WINHTTP_OPTION_DISABLE_FEATURE, &features2, sizeof(features2));
230
    #endif
231
    
232
    // Specify an HTTP server (ensure host is null-terminated)
233
1
    std::wstring host2 = (urlComp.lpszHostName && urlComp.dwHostNameLength > 0)
234
1
        ? std::wstring(urlComp.lpszHostName, urlComp.dwHostNameLength)
235
1
        : std::wstring();
236
1
    INTERNET_PORT port2 = urlComp.nPort ? urlComp.nPort : ((urlComp.nScheme == INTERNET_SCHEME_HTTPS) ? INTERNET_DEFAULT_HTTPS_PORT : INTERNET_DEFAULT_HTTP_PORT);
237
1
    HINTERNET hConnect = WinHttpConnect(hSession, host2.c_str(), port2, 0);
238
    
239
1
    if (!hConnect) {
240
0
        std::cerr << last_winhttp_error("WinHttpConnect(POST)") << std::endl;
241
0
        WinHttpCloseHandle(hSession);
242
0
        return false;
243
0
    }
244
    
245
    // Create an HTTP request handle (POST per API 1.3)
246
1
    LPCWSTR postPath = (urlComp.lpszUrlPath && urlComp.dwUrlPathLength > 0) ? urlComp.lpszUrlPath : L"/";
247
1
    HINTERNET hRequest = WinHttpOpenRequest(hConnect, L"POST",
248
1
                                           postPath,
249
1
                                           NULL, WINHTTP_NO_REFERER,
250
1
                                           WINHTTP_DEFAULT_ACCEPT_TYPES,
251
1
                                           (urlComp.nScheme == INTERNET_SCHEME_HTTPS) ? 
252
1
                                           WINHTTP_FLAG_SECURE : 0);
253
    
254
1
    if (!hRequest) {
255
0
        std::cerr << "[WinHTTP] WinHttpOpenRequest(POST) failed" << std::endl;
256
0
        WinHttpCloseHandle(hConnect);
257
0
        WinHttpCloseHandle(hSession);
258
0
        return false;
259
0
    }
260
    
261
    // Set headers
262
1
    LPCWSTR headers = L"Content-Type: application/json\r\nX-Requested-With: JSONHttpRequest";
263
1
    BOOL bResults = WinHttpAddRequestHeaders(hRequest,
264
1
                                            headers,
265
1
                                            -1L,
266
1
                                            WINHTTP_ADDREQ_FLAG_ADD);
267
    
268
1
    if (!bResults) {
269
0
        std::cerr << last_winhttp_error("WinHttpAddRequestHeaders(POST)") << std::endl;
270
0
        WinHttpCloseHandle(hRequest);
271
0
        WinHttpCloseHandle(hConnect);
272
0
        WinHttpCloseHandle(hSession);
273
0
        return false;
274
0
    }
275
    
276
    // Send a request
277
    // Send UTF-8 bytes as body (raw)
278
1
    if (!WinHttpSendRequest(hRequest,
279
1
                           WINHTTP_NO_ADDITIONAL_HEADERS, 0,
280
1
                           (LPVOID)data.c_str(), (DWORD)data.length(),
281
1
                           (DWORD)data.length(), 0)) {
282
1
        std::cerr << last_winhttp_error("WinHttpSendRequest(POST)") << std::endl;
283
1
        WinHttpCloseHandle(hRequest);
284
1
        WinHttpCloseHandle(hConnect);
285
1
        WinHttpCloseHandle(hSession);
286
1
        return false;
287
1
    }
288
    
289
    // End the request
290
0
    if (!WinHttpReceiveResponse(hRequest, NULL)) {
291
0
        std::cerr << last_winhttp_error("WinHttpReceiveResponse(POST)") << std::endl;
292
0
        WinHttpCloseHandle(hRequest);
293
0
        WinHttpCloseHandle(hConnect);
294
0
        WinHttpCloseHandle(hSession);
295
0
        return false;
296
0
    }
297
    
298
    // Keep checking for data until there is nothing left
299
0
    DWORD dwSize = 0;
300
0
    DWORD dwDownloaded = 0;
301
0
    std::string result;
302
    
303
0
    do {
304
        // Check for available data
305
0
        dwSize = 0;
306
0
        if (!WinHttpQueryDataAvailable(hRequest, &dwSize)) {
307
0
            std::cerr << last_winhttp_error("WinHttpQueryDataAvailable(POST)") << std::endl;
308
0
            WinHttpCloseHandle(hRequest);
309
0
            WinHttpCloseHandle(hConnect);
310
0
            WinHttpCloseHandle(hSession);
311
0
            return false;
312
0
        }
313
        
314
        // Allocate space for the buffer
315
0
        char* pszOutBuffer = new char[dwSize + 1];
316
0
        if (!pszOutBuffer) {
317
0
            WinHttpCloseHandle(hRequest);
318
0
            WinHttpCloseHandle(hConnect);
319
0
            WinHttpCloseHandle(hSession);
320
0
            return false;
321
0
        }
322
        
323
        // Read the data
324
0
        ZeroMemory(pszOutBuffer, dwSize + 1);
325
0
        if (!WinHttpReadData(hRequest, (LPVOID)pszOutBuffer, dwSize, &dwDownloaded)) {
326
0
            std::cerr << last_winhttp_error("WinHttpReadData(POST)") << std::endl;
327
0
            delete[] pszOutBuffer;
328
0
            WinHttpCloseHandle(hRequest);
329
0
            WinHttpCloseHandle(hConnect);
330
0
            WinHttpCloseHandle(hSession);
331
0
            return false;
332
0
        }
333
0
        else {
334
0
            result.append(pszOutBuffer);
335
0
        }
336
        
337
        // Free the memory
338
0
        delete[] pszOutBuffer;
339
        
340
0
    } while (dwSize > 0);
341
    
342
0
    response = result;
343
    
344
    // Close any open handles
345
0
    WinHttpCloseHandle(hRequest);
346
0
    WinHttpCloseHandle(hConnect);
347
0
    WinHttpCloseHandle(hSession);
348
    
349
0
    return true;
350
0
}
351
352
1
bool HttpClient::delete_req(const std::string& url, const std::string& data, std::string& response) {
353
    // Parse URL
354
1
    URL_COMPONENTS urlComp;
355
1
    ZeroMemory(&urlComp, sizeof(urlComp));
356
1
    urlComp.dwStructSize = sizeof(urlComp);
357
    
358
    // Set required component lengths to non-zero to indicate they exist
359
1
    urlComp.dwHostNameLength = (DWORD)-1;
360
1
    urlComp.dwUrlPathLength = (DWORD)-1;
361
1
    urlComp.dwExtraInfoLength = (DWORD)-1;
362
    
363
    // Parse the URL
364
1
    std::wstring wurlDel = utf8_to_wide(url);
365
1
    if (!WinHttpCrackUrl(wurlDel.c_str(), 0, 0, &urlComp)) {
366
0
        return false;
367
0
    }
368
    
369
    // Use WinHttpOpen to obtain a session handle
370
1
    HINTERNET hSession = WinHttpOpen(L"PrivateBin API Client/1.0",
371
1
                                    WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
372
1
                                    WINHTTP_NO_PROXY_NAME,
373
1
                                    WINHTTP_NO_PROXY_BYPASS, 0);
374
    
375
1
    if (!hSession) {
376
0
        std::cerr << last_winhttp_error("WinHttpOpen(DEL)") << std::endl;
377
0
        return false;
378
0
    }
379
1
    DWORD protocols3 = WINHTTP_FLAG_SECURE_PROTOCOL_TLS1 | WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_1 |
380
1
                       WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2 | 0x00002000;
381
1
    WinHttpSetOption(hSession, WINHTTP_OPTION_SECURE_PROTOCOLS, &protocols3, sizeof(protocols3));
382
    #ifdef WINHTTP_DISABLE_FEATURE_HTTP2
383
    DWORD features3 = WINHTTP_DISABLE_FEATURE_HTTP2;
384
    WinHttpSetOption(hSession, WINHTTP_OPTION_DISABLE_FEATURE, &features3, sizeof(features3));
385
    #endif
386
    
387
    // Specify an HTTP server (ensure host is null-terminated)
388
1
    std::wstring host3 = (urlComp.lpszHostName && urlComp.dwHostNameLength > 0)
389
1
        ? std::wstring(urlComp.lpszHostName, urlComp.dwHostNameLength)
390
1
        : std::wstring();
391
1
    INTERNET_PORT port3 = urlComp.nPort ? urlComp.nPort : ((urlComp.nScheme == INTERNET_SCHEME_HTTPS) ? INTERNET_DEFAULT_HTTPS_PORT : INTERNET_DEFAULT_HTTP_PORT);
392
1
    HINTERNET hConnect = WinHttpConnect(hSession, host3.c_str(), port3, 0);
393
    
394
1
    if (!hConnect) {
395
0
        std::cerr << last_winhttp_error("WinHttpConnect(DEL)") << std::endl;
396
0
        WinHttpCloseHandle(hSession);
397
0
        return false;
398
0
    }
399
    
400
    // Create an HTTP request handle
401
1
    LPCWSTR delPath = (urlComp.lpszUrlPath && urlComp.dwUrlPathLength > 0) ? urlComp.lpszUrlPath : L"/";
402
1
    HINTERNET hRequest = WinHttpOpenRequest(hConnect, L"POST",
403
1
                                           delPath,
404
1
                                           NULL, WINHTTP_NO_REFERER,
405
1
                                           WINHTTP_DEFAULT_ACCEPT_TYPES,
406
1
                                           (urlComp.nScheme == INTERNET_SCHEME_HTTPS) ? 
407
1
                                           WINHTTP_FLAG_SECURE : 0);
408
    
409
1
    if (!hRequest) {
410
0
        std::cerr << "[WinHTTP] WinHttpOpenRequest(DEL) failed" << std::endl;
411
0
        WinHttpCloseHandle(hConnect);
412
0
        WinHttpCloseHandle(hSession);
413
0
        return false;
414
0
    }
415
    
416
    // Set headers
417
1
    LPCWSTR headers = L"Content-Type: application/json\r\nX-Requested-With: JSONHttpRequest";
418
1
    BOOL bResults = WinHttpAddRequestHeaders(hRequest,
419
1
                                            headers,
420
1
                                            -1L,
421
1
                                            WINHTTP_ADDREQ_FLAG_ADD);
422
    
423
1
    if (!bResults) {
424
0
        std::cerr << last_winhttp_error("WinHttpAddRequestHeaders(DEL)") << std::endl;
425
0
        WinHttpCloseHandle(hRequest);
426
0
        WinHttpCloseHandle(hConnect);
427
0
        WinHttpCloseHandle(hSession);
428
0
        return false;
429
0
    }
430
    
431
    // Send a request body with DELETE method
432
1
    if (!WinHttpSendRequest(hRequest,
433
1
                           WINHTTP_NO_ADDITIONAL_HEADERS, 0,
434
1
                           (LPVOID)data.c_str(), (DWORD)data.length(),
435
1
                           (DWORD)data.length(), 0)) {
436
1
        std::cerr << last_winhttp_error("WinHttpSendRequest(DEL)") << std::endl;
437
1
        WinHttpCloseHandle(hRequest);
438
1
        WinHttpCloseHandle(hConnect);
439
1
        WinHttpCloseHandle(hSession);
440
1
        return false;
441
1
    }
442
    
443
    // End the request
444
0
    if (!WinHttpReceiveResponse(hRequest, NULL)) {
445
0
        std::cerr << last_winhttp_error("WinHttpReceiveResponse(DEL)") << std::endl;
446
0
        WinHttpCloseHandle(hRequest);
447
0
        WinHttpCloseHandle(hConnect);
448
0
        WinHttpCloseHandle(hSession);
449
0
        return false;
450
0
    }
451
    
452
    // Keep checking for data until there is nothing left
453
0
    DWORD dwSize = 0;
454
0
    DWORD dwDownloaded = 0;
455
0
    std::string result;
456
    
457
0
    do {
458
        // Check for available data
459
0
        dwSize = 0;
460
0
        if (!WinHttpQueryDataAvailable(hRequest, &dwSize)) {
461
0
            std::cerr << last_winhttp_error("WinHttpQueryDataAvailable(DEL)") << std::endl;
462
0
            WinHttpCloseHandle(hRequest);
463
0
            WinHttpCloseHandle(hConnect);
464
0
            WinHttpCloseHandle(hSession);
465
0
            return false;
466
0
        }
467
        
468
        // Allocate space for the buffer
469
0
        char* pszOutBuffer = new char[dwSize + 1];
470
0
        if (!pszOutBuffer) {
471
0
            WinHttpCloseHandle(hRequest);
472
0
            WinHttpCloseHandle(hConnect);
473
0
            WinHttpCloseHandle(hSession);
474
0
            return false;
475
0
        }
476
        
477
        // Read the data
478
0
        ZeroMemory(pszOutBuffer, dwSize + 1);
479
0
        if (!WinHttpReadData(hRequest, (LPVOID)pszOutBuffer, dwSize, &dwDownloaded)) {
480
0
            std::cerr << last_winhttp_error("WinHttpReadData(DEL)") << std::endl;
481
0
            delete[] pszOutBuffer;
482
0
            WinHttpCloseHandle(hRequest);
483
0
            WinHttpCloseHandle(hConnect);
484
0
            WinHttpCloseHandle(hSession);
485
0
            return false;
486
0
        }
487
0
        else {
488
0
            result.append(pszOutBuffer);
489
0
        }
490
        
491
        // Free the memory
492
0
        delete[] pszOutBuffer;
493
        
494
0
    } while (dwSize > 0);
495
    
496
0
    response = result;
497
    
498
    // Close any open handles
499
0
    WinHttpCloseHandle(hRequest);
500
0
    WinHttpCloseHandle(hConnect);
501
0
    WinHttpCloseHandle(hSession);
502
    
503
0
    return true;
504
0
}
505
506
#elif LINUX
507
508
static size_t WriteCallback(void* contents, size_t size, size_t nmemb, std::string* userp) {
509
    size_t realsize = size * nmemb;
510
    userp->append((char*)contents, realsize);
511
    return realsize;
512
}
513
514
bool HttpClient::get(const std::string& url, std::string& response) {
515
    CURL* curl;
516
    CURLcode res;
517
    
518
    curl = curl_easy_init();
519
    if (!curl) {
520
        return false;
521
    }
522
    
523
    curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
524
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
525
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
526
    curl_easy_setopt(curl, CURLOPT_USERAGENT, "PrivateBin API Client/1.0");
527
    
528
    struct curl_slist* headers = NULL;
529
    headers = curl_slist_append(headers, "X-Requested-With: JSONHttpRequest");
530
    curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
531
    
532
    res = curl_easy_perform(curl);
533
    
534
    curl_slist_free_all(headers);
535
    curl_easy_cleanup(curl);
536
    
537
    return (res == CURLE_OK);
538
}
539
540
bool HttpClient::post(const std::string& url, const std::string& data, std::string& response) {
541
    CURL* curl;
542
    CURLcode res;
543
    
544
    curl = curl_easy_init();
545
    if (!curl) {
546
        return false;
547
    }
548
    
549
    curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
550
    curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data.c_str());
551
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
552
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
553
    curl_easy_setopt(curl, CURLOPT_USERAGENT, "PrivateBin API Client/1.0");
554
    
555
    struct curl_slist* headers = NULL;
556
    headers = curl_slist_append(headers, "Content-Type: application/json");
557
    headers = curl_slist_append(headers, "X-Requested-With: JSONHttpRequest");
558
    curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
559
    
560
    res = curl_easy_perform(curl);
561
    
562
    curl_slist_free_all(headers);
563
    curl_easy_cleanup(curl);
564
    
565
    return (res == CURLE_OK);
566
}
567
568
bool HttpClient::delete_req(const std::string& url, const std::string& data, std::string& response) {
569
    CURL* curl;
570
    CURLcode res;
571
    
572
    curl = curl_easy_init();
573
    if (!curl) {
574
        return false;
575
    }
576
    
577
    curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
578
    // PrivateBin API erwartet POST für delete
579
    curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data.c_str());
580
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
581
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
582
    curl_easy_setopt(curl, CURLOPT_USERAGENT, "PrivateBin API Client/1.0");
583
    
584
    struct curl_slist* headers = NULL;
585
    headers = curl_slist_append(headers, "Content-Type: application/json");
586
    headers = curl_slist_append(headers, "X-Requested-With: JSONHttpRequest");
587
    curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
588
    
589
    res = curl_easy_perform(curl);
590
    
591
    curl_slist_free_all(headers);
592
    curl_easy_cleanup(curl);
593
    
594
    return (res == CURLE_OK);
595
}
596
597
#endif
\ No newline at end of file +

Coverage Report

Created: 2025-08-29 12:48

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
C:\Users\mbusc\source\repos\privatebin-cpp\src\http_client.cpp
Line
Count
Source
1
#include "http_client.h"
2
#include <iostream>
3
#include <string>
4
#include <sstream>
5
6
#ifdef WINDOWS
7
#include <windows.h>
8
#include <winhttp.h>
9
#pragma comment(lib, "winhttp.lib")
10
3
static std::string last_winhttp_error(const char* where) {
11
3
    DWORD err = GetLastError();
12
3
    LPVOID lpMsgBuf = nullptr;
13
3
    FormatMessageA(
14
3
        FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
15
3
        NULL,
16
3
        err,
17
3
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
18
3
        (LPSTR)&lpMsgBuf,
19
3
        0, NULL);
20
3
    std::ostringstream os;
21
3
    os << "[WinHTTP] " << where << " failed, error=" << err;
22
3
    if (lpMsgBuf) {
23
0
        os << ": " << (char*)lpMsgBuf;
24
0
        LocalFree(lpMsgBuf);
25
0
    }
26
3
    return os.str();
27
3
}
28
29
4
static std::wstring utf8_to_wide(const std::string& s) {
30
4
    if (s.empty()) return std::wstring();
31
4
    int needed = MultiByteToWideChar(CP_UTF8, 0, s.c_str(), (int)s.size(), nullptr, 0);
32
4
    std::wstring ws(needed, L'\0');
33
4
    MultiByteToWideChar(CP_UTF8, 0, s.c_str(), (int)s.size(), &ws[0], needed);
34
4
    return ws;
35
4
}
36
#elif LINUX
37
#include <curl/curl.h>
38
#endif
39
40
#ifdef WINDOWS
41
42
/* Entfernt: ungenutzte WinHttpData/Callback, um Clang-Warnungen zu vermeiden */
43
44
2
bool HttpClient::get(const std::string& url, std::string& response) {
45
    // Parse URL
46
2
    URL_COMPONENTS urlComp;
47
2
    ZeroMemory(&urlComp, sizeof(urlComp));
48
2
    urlComp.dwStructSize = sizeof(urlComp);
49
    
50
    // Set required component lengths to non-zero to indicate they exist
51
2
    urlComp.dwHostNameLength = (DWORD)-1;
52
2
    urlComp.dwUrlPathLength = (DWORD)-1;
53
2
    urlComp.dwExtraInfoLength = (DWORD)-1;
54
    
55
    // Parse the URL
56
2
    std::wstring wurl = utf8_to_wide(url);
57
2
    if (!WinHttpCrackUrl(wurl.c_str(), 0, 0, &urlComp)) {
58
0
        return false;
59
0
    }
60
    
61
    // Use WinHttpOpen to obtain a session handle
62
2
    HINTERNET hSession = WinHttpOpen(L"PrivateBin API Client/1.0",
63
2
                                    WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
64
2
                                    WINHTTP_NO_PROXY_NAME,
65
2
                                    WINHTTP_NO_PROXY_BYPASS, 0);
66
    
67
2
    if (!hSession) {
68
0
        std::cerr << last_winhttp_error("WinHttpOpen(GET)") << std::endl;
69
0
        return false;
70
0
    }
71
    // Force modern TLS versions to avoid handshake failures on some hosts
72
2
    DWORD protocols = WINHTTP_FLAG_SECURE_PROTOCOL_TLS1 | WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_1 |
73
2
                      WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2 | 0x00002000 /* TLS1_3 if available */;
74
2
    WinHttpSetOption(hSession, WINHTTP_OPTION_SECURE_PROTOCOLS, &protocols, sizeof(protocols));
75
    // Disable HTTP/2 if it causes issues
76
    #ifdef WINHTTP_DISABLE_FEATURE_HTTP2
77
    DWORD features = WINHTTP_DISABLE_FEATURE_HTTP2;
78
    WinHttpSetOption(hSession, WINHTTP_OPTION_DISABLE_FEATURE, &features, sizeof(features));
79
    #endif
80
    
81
    // Specify an HTTP server (host must be null-terminated; urlComp provides length)
82
2
    std::wstring host = (urlComp.lpszHostName && urlComp.dwHostNameLength > 0)
83
2
        ? std::wstring(urlComp.lpszHostName, urlComp.dwHostNameLength)
84
2
        : std::wstring();
85
2
    INTERNET_PORT port = urlComp.nPort ? urlComp.nPort : ((urlComp.nScheme == INTERNET_SCHEME_HTTPS) ? INTERNET_DEFAULT_HTTPS_PORT : INTERNET_DEFAULT_HTTP_PORT);
86
2
    HINTERNET hConnect = WinHttpConnect(hSession, host.c_str(), port, 0);
87
    
88
2
    if (!hConnect) {
89
0
        std::cerr << last_winhttp_error("WinHttpConnect(GET)") << std::endl;
90
0
        WinHttpCloseHandle(hSession);
91
0
        return false;
92
0
    }
93
    
94
    // Build object name = path + extra info (query)
95
2
    std::wstring pathPart = (urlComp.lpszUrlPath && urlComp.dwUrlPathLength > 0)
96
2
        ? std::wstring(urlComp.lpszUrlPath, urlComp.dwUrlPathLength)
97
2
        : std::wstring(L"/");
98
2
    std::wstring extraPart = (urlComp.lpszExtraInfo && urlComp.dwExtraInfoLength > 0)
99
2
        ? std::wstring(urlComp.lpszExtraInfo, urlComp.dwExtraInfoLength)
100
2
        : std::wstring();
101
2
    std::wstring objectName = pathPart + extraPart;
102
103
    // Create an HTTP request handle
104
2
    HINTERNET hRequest = WinHttpOpenRequest(hConnect, L"GET",
105
2
                                           objectName.c_str(),
106
2
                                           NULL, WINHTTP_NO_REFERER,
107
2
                                           WINHTTP_DEFAULT_ACCEPT_TYPES,
108
2
                                           (urlComp.nScheme == INTERNET_SCHEME_HTTPS) ? 
109
2
                                           WINHTTP_FLAG_SECURE : 0);
110
    // Set headers per API requirement
111
2
    LPCWSTR headers = L"X-Requested-With: JSONHttpRequest\r\nAccept: application/json";
112
2
    if (!WinHttpAddRequestHeaders(hRequest, headers, -1L, WINHTTP_ADDREQ_FLAG_ADD)) {
113
0
        std::cerr << last_winhttp_error("WinHttpAddRequestHeaders(GET)") << std::endl;
114
0
    }
115
    
116
2
    if (!hRequest) {
117
0
        WinHttpCloseHandle(hConnect);
118
0
        WinHttpCloseHandle(hSession);
119
0
        return false;
120
0
    }
121
    
122
    // Send a request
123
2
    if (!WinHttpSendRequest(hRequest,
124
2
                           WINHTTP_NO_ADDITIONAL_HEADERS, 0,
125
2
                           WINHTTP_NO_REQUEST_DATA, 0,
126
2
                           0, 0)) {
127
1
        std::cerr << last_winhttp_error("WinHttpSendRequest(GET)") << std::endl;
128
1
        WinHttpCloseHandle(hRequest);
129
1
        WinHttpCloseHandle(hConnect);
130
1
        WinHttpCloseHandle(hSession);
131
1
        return false;
132
1
    }
133
    
134
    // End the request
135
1
    if (!WinHttpReceiveResponse(hRequest, NULL)) {
136
0
        std::cerr << last_winhttp_error("WinHttpReceiveResponse(GET)") << std::endl;
137
0
        WinHttpCloseHandle(hRequest);
138
0
        WinHttpCloseHandle(hConnect);
139
0
        WinHttpCloseHandle(hSession);
140
0
        return false;
141
0
    }
142
    
143
    // Keep checking for data until there is nothing left
144
1
    DWORD dwSize = 0;
145
1
    DWORD dwDownloaded = 0;
146
1
    std::string result;
147
    
148
2
    do {
149
        // Check for available data
150
2
        dwSize = 0;
151
2
        if (!WinHttpQueryDataAvailable(hRequest, &dwSize)) {
152
0
            std::cerr << last_winhttp_error("WinHttpQueryDataAvailable(GET)") << std::endl;
153
0
            WinHttpCloseHandle(hRequest);
154
0
            WinHttpCloseHandle(hConnect);
155
0
            WinHttpCloseHandle(hSession);
156
0
            return false;
157
0
        }
158
        
159
        // Allocate space for the buffer
160
2
        char* pszOutBuffer = new char[dwSize + 1];
161
2
        if (!pszOutBuffer) {
162
0
            WinHttpCloseHandle(hRequest);
163
0
            WinHttpCloseHandle(hConnect);
164
0
            WinHttpCloseHandle(hSession);
165
0
            return false;
166
0
        }
167
        
168
        // Read the data
169
2
        ZeroMemory(pszOutBuffer, dwSize + 1);
170
2
        if (!WinHttpReadData(hRequest, (LPVOID)pszOutBuffer, dwSize, &dwDownloaded)) {
171
0
            std::cerr << last_winhttp_error("WinHttpReadData(GET)") << std::endl;
172
0
            delete[] pszOutBuffer;
173
0
            WinHttpCloseHandle(hRequest);
174
0
            WinHttpCloseHandle(hConnect);
175
0
            WinHttpCloseHandle(hSession);
176
0
            return false;
177
0
        }
178
2
        else {
179
2
            result.append(pszOutBuffer);
180
2
        }
181
        
182
        // Free the memory
183
2
        delete[] pszOutBuffer;
184
        
185
2
    } while (dwSize > 0);
186
    
187
1
    response = result;
188
    
189
    // Close any open handles
190
1
    WinHttpCloseHandle(hRequest);
191
1
    WinHttpCloseHandle(hConnect);
192
1
    WinHttpCloseHandle(hSession);
193
    
194
1
    return true;
195
1
}
196
197
1
bool HttpClient::post(const std::string& url, const std::string& data, std::string& response) {
198
    // Parse URL
199
1
    URL_COMPONENTS urlComp;
200
1
    ZeroMemory(&urlComp, sizeof(urlComp));
201
1
    urlComp.dwStructSize = sizeof(urlComp);
202
    
203
    // Set required component lengths to non-zero to indicate they exist
204
1
    urlComp.dwHostNameLength = (DWORD)-1;
205
1
    urlComp.dwUrlPathLength = (DWORD)-1;
206
1
    urlComp.dwExtraInfoLength = (DWORD)-1;
207
    
208
    // Parse the URL
209
1
    std::wstring wurl = utf8_to_wide(url);
210
1
    if (!WinHttpCrackUrl(wurl.c_str(), 0, 0, &urlComp)) {
211
0
        return false;
212
0
    }
213
    
214
    // Use WinHttpOpen to obtain a session handle
215
1
    HINTERNET hSession = WinHttpOpen(L"PrivateBin API Client/1.0",
216
1
                                    WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
217
1
                                    WINHTTP_NO_PROXY_NAME,
218
1
                                    WINHTTP_NO_PROXY_BYPASS, 0);
219
    
220
1
    if (!hSession) {
221
0
        std::cerr << last_winhttp_error("WinHttpOpen(POST)") << std::endl;
222
0
        return false;
223
0
    }
224
1
    DWORD protocols2 = WINHTTP_FLAG_SECURE_PROTOCOL_TLS1 | WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_1 |
225
1
                       WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2 | 0x00002000;
226
1
    WinHttpSetOption(hSession, WINHTTP_OPTION_SECURE_PROTOCOLS, &protocols2, sizeof(protocols2));
227
    #ifdef WINHTTP_DISABLE_FEATURE_HTTP2
228
    DWORD features2 = WINHTTP_DISABLE_FEATURE_HTTP2;
229
    WinHttpSetOption(hSession, WINHTTP_OPTION_DISABLE_FEATURE, &features2, sizeof(features2));
230
    #endif
231
    
232
    // Specify an HTTP server (ensure host is null-terminated)
233
1
    std::wstring host2 = (urlComp.lpszHostName && urlComp.dwHostNameLength > 0)
234
1
        ? std::wstring(urlComp.lpszHostName, urlComp.dwHostNameLength)
235
1
        : std::wstring();
236
1
    INTERNET_PORT port2 = urlComp.nPort ? urlComp.nPort : ((urlComp.nScheme == INTERNET_SCHEME_HTTPS) ? INTERNET_DEFAULT_HTTPS_PORT : INTERNET_DEFAULT_HTTP_PORT);
237
1
    HINTERNET hConnect = WinHttpConnect(hSession, host2.c_str(), port2, 0);
238
    
239
1
    if (!hConnect) {
240
0
        std::cerr << last_winhttp_error("WinHttpConnect(POST)") << std::endl;
241
0
        WinHttpCloseHandle(hSession);
242
0
        return false;
243
0
    }
244
    
245
    // Create an HTTP request handle (POST per API 1.3)
246
1
    LPCWSTR postPath = (urlComp.lpszUrlPath && urlComp.dwUrlPathLength > 0) ? urlComp.lpszUrlPath : L"/";
247
1
    HINTERNET hRequest = WinHttpOpenRequest(hConnect, L"POST",
248
1
                                           postPath,
249
1
                                           NULL, WINHTTP_NO_REFERER,
250
1
                                           WINHTTP_DEFAULT_ACCEPT_TYPES,
251
1
                                           (urlComp.nScheme == INTERNET_SCHEME_HTTPS) ? 
252
1
                                           WINHTTP_FLAG_SECURE : 0);
253
    
254
1
    if (!hRequest) {
255
0
        std::cerr << "[WinHTTP] WinHttpOpenRequest(POST) failed" << std::endl;
256
0
        WinHttpCloseHandle(hConnect);
257
0
        WinHttpCloseHandle(hSession);
258
0
        return false;
259
0
    }
260
    
261
    // Set headers
262
1
    LPCWSTR headers = L"Content-Type: application/json\r\nX-Requested-With: JSONHttpRequest";
263
1
    BOOL bResults = WinHttpAddRequestHeaders(hRequest,
264
1
                                            headers,
265
1
                                            -1L,
266
1
                                            WINHTTP_ADDREQ_FLAG_ADD);
267
    
268
1
    if (!bResults) {
269
0
        std::cerr << last_winhttp_error("WinHttpAddRequestHeaders(POST)") << std::endl;
270
0
        WinHttpCloseHandle(hRequest);
271
0
        WinHttpCloseHandle(hConnect);
272
0
        WinHttpCloseHandle(hSession);
273
0
        return false;
274
0
    }
275
    
276
    // Send a request
277
    // Send UTF-8 bytes as body (raw)
278
1
    if (!WinHttpSendRequest(hRequest,
279
1
                           WINHTTP_NO_ADDITIONAL_HEADERS, 0,
280
1
                           (LPVOID)data.c_str(), (DWORD)data.length(),
281
1
                           (DWORD)data.length(), 0)) {
282
1
        std::cerr << last_winhttp_error("WinHttpSendRequest(POST)") << std::endl;
283
1
        WinHttpCloseHandle(hRequest);
284
1
        WinHttpCloseHandle(hConnect);
285
1
        WinHttpCloseHandle(hSession);
286
1
        return false;
287
1
    }
288
    
289
    // End the request
290
0
    if (!WinHttpReceiveResponse(hRequest, NULL)) {
291
0
        std::cerr << last_winhttp_error("WinHttpReceiveResponse(POST)") << std::endl;
292
0
        WinHttpCloseHandle(hRequest);
293
0
        WinHttpCloseHandle(hConnect);
294
0
        WinHttpCloseHandle(hSession);
295
0
        return false;
296
0
    }
297
    
298
    // Keep checking for data until there is nothing left
299
0
    DWORD dwSize = 0;
300
0
    DWORD dwDownloaded = 0;
301
0
    std::string result;
302
    
303
0
    do {
304
        // Check for available data
305
0
        dwSize = 0;
306
0
        if (!WinHttpQueryDataAvailable(hRequest, &dwSize)) {
307
0
            std::cerr << last_winhttp_error("WinHttpQueryDataAvailable(POST)") << std::endl;
308
0
            WinHttpCloseHandle(hRequest);
309
0
            WinHttpCloseHandle(hConnect);
310
0
            WinHttpCloseHandle(hSession);
311
0
            return false;
312
0
        }
313
        
314
        // Allocate space for the buffer
315
0
        char* pszOutBuffer = new char[dwSize + 1];
316
0
        if (!pszOutBuffer) {
317
0
            WinHttpCloseHandle(hRequest);
318
0
            WinHttpCloseHandle(hConnect);
319
0
            WinHttpCloseHandle(hSession);
320
0
            return false;
321
0
        }
322
        
323
        // Read the data
324
0
        ZeroMemory(pszOutBuffer, dwSize + 1);
325
0
        if (!WinHttpReadData(hRequest, (LPVOID)pszOutBuffer, dwSize, &dwDownloaded)) {
326
0
            std::cerr << last_winhttp_error("WinHttpReadData(POST)") << std::endl;
327
0
            delete[] pszOutBuffer;
328
0
            WinHttpCloseHandle(hRequest);
329
0
            WinHttpCloseHandle(hConnect);
330
0
            WinHttpCloseHandle(hSession);
331
0
            return false;
332
0
        }
333
0
        else {
334
0
            result.append(pszOutBuffer);
335
0
        }
336
        
337
        // Free the memory
338
0
        delete[] pszOutBuffer;
339
        
340
0
    } while (dwSize > 0);
341
    
342
0
    response = result;
343
    
344
    // Close any open handles
345
0
    WinHttpCloseHandle(hRequest);
346
0
    WinHttpCloseHandle(hConnect);
347
0
    WinHttpCloseHandle(hSession);
348
    
349
0
    return true;
350
0
}
351
352
1
bool HttpClient::delete_req(const std::string& url, const std::string& data, std::string& response) {
353
    // Parse URL
354
1
    URL_COMPONENTS urlComp;
355
1
    ZeroMemory(&urlComp, sizeof(urlComp));
356
1
    urlComp.dwStructSize = sizeof(urlComp);
357
    
358
    // Set required component lengths to non-zero to indicate they exist
359
1
    urlComp.dwHostNameLength = (DWORD)-1;
360
1
    urlComp.dwUrlPathLength = (DWORD)-1;
361
1
    urlComp.dwExtraInfoLength = (DWORD)-1;
362
    
363
    // Parse the URL
364
1
    std::wstring wurlDel = utf8_to_wide(url);
365
1
    if (!WinHttpCrackUrl(wurlDel.c_str(), 0, 0, &urlComp)) {
366
0
        return false;
367
0
    }
368
    
369
    // Use WinHttpOpen to obtain a session handle
370
1
    HINTERNET hSession = WinHttpOpen(L"PrivateBin API Client/1.0",
371
1
                                    WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
372
1
                                    WINHTTP_NO_PROXY_NAME,
373
1
                                    WINHTTP_NO_PROXY_BYPASS, 0);
374
    
375
1
    if (!hSession) {
376
0
        std::cerr << last_winhttp_error("WinHttpOpen(DEL)") << std::endl;
377
0
        return false;
378
0
    }
379
1
    DWORD protocols3 = WINHTTP_FLAG_SECURE_PROTOCOL_TLS1 | WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_1 |
380
1
                       WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2 | 0x00002000;
381
1
    WinHttpSetOption(hSession, WINHTTP_OPTION_SECURE_PROTOCOLS, &protocols3, sizeof(protocols3));
382
    #ifdef WINHTTP_DISABLE_FEATURE_HTTP2
383
    DWORD features3 = WINHTTP_DISABLE_FEATURE_HTTP2;
384
    WinHttpSetOption(hSession, WINHTTP_OPTION_DISABLE_FEATURE, &features3, sizeof(features3));
385
    #endif
386
    
387
    // Specify an HTTP server (ensure host is null-terminated)
388
1
    std::wstring host3 = (urlComp.lpszHostName && urlComp.dwHostNameLength > 0)
389
1
        ? std::wstring(urlComp.lpszHostName, urlComp.dwHostNameLength)
390
1
        : std::wstring();
391
1
    INTERNET_PORT port3 = urlComp.nPort ? urlComp.nPort : ((urlComp.nScheme == INTERNET_SCHEME_HTTPS) ? INTERNET_DEFAULT_HTTPS_PORT : INTERNET_DEFAULT_HTTP_PORT);
392
1
    HINTERNET hConnect = WinHttpConnect(hSession, host3.c_str(), port3, 0);
393
    
394
1
    if (!hConnect) {
395
0
        std::cerr << last_winhttp_error("WinHttpConnect(DEL)") << std::endl;
396
0
        WinHttpCloseHandle(hSession);
397
0
        return false;
398
0
    }
399
    
400
    // Create an HTTP request handle
401
1
    LPCWSTR delPath = (urlComp.lpszUrlPath && urlComp.dwUrlPathLength > 0) ? urlComp.lpszUrlPath : L"/";
402
1
    HINTERNET hRequest = WinHttpOpenRequest(hConnect, L"POST",
403
1
                                           delPath,
404
1
                                           NULL, WINHTTP_NO_REFERER,
405
1
                                           WINHTTP_DEFAULT_ACCEPT_TYPES,
406
1
                                           (urlComp.nScheme == INTERNET_SCHEME_HTTPS) ? 
407
1
                                           WINHTTP_FLAG_SECURE : 0);
408
    
409
1
    if (!hRequest) {
410
0
        std::cerr << "[WinHTTP] WinHttpOpenRequest(DEL) failed" << std::endl;
411
0
        WinHttpCloseHandle(hConnect);
412
0
        WinHttpCloseHandle(hSession);
413
0
        return false;
414
0
    }
415
    
416
    // Set headers
417
1
    LPCWSTR headers = L"Content-Type: application/json\r\nX-Requested-With: JSONHttpRequest";
418
1
    BOOL bResults = WinHttpAddRequestHeaders(hRequest,
419
1
                                            headers,
420
1
                                            -1L,
421
1
                                            WINHTTP_ADDREQ_FLAG_ADD);
422
    
423
1
    if (!bResults) {
424
0
        std::cerr << last_winhttp_error("WinHttpAddRequestHeaders(DEL)") << std::endl;
425
0
        WinHttpCloseHandle(hRequest);
426
0
        WinHttpCloseHandle(hConnect);
427
0
        WinHttpCloseHandle(hSession);
428
0
        return false;
429
0
    }
430
    
431
    // Send a request body with DELETE method
432
1
    if (!WinHttpSendRequest(hRequest,
433
1
                           WINHTTP_NO_ADDITIONAL_HEADERS, 0,
434
1
                           (LPVOID)data.c_str(), (DWORD)data.length(),
435
1
                           (DWORD)data.length(), 0)) {
436
1
        std::cerr << last_winhttp_error("WinHttpSendRequest(DEL)") << std::endl;
437
1
        WinHttpCloseHandle(hRequest);
438
1
        WinHttpCloseHandle(hConnect);
439
1
        WinHttpCloseHandle(hSession);
440
1
        return false;
441
1
    }
442
    
443
    // End the request
444
0
    if (!WinHttpReceiveResponse(hRequest, NULL)) {
445
0
        std::cerr << last_winhttp_error("WinHttpReceiveResponse(DEL)") << std::endl;
446
0
        WinHttpCloseHandle(hRequest);
447
0
        WinHttpCloseHandle(hConnect);
448
0
        WinHttpCloseHandle(hSession);
449
0
        return false;
450
0
    }
451
    
452
    // Keep checking for data until there is nothing left
453
0
    DWORD dwSize = 0;
454
0
    DWORD dwDownloaded = 0;
455
0
    std::string result;
456
    
457
0
    do {
458
        // Check for available data
459
0
        dwSize = 0;
460
0
        if (!WinHttpQueryDataAvailable(hRequest, &dwSize)) {
461
0
            std::cerr << last_winhttp_error("WinHttpQueryDataAvailable(DEL)") << std::endl;
462
0
            WinHttpCloseHandle(hRequest);
463
0
            WinHttpCloseHandle(hConnect);
464
0
            WinHttpCloseHandle(hSession);
465
0
            return false;
466
0
        }
467
        
468
        // Allocate space for the buffer
469
0
        char* pszOutBuffer = new char[dwSize + 1];
470
0
        if (!pszOutBuffer) {
471
0
            WinHttpCloseHandle(hRequest);
472
0
            WinHttpCloseHandle(hConnect);
473
0
            WinHttpCloseHandle(hSession);
474
0
            return false;
475
0
        }
476
        
477
        // Read the data
478
0
        ZeroMemory(pszOutBuffer, dwSize + 1);
479
0
        if (!WinHttpReadData(hRequest, (LPVOID)pszOutBuffer, dwSize, &dwDownloaded)) {
480
0
            std::cerr << last_winhttp_error("WinHttpReadData(DEL)") << std::endl;
481
0
            delete[] pszOutBuffer;
482
0
            WinHttpCloseHandle(hRequest);
483
0
            WinHttpCloseHandle(hConnect);
484
0
            WinHttpCloseHandle(hSession);
485
0
            return false;
486
0
        }
487
0
        else {
488
0
            result.append(pszOutBuffer);
489
0
        }
490
        
491
        // Free the memory
492
0
        delete[] pszOutBuffer;
493
        
494
0
    } while (dwSize > 0);
495
    
496
0
    response = result;
497
    
498
    // Close any open handles
499
0
    WinHttpCloseHandle(hRequest);
500
0
    WinHttpCloseHandle(hConnect);
501
0
    WinHttpCloseHandle(hSession);
502
    
503
0
    return true;
504
0
}
505
506
#elif LINUX
507
508
static size_t WriteCallback(void* contents, size_t size, size_t nmemb, std::string* userp) {
509
    size_t realsize = size * nmemb;
510
    userp->append((char*)contents, realsize);
511
    return realsize;
512
}
513
514
bool HttpClient::get(const std::string& url, std::string& response) {
515
    CURL* curl;
516
    CURLcode res;
517
    
518
    curl = curl_easy_init();
519
    if (!curl) {
520
        return false;
521
    }
522
    
523
    curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
524
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
525
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
526
    curl_easy_setopt(curl, CURLOPT_USERAGENT, "PrivateBin API Client/1.0");
527
    
528
    struct curl_slist* headers = NULL;
529
    headers = curl_slist_append(headers, "X-Requested-With: JSONHttpRequest");
530
    curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
531
    
532
    res = curl_easy_perform(curl);
533
    
534
    curl_slist_free_all(headers);
535
    curl_easy_cleanup(curl);
536
    
537
    return (res == CURLE_OK);
538
}
539
540
bool HttpClient::post(const std::string& url, const std::string& data, std::string& response) {
541
    CURL* curl;
542
    CURLcode res;
543
    
544
    curl = curl_easy_init();
545
    if (!curl) {
546
        return false;
547
    }
548
    
549
    curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
550
    curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data.c_str());
551
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
552
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
553
    curl_easy_setopt(curl, CURLOPT_USERAGENT, "PrivateBin API Client/1.0");
554
    
555
    struct curl_slist* headers = NULL;
556
    headers = curl_slist_append(headers, "Content-Type: application/json");
557
    headers = curl_slist_append(headers, "X-Requested-With: JSONHttpRequest");
558
    curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
559
    
560
    res = curl_easy_perform(curl);
561
    
562
    curl_slist_free_all(headers);
563
    curl_easy_cleanup(curl);
564
    
565
    return (res == CURLE_OK);
566
}
567
568
bool HttpClient::delete_req(const std::string& url, const std::string& data, std::string& response) {
569
    CURL* curl;
570
    CURLcode res;
571
    
572
    curl = curl_easy_init();
573
    if (!curl) {
574
        return false;
575
    }
576
    
577
    curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
578
    // PrivateBin API erwartet POST für delete
579
    curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data.c_str());
580
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
581
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
582
    curl_easy_setopt(curl, CURLOPT_USERAGENT, "PrivateBin API Client/1.0");
583
    
584
    struct curl_slist* headers = NULL;
585
    headers = curl_slist_append(headers, "Content-Type: application/json");
586
    headers = curl_slist_append(headers, "X-Requested-With: JSONHttpRequest");
587
    curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
588
    
589
    res = curl_easy_perform(curl);
590
    
591
    curl_slist_free_all(headers);
592
    curl_easy_cleanup(curl);
593
    
594
    return (res == CURLE_OK);
595
}
596
597
#endif
\ No newline at end of file diff --git a/build-clang/coverage/html/coverage/Users/mbusc/source/repos/privatebin-cpp/src/json_parser.cpp.html b/build-clang/coverage/html/coverage/Users/mbusc/source/repos/privatebin-cpp/src/json_parser.cpp.html index 5ede9b5..a52ccba 100644 --- a/build-clang/coverage/html/coverage/Users/mbusc/source/repos/privatebin-cpp/src/json_parser.cpp.html +++ b/build-clang/coverage/html/coverage/Users/mbusc/source/repos/privatebin-cpp/src/json_parser.cpp.html @@ -1 +1 @@ -

Coverage Report

Created: 2025-08-29 12:44

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
C:\Users\mbusc\source\repos\privatebin-cpp\src\json_parser.cpp
Line
Count
Source
1
#include "json_parser.h"
2
#include <iostream>
3
#include <chrono>
4
#include <cryptopp/base64.h>
5
#include <cryptopp/filters.h>
6
#include <cryptopp/queue.h>
7
8
// Helper: Base64 encode without line breaks
9
6
static std::string encode_base64(const std::vector<unsigned char>& data) {
10
6
    std::string result;
11
6
    CryptoPP::StringSource ss(
12
6
        data.data(), data.size(), true,
13
6
        new CryptoPP::Base64Encoder(new CryptoPP::StringSink(result), false /* insertLineBreaks */)
14
6
    );
15
6
    return result;
16
6
}
17
18
// Helper: Base64 decode
19
3
static std::vector<unsigned char> decode_base64(const std::string& b64) {
20
3
    std::string decoded;
21
3
    CryptoPP::StringSource ss(
22
3
        b64, true, new CryptoPP::Base64Decoder(new CryptoPP::StringSink(decoded))
23
3
    );
24
3
    return std::vector<unsigned char>(decoded.begin(), decoded.end());
25
3
}
26
27
json JsonParser::create_paste_json(const std::vector<unsigned char>& cipher_text,
28
                                 const std::vector<unsigned char>& auth_tag,
29
                                 const std::vector<unsigned char>& iv,
30
                                 const std::vector<unsigned char>& salt,
31
                                 const std::string& expiration,
32
                                 const std::string& format,
33
                                 bool burn_after_reading,
34
2
                                 bool open_discussion) {
35
    
36
    // Convert binary data to base64 strings
37
2
    std::string cipher_text_b64 = encode_base64(cipher_text);
38
2
    std::string iv_b64 = encode_base64(iv);
39
2
    std::string salt_b64 = encode_base64(salt);
40
    
41
    // Hinweis: Zeitstempel wird aktuell nicht benötigt; entfernte Berechnung verhinderte eine
42
    // ungenutzte Variable unter Clang.
43
    
44
    // Create the metadata array according to PrivateBin v2
45
2
    json metadata = {
46
2
        {
47
2
            iv_b64,           // base64(cipher_iv)
48
2
            salt_b64,         // base64(kdf_salt)
49
2
            100000,           // iterations
50
2
            256,              // key size
51
2
            128,              // tag size
52
2
            "aes",            // cipher
53
2
            "gcm",            // mode
54
2
            "zlib"            // compression
55
2
        },
56
2
        format,               // formatter key
57
2
        burn_after_reading ? 1 : 0,  // burn after reading
58
2
        open_discussion ? 1 : 0      // open discussion
59
2
    };
60
    
61
    // Create the main JSON structure
62
    // JSON close to v1.3 JSON-LD: meta.expire used by server
63
2
    json paste_json = {
64
2
        {"v", 2},                 // version
65
2
        {"adata", metadata},      // metadata
66
2
        {"ct", cipher_text_b64},  // ciphertext (base64)
67
2
        {"meta", {
68
2
            {"expire", expiration}
69
2
        }}
70
2
    };
71
    
72
2
    return paste_json;
73
2
}
74
75
bool JsonParser::parse_paste_json(const json& json_data,
76
                                std::vector<unsigned char>& cipher_text,
77
                                std::vector<unsigned char>& auth_tag,
78
                                std::vector<unsigned char>& iv,
79
                                std::vector<unsigned char>& salt,
80
1
                                std::string& expiration) {
81
    
82
1
    try {
83
        // Extract and decode cipher text
84
1
        std::string ct_b64 = json_data.at("ct");
85
1
        cipher_text = decode_base64(ct_b64);
86
        
87
        // Optional explicit tag field
88
1
        if (json_data.contains("tag")) {
89
0
            std::string tag_b64 = json_data.at("tag");
90
0
            auth_tag = decode_base64(tag_b64);
91
1
        } else {
92
1
            auth_tag.clear();
93
1
        }
94
        
95
        // Extract metadata and decode IV & salt
96
1
        json adata = json_data.at("adata");
97
1
        if (adata.size() >= 1) {
98
1
            json first_element = adata[0];
99
1
            if (first_element.is_array() && first_element.size() >= 2) {
100
1
                std::string iv_b64 = first_element[0];
101
1
                std::string salt_b64 = first_element[1];
102
1
                iv = decode_base64(iv_b64);
103
1
                salt = decode_base64(salt_b64);
104
1
            }
105
1
        }
106
        
107
        // Extract expiration; servers may return either meta.expire (string)
108
        // or meta.time_to_live (integer seconds). Both are optional for our use.
109
1
        try {
110
1
            expiration = json_data.at("meta").at("expire");
111
1
        } catch (...) {
112
0
            try {
113
0
                auto ttl = json_data.at("meta").at("time_to_live").get<int>();
114
0
                expiration = std::to_string(ttl);
115
0
            } catch (...) {
116
0
                expiration.clear();
117
0
            }
118
0
        }
119
        
120
1
        return true;
121
1
    } catch (...) {
122
0
        return false;
123
0
    }
124
1
}
125
126
bool JsonParser::parse_response(const std::string& response,
127
                              int& status,
128
                              std::string& message,
129
                              std::string& paste_id,
130
                              std::string& url,
131
1
                              std::string& delete_token) {
132
    
133
1
    try {
134
1
        json json_response = json::parse(response);
135
1
        status = json_response.value("status", 1);
136
1
        if (status == 0) {
137
1
            paste_id = json_response.value("id", "");
138
1
            url = json_response.value("url", "");
139
1
            delete_token = json_response.value("deletetoken", "");
140
1
        } else {
141
0
            message = json_response.value("message", "");
142
0
        }
143
        
144
1
        return true;
145
1
    } catch (...) {
146
0
        return false;
147
0
    }
148
1
}
\ No newline at end of file +

Coverage Report

Created: 2025-08-29 12:48

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
C:\Users\mbusc\source\repos\privatebin-cpp\src\json_parser.cpp
Line
Count
Source
1
#include "json_parser.h"
2
#include <iostream>
3
#include <chrono>
4
#include <cryptopp/base64.h>
5
#include <cryptopp/filters.h>
6
#include <cryptopp/queue.h>
7
8
// Helper: Base64 encode without line breaks
9
6
static std::string encode_base64(const std::vector<unsigned char>& data) {
10
6
    std::string result;
11
6
    CryptoPP::StringSource ss(
12
6
        data.data(), data.size(), true,
13
6
        new CryptoPP::Base64Encoder(new CryptoPP::StringSink(result), false /* insertLineBreaks */)
14
6
    );
15
6
    return result;
16
6
}
17
18
// Helper: Base64 decode
19
3
static std::vector<unsigned char> decode_base64(const std::string& b64) {
20
3
    std::string decoded;
21
3
    CryptoPP::StringSource ss(
22
3
        b64, true, new CryptoPP::Base64Decoder(new CryptoPP::StringSink(decoded))
23
3
    );
24
3
    return std::vector<unsigned char>(decoded.begin(), decoded.end());
25
3
}
26
27
json JsonParser::create_paste_json(const std::vector<unsigned char>& cipher_text,
28
                                 const std::vector<unsigned char>& auth_tag,
29
                                 const std::vector<unsigned char>& iv,
30
                                 const std::vector<unsigned char>& salt,
31
                                 const std::string& expiration,
32
                                 const std::string& format,
33
                                 bool burn_after_reading,
34
2
                                 bool open_discussion) {
35
    
36
    // Convert binary data to base64 strings
37
2
    std::string cipher_text_b64 = encode_base64(cipher_text);
38
2
    std::string iv_b64 = encode_base64(iv);
39
2
    std::string salt_b64 = encode_base64(salt);
40
    
41
    // Hinweis: Zeitstempel wird aktuell nicht benötigt; entfernte Berechnung verhinderte eine
42
    // ungenutzte Variable unter Clang.
43
    
44
    // Create the metadata array according to PrivateBin v2
45
2
    json metadata = {
46
2
        {
47
2
            iv_b64,           // base64(cipher_iv)
48
2
            salt_b64,         // base64(kdf_salt)
49
2
            100000,           // iterations
50
2
            256,              // key size
51
2
            128,              // tag size
52
2
            "aes",            // cipher
53
2
            "gcm",            // mode
54
2
            "zlib"            // compression
55
2
        },
56
2
        format,               // formatter key
57
2
        burn_after_reading ? 1 : 0,  // burn after reading
58
2
        open_discussion ? 1 : 0      // open discussion
59
2
    };
60
    
61
    // Create the main JSON structure
62
    // JSON close to v1.3 JSON-LD: meta.expire used by server
63
2
    json paste_json = {
64
2
        {"v", 2},                 // version
65
2
        {"adata", metadata},      // metadata
66
2
        {"ct", cipher_text_b64},  // ciphertext (base64)
67
2
        {"meta", {
68
2
            {"expire", expiration}
69
2
        }}
70
2
    };
71
    
72
2
    return paste_json;
73
2
}
74
75
bool JsonParser::parse_paste_json(const json& json_data,
76
                                std::vector<unsigned char>& cipher_text,
77
                                std::vector<unsigned char>& auth_tag,
78
                                std::vector<unsigned char>& iv,
79
                                std::vector<unsigned char>& salt,
80
1
                                std::string& expiration) {
81
    
82
1
    try {
83
        // Extract and decode cipher text
84
1
        std::string ct_b64 = json_data.at("ct");
85
1
        cipher_text = decode_base64(ct_b64);
86
        
87
        // Optional explicit tag field
88
1
        if (json_data.contains("tag")) {
89
0
            std::string tag_b64 = json_data.at("tag");
90
0
            auth_tag = decode_base64(tag_b64);
91
1
        } else {
92
1
            auth_tag.clear();
93
1
        }
94
        
95
        // Extract metadata and decode IV & salt
96
1
        json adata = json_data.at("adata");
97
1
        if (adata.size() >= 1) {
98
1
            json first_element = adata[0];
99
1
            if (first_element.is_array() && first_element.size() >= 2) {
100
1
                std::string iv_b64 = first_element[0];
101
1
                std::string salt_b64 = first_element[1];
102
1
                iv = decode_base64(iv_b64);
103
1
                salt = decode_base64(salt_b64);
104
1
            }
105
1
        }
106
        
107
        // Extract expiration; servers may return either meta.expire (string)
108
        // or meta.time_to_live (integer seconds). Both are optional for our use.
109
1
        try {
110
1
            expiration = json_data.at("meta").at("expire");
111
1
        } catch (...) {
112
0
            try {
113
0
                auto ttl = json_data.at("meta").at("time_to_live").get<int>();
114
0
                expiration = std::to_string(ttl);
115
0
            } catch (...) {
116
0
                expiration.clear();
117
0
            }
118
0
        }
119
        
120
1
        return true;
121
1
    } catch (...) {
122
0
        return false;
123
0
    }
124
1
}
125
126
bool JsonParser::parse_response(const std::string& response,
127
                              int& status,
128
                              std::string& message,
129
                              std::string& paste_id,
130
                              std::string& url,
131
1
                              std::string& delete_token) {
132
    
133
1
    try {
134
1
        json json_response = json::parse(response);
135
1
        status = json_response.value("status", 1);
136
1
        if (status == 0) {
137
1
            paste_id = json_response.value("id", "");
138
1
            url = json_response.value("url", "");
139
1
            delete_token = json_response.value("deletetoken", "");
140
1
        } else {
141
0
            message = json_response.value("message", "");
142
0
        }
143
        
144
1
        return true;
145
1
    } catch (...) {
146
0
        return false;
147
0
    }
148
1
}
\ No newline at end of file diff --git a/build-clang/coverage/html/coverage/Users/mbusc/source/repos/privatebin-cpp/src/privatebinapi.cpp.html b/build-clang/coverage/html/coverage/Users/mbusc/source/repos/privatebin-cpp/src/privatebinapi.cpp.html index cbf7c50..04db8ff 100644 --- a/build-clang/coverage/html/coverage/Users/mbusc/source/repos/privatebin-cpp/src/privatebinapi.cpp.html +++ b/build-clang/coverage/html/coverage/Users/mbusc/source/repos/privatebin-cpp/src/privatebinapi.cpp.html @@ -1 +1 @@ -

Coverage Report

Created: 2025-08-29 12:44

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
C:\Users\mbusc\source\repos\privatebin-cpp\src\privatebinapi.cpp
Line
Count
Source
1
#include "privatebinapi.h"
2
#include "http_client.h"
3
#include "crypto.h"
4
#include "json_parser.h"
5
#include "base58.h"
6
#include <string>
7
#include <vector>
8
#include <cstring>
9
#include <cstdlib>
10
#include <iostream>
11
#include <fstream>
12
#include <sstream>
13
14
#define PRIVATEBIN_API_VERSION "1.3"
15
16
// Error codes
17
0
#define ERROR_SUCCESS 0
18
3
#define ERROR_NETWORK 1
19
0
#define ERROR_CRYPTO 2
20
0
#define ERROR_INVALID_INPUT 3
21
0
#define ERROR_SERVER 4
22
0
#define ERROR_JSON_PARSE 5
23
24
// Helper function to copy string to output parameter
25
0
static void copy_string_to_output(const std::string& source, char** destination) {
26
0
    if (destination) {
27
0
        *destination = static_cast<char*>(malloc(source.length() + 1));
28
0
        if (*destination) {
29
0
            strcpy_s(*destination, source.length() + 1, source.c_str());
30
0
        }
31
0
    }
32
0
}
33
34
extern "C" {
35
36
PRIVATEBIN_API int create_paste(const char* server_url, const char* content, 
37
                 const char* password, const char* expiration,
38
                 const char* format, int burn_after_reading, 
39
                 int open_discussion, char** paste_url, 
40
1
                 char** delete_token) {
41
    
42
1
    if (!server_url || !content) {
43
0
        return ERROR_INVALID_INPUT;
44
0
    }
45
46
1
    try {
47
        // Generate a random 32-byte paste key
48
1
        std::vector<unsigned char> paste_key = Crypto::generate_key(32);
49
        
50
        // If password provided, append it to the paste key
51
1
        std::string paste_passphrase(reinterpret_cast<char*>(paste_key.data()), paste_key.size());
52
1
        if (password) {
53
0
            paste_passphrase += password;
54
0
        }
55
        
56
        // Convert content to bytes
57
1
        std::vector<unsigned char> plaintext(content, content + strlen(content));
58
        
59
        // Compress the plaintext
60
1
        std::cerr << "[privatebinapi] compress..." << std::endl;
61
1
        std::vector<unsigned char> compressed_data = Crypto::compress(plaintext);
62
        
63
        // Generate random salt and IV
64
1
        std::vector<unsigned char> salt = Crypto::generate_key(8);
65
1
        std::vector<unsigned char> iv = Crypto::generate_key(16);
66
        
67
        // Derive key using PBKDF2
68
1
        std::cerr << "[privatebinapi] pbkdf2..." << std::endl;
69
1
        std::vector<unsigned char> derived_key = Crypto::pbkdf2_hmac_sha256(
70
1
            paste_passphrase, salt, 100000, 32);
71
        
72
        // Encrypt the data
73
1
        std::cerr << "[privatebinapi] encrypt..." << std::endl;
74
1
        std::vector<unsigned char> auth_tag;
75
1
        std::vector<unsigned char> cipher_text = Crypto::encrypt(
76
1
            compressed_data, derived_key, iv, auth_tag);
77
        
78
        // Create the JSON structure
79
1
        json paste_json = JsonParser::create_paste_json(
80
1
            cipher_text, auth_tag, iv, salt,
81
1
            expiration ? expiration : "1day",
82
1
            format ? format : "plaintext",
83
1
            burn_after_reading != 0,
84
1
            open_discussion != 0);
85
        
86
        // Serialize JSON
87
1
        std::string json_data = paste_json.dump();
88
        
89
        // Send POST request
90
1
        HttpClient client;
91
1
        std::string response;
92
1
        if (!client.post(server_url, json_data, response)) {
93
1
            return ERROR_NETWORK;
94
1
        }
95
        
96
        // Parse response
97
0
        int status;
98
0
        std::string message, paste_id, url, del_token;
99
0
        if (!JsonParser::parse_response(response, status, message, paste_id, url, del_token)) {
100
0
            std::cerr << "[privatebinapi] raw response (unparsed): " << response << std::endl;
101
0
            return ERROR_JSON_PARSE;
102
0
        }
103
0
        if (status != 0) {
104
0
            std::cerr << "[privatebinapi] server status=" << status << ", message= " << message << std::endl;
105
0
        }
106
        
107
0
        if (status != 0) {
108
0
            return ERROR_SERVER;
109
0
        }
110
        
111
        // Encode the paste key with Base58
112
0
        std::string encoded_key = Base58::encode(paste_key);
113
        
114
        // Construct the full URL
115
0
        std::string full_url = url + "#" + encoded_key;
116
        
117
        // Copy results to output parameters
118
0
        copy_string_to_output(full_url, paste_url);
119
0
        copy_string_to_output(del_token, delete_token);
120
        
121
0
        return ERROR_SUCCESS;
122
0
    } catch (const std::exception& e) {
123
0
        std::cerr << "[privatebinapi] crypto error: " << e.what() << std::endl;
124
0
        return ERROR_CRYPTO;
125
0
    } catch (...) {
126
0
        std::cerr << "[privatebinapi] unknown crypto error" << std::endl;
127
0
        return ERROR_CRYPTO;
128
0
    }
129
1
}
130
131
PRIVATEBIN_API int upload_file(const char* server_url, const char* file_path,
132
               const char* password, const char* expiration,
133
               int burn_after_reading, int open_discussion,
134
0
               char** paste_url, char** delete_token) {
135
    
136
0
    if (!server_url || !file_path) {
137
0
        return ERROR_INVALID_INPUT;
138
0
    }
139
140
0
    try {
141
        // Read the file content
142
0
        std::ifstream file(file_path, std::ios::binary);
143
0
        if (!file.is_open()) {
144
0
            std::cerr << "[privatebinapi] Failed to open file: " << file_path << std::endl;
145
0
            return ERROR_INVALID_INPUT;
146
0
        }
147
        
148
        // Get file size
149
0
        file.seekg(0, std::ios::end);
150
0
        std::streamsize file_size = file.tellg();
151
0
        file.seekg(0, std::ios::beg);
152
        
153
        // Check if file is too large (PrivateBin typically has limits)
154
0
        if (file_size > 100 * 1024 * 1024) { // 100MB limit
155
0
            std::cerr << "[privatebinapi] File too large: " << file_size << " bytes" << std::endl;
156
0
            return ERROR_INVALID_INPUT;
157
0
        }
158
        
159
        // Read file content into vector
160
0
        std::vector<unsigned char> file_content(file_size);
161
0
        if (!file.read(reinterpret_cast<char*>(file_content.data()), file_size)) {
162
0
            std::cerr << "[privatebinapi] Failed to read file content" << std::endl;
163
0
            return ERROR_INVALID_INPUT;
164
0
        }
165
0
        file.close();
166
        
167
        // Generate a random 32-byte paste key
168
0
        std::vector<unsigned char> paste_key = Crypto::generate_key(32);
169
        
170
        // If password provided, append it to the paste key
171
0
        std::string paste_passphrase(reinterpret_cast<char*>(paste_key.data()), paste_key.size());
172
0
        if (password) {
173
0
            paste_passphrase += password;
174
0
        }
175
        
176
        // Compress the file content
177
0
        std::cerr << "[privatebinapi] compress file..." << std::endl;
178
0
        std::vector<unsigned char> compressed_data = Crypto::compress(file_content);
179
        
180
        // Generate random salt and IV
181
0
        std::vector<unsigned char> salt = Crypto::generate_key(8);
182
0
        std::vector<unsigned char> iv = Crypto::generate_key(16);
183
        
184
        // Derive key using PBKDF2
185
0
        std::cerr << "[privatebinapi] pbkdf2..." << std::endl;
186
0
        std::vector<unsigned char> derived_key = Crypto::pbkdf2_hmac_sha256(
187
0
            paste_passphrase, salt, 100000, 32);
188
        
189
        // Encrypt the data
190
0
        std::cerr << "[privatebinapi] encrypt file..." << std::endl;
191
0
        std::vector<unsigned char> auth_tag;
192
0
        std::vector<unsigned char> cipher_text = Crypto::encrypt(
193
0
            compressed_data, derived_key, iv, auth_tag);
194
        
195
        // Create the JSON structure for file upload
196
0
        json paste_json = JsonParser::create_paste_json(
197
0
            cipher_text, auth_tag, iv, salt,
198
0
            expiration ? expiration : "1day",
199
0
            "plaintext", // Files are treated as plaintext
200
0
            burn_after_reading != 0,
201
0
            open_discussion != 0);
202
        
203
        // Note: File metadata is not added to avoid server compatibility issues
204
        // The file content is encrypted and uploaded as a binary paste
205
        
206
        // Serialize JSON
207
0
        std::string json_data = paste_json.dump();
208
        
209
        // Send POST request
210
0
        HttpClient client;
211
0
        std::string response;
212
0
        if (!client.post(server_url, json_data, response)) {
213
0
            return ERROR_NETWORK;
214
0
        }
215
        
216
        // Parse response
217
0
        int status;
218
0
        std::string message, paste_id, url, del_token;
219
0
        if (!JsonParser::parse_response(response, status, message, paste_id, url, del_token)) {
220
0
            std::cerr << "[privatebinapi] raw response (unparsed): " << response << std::endl;
221
0
            return ERROR_JSON_PARSE;
222
0
        }
223
0
        if (status != 0) {
224
0
            std::cerr << "[privatebinapi] server status=" << status << ", message= " << message << std::endl;
225
0
        }
226
        
227
0
        if (status != 0) {
228
0
            return ERROR_SERVER;
229
0
        }
230
        
231
        // Encode the paste key with Base58
232
0
        std::string encoded_key = Base58::encode(paste_key);
233
        
234
        // Construct the full URL
235
0
        std::string full_url = url + "#" + encoded_key;
236
        
237
        // Copy results to output parameters
238
0
        copy_string_to_output(full_url, paste_url);
239
0
        copy_string_to_output(del_token, delete_token);
240
        
241
0
        return ERROR_SUCCESS;
242
0
    } catch (const std::exception& e) {
243
0
        std::cerr << "[privatebinapi] file upload error: " << e.what() << std::endl;
244
0
        return ERROR_CRYPTO;
245
0
    } catch (...) {
246
0
        std::cerr << "[privatebinapi] unknown file upload error" << std::endl;
247
0
        return ERROR_CRYPTO;
248
0
    }
249
0
}
250
251
PRIVATEBIN_API int get_paste(const char* server_url, const char* paste_id, 
252
1
              const char* key, char** content) {
253
    
254
1
    if (!server_url || !paste_id || !key || !content) {
255
0
        return ERROR_INVALID_INPUT;
256
0
    }
257
258
1
    try {
259
        // Construct the URL with query per API: base?pasteID
260
1
        std::string url = std::string(server_url) + "?" + paste_id;
261
        
262
        // Send GET request
263
1
        HttpClient client;
264
1
        std::string response;
265
1
        std::cerr << "[privatebinapi] GET " << url << std::endl;
266
1
        if (!client.get(url, response)) {
267
1
            return ERROR_NETWORK;
268
1
        }
269
0
        std::cerr << "[privatebinapi] GET response: " << response << std::endl;
270
        
271
        // Parse the JSON response
272
0
        json json_data = json::parse(response);
273
        
274
        // Extract encrypted data
275
0
        std::vector<unsigned char> cipher_text, auth_tag, iv, salt;
276
0
        std::string expiration;
277
0
        if (!JsonParser::parse_paste_json(json_data, cipher_text, auth_tag, iv, salt, expiration)) {
278
0
            return ERROR_JSON_PARSE;
279
0
        }
280
        
281
        // Decode the key from Base58
282
0
        std::vector<unsigned char> paste_key = Base58::decode(key);
283
        
284
        // Derive key using PBKDF2
285
0
        std::string paste_passphrase(reinterpret_cast<char*>(paste_key.data()), paste_key.size());
286
0
        std::vector<unsigned char> derived_key = Crypto::pbkdf2_hmac_sha256(
287
0
            paste_passphrase, salt, 100000, 32);
288
        
289
        // Decrypt the data
290
0
        std::vector<unsigned char> compressed_data = Crypto::decrypt(
291
0
            cipher_text, derived_key, iv, auth_tag);
292
        
293
        // Decompress the data
294
0
        std::vector<unsigned char> plaintext = Crypto::decompress(compressed_data);
295
        
296
        // Convert to string
297
0
        std::string result(reinterpret_cast<char*>(plaintext.data()), plaintext.size());
298
        
299
        // Copy result to output parameter
300
0
        copy_string_to_output(result, content);
301
        
302
0
        return ERROR_SUCCESS;
303
0
    } catch (...) {
304
0
        return ERROR_CRYPTO;
305
0
    }
306
1
}
307
308
PRIVATEBIN_API int delete_paste(const char* server_url, const char* paste_id, 
309
1
                 const char* delete_token) {
310
    
311
1
    if (!server_url || !paste_id || !delete_token) {
312
0
        return ERROR_INVALID_INPUT;
313
0
    }
314
315
1
    try {
316
        // Create the JSON payload
317
1
        json payload = {
318
1
            {"pasteid", paste_id},
319
1
            {"deletetoken", delete_token}
320
1
        };
321
        
322
1
        std::string json_data = payload.dump();
323
        
324
        // Send DELETE request
325
1
        HttpClient client;
326
1
        std::string response;
327
1
        std::cerr << "[privatebinapi] DELETE payload: " << json_data << std::endl;
328
1
        if (!client.delete_req(server_url, json_data, response)) {
329
1
            return ERROR_NETWORK;
330
1
        }
331
0
        std::cerr << "[privatebinapi] DELETE response: " << response << std::endl;
332
        
333
        // Parse response
334
0
        int status;
335
0
        std::string message, paste_id_result, url, del_token;
336
0
        if (!JsonParser::parse_response(response, status, message, paste_id_result, url, del_token)) {
337
0
            return ERROR_JSON_PARSE;
338
0
        }
339
        
340
0
        if (status != 0) {
341
0
            return ERROR_SERVER;
342
0
        }
343
        
344
0
        return ERROR_SUCCESS;
345
0
    } catch (...) {
346
0
        return ERROR_CRYPTO;
347
0
    }
348
1
}
349
350
0
PRIVATEBIN_API void free_string(char* str) {
351
0
    if (str) {
352
0
        free(str);
353
0
    }
354
0
}
355
356
} // extern "C"
\ No newline at end of file +

Coverage Report

Created: 2025-08-29 12:48

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
C:\Users\mbusc\source\repos\privatebin-cpp\src\privatebinapi.cpp
Line
Count
Source
1
#include "privatebinapi.h"
2
#include "http_client.h"
3
#include "crypto.h"
4
#include "json_parser.h"
5
#include "base58.h"
6
#include <string>
7
#include <vector>
8
#include <cstring>
9
#include <cstdlib>
10
#include <iostream>
11
#include <fstream>
12
#include <sstream>
13
14
#define PRIVATEBIN_API_VERSION "1.3"
15
16
// Error codes
17
0
#define ERROR_SUCCESS 0
18
3
#define ERROR_NETWORK 1
19
0
#define ERROR_CRYPTO 2
20
0
#define ERROR_INVALID_INPUT 3
21
0
#define ERROR_SERVER 4
22
0
#define ERROR_JSON_PARSE 5
23
24
// Helper function to copy string to output parameter
25
0
static void copy_string_to_output(const std::string& source, char** destination) {
26
0
    if (destination) {
27
0
        *destination = static_cast<char*>(malloc(source.length() + 1));
28
0
        if (*destination) {
29
0
            strcpy_s(*destination, source.length() + 1, source.c_str());
30
0
        }
31
0
    }
32
0
}
33
34
extern "C" {
35
36
PRIVATEBIN_API int create_paste(const char* server_url, const char* content, 
37
                 const char* password, const char* expiration,
38
                 const char* format, int burn_after_reading, 
39
                 int open_discussion, char** paste_url, 
40
1
                 char** delete_token) {
41
    
42
1
    if (!server_url || !content) {
43
0
        return ERROR_INVALID_INPUT;
44
0
    }
45
46
1
    try {
47
        // Generate a random 32-byte paste key
48
1
        std::vector<unsigned char> paste_key = Crypto::generate_key(32);
49
        
50
        // If password provided, append it to the paste key
51
1
        std::string paste_passphrase(reinterpret_cast<char*>(paste_key.data()), paste_key.size());
52
1
        if (password) {
53
0
            paste_passphrase += password;
54
0
        }
55
        
56
        // Convert content to bytes
57
1
        std::vector<unsigned char> plaintext(content, content + strlen(content));
58
        
59
        // Compress the plaintext
60
1
        std::cerr << "[privatebinapi] compress..." << std::endl;
61
1
        std::vector<unsigned char> compressed_data = Crypto::compress(plaintext);
62
        
63
        // Generate random salt and IV
64
1
        std::vector<unsigned char> salt = Crypto::generate_key(8);
65
1
        std::vector<unsigned char> iv = Crypto::generate_key(16);
66
        
67
        // Derive key using PBKDF2
68
1
        std::cerr << "[privatebinapi] pbkdf2..." << std::endl;
69
1
        std::vector<unsigned char> derived_key = Crypto::pbkdf2_hmac_sha256(
70
1
            paste_passphrase, salt, 100000, 32);
71
        
72
        // Encrypt the data
73
1
        std::cerr << "[privatebinapi] encrypt..." << std::endl;
74
1
        std::vector<unsigned char> auth_tag;
75
1
        std::vector<unsigned char> cipher_text = Crypto::encrypt(
76
1
            compressed_data, derived_key, iv, auth_tag);
77
        
78
        // Create the JSON structure
79
1
        json paste_json = JsonParser::create_paste_json(
80
1
            cipher_text, auth_tag, iv, salt,
81
1
            expiration ? expiration : "1day",
82
1
            format ? format : "plaintext",
83
1
            burn_after_reading != 0,
84
1
            open_discussion != 0);
85
        
86
        // Serialize JSON
87
1
        std::string json_data = paste_json.dump();
88
        
89
        // Send POST request
90
1
        HttpClient client;
91
1
        std::string response;
92
1
        if (!client.post(server_url, json_data, response)) {
93
1
            return ERROR_NETWORK;
94
1
        }
95
        
96
        // Parse response
97
0
        int status;
98
0
        std::string message, paste_id, url, del_token;
99
0
        if (!JsonParser::parse_response(response, status, message, paste_id, url, del_token)) {
100
0
            std::cerr << "[privatebinapi] raw response (unparsed): " << response << std::endl;
101
0
            return ERROR_JSON_PARSE;
102
0
        }
103
0
        if (status != 0) {
104
0
            std::cerr << "[privatebinapi] server status=" << status << ", message= " << message << std::endl;
105
0
        }
106
        
107
0
        if (status != 0) {
108
0
            return ERROR_SERVER;
109
0
        }
110
        
111
        // Encode the paste key with Base58
112
0
        std::string encoded_key = Base58::encode(paste_key);
113
        
114
        // Construct the full URL
115
0
        std::string full_url = url + "#" + encoded_key;
116
        
117
        // Copy results to output parameters
118
0
        copy_string_to_output(full_url, paste_url);
119
0
        copy_string_to_output(del_token, delete_token);
120
        
121
0
        return ERROR_SUCCESS;
122
0
    } catch (const std::exception& e) {
123
0
        std::cerr << "[privatebinapi] crypto error: " << e.what() << std::endl;
124
0
        return ERROR_CRYPTO;
125
0
    } catch (...) {
126
0
        std::cerr << "[privatebinapi] unknown crypto error" << std::endl;
127
0
        return ERROR_CRYPTO;
128
0
    }
129
1
}
130
131
PRIVATEBIN_API int upload_file(const char* server_url, const char* file_path,
132
               const char* password, const char* expiration,
133
               int burn_after_reading, int open_discussion,
134
0
               char** paste_url, char** delete_token) {
135
    
136
0
    if (!server_url || !file_path) {
137
0
        return ERROR_INVALID_INPUT;
138
0
    }
139
140
0
    try {
141
        // Read the file content
142
0
        std::ifstream file(file_path, std::ios::binary);
143
0
        if (!file.is_open()) {
144
0
            std::cerr << "[privatebinapi] Failed to open file: " << file_path << std::endl;
145
0
            return ERROR_INVALID_INPUT;
146
0
        }
147
        
148
        // Get file size
149
0
        file.seekg(0, std::ios::end);
150
0
        std::streamsize file_size = file.tellg();
151
0
        file.seekg(0, std::ios::beg);
152
        
153
        // Check if file is too large (PrivateBin typically has limits)
154
0
        if (file_size > 100 * 1024 * 1024) { // 100MB limit
155
0
            std::cerr << "[privatebinapi] File too large: " << file_size << " bytes" << std::endl;
156
0
            return ERROR_INVALID_INPUT;
157
0
        }
158
        
159
        // Read file content into vector
160
0
        std::vector<unsigned char> file_content(file_size);
161
0
        if (!file.read(reinterpret_cast<char*>(file_content.data()), file_size)) {
162
0
            std::cerr << "[privatebinapi] Failed to read file content" << std::endl;
163
0
            return ERROR_INVALID_INPUT;
164
0
        }
165
0
        file.close();
166
        
167
        // Generate a random 32-byte paste key
168
0
        std::vector<unsigned char> paste_key = Crypto::generate_key(32);
169
        
170
        // If password provided, append it to the paste key
171
0
        std::string paste_passphrase(reinterpret_cast<char*>(paste_key.data()), paste_key.size());
172
0
        if (password) {
173
0
            paste_passphrase += password;
174
0
        }
175
        
176
        // Compress the file content
177
0
        std::cerr << "[privatebinapi] compress file..." << std::endl;
178
0
        std::vector<unsigned char> compressed_data = Crypto::compress(file_content);
179
        
180
        // Generate random salt and IV
181
0
        std::vector<unsigned char> salt = Crypto::generate_key(8);
182
0
        std::vector<unsigned char> iv = Crypto::generate_key(16);
183
        
184
        // Derive key using PBKDF2
185
0
        std::cerr << "[privatebinapi] pbkdf2..." << std::endl;
186
0
        std::vector<unsigned char> derived_key = Crypto::pbkdf2_hmac_sha256(
187
0
            paste_passphrase, salt, 100000, 32);
188
        
189
        // Encrypt the data
190
0
        std::cerr << "[privatebinapi] encrypt file..." << std::endl;
191
0
        std::vector<unsigned char> auth_tag;
192
0
        std::vector<unsigned char> cipher_text = Crypto::encrypt(
193
0
            compressed_data, derived_key, iv, auth_tag);
194
        
195
        // Create the JSON structure for file upload
196
0
        json paste_json = JsonParser::create_paste_json(
197
0
            cipher_text, auth_tag, iv, salt,
198
0
            expiration ? expiration : "1day",
199
0
            "plaintext", // Files are treated as plaintext
200
0
            burn_after_reading != 0,
201
0
            open_discussion != 0);
202
        
203
        // Note: File metadata is not added to avoid server compatibility issues
204
        // The file content is encrypted and uploaded as a binary paste
205
        
206
        // Serialize JSON
207
0
        std::string json_data = paste_json.dump();
208
        
209
        // Send POST request
210
0
        HttpClient client;
211
0
        std::string response;
212
0
        if (!client.post(server_url, json_data, response)) {
213
0
            return ERROR_NETWORK;
214
0
        }
215
        
216
        // Parse response
217
0
        int status;
218
0
        std::string message, paste_id, url, del_token;
219
0
        if (!JsonParser::parse_response(response, status, message, paste_id, url, del_token)) {
220
0
            std::cerr << "[privatebinapi] raw response (unparsed): " << response << std::endl;
221
0
            return ERROR_JSON_PARSE;
222
0
        }
223
0
        if (status != 0) {
224
0
            std::cerr << "[privatebinapi] server status=" << status << ", message= " << message << std::endl;
225
0
        }
226
        
227
0
        if (status != 0) {
228
0
            return ERROR_SERVER;
229
0
        }
230
        
231
        // Encode the paste key with Base58
232
0
        std::string encoded_key = Base58::encode(paste_key);
233
        
234
        // Construct the full URL
235
0
        std::string full_url = url + "#" + encoded_key;
236
        
237
        // Copy results to output parameters
238
0
        copy_string_to_output(full_url, paste_url);
239
0
        copy_string_to_output(del_token, delete_token);
240
        
241
0
        return ERROR_SUCCESS;
242
0
    } catch (const std::exception& e) {
243
0
        std::cerr << "[privatebinapi] file upload error: " << e.what() << std::endl;
244
0
        return ERROR_CRYPTO;
245
0
    } catch (...) {
246
0
        std::cerr << "[privatebinapi] unknown file upload error" << std::endl;
247
0
        return ERROR_CRYPTO;
248
0
    }
249
0
}
250
251
PRIVATEBIN_API int get_paste(const char* server_url, const char* paste_id, 
252
1
              const char* key, char** content) {
253
    
254
1
    if (!server_url || !paste_id || !key || !content) {
255
0
        return ERROR_INVALID_INPUT;
256
0
    }
257
258
1
    try {
259
        // Construct the URL with query per API: base?pasteID
260
1
        std::string url = std::string(server_url) + "?" + paste_id;
261
        
262
        // Send GET request
263
1
        HttpClient client;
264
1
        std::string response;
265
1
        std::cerr << "[privatebinapi] GET " << url << std::endl;
266
1
        if (!client.get(url, response)) {
267
1
            return ERROR_NETWORK;
268
1
        }
269
0
        std::cerr << "[privatebinapi] GET response: " << response << std::endl;
270
        
271
        // Parse the JSON response
272
0
        json json_data = json::parse(response);
273
        
274
        // Extract encrypted data
275
0
        std::vector<unsigned char> cipher_text, auth_tag, iv, salt;
276
0
        std::string expiration;
277
0
        if (!JsonParser::parse_paste_json(json_data, cipher_text, auth_tag, iv, salt, expiration)) {
278
0
            return ERROR_JSON_PARSE;
279
0
        }
280
        
281
        // Decode the key from Base58
282
0
        std::vector<unsigned char> paste_key = Base58::decode(key);
283
        
284
        // Derive key using PBKDF2
285
0
        std::string paste_passphrase(reinterpret_cast<char*>(paste_key.data()), paste_key.size());
286
0
        std::vector<unsigned char> derived_key = Crypto::pbkdf2_hmac_sha256(
287
0
            paste_passphrase, salt, 100000, 32);
288
        
289
        // Decrypt the data
290
0
        std::vector<unsigned char> compressed_data = Crypto::decrypt(
291
0
            cipher_text, derived_key, iv, auth_tag);
292
        
293
        // Decompress the data
294
0
        std::vector<unsigned char> plaintext = Crypto::decompress(compressed_data);
295
        
296
        // Convert to string
297
0
        std::string result(reinterpret_cast<char*>(plaintext.data()), plaintext.size());
298
        
299
        // Copy result to output parameter
300
0
        copy_string_to_output(result, content);
301
        
302
0
        return ERROR_SUCCESS;
303
0
    } catch (...) {
304
0
        return ERROR_CRYPTO;
305
0
    }
306
1
}
307
308
PRIVATEBIN_API int delete_paste(const char* server_url, const char* paste_id, 
309
1
                 const char* delete_token) {
310
    
311
1
    if (!server_url || !paste_id || !delete_token) {
312
0
        return ERROR_INVALID_INPUT;
313
0
    }
314
315
1
    try {
316
        // Create the JSON payload
317
1
        json payload = {
318
1
            {"pasteid", paste_id},
319
1
            {"deletetoken", delete_token}
320
1
        };
321
        
322
1
        std::string json_data = payload.dump();
323
        
324
        // Send DELETE request
325
1
        HttpClient client;
326
1
        std::string response;
327
1
        std::cerr << "[privatebinapi] DELETE payload: " << json_data << std::endl;
328
1
        if (!client.delete_req(server_url, json_data, response)) {
329
1
            return ERROR_NETWORK;
330
1
        }
331
0
        std::cerr << "[privatebinapi] DELETE response: " << response << std::endl;
332
        
333
        // Parse response
334
0
        int status;
335
0
        std::string message, paste_id_result, url, del_token;
336
0
        if (!JsonParser::parse_response(response, status, message, paste_id_result, url, del_token)) {
337
0
            return ERROR_JSON_PARSE;
338
0
        }
339
        
340
0
        if (status != 0) {
341
0
            return ERROR_SERVER;
342
0
        }
343
        
344
0
        return ERROR_SUCCESS;
345
0
    } catch (...) {
346
0
        return ERROR_CRYPTO;
347
0
    }
348
1
}
349
350
0
PRIVATEBIN_API void free_string(char* str) {
351
0
    if (str) {
352
0
        free(str);
353
0
    }
354
0
}
355
356
} // extern "C"
\ No newline at end of file diff --git a/build-clang/coverage/html/index.html b/build-clang/coverage/html/index.html index 3adecff..6056384 100644 --- a/build-clang/coverage/html/index.html +++ b/build-clang/coverage/html/index.html @@ -1 +1 @@ -

Coverage Report

Created: 2025-08-29 12:44

Click here for information about interpreting this report.

FilenameFunction CoverageLine CoverageRegion CoverageBranch Coverage
base58.cpp
   0.00% (0/2)
   0.00% (0/67)
   0.00% (0/48)
   0.00% (0/34)
crypto.cpp
  66.67% (4/6)
  56.19% (59/105)
  36.84% (7/19)
   0.00% (0/2)
http_client.cpp
 100.00% (5/5)
  51.67% (186/360)
  66.67% (170/255)
  41.38% (48/116)
json_parser.cpp
 100.00% (5/5)
  84.21% (80/95)
  71.88% (23/32)
  50.00% (7/14)
privatebinapi.cpp
  50.00% (3/6)
  27.01% (57/211)
  28.15% (38/135)
  21.43% (15/70)
Totals
  70.83% (17/24)
  45.58% (382/838)
  48.67% (238/489)
  29.66% (70/236)
Generated by llvm-cov -- llvm version 21.1.0
\ No newline at end of file +

Coverage Report

Created: 2025-08-29 12:48

Click here for information about interpreting this report.

FilenameFunction CoverageLine CoverageRegion CoverageBranch Coverage
base58.cpp
   0.00% (0/2)
   0.00% (0/67)
   0.00% (0/48)
   0.00% (0/34)
crypto.cpp
  66.67% (4/6)
  56.19% (59/105)
  36.84% (7/19)
   0.00% (0/2)
http_client.cpp
 100.00% (5/5)
  51.67% (186/360)
  66.67% (170/255)
  41.38% (48/116)
json_parser.cpp
 100.00% (5/5)
  84.21% (80/95)
  71.88% (23/32)
  50.00% (7/14)
privatebinapi.cpp
  50.00% (3/6)
  27.01% (57/211)
  28.15% (38/135)
  21.43% (15/70)
Totals
  70.83% (17/24)
  45.58% (382/838)
  48.67% (238/489)
  29.66% (70/236)
Generated by llvm-cov -- llvm version 21.1.0
\ No newline at end of file diff --git a/build-clang/coverage/m.profraw b/build-clang/coverage/m.profraw index 575250f8e9d5a404a4818ae534ce1eb48c17fb8d..c984f3c91a75f4927d5164ccb2d554af063174b2 100644 GIT binary patch delta 14 Vcmez2^TTI@mH=b!W^DmqF#s^w1u_5t delta 14 Vcmez2^TTI@mH;EeW^DmqF#s+~1jPUV diff --git a/scripts/create_release.ps1 b/scripts/create_release.ps1 index fb8e9f5..2262152 100644 --- a/scripts/create_release.ps1 +++ b/scripts/create_release.ps1 @@ -8,7 +8,7 @@ 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_simple.ps1 -Token 'your_token' oder setze GITEA_TOKEN Umgebungsvariable" -ForegroundColor Yellow + Write-Host "Verwendung: .\create_release.ps1 -Token 'your_token' oder setze GITEA_TOKEN Umgebungsvariable" -ForegroundColor Yellow Write-Host "Das Script führt automatisch einen Build durch und lädt alle Artefakte hoch." -ForegroundColor Cyan exit 1 } @@ -25,15 +25,20 @@ if (-not $lastTag) { Write-Host "Letzter Tag: $lastTag" -ForegroundColor Green -# Version parsen +# Version parsen und nächsten verfügbaren Tag finden 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" + # Finde den nächsten verfügbaren Patch-Level + $newPatch = $patch + do { + $newPatch++ + $newVersion = "v$major.$minor.$newPatch$suffix" + $tagExists = git tag --list $newVersion 2>$null + } while ($tagExists) Write-Host "Neue Version: $newVersion" -ForegroundColor Green } else { @@ -173,7 +178,7 @@ if (-not $remoteUrl) { $firstRemote = $allRemotes[0] $remoteUrl = git remote get-url $firstRemote 2>$null $remoteName = $firstRemote - Write-Host "Verwende ersten verfügbaren Remote '$firstRemote': $remoteUrl" -ForegroundColor Yellow + Write-Host "Verwende ersten verfügbaren Remote '$firstRemote': $url" -ForegroundColor Yellow } }