Crossfire Client, Trunk  R19673
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-2013 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 <stdio.h>
23 #include <stdlib.h>
24 
25 #include "client.h"
26 #include "external.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 
35 
37 
38 int meta_sort(Meta_Info *m1, Meta_Info *m2) {
39  return g_ascii_strcasecmp(m1->hostname, m2->hostname);
40 }
41 
53 int metaserver_check_version(int entry) {
54  /* No version information - nothing to do. */
55  if (!meta_servers[entry].sc_version || !meta_servers[entry].cs_version) {
56  return 1;
57  }
58 
59  if (meta_servers[entry].sc_version != VERSION_SC) {
60  /* 1027->1028 removed a bunch of old commands, so a 1028
61  * version client can still play on a 1027 server, so
62  * special hard code that.
63  *
64  * Likewise, 1028->1029 just changed how weapon_speed
65  * should be interperted on the client - the client
66  * does the right thing, so not problem with a 1029
67  * client playing on 1028 or 1027 server.
68  *
69  * A 1028 client could in practice play on a 1029
70  * server, since at the protocol level, data is the same -
71  * the client would just have screwed up weapon_sp values.
72  */
73  if ((VERSION_SC == 1028 || VERSION_SC == 1029) &&
74  (meta_servers[entry].sc_version == 1027 ||
75  meta_servers[entry].sc_version == 1028)) {
76  return 1;
77  }
78  }
79  if (meta_servers[entry].cs_version != VERSION_CS) {
80  return 0;
81  }
82 
83  return 1;
84 }
85 
86 /*****************************************************************************
87  * Start of cache related functions.
88  *****************************************************************************/
92 
93 static int cached_servers_loaded = 0;
94 static char *cached_server_file = NULL;
95 
110 static void metaserver_cache_load(void) {
111  char name[MS_LARGE_BUF], ip[MS_LARGE_BUF];
112  FILE *cache;
113 
114  // The server cache file path should have been set during initialization.
115  g_assert(cached_server_file != NULL);
116 
117  if (cached_servers_loaded) {
118  return;
119  }
120 
121  /* If failure, we don't want to load again */
123  cached_servers_num = 0;
124 
125  cache = fopen(cached_server_file, "r");
126  if (!cache) {
127  return;
128  }
129 
131  && fgets(name, MS_LARGE_BUF, cache) != NULL
132  && fgets(ip, MS_LARGE_BUF, cache) != NULL) {
133  ip[strlen(ip) - 1] = 0;
134  name[strlen(name) - 1] = 0;
135  cached_servers_ip[cached_servers_num] = g_strdup(ip);
136  cached_servers_name[cached_servers_num++] = g_strdup(name);
137  }
138  fclose(cache);
139 }
140 
144 static void metaserver_cache_save(void) {
145  FILE *cache;
146  int server;
147 
148  if (!cached_server_file) {
149  return;
150  }
151 
152  cache = fopen(cached_server_file, "w");
153  if (!cache) {
154  return;
155  }
156 
157  for (server = 0; server < cached_servers_num; server++) {
158  fprintf(cache, "%s\n", cached_servers_name[server]);
159  fprintf(cache, "%s\n", cached_servers_ip[server]);
160  }
161  fclose(cache);
162 }
163 
169 void metaserver_cache_add(const char *server_name, const char *server_ip) {
170  int index;
171 
172  /*
173  * Try to find the given server name in the existing server cache. If the
174  * zero-based index ends up equal to the one-based number of cached
175  * servers, it was not found.
176  */
177  for (index = 0; index < cached_servers_num; index++) {
178  if (strcmp(server_name, cached_servers_name[index]) == 0) {
179  break;
180  }
181  }
182 
183  /*
184  * If server is already first in the cache list, nothing else needs to be
185  * done, otherwise, the server needs to be cached.
186  */
187  if (index != 0 || !cached_servers_num) {
188  char *name;
189  char *ip;
190  int copy;
191 
192  if (index == cached_servers_num) {
193  /*
194  * If the server was not found in the cache, expand the cache size
195  * by one unless that creates too many entries.
196  */
197  name = g_strdup(server_name);
198  ip = g_strdup(server_ip);
199  cached_servers_num++;
200  if (cached_servers_num > CACHED_SERVERS_MAX) {
201  cached_servers_num--;
202  free(cached_servers_name[cached_servers_num - 1]);
203  free(cached_servers_ip[cached_servers_num - 1]);
204  }
205  } else {
206  /*
207  * If the server was already listed in the cache, grab a copy of
208  * the prior listing.
209  */
210  name = cached_servers_name[index];
211  ip = cached_servers_ip[index];
212  }
213 
214  /*
215  * If the server as already listed, move all the cached items above
216  * the listing down a slot, otherwise, move the whole list down a
217  * notch. This "empties" the top slot.
218  */
219  for (copy = MIN(index, CACHED_SERVERS_MAX - 1); copy > 0; copy--) {
220  cached_servers_name[copy] = cached_servers_name[copy - 1];
221  cached_servers_ip[copy] = cached_servers_ip[copy - 1];
222  }
223 
224  /*
225  * Put the added server information at the top of the cache list, and
226  * save the changes.
227  */
229  cached_servers_ip[0] = ip;
231  }
232 }
233 
234 /*****************************************************************************
235  * End of cache related functions.
236  *****************************************************************************/
237 
238 /******************************************************************************
239  * Metaserver2 support starts here.
240  *
241  ******************************************************************************/
242 
244 
245 /* we use threads so that the GUI keeps responding while we wait for
246  * data. But we need to note if the thread is running or not,
247  * so we store it here. This, like the other metaserver2 data,
248  * should be protected by using the ms2_info_mutext.
249  */
250 static int ms2_is_running = 0;
251 
252 /* list of metaserver URL to get information from - this should generally
253  * correspond to the value in the metaserver2 server file, but instead
254  * of meta_update.php, use meta_client.php.
255  *
256  * These could perhaps be in some other file (config.h or the like), but
257  * it seems unlikely that these will change very often, and certainly not
258  * at a level where we would expect users to go about changing the values.
259  */
260 static char *metaservers[] = {"http://crossfire.real-time.com/metaserver2/meta_client.php"};
261 
286 static size_t metaserver2_writer(void *ptr, size_t size, size_t nmemb, void *data) {
287 #ifdef HAVE_CURL_CURL_H
288  size_t realsize = size * nmemb;
289  char *cp, *newline, *eq, inbuf[CURL_MAX_WRITE_SIZE * 2 + 1], *leftover;
290 
291  leftover = (char*) data;
292 
293  if (realsize > CURL_MAX_WRITE_SIZE) {
294  LOG(LOG_CRITICAL, "common::metaserver2_writer", "Function called with more data than allowed!");
295  }
296 
297  /* This memcpy here is to just give us a null terminated character
298  * array - easier to do with than having to check lengths as well as other
299  * values. Also, it makes it easier to deal with unprocessed data from
300  * the last call.
301  */
302  memcpy(inbuf, leftover, strlen(leftover));
303  memcpy(inbuf + strlen(leftover), ptr, realsize);
304  inbuf[strlen(leftover) + realsize] = 0;
305  leftover[0] = 0;
306 
307  /* Processing this block of data shouldn't take very long, even on
308  * slow machines, so putting the lock here, instead of each time
309  * we update a variable is cleaner
310  */
311  g_mutex_lock(&ms2_info_mutex);
312 
313  for (cp = inbuf; cp != NULL && *cp != 0; cp = newline) {
314  newline = strchr(cp, '\n');
315  if (newline) {
316  *newline = 0;
317  newline++;
318  } else {
319  /* If we didn't get a newline, then this is the
320  * end of the block of data for this call - store
321  * away the extra for the next call.
322  */
323  strncpy(leftover, cp, CURL_MAX_WRITE_SIZE - 1);
324  leftover[CURL_MAX_WRITE_SIZE - 1] = 0;
325  break;
326  }
327 
328  eq = strchr(cp, '=');
329  if (eq) {
330  *eq = 0;
331  eq++;
332  }
333 
334  if (!strcmp(cp, "START_SERVER_DATA")) {
335  /* Clear out all data - MS2 doesn't necessarily use all the
336  * fields, so blank out any that we are not using.
337  */
338  memset(&meta_servers[meta_numservers], 0, sizeof (Meta_Info));
339  } else if (!strcmp(cp, "END_SERVER_DATA")) {
340  int i;
341 
342  /* we can get data from both metaserver1 & 2 - no reason to keep
343  * both. So check for duplicates, and consider metaserver2
344  * data 'better'.
345  */
346  for (i = 0; i < meta_numservers; i++) {
347  if (!g_ascii_strcasecmp(meta_servers[i].hostname, meta_servers[meta_numservers].hostname)) {
348  memcpy(&meta_servers[i], &meta_servers[meta_numservers], sizeof (Meta_Info));
349  break;
350  }
351  }
352  if (i >= meta_numservers) {
353  meta_numservers++;
354  }
355  } else {
356  /* If we get here, these should be variable=value pairs.
357  * if we don't have a value, can't do anything, and
358  * report an error. This would normally be incorrect
359  * data from the server.
360  */
361  if (!eq) {
362  LOG(LOG_ERROR, "common::metaserver2_writer", "Unknown line: %s", cp);
363  continue;
364  }
365  if (!strcmp(cp, "hostname")) {
366  strncpy(meta_servers[meta_numservers].hostname, eq, sizeof (meta_servers[meta_numservers].hostname));
367  } else if (!strcmp(cp, "port")) {
368  meta_servers[meta_numservers].port = atoi(eq);
369  } else if (!strcmp(cp, "html_comment")) {
370  strncpy(meta_servers[meta_numservers].html_comment, eq, sizeof (meta_servers[meta_numservers].html_comment));
371  } else if (!strcmp(cp, "text_comment")) {
372  strncpy(meta_servers[meta_numservers].text_comment, eq, sizeof (meta_servers[meta_numservers].text_comment));
373  } else if (!strcmp(cp, "archbase")) {
374  strncpy(meta_servers[meta_numservers].archbase, eq, sizeof (meta_servers[meta_numservers].archbase));
375  } else if (!strcmp(cp, "mapbase")) {
376  strncpy(meta_servers[meta_numservers].mapbase, eq, sizeof (meta_servers[meta_numservers].mapbase));
377  } else if (!strcmp(cp, "codebase")) {
378  strncpy(meta_servers[meta_numservers].codebase, eq, sizeof (meta_servers[meta_numservers].codebase));
379  } else if (!strcmp(cp, "flags")) {
380  strncpy(meta_servers[meta_numservers].flags, eq, sizeof (meta_servers[meta_numservers].flags));
381  } else if (!strcmp(cp, "version")) {
382  strncpy(meta_servers[meta_numservers].version, eq, sizeof (meta_servers[meta_numservers].version));
383  } else if (!strcmp(cp, "num_players")) {
384  meta_servers[meta_numservers].num_players = atoi(eq);
385  } else if (!strcmp(cp, "in_bytes")) {
386  meta_servers[meta_numservers].in_bytes = atoi(eq);
387  } else if (!strcmp(cp, "out_bytes")) {
388  meta_servers[meta_numservers].out_bytes = atoi(eq);
389  } else if (!strcmp(cp, "uptime")) {
390  meta_servers[meta_numservers].uptime = atoi(eq);
391  } else if (!strcmp(cp, "sc_version")) {
392  meta_servers[meta_numservers].sc_version = atoi(eq);
393  } else if (!strcmp(cp, "cs_version")) {
394  meta_servers[meta_numservers].cs_version = atoi(eq);
395  } else if (!strcmp(cp, "last_update")) {
396  /* MS2 reports update time as when it last got an update,
397  * where as we want actual elapsed time since last update.
398  * So do the conversion. Second check is because of clock
399  * skew - my clock may be fast, and we don't want negative times.
400  */
401  meta_servers[meta_numservers].idle_time = time(NULL) - atoi(eq);
402  if (meta_servers[meta_numservers].idle_time < 0) {
403  meta_servers[meta_numservers].idle_time = 0;
404  }
405  } else {
406  LOG(LOG_ERROR, "common::metaserver2_writer", "Unknown line: %s=%s", cp, eq);
407  }
408  }
409  }
410  g_mutex_unlock(&ms2_info_mutex);
411  return realsize;
412 #else
413  return 0;
414 #endif
415 }
416 
425 static int get_metaserver2_data(char *metaserver2) {
426 #ifdef HAVE_CURL_CURL_H
427  CURL *curl;
428  CURLcode res;
429  char leftover[CURL_MAX_WRITE_SIZE];
430 
431  curl = curl_easy_init();
432  if (!curl) {
433  return 0;
434  }
435  leftover[0] = 0;
436  curl_easy_setopt(curl, CURLOPT_URL, metaserver2);
437  curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, metaserver2_writer);
438  curl_easy_setopt(curl, CURLOPT_WRITEDATA, leftover);
439  res = curl_easy_perform(curl);
440  curl_easy_cleanup(curl);
441 
442  if (res) {
443  return 0;
444  } else {
445  return 1;
446  }
447 #else
448  return 1;
449 #endif
450 }
451 
458 static void *metaserver2_thread() {
459  int metaserver_choice, tries = 0;
460 
461  do {
462  metaserver_choice = g_random_int() % (sizeof (metaservers) / sizeof (char*));
463  tries++;
464  if (tries > 5) {
465  break;
466  }
467  } while (!get_metaserver2_data(metaservers[metaserver_choice]));
468 
469  g_mutex_lock(&ms2_info_mutex);
470  qsort(meta_servers, meta_numservers, sizeof (Meta_Info), (int (*)(const void *, const void *))meta_sort);
471  ms2_is_running = 0;
472  g_mutex_unlock(&ms2_info_mutex);
473  g_thread_exit(NULL);
474  return NULL;
475 }
476 
481  GThread *thread;
482 
483  meta_numservers = 0;
484 
485  if (!METASERVER2) {
486  return 0;
487  }
488 #ifndef HAVE_CURL_CURL_H
489  return 0;
490 #endif
491 
493 
494  g_mutex_lock(&ms2_info_mutex);
495  if (!meta_servers) {
496  meta_servers = calloc(MAX_METASERVER, sizeof (Meta_Info));
497  }
498 
499  ms2_is_running = 1;
500  g_mutex_unlock(&ms2_info_mutex);
501 
502  thread = g_thread_try_new("metaserver", metaserver2_thread, NULL, NULL);
503  if (thread == NULL) {
504  LOG(LOG_ERROR, "common::metaserver2_get_info", "Thread creation failed.");
505  g_mutex_lock(&ms2_info_mutex);
506  ms2_is_running = 0;
507  g_mutex_unlock(&ms2_info_mutex);
508  }
509 
510  return 0;
511 }
512 
518  char buf[MAX_BUF];
519 
520  // Create and store the path to the server cache.
521  g_assert(cache_dir != NULL);
522  snprintf(buf, MAX_BUF, "%s/servers.cache", cache_dir);
523  cached_server_file = g_strdup(buf);
524 
525 #ifdef HAVE_CURL_CURL_H
526  curl_global_init(CURL_GLOBAL_ALL);
527 #endif
528 }
529 
530 /******************************************************************************
531  * End of Metasever2 functions.
532  ******************************************************************************/
533 
534 /******************************************************************************
535  * This is start of common logic - the above sections are actually getting
536  * the data. The code below here is just displaying the data we got
537  */
538 
550  int status;
551 
552  g_mutex_lock(&ms2_info_mutex);
553  status = ms2_is_running;
554  g_mutex_unlock(&ms2_info_mutex);
555 
556  return status;
557 }
#define MS_LARGE_BUF
Definition: metaserver.h:13
#define VERSION_CS
Definition: client.h:31
char * cached_servers_ip[CACHED_SERVERS_MAX]
Definition: metaserver.c:91
int metaserver_check_status()
Sees if we are gathering data or not.
Definition: metaserver.c:549
static void metaserver_cache_load(void)
Load server names and addresses or DNS names from a cache file found in the player's client data fold...
Definition: metaserver.c:110
int sc_version
Definition: metaserver.h:34
static char * metaservers[]
Definition: metaserver.c:260
void metaserver_init()
Initialize metaserver functions.
Definition: metaserver.c:517
void metaserver_cache_add(const char *server_name, const char *server_ip)
Add a server to the players server cache file.
Definition: metaserver.c:169
guint32 in_bytes
Definition: metaserver.h:29
int port
Definition: metaserver.h:21
int cs_version
Definition: metaserver.h:35
const char * cache_dir
Definition: client.c:53
int meta_sort(Meta_Info *m1, Meta_Info *m2)
Definition: metaserver.c:38
Contains external calls that the common area makes callbacks to.
int uptime
Definition: metaserver.h:32
char hostname[MS_LARGE_BUF]
Definition: metaserver.h:20
#define CACHED_SERVERS_MAX
Definition: metaserver.h:52
char * server
Definition: client.c:46
Metaserver settings, structures, and prototypes.
int metaserver_get()
Fetch a list of public servers from the official metaserver.
Definition: metaserver.c:480
Meta_Info * meta_servers
Definition: metaserver.c:34
void LOG(LogLevel level, const char *origin, const char *format,...)
Log messages of a certain importance to stderr.
Definition: misc.c:111
GMutex ms2_info_mutex
Definition: metaserver.c:243
static void * metaserver2_thread()
Thread function that goes off and collects metaserver data.
Definition: metaserver.c:458
Information about individual servers from the metaserver.
Definition: metaserver.h:19
static char * cached_server_file
Definition: metaserver.c:94
int metaserver_check_version(int entry)
This checks the servers sc_version and cs_version to see if they are compatible.
Definition: metaserver.c:53
static size_t metaserver2_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:286
static int cached_servers_loaded
Definition: metaserver.c:93
int num_players
Definition: metaserver.h:28
#define MAX_BUF
Definition: client-types.h:39
int cached_servers_num
Definition: metaserver.c:89
char * cached_servers_name[CACHED_SERVERS_MAX]
Definition: metaserver.c:90
static int get_metaserver2_data(char *metaserver2)
Connects to the URL and gets metaserver data.
Definition: metaserver.c:425
#define VERSION_SC
Definition: client.h:32
#define MAX_METASERVER
Definition: metaserver.h:9
Includes various dependencies header files needed by most everything.
int idle_time
Definition: metaserver.h:31
char * name
Definition: image.c:56
int meta_numservers
Definition: metaserver.c:36
static int ms2_is_running
Definition: metaserver.c:250
guint32 out_bytes
Definition: metaserver.h:30
#define MIN(X__, Y__)
Definition: client.h:617
static void metaserver_cache_save(void)
Definition: metaserver.c:144