244 lines
6.8 KiB
C
244 lines
6.8 KiB
C
/*
|
|
* Kangaroo Punch MultiPlayer Game Server Mark II
|
|
* Copyright (C) 2020-2021 Scott Duensing
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
|
|
#include <stdarg.h>
|
|
#include <curl/curl.h>
|
|
#include <openssl/err.h>
|
|
|
|
#include "os.h"
|
|
#include "rest.h"
|
|
#include "array.h"
|
|
#include "console.h"
|
|
|
|
|
|
typedef struct RestResponseS {
|
|
uint64_t length;
|
|
char *data;
|
|
} RestResponseT;
|
|
|
|
|
|
static char *_restURL = NULL;
|
|
static char *_restUser = NULL;
|
|
static char *_restPass = NULL;
|
|
static pthread_mutex_t *_restMutexBuffer = NULL;
|
|
|
|
|
|
static void restMutexLocker(int mode, int n, const char *file, int line);
|
|
static size_t restResponseWrite(void *ptr, size_t size, size_t nmemb, RestResponseT *s);
|
|
static unsigned long restThreadtIdGet(void);
|
|
static json_object *restUrlPost(json_object *request);
|
|
|
|
|
|
#pragma GCC diagnostic push
|
|
#pragma GCC diagnostic ignored "-Wunused-function" // It's used, but the compiler isn't picking it up because it's via a callback.
|
|
static void restMutexLocker(int mode, int n, const char *file, int line) {
|
|
(void)file;
|
|
(void)line;
|
|
|
|
if (mode & CRYPTO_LOCK) {
|
|
pthread_mutex_lock(&_restMutexBuffer[n]);
|
|
} else {
|
|
pthread_mutex_unlock(&_restMutexBuffer[n]);
|
|
}
|
|
}
|
|
#pragma GCC diagnostic pop
|
|
|
|
|
|
void restRelease(json_object *object) {
|
|
json_object_put(object);
|
|
}
|
|
|
|
|
|
json_object *restRequest(char *command, char *format, ...) {
|
|
va_list args = { 0 };
|
|
json_object *request = json_object_new_object();
|
|
json_object *response = NULL;
|
|
char *key = NULL;
|
|
char *string = NULL;
|
|
uint8_t result = 0;
|
|
|
|
json_object_object_add(request, "command", json_object_new_string(command));
|
|
|
|
va_start(args, format);
|
|
if (format) {
|
|
while (*format != 0) {
|
|
key = va_arg(args, char *);
|
|
switch (*format) {
|
|
case 's':
|
|
json_object_object_add(request, key, json_object_new_string(va_arg(args, char *)));
|
|
break;
|
|
|
|
case 'i':
|
|
json_object_object_add(request, key, json_object_new_int64(va_arg(args, int64_t)));
|
|
break;
|
|
|
|
default:
|
|
utilDie("restRequest: Unknown format option '%c'.\n", *format);
|
|
break;
|
|
}
|
|
format++;
|
|
}
|
|
}
|
|
va_end(args);
|
|
|
|
//logWrite("Request: %s\n", json_object_to_json_string_ext(request, JSON_C_TO_STRING_PRETTY));
|
|
response = restUrlPost(request);
|
|
json_object_put(request);
|
|
//logWrite("Response: %s\n", json_object_to_json_string_ext(response, JSON_C_TO_STRING_PRETTY));
|
|
|
|
if (response) {
|
|
string = (char *)json_object_get_string(json_object_object_get(response, "result"));
|
|
result = (string[0] == 't' || string[0] == 'T') ? 1 : 0;
|
|
if (!result) {
|
|
logWrite("restRequest: %s\n", json_object_get_string(json_object_object_get(response, "reason")));
|
|
json_object_put(response);
|
|
response = NULL;
|
|
}
|
|
}
|
|
|
|
return response;
|
|
}
|
|
|
|
|
|
static size_t restResponseWrite(void *ptr, size_t size, size_t nmemb, RestResponseT *s) {
|
|
size_t newLength = s->length + size * nmemb;
|
|
|
|
s->data = realloc(s->data, newLength + 1);
|
|
if (s->data == NULL) {
|
|
utilDie("restResponseWrite: realloc() failed.\n");
|
|
}
|
|
memcpy(s->data + s->length, ptr, size*nmemb);
|
|
s->data[newLength] = 0;
|
|
s->length = newLength;
|
|
|
|
return size * nmemb;
|
|
}
|
|
|
|
|
|
void restShutdown(void) {
|
|
uint64_t i;
|
|
|
|
DEL(_restPass);
|
|
DEL(_restUser);
|
|
DEL(_restURL);
|
|
|
|
if (_restMutexBuffer) {
|
|
CRYPTO_set_id_callback(NULL);
|
|
CRYPTO_set_locking_callback(NULL);
|
|
for (i=0; i<CRYPTO_num_locks(); i++) {
|
|
pthread_mutex_destroy(&_restMutexBuffer[i]);
|
|
}
|
|
DEL(_restMutexBuffer);
|
|
}
|
|
|
|
curl_global_cleanup();
|
|
}
|
|
|
|
|
|
uint8_t restStartup(char *url, char *user, char *password) {
|
|
uint64_t i = 0;
|
|
json_object *response = NULL;
|
|
uint8_t result = SUCCESS;
|
|
|
|
curl_global_init(CURL_GLOBAL_ALL);
|
|
|
|
_restMutexBuffer = malloc(CRYPTO_num_locks() * sizeof(pthread_mutex_t));
|
|
if (!_restMutexBuffer) {
|
|
logWrite("restSetup: Mutex buffer creation failed.\n");
|
|
return FAIL;
|
|
}
|
|
|
|
for (i=0; i<CRYPTO_num_locks(); i++) {
|
|
if (pthread_mutex_init(&_restMutexBuffer[i], NULL)) {
|
|
logWrite("restSetup: Mutex creation failed.\n");
|
|
return FAIL;
|
|
}
|
|
}
|
|
|
|
CRYPTO_set_id_callback(restThreadtIdGet);
|
|
CRYPTO_set_locking_callback(restMutexLocker);
|
|
|
|
_restURL = strdup(url);
|
|
_restUser = strdup(user);
|
|
_restPass = strdup(password);
|
|
|
|
// Is the remote server there?
|
|
response = restRequest("API_AVAILABLE", NULL);
|
|
if (!response) result = FAIL;
|
|
restRelease(response);
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
#pragma GCC diagnostic push
|
|
#pragma GCC diagnostic ignored "-Wunused-function" // It's used, but the compiler isn't picking it up because it's via a callback.
|
|
static unsigned long restThreadtIdGet(void) {
|
|
return ((unsigned long)pthread_self());
|
|
}
|
|
#pragma GCC diagnostic pop
|
|
|
|
|
|
static json_object *restUrlPost(json_object *request) {
|
|
CURL *curl = NULL;
|
|
CURLcode res = 0;
|
|
RestResponseT data = { 0 };
|
|
json_object *response = NULL;
|
|
struct curl_slist *headers = NULL;
|
|
enum json_tokener_error error = json_tokener_success;
|
|
|
|
data.length = 0;
|
|
data.data = malloc(data.length + 1);
|
|
if (data.data == NULL) {
|
|
logWrite("restUrlPost: malloc() failed.\n");
|
|
return NULL;
|
|
}
|
|
data.data[0] = 0;
|
|
|
|
curl = curl_easy_init();
|
|
curl_easy_setopt(curl, CURLOPT_URL, _restURL);
|
|
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST");
|
|
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
|
|
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, restResponseWrite);
|
|
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &data);
|
|
curl_easy_setopt(curl, CURLOPT_HTTPAUTH, (long)CURLAUTH_BASIC);
|
|
curl_easy_setopt(curl, CURLOPT_USERNAME, _restUser);
|
|
curl_easy_setopt(curl, CURLOPT_PASSWORD, _restPass);
|
|
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, json_object_to_json_string(request));
|
|
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); // Make the URL work even if your CA bundle is missing.
|
|
curl_easy_setopt(curl, CURLOPT_VERBOSE, 0L);
|
|
|
|
headers = curl_slist_append(headers, "Content-Type: application/json");
|
|
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
|
|
|
|
res = curl_easy_perform(curl);
|
|
if (res == CURLE_OK) {
|
|
response = json_tokener_parse_verbose(data.data, &error);
|
|
if (error) {
|
|
logWrite("Error: %s\n", json_tokener_error_desc(error));
|
|
}
|
|
}
|
|
|
|
curl_slist_free_all(headers);
|
|
DEL(data.data);
|
|
curl_easy_cleanup(curl);
|
|
|
|
return response;
|
|
}
|