Crossfire Client, Trunk
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 "client.h"
22 
23 #ifdef HAVE_CURL_CURL_H
24 #include <curl/curl.h>
25 #endif
26 
27 #include "metaserver.h"
28 
29 /* list of metaserver URL to get information from - this should generally
30  * correspond to the value in the metaserver2 server file, but instead
31  * of meta_update.php, use meta_client.php.
32  *
33  * These could perhaps be in some other file (config.h or the like), but
34  * it seems unlikely that these will change very often, and certainly not
35  * at a level where we would expect users to go about changing the values.
36  */
37 static const char *metaservers[] = {
38  "http://crossfire.real-time.com/metaserver2/meta_client.php",
39  "http://metaserver.eu.cross-fire.org/meta_client.php",
40  "http://metaserver.us.cross-fire.org/meta_client.php",
41 };
42 
46 struct mbuf {
47  char *memory;
48  size_t size;
49 };
50 
55 static size_t mbuf_write(void *contents, size_t size, size_t nmemb,
56  void *userp) {
57  size_t realsize = size * nmemb;
58  struct mbuf *mem = (struct mbuf *)userp;
59 
60  mem->memory = realloc(mem->memory, mem->size + realsize + 1);
61  if (mem->memory == NULL) {
62  /* out of memory! */
63  printf("not enough memory (realloc returned NULL)\n");
64  return 0;
65  }
66 
67  memcpy(&(mem->memory[mem->size]), contents, realsize);
68  mem->size += realsize;
69  mem->memory[mem->size] = 0;
70  return realsize;
71 }
72 
85 static bool ms_check_version(Meta_Info *server) {
86  /* No version information - nothing to do. */
87  if (!server->sc_version || !server->cs_version) {
88  return true;
89  }
90 
91  if (server->sc_version != VERSION_SC) {
92  /* 1027->1028 removed a bunch of old commands, so a 1028
93  * version client can still play on a 1027 server, so
94  * special hard code that.
95  *
96  * Likewise, 1028->1029 just changed how weapon_speed
97  * should be interperted on the client - the client
98  * does the right thing, so not problem with a 1029
99  * client playing on 1028 or 1027 server.
100  *
101  * A 1028 client could in practice play on a 1029
102  * server, since at the protocol level, data is the same -
103  * the client would just have screwed up weapon_sp values.
104  */
105  if ((VERSION_SC == 1028 || VERSION_SC == 1029) &&
106  (server->sc_version == 1027 ||
107  server->sc_version == 1028)) {
108  return true;
109  }
110  }
111  if (server->cs_version != VERSION_CS) {
112  return false;
113  }
114 
115  return true;
116 }
117 
118 static void parse_meta(char inbuf[static 1], ms_callback callback) {
119  Meta_Info metaserver;
120  char *newline;
121  for (char *cp = inbuf; cp != NULL && *cp != 0; cp = newline) {
122  newline = strchr(cp, '\n');
123  if (newline) {
124  *newline = 0;
125  newline++;
126  } else {
127  /* If we didn't get a newline, then this is the
128  * end of the block of data for this call - store
129  * away the extra for the next call.
130  */
131  // Can no longer happen after removing ms_writer().
132  return;
133  }
134 
135  char *eq = strchr(cp, '=');
136  if (eq) {
137  *eq = 0;
138  eq++;
139  }
140 
141  if (!strcmp(cp, "START_SERVER_DATA")) {
142  /* Clear out all data - MS2 doesn't necessarily use all the
143  * fields, so blank out any that we are not using.
144  */
145  memset(&metaserver, 0, sizeof (Meta_Info));
146  } else if (!strcmp(cp, "END_SERVER_DATA")) {
147  char buf[MS_LARGE_BUF];
148 
149  // If server is running custom port, add it to server name.
150  if (metaserver.port != EPORT) {
151  snprintf(buf, sizeof(buf), "%s:%d",
152  metaserver.hostname, metaserver.port);
153  } else {
154  snprintf(buf, sizeof(buf), "%s", metaserver.hostname);
155  }
156 
157  callback(buf, metaserver.idle_time, metaserver.num_players,
158  metaserver.version, metaserver.text_comment,
159  ms_check_version(&metaserver));
160  } else {
161  /* If we get here, these should be variable=value pairs.
162  * if we don't have a value, can't do anything, and
163  * report an error. This would normally be incorrect
164  * data from the server.
165  */
166  if (!eq) {
167  fprintf(stderr, "parse_meta: unknown line '%s'\n", cp);
168  continue;
169  }
170  if (!strcmp(cp, "hostname")) {
171  strncpy(metaserver.hostname, eq, sizeof (metaserver.hostname));
172  } else if (!strcmp(cp, "port")) {
173  metaserver.port = atoi(eq);
174  } else if (!strcmp(cp, "html_comment")) {
175  strncpy(metaserver.html_comment, eq, sizeof (metaserver.html_comment));
176  } else if (!strcmp(cp, "text_comment")) {
177  strncpy(metaserver.text_comment, eq, sizeof (metaserver.text_comment));
178  } else if (!strcmp(cp, "archbase")) {
179  strncpy(metaserver.archbase, eq, sizeof (metaserver.archbase));
180  } else if (!strcmp(cp, "mapbase")) {
181  strncpy(metaserver.mapbase, eq, sizeof (metaserver.mapbase));
182  } else if (!strcmp(cp, "codebase")) {
183  strncpy(metaserver.codebase, eq, sizeof (metaserver.codebase));
184  } else if (!strcmp(cp, "flags")) {
185  strncpy(metaserver.flags, eq, sizeof (metaserver.flags));
186  } else if (!strcmp(cp, "version")) {
187  strncpy(metaserver.version, eq, sizeof (metaserver.version));
188  } else if (!strcmp(cp, "num_players")) {
189  metaserver.num_players = atoi(eq);
190  } else if (!strcmp(cp, "in_bytes")) {
191  metaserver.in_bytes = atoi(eq);
192  } else if (!strcmp(cp, "out_bytes")) {
193  metaserver.out_bytes = atoi(eq);
194  } else if (!strcmp(cp, "uptime")) {
195  metaserver.uptime = atoi(eq);
196  } else if (!strcmp(cp, "sc_version")) {
197  metaserver.sc_version = atoi(eq);
198  } else if (!strcmp(cp, "cs_version")) {
199  metaserver.cs_version = atoi(eq);
200  } else if (!strcmp(cp, "last_update")) {
201  /* MS2 reports update time as when it last got an update,
202  * where as we want actual elapsed time since last update.
203  * So do the conversion. Second check is because of clock
204  * skew - my clock may be fast, and we don't want negative times.
205  */
206  metaserver.idle_time = time(NULL) - atoi(eq);
207  if (metaserver.idle_time < 0) {
208  metaserver.idle_time = 0;
209  }
210  } else {
211  fprintf(stderr, "parse_meta: unknown line '%s=%s'\n", cp, eq);
212  }
213  }
214  }
215 }
216 
223 static bool ms_fetch_server(const char *metaserver2, ms_callback callback) {
224 #ifdef HAVE_CURL_CURL_H
225  CURL *curl = curl_easy_init();
226  if (!curl) {
227  return false;
228  }
229 
230  struct mbuf chunk;
231  chunk.memory = malloc(1);
232  chunk.size = 0;
233  if (!chunk.memory) {
234  abort();
235  }
236 
237  curl_easy_setopt(curl, CURLOPT_URL, metaserver2);
238  curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, mbuf_write);
239  curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk);
240  CURLcode res = curl_easy_perform(curl);
241  curl_easy_cleanup(curl);
242 
243  if (!res) {
244  parse_meta(chunk.memory, callback);
245  }
246  free(chunk.memory);
247  return !res;
248 #else
249  return true;
250 #endif
251 }
252 
257 void ms_init() {
258 #ifdef HAVE_CURL_CURL_H
259  curl_global_init(CURL_GLOBAL_ALL);
260 #endif
261 }
262 
269 void ms_fetch(ms_callback callback) {
270  for (size_t i = 0; i < sizeof(metaservers) / sizeof(char *); i++) {
271  ms_fetch_server(metaservers[i], callback);
272  }
273 }
VERSION_CS
#define VERSION_CS
Definition: client.h:67
metaserver.h
Meta_Info::version
char version[MS_SMALL_BUF]
Definition: metaserver.h:32
Meta_Info::mapbase
char mapbase[MS_SMALL_BUF]
Definition: metaserver.h:24
Meta_Info::html_comment
char html_comment[MS_LARGE_BUF]
Definition: metaserver.h:21
Meta_Info::flags
char flags[MS_SMALL_BUF]
Definition: metaserver.h:26
Meta_Info::text_comment
char text_comment[MS_LARGE_BUF]
Definition: metaserver.h:22
mbuf::memory
char * memory
Definition: metaserver.c:47
Meta_Info::sc_version
int sc_version
Definition: metaserver.h:33
mbuf_write
static size_t mbuf_write(void *contents, size_t size, size_t nmemb, void *userp)
Definition: metaserver.c:55
Meta_Info::port
int port
Definition: metaserver.h:20
metaservers
static const char * metaservers[]
Definition: metaserver.c:37
ms_fetch_server
static bool ms_fetch_server(const char *metaserver2, ms_callback callback)
Definition: metaserver.c:223
Meta_Info::uptime
int uptime
Definition: metaserver.h:31
MS_LARGE_BUF
#define MS_LARGE_BUF
Definition: metaserver.h:12
ms_init
void ms_init()
Definition: metaserver.c:257
EPORT
#define EPORT
Definition: client.h:62
ms_fetch
void ms_fetch(ms_callback callback)
Definition: metaserver.c:269
Meta_Info::in_bytes
guint32 in_bytes
Definition: metaserver.h:28
Meta_Info::num_players
int num_players
Definition: metaserver.h:27
ms_check_version
static bool ms_check_version(Meta_Info *server)
Definition: metaserver.c:85
Meta_Info::out_bytes
guint32 out_bytes
Definition: metaserver.h:29
VERSION_SC
#define VERSION_SC
Definition: client.h:68
mbuf::size
size_t size
Definition: metaserver.c:48
parse_meta
static void parse_meta(char inbuf[static 1], ms_callback callback)
Definition: metaserver.c:118
mbuf
Definition: metaserver.c:46
Meta_Info::idle_time
int idle_time
Definition: metaserver.h:30
Meta_Info
Definition: metaserver.h:18
Meta_Info::hostname
char hostname[MS_LARGE_BUF]
Definition: metaserver.h:19
Meta_Info::archbase
char archbase[MS_SMALL_BUF]
Definition: metaserver.h:23
client.h
Meta_Info::codebase
char codebase[MS_SMALL_BUF]
Definition: metaserver.h:25
ms_callback
void(* ms_callback)(char *, int, int, char *, char *, bool)
Definition: metaserver.h:9
Meta_Info::cs_version
int cs_version
Definition: metaserver.h:34