// 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 "m_menu.h" #include "mserv.h" #include "i_tcp.h"/* for current_port */ /* 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; (void)_1; buffer = userdata; if (n < (size_t)( 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 = malloc(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 = malloc(sizeof *buffer); buffer->curl = curl; /* I just allocated 4k and fuck it! */ buffer->end = 4096; buffer->buffer = malloc(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); 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); free(buffer->buffer); free(buffer); } int HMS_fetch_rooms (int joining, int query_id) { struct HMS_buffer *hms; int ok; int doing_shit; char *id; char *title; char *room_motd; int id_no; char *p; char *end; int i; (void)query_id; hms = HMS_connect("rooms"); if (! hms) return 0; if (HMS_do(hms)) { doing_shit = 1; p = hms->buffer; for (i = 0; i < NUM_LIST_ROOMS && ( end = strstr(p, "\n\n\n") );) { *end = '\0'; id = strtok(p, "\n"); title = strtok(0, "\n"); room_motd = strtok(0, ""); if (id && title && room_motd) { id_no = atoi(id); /* Don't show the 'All' room if hosting. And it's a hack like this because I'm way too lazy to add another feature to the MS. */ if (joining || id_no != 0) { #ifdef HAVE_THREADS I_lock_mutex(&ms_QueryId_mutex); { if (query_id != ms_QueryId) doing_shit = 0; } I_unlock_mutex(ms_QueryId_mutex); if (! doing_shit) break; #endif room_list[i].header.buffer[0] = 1; room_list[i].id = id_no; strlcpy(room_list[i].name, title, sizeof room_list[i].name); strlcpy(room_list[i].motd, room_motd, sizeof room_list[i].motd); i++; } p = ( end + 3 );/* skip the three linefeeds */ } else break; } if (doing_shit) room_list[i].header.buffer[0] = 0; ok = 1; if (doing_shit) { #ifdef HAVE_THREADS I_lock_mutex(&m_menu_mutex); #endif { for (i = 0; room_list[i].header.buffer[0]; i++) { if(*room_list[i].name != '\0') { MP_RoomMenu[i+1].text = room_list[i].name; roomIds[i] = room_list[i].id; MP_RoomMenu[i+1].status = IT_STRING|IT_CALL; } } } #ifdef HAVE_THREADS I_unlock_mutex(m_menu_mutex); #endif } } 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); if (! hms) return 0; 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 = strdup(strtok(hms->buffer, "\n")); } HMS_end(hms); return ok; } int HMS_unlist (void) { struct HMS_buffer *hms; int ok; hms = HMS_connect("servers/%s/unlist", hms_server_token); if (! hms) return 0; curl_easy_setopt(hms->curl, CURLOPT_CUSTOMREQUEST, "POST"); ok = HMS_do(hms); HMS_end(hms); free(hms_server_token); return ok; } int HMS_update (void) { struct HMS_buffer *hms; int ok; char post[256]; char *title; hms = HMS_connect("servers/%s/update", hms_server_token); if (! hms) return 0; 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) return; 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, int query_id) { struct HMS_buffer *hms; int doing_shit; char local_version[9]; char *room; char *address; char *port; char *title; char *version; char *end; char *section_end; char *p; int i; (void)query_id; if (room_number > 0) { hms = HMS_connect("rooms/%d/servers", room_number); } else hms = HMS_connect("servers"); if (! hms) return NULL; if (HMS_do(hms)) { doing_shit = 1; 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) { #ifdef HAVE_THREADS I_lock_mutex(&ms_QueryId_mutex); { if (query_id != ms_QueryId) doing_shit = 0; } I_unlock_mutex(ms_QueryId_mutex); if (! doing_shit) break; #endif 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; } } if (! doing_shit) break; p = ( section_end + 2 ); } while (section_end) ; if (doing_shit) 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); if (! hms) return 0; 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; }