Crossfire Client, Trunk  R19726
metaserver.c
Go to the documentation of this file.
1 /*
2  * Crossfire -- cooperative multi-player graphical RPG and adventure game
3  *
4  * Copyright (c) 1999-2014 Mark Wedel and the Crossfire Development Team
5  * Copyright (c) 1992 Frank Tore Johansen
6  *
7  * Crossfire is free software and comes with ABSOLUTELY NO WARRANTY. You are
8  * welcome to redistribute it under certain conditions. For details, please
9  * see COPYING and LICENSE.
10  *
11  * The authors can be reached via e-mail at <crossfire@metalforge.org>.
12  */
13 
21 #include <ctype.h>
22 #include <stdbool.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 
26 #include "client.h"
27 #include "metaserver.h"
28 
29 #ifdef HAVE_CURL_CURL_H
30 #include <curl/curl.h>
31 #include <curl/easy.h>
32 #endif
33 
34 /* list of metaserver URL to get information from - this should generally
35  * correspond to the value in the metaserver2 server file, but instead
36  * of meta_update.php, use meta_client.php.
37  *
38  * These could perhaps be in some other file (config.h or the like), but
39  * it seems unlikely that these will change very often, and certainly not
40  * at a level where we would expect users to go about changing the values.
41  */
42 static const char *metaservers[] = {
43  "http://crossfire.real-time.com/metaserver2/meta_client.php",
44  "http://metaserver.eu.cross-fire.org/meta_client.php",
45 };
46 
49 
54 void ms_set_callback(ms_callback function) {
55  callback = function;
56 }
57 
71  /* No version information - nothing to do. */
72  if (!server->sc_version || !server->cs_version) {
73  return true;
74  }
75 
76  if (server->sc_version != VERSION_SC) {
77  /* 1027->1028 removed a bunch of old commands, so a 1028
78  * version client can still play on a 1027 server, so
79  * special hard code that.
80  *
81  * Likewise, 1028->1029 just changed how weapon_speed
82  * should be interperted on the client - the client
83  * does the right thing, so not problem with a 1029
84  * client playing on 1028 or 1027 server.
85  *
86  * A 1028 client could in practice play on a 1029
87  * server, since at the protocol level, data is the same -
88  * the client would just have screwed up weapon_sp values.
89  */
90  if ((VERSION_SC == 1028 || VERSION_SC == 1029) &&
91  (server->sc_version == 1027 ||
92  server->sc_version == 1028)) {
93  return true;
94  }
95  }
96  if (server->cs_version != VERSION_CS) {
97  return false;
98  }
99 
100  return true;
101 }
102 
127 static size_t ms_writer(void *ptr, size_t size, size_t nmemb, void *data) {
128  Meta_Info metaserver;
129 
130 #ifdef HAVE_CURL_CURL_H
131  size_t realsize = size * nmemb;
132  char *cp, *newline, *eq, inbuf[CURL_MAX_WRITE_SIZE * 2 + 1], *leftover;
133 
134  leftover = (char*) data;
135 
136  if (realsize > CURL_MAX_WRITE_SIZE) {
137  LOG(LOG_CRITICAL, "common::metaserver2_writer", "Function called with more data than allowed!");
138  }
139 
140  /* This memcpy here is to just give us a null terminated character
141  * array - easier to do with than having to check lengths as well as other
142  * values. Also, it makes it easier to deal with unprocessed data from
143  * the last call.
144  */
145  memcpy(inbuf, leftover, strlen(leftover));
146  memcpy(inbuf + strlen(leftover), ptr, realsize);
147  inbuf[strlen(leftover) + realsize] = 0;
148  leftover[0] = 0;
149 
150  for (cp = inbuf; cp != NULL && *cp != 0; cp = newline) {
151  newline = strchr(cp, '\n');
152  if (newline) {
153  *newline = 0;
154  newline++;
155  } else {
156  /* If we didn't get a newline, then this is the
157  * end of the block of data for this call - store
158  * away the extra for the next call.
159  */
160  strncpy(leftover, cp, CURL_MAX_WRITE_SIZE - 1);
161  leftover[CURL_MAX_WRITE_SIZE - 1] = 0;
162  break;
163  }
164 
165  eq = strchr(cp, '=');
166  if (eq) {
167  *eq = 0;
168  eq++;
169  }
170 
171  if (!strcmp(cp, "START_SERVER_DATA")) {
172  /* Clear out all data - MS2 doesn't necessarily use all the
173  * fields, so blank out any that we are not using.
174  */
175  memset(&metaserver, 0, sizeof (Meta_Info));
176  } else if (!strcmp(cp, "END_SERVER_DATA")) {
177  char buf[MS_LARGE_BUF];
178 
179  // If server is running custom port, add it to server name.
180  if (metaserver.port != EPORT) {
181  snprintf(buf, sizeof(buf), "%s:%d",
182  metaserver.hostname, metaserver.port);
183  } else {
184  snprintf(buf, sizeof(buf), "%s", metaserver.hostname);
185  }
186 
187  callback(buf, metaserver.idle_time, metaserver.num_players,
188  metaserver.version, metaserver.text_comment,
189  ms_check_version(&metaserver));
190  } else {
191  /* If we get here, these should be variable=value pairs.
192  * if we don't have a value, can't do anything, and
193  * report an error. This would normally be incorrect
194  * data from the server.
195  */
196  if (!eq) {
197  LOG(LOG_ERROR, "common::metaserver2_writer", "Unknown line: %s", cp);
198  continue;
199  }
200  if (!strcmp(cp, "hostname")) {
201  strncpy(metaserver.hostname, eq, sizeof (metaserver.hostname));
202  } else if (!strcmp(cp, "port")) {
203  metaserver.port = atoi(eq);
204  } else if (!strcmp(cp, "html_comment")) {
205  strncpy(metaserver.html_comment, eq, sizeof (metaserver.html_comment));
206  } else if (!strcmp(cp, "text_comment")) {
207  strncpy(metaserver.text_comment, eq, sizeof (metaserver.text_comment));
208  } else if (!strcmp(cp, "archbase")) {
209  strncpy(metaserver.archbase, eq, sizeof (metaserver.archbase));
210  } else if (!strcmp(cp, "mapbase")) {
211  strncpy(metaserver.mapbase, eq, sizeof (metaserver.mapbase));
212  } else if (!strcmp(cp, "codebase")) {
213  strncpy(metaserver.codebase, eq, sizeof (metaserver.codebase));
214  } else if (!strcmp(cp, "flags")) {
215  strncpy(metaserver.flags, eq, sizeof (metaserver.flags));
216  } else if (!strcmp(cp, "version")) {
217  strncpy(metaserver.version, eq, sizeof (metaserver.version));
218  } else if (!strcmp(cp, "num_players")) {
219  metaserver.num_players = atoi(eq);
220  } else if (!strcmp(cp, "in_bytes")) {
221  metaserver.in_bytes = atoi(eq);
222  } else if (!strcmp(cp, "out_bytes")) {
223  metaserver.out_bytes = atoi(eq);
224  } else if (!strcmp(cp, "uptime")) {
225  metaserver.uptime = atoi(eq);
226  } else if (!strcmp(cp, "sc_version")) {
227  metaserver.sc_version = atoi(eq);
228  } else if (!strcmp(cp, "cs_version")) {
229  metaserver.cs_version = atoi(eq);
230  } else if (!strcmp(cp, "last_update")) {
231  /* MS2 reports update time as when it last got an update,
232  * where as we want actual elapsed time since last update.
233  * So do the conversion. Second check is because of clock
234  * skew - my clock may be fast, and we don't want negative times.
235  */
236  metaserver.idle_time = time(NULL) - atoi(eq);
237  if (metaserver.idle_time < 0) {
238  metaserver.idle_time = 0;
239  }
240  } else {
241  LOG(LOG_ERROR, "common::metaserver2_writer", "Unknown line: %s=%s", cp, eq);
242  }
243  }
244  }
245  return realsize;
246 #else
247  return 0;
248 #endif
249 }
250 
257 bool ms_fetch_server(const char *metaserver2) {
258 #ifdef HAVE_CURL_CURL_H
259  CURL *curl;
260  CURLcode res;
261  char leftover[CURL_MAX_WRITE_SIZE];
262 
263  curl = curl_easy_init();
264  if (!curl) {
265  return false;
266  }
267 
268  leftover[0] = 0;
269  curl_easy_setopt(curl, CURLOPT_URL, metaserver2);
270  curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, ms_writer);
271  curl_easy_setopt(curl, CURLOPT_WRITEDATA, leftover);
272  res = curl_easy_perform(curl);
273  curl_easy_cleanup(curl);
274 
275  if (res) {
276  return false;
277  } else {
278  return true;
279  }
280 #else
281  return true;
282 #endif
283 }
284 
291 void ms_fetch() {
292  const int count = sizeof(metaservers) / sizeof(char *);
293 
294  for (int i = 0; i < count; i++) {
296  }
297 }
298 
303 void ms_init() {
304 #ifdef HAVE_CURL_CURL_H
305  curl_global_init(CURL_GLOBAL_ALL);
306 #endif
307 }
#define MS_LARGE_BUF
Definition: metaserver.h:11
#define VERSION_CS
Definition: client.h:31
Fatal crash-worthy error.
Definition: client.h:411
int sc_version
Definition: metaserver.h:32
void ms_init()
Initialize metaserver client.
Definition: metaserver.c:303
guint32 in_bytes
Definition: metaserver.h:27
int port
Definition: metaserver.h:19
int cs_version
Definition: metaserver.h:33
int uptime
Definition: metaserver.h:30
static bool ms_check_version(Meta_Info *server)
This checks the servers sc_version and cs_version to see if they are compatible.
Definition: metaserver.c:70
char codebase[MS_SMALL_BUF]
Definition: metaserver.h:24
char hostname[MS_LARGE_BUF]
Definition: metaserver.h:18
char text_comment[MS_LARGE_BUF]
Definition: metaserver.h:21
char * server
Definition: client.c:46
void ms_fetch()
Fetch a list of servers from the built-in official metaservers.
Definition: metaserver.c:291
#define EPORT
Definition: client.h:26
Metaserver functions and data structures.
void LOG(LogLevel level, const char *origin, const char *format,...)
Log messages of a certain importance to stderr.
Definition: misc.c:115
void ms_set_callback(ms_callback function)
Set a callback function to run whenever another metaserver is available.
Definition: metaserver.c:54
static ms_callback callback
Function to call when a new metaserver is available.
Definition: metaserver.c:48
Information about individual servers from the metaserver.
Definition: metaserver.h:17
char archbase[MS_SMALL_BUF]
Definition: metaserver.h:22
char flags[MS_SMALL_BUF]
Definition: metaserver.h:25
char html_comment[MS_LARGE_BUF]
Definition: metaserver.h:20
int num_players
Definition: metaserver.h:26
Warning that something definitely didn't work.
Definition: client.h:410
char mapbase[MS_SMALL_BUF]
Definition: metaserver.h:23
#define VERSION_SC
Definition: client.h:32
static size_t ms_writer(void *ptr, size_t size, size_t nmemb, void *data)
Curl doesn't really have any built in way to get data from the URL into string data - instead...
Definition: metaserver.c:127
Includes various dependencies header files needed by most everything.
int idle_time
Definition: metaserver.h:29
bool ms_fetch_server(const char *metaserver2)
Fetch a list of servers from the given URL.
Definition: metaserver.c:257
char version[MS_SMALL_BUF]
Definition: metaserver.h:31
guint32 out_bytes
Definition: metaserver.h:28
static const char * metaservers[]
Definition: metaserver.c:42
void(* ms_callback)(char *, int, int, char *, char *, bool)
Definition: metaserver.h:8