229 lines
7.1 KiB
C++
229 lines
7.1 KiB
C++
|
#include "cpr/util.h"
|
||
|
|
||
|
#include <algorithm>
|
||
|
#include <cassert>
|
||
|
#include <cctype>
|
||
|
#include <chrono>
|
||
|
#include <cstdint>
|
||
|
#include <fstream>
|
||
|
#include <iomanip>
|
||
|
#include <ios>
|
||
|
#include <sstream>
|
||
|
#include <string>
|
||
|
#include <vector>
|
||
|
|
||
|
#if defined(_Win32)
|
||
|
#include <Windows.h>
|
||
|
#else
|
||
|
// https://en.cppreference.com/w/c/string/byte/memset
|
||
|
// NOLINTNEXTLINE(bugprone-reserved-identifier, cert-dcl37-c, cert-dcl51-cpp, cppcoreguidelines-macro-usage)
|
||
|
#define __STDC_WANT_LIB_EXT1__ 1
|
||
|
#include <cstring>
|
||
|
#endif
|
||
|
|
||
|
namespace cpr::util {
|
||
|
|
||
|
enum class CurlHTTPCookieField : size_t {
|
||
|
Domain = 0,
|
||
|
IncludeSubdomains,
|
||
|
Path,
|
||
|
HttpsOnly,
|
||
|
Expires,
|
||
|
Name,
|
||
|
Value,
|
||
|
};
|
||
|
|
||
|
Cookies parseCookies(curl_slist* raw_cookies) {
|
||
|
const int CURL_HTTP_COOKIE_SIZE = static_cast<int>(CurlHTTPCookieField::Value) + 1;
|
||
|
Cookies cookies;
|
||
|
for (curl_slist* nc = raw_cookies; nc; nc = nc->next) {
|
||
|
std::vector<std::string> tokens = cpr::util::split(nc->data, '\t');
|
||
|
while (tokens.size() < CURL_HTTP_COOKIE_SIZE) {
|
||
|
tokens.emplace_back("");
|
||
|
}
|
||
|
const std::time_t expires = static_cast<time_t>(std::stoul(tokens.at(static_cast<size_t>(CurlHTTPCookieField::Expires))));
|
||
|
cookies.emplace_back(Cookie{
|
||
|
tokens.at(static_cast<size_t>(CurlHTTPCookieField::Name)),
|
||
|
tokens.at(static_cast<size_t>(CurlHTTPCookieField::Value)),
|
||
|
tokens.at(static_cast<size_t>(CurlHTTPCookieField::Domain)),
|
||
|
isTrue(tokens.at(static_cast<size_t>(CurlHTTPCookieField::IncludeSubdomains))),
|
||
|
tokens.at(static_cast<size_t>(CurlHTTPCookieField::Path)),
|
||
|
isTrue(tokens.at(static_cast<size_t>(CurlHTTPCookieField::HttpsOnly))),
|
||
|
std::chrono::system_clock::from_time_t(expires),
|
||
|
});
|
||
|
}
|
||
|
return cookies;
|
||
|
}
|
||
|
|
||
|
Header parseHeader(const std::string& headers, std::string* status_line, std::string* reason) {
|
||
|
Header header;
|
||
|
std::vector<std::string> lines;
|
||
|
std::istringstream stream(headers);
|
||
|
{
|
||
|
std::string line;
|
||
|
while (std::getline(stream, line, '\n')) {
|
||
|
lines.push_back(line);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for (std::string& line : lines) {
|
||
|
if (line.substr(0, 5) == "HTTP/") {
|
||
|
// set the status_line if it was given
|
||
|
if ((status_line != nullptr) || (reason != nullptr)) {
|
||
|
line.resize(std::min<size_t>(line.size(), line.find_last_not_of("\t\n\r ") + 1));
|
||
|
if (status_line != nullptr) {
|
||
|
*status_line = line;
|
||
|
}
|
||
|
|
||
|
// set the reason if it was given
|
||
|
if (reason != nullptr) {
|
||
|
const size_t pos1 = line.find_first_of("\t ");
|
||
|
size_t pos2 = std::string::npos;
|
||
|
if (pos1 != std::string::npos) {
|
||
|
pos2 = line.find_first_of("\t ", pos1 + 1);
|
||
|
}
|
||
|
if (pos2 != std::string::npos) {
|
||
|
line.erase(0, pos2 + 1);
|
||
|
*reason = line;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
header.clear();
|
||
|
}
|
||
|
|
||
|
if (line.length() > 0) {
|
||
|
const size_t found = line.find(':');
|
||
|
if (found != std::string::npos) {
|
||
|
std::string value = line.substr(found + 1);
|
||
|
value.erase(0, value.find_first_not_of("\t "));
|
||
|
value.resize(std::min<size_t>(value.size(), value.find_last_not_of("\t\n\r ") + 1));
|
||
|
header[line.substr(0, found)] = value;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return header;
|
||
|
}
|
||
|
|
||
|
std::vector<std::string> split(const std::string& to_split, char delimiter) {
|
||
|
std::vector<std::string> tokens;
|
||
|
|
||
|
std::stringstream stream(to_split);
|
||
|
std::string item;
|
||
|
while (std::getline(stream, item, delimiter)) {
|
||
|
tokens.push_back(item);
|
||
|
}
|
||
|
|
||
|
return tokens;
|
||
|
}
|
||
|
|
||
|
size_t readUserFunction(char* ptr, size_t size, size_t nitems, const ReadCallback* read) {
|
||
|
size *= nitems;
|
||
|
return (*read)(ptr, size) ? size : CURL_READFUNC_ABORT;
|
||
|
}
|
||
|
|
||
|
size_t headerUserFunction(char* ptr, size_t size, size_t nmemb, const HeaderCallback* header) {
|
||
|
size *= nmemb;
|
||
|
return (*header)({ptr, size}) ? size : 0;
|
||
|
}
|
||
|
|
||
|
size_t writeFunction(char* ptr, size_t size, size_t nmemb, std::string* data) {
|
||
|
size *= nmemb;
|
||
|
data->append(ptr, size);
|
||
|
return size;
|
||
|
}
|
||
|
|
||
|
size_t writeFileFunction(char* ptr, size_t size, size_t nmemb, std::ofstream* file) {
|
||
|
size *= nmemb;
|
||
|
file->write(ptr, static_cast<std::streamsize>(size));
|
||
|
return size;
|
||
|
}
|
||
|
|
||
|
size_t writeUserFunction(char* ptr, size_t size, size_t nmemb, const WriteCallback* write) {
|
||
|
size *= nmemb;
|
||
|
return (*write)({ptr, size}) ? size : 0;
|
||
|
}
|
||
|
|
||
|
int debugUserFunction(CURL* /*handle*/, curl_infotype type, char* data, size_t size, const DebugCallback* debug) {
|
||
|
(*debug)(static_cast<DebugCallback::InfoType>(type), std::string(data, size));
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Creates a temporary CurlHolder object and uses it to escape the given string.
|
||
|
* If you plan to use this methode on a regular basis think about creating a CurlHolder
|
||
|
* object and calling urlEncode(std::string) on it.
|
||
|
*
|
||
|
* Example:
|
||
|
* CurlHolder holder;
|
||
|
* std::string input = "Hello World!";
|
||
|
* std::string result = holder.urlEncode(input);
|
||
|
**/
|
||
|
std::string urlEncode(const std::string& s) {
|
||
|
const CurlHolder holder; // Create a temporary new holder for URL encoding
|
||
|
return holder.urlEncode(s);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Creates a temporary CurlHolder object and uses it to unescape the given string.
|
||
|
* If you plan to use this methode on a regular basis think about creating a CurlHolder
|
||
|
* object and calling urlDecode(std::string) on it.
|
||
|
*
|
||
|
* Example:
|
||
|
* CurlHolder holder;
|
||
|
* std::string input = "Hello%20World%21";
|
||
|
* std::string result = holder.urlDecode(input);
|
||
|
**/
|
||
|
std::string urlDecode(const std::string& s) {
|
||
|
const CurlHolder holder; // Create a temporary new holder for URL decoding
|
||
|
return holder.urlDecode(s);
|
||
|
}
|
||
|
|
||
|
#if defined(__STDC_LIB_EXT1__)
|
||
|
void secureStringClear(std::string& s) {
|
||
|
if (s.empty()) {
|
||
|
return;
|
||
|
}
|
||
|
memset_s(&s.front(), s.length(), 0, s.length());
|
||
|
s.clear();
|
||
|
}
|
||
|
#elif defined(_WIN32)
|
||
|
void secureStringClear(std::string& s) {
|
||
|
if (s.empty()) {
|
||
|
return;
|
||
|
}
|
||
|
SecureZeroMemory(&s.front(), s.length());
|
||
|
s.clear();
|
||
|
}
|
||
|
#else
|
||
|
#if defined(__clang__)
|
||
|
#pragma clang optimize off // clang
|
||
|
#elif defined(__GNUC__) || defined(__MINGW32__) || defined(__MINGW32__) || defined(__MINGW64__)
|
||
|
#pragma GCC push_options // g++
|
||
|
#pragma GCC optimize("O0") // g++
|
||
|
#endif
|
||
|
void secureStringClear(std::string& s) {
|
||
|
if (s.empty()) {
|
||
|
return;
|
||
|
}
|
||
|
// NOLINTNEXTLINE (readability-container-data-pointer)
|
||
|
char* ptr = &(s[0]);
|
||
|
memset(ptr, '\0', s.length());
|
||
|
s.clear();
|
||
|
}
|
||
|
|
||
|
#if defined(__clang__)
|
||
|
#pragma clang optimize on // clang
|
||
|
#elif defined(__GNUC__) || defined(__MINGW32__) || defined(__MINGW32__) || defined(__MINGW64__)
|
||
|
#pragma GCC pop_options // g++
|
||
|
#endif
|
||
|
#endif
|
||
|
|
||
|
bool isTrue(const std::string& s) {
|
||
|
std::string temp_string{s};
|
||
|
std::transform(temp_string.begin(), temp_string.end(), temp_string.begin(), [](unsigned char c) { return std::tolower(c); });
|
||
|
return temp_string == "true";
|
||
|
}
|
||
|
|
||
|
} // namespace cpr::util
|