// SONIC ROBO BLAST 2 KART //----------------------------------------------------------------------------- // Copyright (C) 2020 by James R. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. // See the 'LICENSE' file for more details. //----------------------------------------------------------------------------- // \brief HTTP based master server /* Documentation available here. */ #include #include "doomdef.h" #include "d_clisrv.h" #include "command.h" #include "mserv.h" #include "i_tcp.h"/* for current_port */ #include "z_zone.h" /* I just stop myself from making macros anymore. */ #define Blame( ... ) \ CONS_Printf("\x85" __VA_ARGS__) consvar_t cv_masterserver_debug = { "masterserver_debug", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL/* C90 moment */ }; static int hms_started; static char *hms_server_token; struct HMS_buffer { CURL *curl; char *buffer; int needle; int end; }; static void Contact_error (void) { CONS_Alert(CONS_ERROR, "There was a problem contacting the master server...\n" ); } static size_t HMS_on_read (char *s, size_t _1, size_t n, void *userdata) { struct HMS_buffer *buffer; buffer = userdata; if (n < ( buffer->end - buffer->needle )) { memcpy(&buffer->buffer[buffer->needle], s, n); buffer->needle += n; return n; } else return 0; } static struct HMS_buffer * HMS_connect (const char *format, ...) { va_list ap; CURL *curl; char *url; size_t seek; struct HMS_buffer *buffer; if (! hms_started) { if (curl_global_init(CURL_GLOBAL_ALL) != 0) { Contact_error(); Blame("From curl_global_init.\n"); return NULL; } else { atexit(curl_global_cleanup); hms_started = 1; } } curl = curl_easy_init(); if (! curl) { Contact_error(); Blame("From curl_easy_init.\n"); return NULL; } seek = strlen(ms_API) + 1;/* + '/' */ va_start (ap, format); url = ZZ_Alloc(seek + vsnprintf(0, 0, format, ap) + 1); va_end (ap); sprintf(url, "%s/", ms_API); va_start (ap, format); vsprintf(&url[seek], format, ap); va_end (ap); CONS_Printf("HMS: connecting '%s'...\n", url); buffer = ZZ_Alloc(sizeof *buffer); buffer->curl = curl; /* I just allocated 4k and fuck it! */ buffer->end = 4096; buffer->buffer = ZZ_Alloc(buffer->end); buffer->needle = 0; if (cv_masterserver_debug.value) { curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); curl_easy_setopt(curl, CURLOPT_STDERR, logstream); } curl_easy_setopt(curl, CURLOPT_URL, url); curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, HMS_on_read); curl_easy_setopt(curl, CURLOPT_WRITEDATA, buffer); Z_Free(url); return buffer; } static int HMS_do (struct HMS_buffer *buffer) { CURLcode cc; long status; char *p; cc = curl_easy_perform(buffer->curl); if (cc != CURLE_OK) { Contact_error(); Blame( "From curl_easy_perform: %s\n", curl_easy_strerror(cc) ); return 0; } buffer->buffer[buffer->needle] = '\0'; curl_easy_getinfo(buffer->curl, CURLINFO_RESPONSE_CODE, &status); if (status != 200) { p = strchr(buffer->buffer, '\n'); if (p) *p = '\0'; Contact_error(); Blame( "Master server error %ld: %s%s\n", status, buffer->buffer, ( (p) ? "" : " (malformed)" ) ); return 0; } else return 1; } static void HMS_end (struct HMS_buffer *buffer) { curl_easy_cleanup(buffer->curl); Z_Free(buffer->buffer); Z_Free(buffer); } int HMS_fetch_rooms (int joining) { struct HMS_buffer *hms; int ok; char *id; char *title; char *motd; char *p; char *end; int i; hms = HMS_connect("rooms"); if (HMS_do(hms)) { p = hms->buffer; for (i = 0; i < NUM_LIST_ROOMS && ( end = strstr(p, "\n\n\n") ); ++i) { *end = '\0'; id = strtok(p, "\n"); title = strtok(0, "\n"); motd = strtok(0, ""); if (id && title && motd) { room_list[i].header.buffer[0] = 1; room_list[i].id = atoi(id); strlcpy(room_list[i].name, title, sizeof room_list[i].name); strlcpy(room_list[i].motd, motd, sizeof room_list[i].motd); p = ( end + 3 );/* skip the three linefeeds */ } else break; } room_list[i].header.buffer[0] = 0; ok = 1; } else ok = 0; HMS_end(hms); return ok; } int HMS_register (void) { struct HMS_buffer *hms; int ok; char post[256]; char *title; hms = HMS_connect("rooms/%d/register", ms_RoomId); title = curl_easy_escape(hms->curl, cv_servername.string, 0); snprintf(post, sizeof post, "port=%d&" "title=%s&" "version=%d.%d.%d", current_port, title, VERSION/100, VERSION%100, SUBVERSION ); curl_free(title); curl_easy_setopt(hms->curl, CURLOPT_POSTFIELDS, post); ok = HMS_do(hms); if (ok) { hms_server_token = Z_StrDup(strtok(hms->buffer, "\n")); } HMS_end(hms); return ok; } void HMS_unlist (void) { struct HMS_buffer *hms; hms = HMS_connect("servers/%s/unlist", hms_server_token); curl_easy_setopt(hms->curl, CURLOPT_CUSTOMREQUEST, "POST"); HMS_do(hms); HMS_end(hms); Z_Free(hms_server_token); } int HMS_update (void) { struct HMS_buffer *hms; int ok; char post[256]; char *title; hms = HMS_connect("servers/%s/update", hms_server_token); title = curl_easy_escape(hms->curl, cv_servername.string, 0); snprintf(post, sizeof post, "title=%s", title ); curl_free(title); curl_easy_setopt(hms->curl, CURLOPT_POSTFIELDS, post); ok = HMS_do(hms); HMS_end(hms); return ok; } void HMS_list_servers (void) { struct HMS_buffer *hms; char *p; hms = HMS_connect("servers"); if (HMS_do(hms)) { p = &hms->buffer[strlen(hms->buffer)]; while (*--p == '\n') ; CONS_Printf("%s\n", hms->buffer); } HMS_end(hms); } msg_server_t * HMS_fetch_servers (msg_server_t *list, int room_number) { struct HMS_buffer *hms; char local_version[9]; char *room; char *address; char *port; char *title; char *version; char *end; char *section_end; char *p; int i; if (room_number > 0) { hms = HMS_connect("rooms/%d/servers", room_number); } else hms = HMS_connect("servers"); if (HMS_do(hms)) { snprintf(local_version, sizeof local_version, "%d.%d.%d", VERSION/100, VERSION%100, SUBVERSION ); p = hms->buffer; i = 0; do { section_end = strstr(p, "\n\n"); room = strtok(p, "\n"); p = strtok(0, ""); if (! p) break; while (i < MAXSERVERLIST && ( end = strchr(p, '\n') )) { *end = '\0'; address = strtok(p, " "); port = strtok(0, " "); title = strtok(0, " "); version = strtok(0, ""); if (address && port && title && version) { if (strcmp(version, local_version) == 0) { strlcpy(list[i].ip, address, sizeof list[i].ip); strlcpy(list[i].port, port, sizeof list[i].port); strlcpy(list[i].name, title, sizeof list[i].name); strlcpy(list[i].version, version, sizeof list[i].version); list[i].room = atoi(room); list[i].header.buffer[0] = 1; i++; } if (end == section_end)/* end of list for this room */ break; else p = ( end + 1 );/* skip server delimiter */ } else { section_end = 0;/* malformed so quit the parsing */ break; } } p = ( section_end + 2 ); } while (section_end) ; list[i].header.buffer[0] = 0; } else list = NULL; HMS_end(hms); return list; } int HMS_compare_mod_version (char *buffer, size_t buffer_size) { struct HMS_buffer *hms; int ok; char *version; char *version_name; hms = HMS_connect("versions/%d", MODID); ok = 0; if (HMS_do(hms)) { version = strtok(hms->buffer, " "); version_name = strtok(0, "\n"); if (version && version_name) { if (atoi(version) != MODVERSION) { strlcpy(buffer, version_name, buffer_size); ok = 1; } else ok = -1; } } HMS_end(hms); return ok; }