forked from MrBesen/lolautoaccept
188 lines
5.3 KiB
C++
188 lines
5.3 KiB
C++
#include "restclient.h"
|
|
|
|
#include <iomanip>
|
|
#include <Log.h>
|
|
|
|
static size_t arrayWriteCallback(char* contents, size_t size, size_t nmemb, void* userdata) {
|
|
if (userdata) {
|
|
QByteArray* arr = (QByteArray*) userdata;
|
|
arr->append(contents, size * nmemb);
|
|
return size * nmemb;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void dump(const char* text, FILE* stream, unsigned char* ptr, size_t size) {
|
|
const unsigned int width = 0x40;
|
|
|
|
fprintf(stream, "%s, %10.10lu bytes (0x%8.8lx)\n", text, (unsigned long) size, (unsigned long) size);
|
|
|
|
for (size_t i = 0; i < size; i += width) {
|
|
|
|
fprintf(stream, "%4.4lx: ", (unsigned long) i);
|
|
|
|
for (size_t c = 0; (c < width) && (i + c < size); c++) {
|
|
/* check for 0D0A; if found, skip past and start a new line of output */
|
|
fprintf(stream, "%c", (ptr[i + c] >= 0x20) && (ptr[i + c] < 0x80) ? ptr[i + c] : '.');
|
|
}
|
|
fputc('\n', stream); /* newline */
|
|
}
|
|
fflush(stream);
|
|
}
|
|
|
|
static int my_trace(CURL* handle, curl_infotype type, char* data, size_t size, void* userp) {
|
|
const char* text;
|
|
(void) handle; /* prevent compiler warning */
|
|
(void) userp;
|
|
|
|
switch (type) {
|
|
case CURLINFO_TEXT:
|
|
fprintf(stderr, "== Info: %s", data);
|
|
/* FALLTHROUGH */
|
|
default: /* in case a new one is introduced to shock us */
|
|
return 0;
|
|
|
|
case CURLINFO_HEADER_OUT:
|
|
text = "=> Send header";
|
|
break;
|
|
case CURLINFO_DATA_OUT:
|
|
text = "=> Send data";
|
|
break;
|
|
case CURLINFO_HEADER_IN:
|
|
text = "<= Recv header";
|
|
break;
|
|
case CURLINFO_DATA_IN:
|
|
text = "<= Recv data";
|
|
break;
|
|
case CURLINFO_SSL_DATA_OUT:
|
|
case CURLINFO_SSL_DATA_IN:
|
|
return 0;
|
|
}
|
|
|
|
dump(text, stderr, (unsigned char*) data, size);
|
|
return 0;
|
|
}
|
|
|
|
RestClient::RestClient(const QString& base) : baseurl(base) {
|
|
curl = curl_easy_init();
|
|
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
|
|
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, arrayWriteCallback);
|
|
}
|
|
|
|
RestClient::~RestClient() {
|
|
curl_easy_cleanup(curl);
|
|
curl = nullptr;
|
|
}
|
|
|
|
RestClient::WebException::WebException(CURLcode c) : curlresponse(c) {
|
|
}
|
|
|
|
QByteArray RestClient::requestRaw(const QString& url, Method m, const QString& data) {
|
|
if (!curl) return {};
|
|
|
|
QString requrl = baseurl + url;
|
|
const QByteArray reqArr = requrl.toLocal8Bit();
|
|
curl_easy_setopt(curl, CURLOPT_URL, reqArr.data());
|
|
|
|
const QByteArray basicArr = basicauth.toLocal8Bit();
|
|
curl_easy_setopt(curl, CURLOPT_USERPWD, basicArr.data());
|
|
curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
|
|
if (disableCertCheck) {
|
|
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
|
|
}
|
|
|
|
// restore default HTTP Options
|
|
curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L);
|
|
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, NULL);
|
|
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, NULL);
|
|
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, NULL);
|
|
|
|
// curl header
|
|
struct curl_slist* headerlist = NULL;
|
|
|
|
headerlist = curl_slist_append(headerlist, "Accept: application/json");
|
|
const QByteArray dataArr = data.toLocal8Bit();
|
|
|
|
switch (m) {
|
|
default:
|
|
case Method::GET:
|
|
curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); break;
|
|
case Method::POST:
|
|
curl_easy_setopt(curl, CURLOPT_POST, 1L);
|
|
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, dataArr.data());
|
|
headerlist = curl_slist_append(headerlist, "Content-Type: application/json");
|
|
break;
|
|
case Method::PUT:
|
|
case Method::PATCH:
|
|
case Method::DELETE:
|
|
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, toString(m)); // to use the POSTFIELDS (do not use CURLOPT_PUT, it does not support POSTFIELDS)
|
|
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, dataArr.data());
|
|
headerlist = curl_slist_append(headerlist, "Content-Type: application/json");
|
|
break;
|
|
}
|
|
|
|
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headerlist);
|
|
|
|
QByteArray ba; //buffer
|
|
// set callback data
|
|
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &ba);
|
|
|
|
CURLcode res = curl_easy_perform(curl);
|
|
if (headerlist) {
|
|
curl_slist_free_all(headerlist);
|
|
}
|
|
// Check for errors
|
|
if (res != CURLE_OK) {
|
|
if (res == CURLE_HTTP_RETURNED_ERROR) {
|
|
long responsecode = -1;
|
|
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &responsecode);
|
|
qWarning() << "API request failed: " << baseurl << " " << url << " -> " << responsecode;
|
|
} else {
|
|
qWarning() << "API request failed: " << baseurl << " " << url << " " << curl_easy_strerror(res);
|
|
if(res == CURLE_COULDNT_CONNECT) {
|
|
throw WebException(res);
|
|
}
|
|
}
|
|
return {};
|
|
}
|
|
return ba;
|
|
}
|
|
|
|
QJsonDocument RestClient::request(const QString& url, Method m, const QString& data) {
|
|
QByteArray arr = requestRaw(url, m, data);
|
|
if (arr.isEmpty()) return {};
|
|
|
|
QJsonParseError err;
|
|
QJsonDocument parsed = QJsonDocument::fromJson(arr, &err);
|
|
if (parsed.isNull() || err.error != QJsonParseError::NoError) {
|
|
qCritical() << "API Json parse error " << err.errorString() << " offset: " << err.offset;
|
|
return {};
|
|
}
|
|
|
|
return parsed;
|
|
}
|
|
|
|
void RestClient::enableDebugging(bool enabled) {
|
|
// enable debugging
|
|
if(enabled) {
|
|
curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, my_trace);
|
|
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
|
|
} else {
|
|
curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, NULL);
|
|
curl_easy_setopt(curl, CURLOPT_VERBOSE, 0L);
|
|
}
|
|
}
|
|
|
|
QString RestClient::escape(const QString& in) const {
|
|
char* e = curl_easy_escape(curl, in.toLocal8Bit().data(), in.length());
|
|
QString esc(e);
|
|
curl_free(e);
|
|
return esc;
|
|
}
|
|
|
|
const char* toString(RestClient::Method m) {
|
|
static const char* MethodNames[] = {"GET", "POST", "PUT", "PATCH", "DELETE"};
|
|
|
|
return MethodNames[(int) m];
|
|
}
|