Crossfire Client, Branches  R11627
metaserver.c
Go to the documentation of this file.
1 const char * const rcsid_common_metaserver_c =
2  "$Id: metaserver.c 11626 2009-04-04 12:41:46Z lalo $";
3 /*
4  Crossfire client, a client program for the crossfire program.
5 
6  Copyright (C) 2001 Mark Wedel & Crossfire Development Team
7 
8  This program is free software; you can redistribute it and/or modify
9  it under the terms of the GNU General Public License as published by
10  the Free Software Foundation; either version 2 of the License, or
11  (at your option) any later version.
12 
13  This program is distributed in the hope that it will be useful,
14  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  GNU General Public License for more details.
17 
18  You should have received a copy of the GNU General Public License
19  along with this program; if not, write to the Free Software
20  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 
22  The author can be reached via e-mail to crossfire-devel@real-time.com
23 */
24 
25 /* This file deals with contact the metaserver, getting a list of hosts,
26  * displaying/returning them to calling function, and then connecting
27  * to the server when requested.
28  */
29 
30 #ifndef WIN32
31 #include <netdb.h>
32 #include <sys/types.h>
33 #include <sys/socket.h>
34 #include <netinet/in.h>
35 #include <arpa/inet.h>
36 #endif /* WIN32 */
37 
38 #include <ctype.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 
42 #include <client.h>
43 #include <cconfig.h>
44 #include <external.h>
45 
46 #include <metaserver.h>
47 
48 #ifdef HAVE_CURL_CURL_H
49 #include <curl/curl.h>
50 #include <curl/types.h>
51 #include <curl/easy.h>
52 #endif
53 
54 
56 
58 
59 /* This checks the servers sc_version and cs_version to see
60  * if they are compatible.
61  * @parm entry
62  * entry number in the metaservers array to check.
63  * @return
64  * 1 if this entry is compatible, 0 if it is not. Note that this can
65  * only meaningfully check metaserver2 data - metaserver1 doesn't
66  * include protocol version number, so treats all of those as
67  * OK.
68  */
69 int check_server_version(int entry)
70 {
71 
72  /* No version information - nothing to do. */
73  if (!meta_servers[entry].sc_version || !meta_servers[entry].cs_version)
74  return 1;
75 
76  if (meta_servers[entry].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  (meta_servers[entry].sc_version==1027 ||
92  meta_servers[entry].sc_version==1028))
93  return 1;
94  }
95  if (meta_servers[entry].cs_version != VERSION_CS) return 0;
96 
97  return 1;
98 }
99 
100 /*****************************************************************************
101  * Start of cache related functions.
102  *****************************************************************************/
106 static int cached_servers_loaded = 0;
107 const char *cached_server_file = NULL;
108 
109 
110 static void metaserver_load_cache(void) {
111  FILE *cache;
112  char buf[ MS_LARGE_BUF ];
113  int name;
114 
116  return;
117 
118  /* If failure, we don't want to load again */
120  cached_servers_num = 0;
121 
122  cache = fopen(cached_server_file, "r");
123  if (!cache)
124  return;
125 
126  name = 0;
127  while (fgets(buf, MS_LARGE_BUF, cache) != NULL && cached_servers_num < CACHED_SERVERS_MAX) {
128  buf[strlen(buf)-1] = 0;
129  if (!name) {
130  name = 1;
132  } else {
133  name = 0;
134  cached_servers_ip[cached_servers_num++] = strdup(buf);
135  }
136  }
137  fclose(cache);
138  if (name) {
139  /* Missing IP? */
141  }
142 }
143 
144 static void metaserver_save_cache(void) {
145  FILE *cache;
146  int server;
147 
148  if (!cached_server_file)
149  return;
150 
151  cache = fopen(cached_server_file, "w");
152  if (!cache)
153  return;
154 
155  for (server = 0; server < cached_servers_num; server++) {
156  fprintf(cache, "%s\n", cached_servers_name[server]);
157  fprintf(cache, "%s\n", cached_servers_ip[server]);
158  }
159  fclose(cache);
160 }
161 
162 /*****************************************************************************
163  * End of cache related functions.
164  *****************************************************************************/
165 
166 /******************************************************************************
167  * Metaserver2 support starts here.
168  *
169  ******************************************************************************/
170 
171 pthread_mutex_t ms2_info_mutex;
172 
173 /* we use threads so that the GUI keeps responding while we wait for
174  * data. But we need to note if the thread is running or not,
175  * so we store it here. This, like the other metaserver2 data,
176  * should be protected by using the ms2_info_mutext.
177  */
178 static int ms2_is_running=0;
179 
180 /* list of metaserver URL to get information from - this should generally
181  * correspond to the value in the metaserver2 server file, but instead
182  * of meta_update.php, use meta_client.php.
183  *
184  * These could perhaps be in some other file (config.h or the like), but
185  * it seems unlikely that these will change very often, and certainly not
186  * at a level where we would expect users to go about changing the values.
187  */
188 static char *metaservers[] = {"http://crossfire.real-time.com/metaserver2/meta_client.php"};
189 
214 size_t metaserver2_writer(void *ptr, size_t size, size_t nmemb, void *data)
215 {
216 #ifdef HAVE_CURL_CURL_H
217  size_t realsize = size * nmemb;
218  char *cp, *newline, *eq, inbuf[CURL_MAX_WRITE_SIZE*2+1], *leftover;
219 
220  leftover = (char*) data;
221 
222  if (realsize > CURL_MAX_WRITE_SIZE) {
223  LOG(LOG_CRITICAL, "common::metaserver2_writer", "Function called with more data than allowed!");
224  }
225 
226  /* This memcpy here is to just give us a null terminated character
227  * array - easier to do with than having to check lengths as well as other
228  * values. Also, it makes it easier to deal with unprocessed data from
229  * the last call.
230  */
231  memcpy(inbuf, leftover, strlen(leftover));
232  memcpy(inbuf+strlen(leftover), ptr, realsize);
233  inbuf[realsize] = 0;
234 
235  /* Processing this block of data shouldn't take very long, even on
236  * slow machines, so putting the lock here, instead of each time
237  * we update a variable is cleaner
238  */
239  pthread_mutex_lock(&ms2_info_mutex);
240 
241  for (cp = inbuf; cp != NULL && *cp!=0; cp=newline) {
242  newline=strchr(cp, '\n');
243  if (newline) {
244  *newline = 0;
245  newline++;
246  } else {
247  /* If we didn't get a newline, then this is the
248  * end of the block of data for this call - store
249  * away the extra for the next call.
250  */
251  strncpy(leftover, cp, CURL_MAX_WRITE_SIZE-1);
252  leftover[CURL_MAX_WRITE_SIZE-1] = 0;
253  break;
254  }
255 
256  eq = strchr(cp,'=');
257  if (eq) {
258  *eq = 0;
259  eq++;
260  }
261 
262  if (!strcmp(cp, "START_SERVER_DATA")) {
263  /* Clear out all data - MS2 doesn't necessarily use all the
264  * fields, so blank out any that we are not using.
265  */
266  memset(&meta_servers[meta_numservers], 0, sizeof(Meta_Info));
267  }
268  else if (!strcmp(cp, "END_SERVER_DATA")) {
269  int i;
270 
271  /* we can get data from both metaserver1 & 2 - no reason to keep
272  * both. So check for duplicates, and consider metaserver2
273  * data 'better'.
274  */
275  for (i=0; i<meta_numservers; i++) {
276  if (!strcasecmp(meta_servers[i].hostname, meta_servers[meta_numservers].hostname)) {
277  memcpy(&meta_servers[i], &meta_servers[meta_numservers], sizeof(Meta_Info));
278  break;
279  }
280  }
281  if (i>=meta_numservers) {
282  meta_numservers++;
283  }
284  } else {
285  /* If we get here, these should be variable=value pairs.
286  * if we don't have a value, can't do anything, and
287  * report an error. This would normally be incorrect
288  * data from the server.
289  */
290  if (!eq) {
291  LOG(LOG_ERROR, "common::metaserver2_writer", "Unknown line: %s",cp);
292  continue;
293  }
294  if (!strcmp(cp,"hostname")) {
295  strncpy(meta_servers[meta_numservers].hostname, eq, sizeof(meta_servers[meta_numservers].hostname));
296  }
297  else if (!strcmp(cp,"port")) {
298  meta_servers[meta_numservers].port = atoi(eq);
299  }
300  else if (!strcmp(cp,"html_comment")) {
301  strncpy(meta_servers[meta_numservers].html_comment, eq, sizeof(meta_servers[meta_numservers].html_comment));
302  }
303  else if (!strcmp(cp,"text_comment")) {
304  strncpy(meta_servers[meta_numservers].text_comment, eq, sizeof(meta_servers[meta_numservers].text_comment));
305  }
306  else if (!strcmp(cp,"archbase")) {
307  strncpy(meta_servers[meta_numservers].archbase, eq, sizeof(meta_servers[meta_numservers].archbase));
308  }
309  else if (!strcmp(cp,"mapbase")) {
310  strncpy(meta_servers[meta_numservers].mapbase, eq, sizeof(meta_servers[meta_numservers].mapbase));
311  }
312  else if (!strcmp(cp,"codebase")) {
313  strncpy(meta_servers[meta_numservers].codebase, eq, sizeof(meta_servers[meta_numservers].codebase));
314  }
315  else if (!strcmp(cp,"flags")) {
316  strncpy(meta_servers[meta_numservers].flags, eq, sizeof(meta_servers[meta_numservers].flags));
317  }
318  else if (!strcmp(cp,"version")) {
319  strncpy(meta_servers[meta_numservers].version, eq, sizeof(meta_servers[meta_numservers].version));
320  }
321  else if (!strcmp(cp,"num_players")) {
322  meta_servers[meta_numservers].num_players = atoi(eq);
323  }
324  else if (!strcmp(cp,"in_bytes")) {
325  meta_servers[meta_numservers].in_bytes = atoi(eq);
326  }
327  else if (!strcmp(cp,"out_bytes")) {
328  meta_servers[meta_numservers].out_bytes = atoi(eq);
329  }
330  else if (!strcmp(cp,"uptime")) {
331  meta_servers[meta_numservers].uptime = atoi(eq);
332  }
333  else if (!strcmp(cp,"sc_version")) {
334  meta_servers[meta_numservers].sc_version = atoi(eq);
335  }
336  else if (!strcmp(cp,"cs_version")) {
337  meta_servers[meta_numservers].cs_version = atoi(eq);
338  }
339  else if (!strcmp(cp,"last_update")) {
340  /* MS2 reports update time as when it last got an update,
341  * where as we want actual elapsed time since last update.
342  * So do the conversion. Second check is because of clock
343  * skew - my clock may be fast, and we don't want negative times.
344  */
345  meta_servers[meta_numservers].idle_time = time(NULL) - atoi(eq);
346  if (meta_servers[meta_numservers].idle_time < 0)
347  meta_servers[meta_numservers].idle_time = 0;
348  }
349  else {
350  LOG(LOG_ERROR, "common::metaserver2_writer", "Unknown line: %s=%s",cp,eq);
351  }
352  }
353  }
354  pthread_mutex_unlock(&ms2_info_mutex);
355  return realsize;
356 #else
357  return 0;
358 #endif
359 }
360 
369 static int get_metaserver2_data(char *metaserver2) {
370 #ifdef HAVE_CURL_CURL_H
371  CURL *curl;
372  CURLcode res;
373  char leftover[CURL_MAX_WRITE_SIZE];
374 
375  curl = curl_easy_init();
376  if (!curl) return 0;
377  leftover[0] =0;
378  curl_easy_setopt(curl, CURLOPT_URL, metaserver2);
379  curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, metaserver2_writer);
380  curl_easy_setopt(curl, CURLOPT_WRITEDATA, leftover);
381  res = curl_easy_perform(curl);
382  curl_easy_cleanup(curl);
383 
384  if (res) return 0;
385  else return 1;
386 #else
387  return 1;
388 #endif
389 }
390 
397 void *metaserver2_thread(void *junk)
398 {
399  int metaserver_choice;
400 
401  do {
402  metaserver_choice = random() % (sizeof(metaservers) / sizeof(char*));
403  } while (!get_metaserver2_data(metaservers[metaserver_choice]));
404 
405  pthread_mutex_lock(&ms2_info_mutex);
406  qsort(meta_servers, meta_numservers, sizeof(Meta_Info), (int (*)(const void *, const void *))meta_sort);
407  ms2_is_running=0;
408  pthread_mutex_unlock(&ms2_info_mutex);
409  pthread_exit(NULL);
410  // never reached, just to make the compiler happy.
411  return NULL;
412 }
413 
414 
424  pthread_t thread_id;
425  int ret;
426 
427  if (!metaserver2_on) {
428  return 0;
429  }
430 #ifndef HAVE_CURL_CURL_H
431  return 0;
432 #endif
433 
435 
436  pthread_mutex_lock(&ms2_info_mutex);
437  if (!meta_servers)
438  meta_servers = calloc(MAX_METASERVER, sizeof(Meta_Info));
439 
440  ms2_is_running=1;
441  pthread_mutex_unlock(&ms2_info_mutex);
442 
443  ret=pthread_create(&thread_id, NULL, metaserver2_thread, NULL);
444  if (ret) {
445  LOG(LOG_ERROR, "common::metaserver2_get_info", "Thread creation failed.");
446  pthread_mutex_lock(&ms2_info_mutex);
447  ms2_is_running=0;
448  pthread_mutex_unlock(&ms2_info_mutex);
449  }
450 
451  return 0;
452 }
453 
454 
455 
459 void init_metaserver(void)
460 {
461  pthread_mutex_init(&ms2_info_mutex, NULL);
462 #ifdef HAVE_CURL_CURL_H
463  curl_global_init(CURL_GLOBAL_ALL);
464 #endif
465 }
466 
467 /******************************************************************************
468  * End of Metasever2 functions.
469  ******************************************************************************/
470 
471 /******************************************************************************
472  * Start of metaserver1 logic
473  *
474  * Note that this shares the same mutex as metaserver2, since it is updating
475  * most of the same structures.
476  *******************************************************************************/
477 
478 static int ms1_is_running=0;
479 
480 
481 #ifdef WIN32
482 /* Need script.h for script_killall */
483 #include <script.h>
484 
485 /* This gets input from a socket, and returns it one line at a time.
486  */
487 /* This is a Windows-specific function, since you can't use fgets under Win32 */
488 char *get_line_from_sock(char *s, size_t n, int fd) {
489  static long charsleft = 0;
490  static char inbuf[MS_LARGE_BUF*4];
491  char *cp;
492  int ct;
493 
494  if (!s)
495  return s;
496  if (n != MS_LARGE_BUF*4-1) {
497  LOG(LOG_CRITICAL, "common::get_line_from_sock", "Serious program logic error in get_line_from_sock().");
498  exit(-1);
499  }
500 
501  if (charsleft > MS_LARGE_BUF*4-3 && strchr(inbuf, '\n') == NULL) {
502  draw_info("Metaserver returned an overly long line.", NDI_BLACK);
503  return NULL;
504  }
505 
506  /* If there is no line in the buffer */
507  while (charsleft == 0 || (cp = strchr(inbuf, '\n')) == NULL) {
508  FD_SET fdset;
509  TIMEVAL tv = {3, 0}; /* 3 second timeout on reads */
510  int nlen;
511  FD_ZERO(&fdset);
512  FD_SET(fd, &fdset);
513  if (select(0, &fdset, NULL, NULL, &tv) == 0) {
514  draw_info("Metaserver timed out.", NDI_BLACK);
515  return NULL;
516  }
517 
518  nlen = recv(fd, inbuf+charsleft-1, MS_LARGE_BUF*4-1-charsleft, 0);
519  if (nlen == SOCKET_ERROR || nlen <= 0) /* Probably EOF */
520  return NULL;
521 
522  charsleft += nlen;
523  }
524 
525  /* OK, inbuf contains a null terminated string with at least one \n
526  * Copy the string up to the \n to s, and then move the rest of the
527  * inbuf string to the beginning of the buffer. And finally, set
528  * charsleft to the number of characters left in inbuf, or 0.
529  * Oh, and cp contains the location of the \n.
530  */
531 
532  memcpy(s, inbuf, cp-inbuf+1); /* Extract the line, including the \n. */
533  s[cp-inbuf+1] = 0; /* null terminate it */
534 
535  /* Copy cp to inbuf up to the \0, (skipping the \n) */
536  ct = 0;
537  while (cp[++ct] != 0) {
538  inbuf[ct-1] = cp[ct];
539  }
540  inbuf[ct-1] = 0;
541  charsleft = ct; /* And keep track of how many characters are left. */
542 
543  return s;
544 }
545 
546 #endif /* Win32 */
547 
548 void *metaserver1_thread(void *junk)
549 {
550  struct protoent *protox;
551  int fd;
552  struct sockaddr_in insock;
553 #ifndef WIN32
554  FILE *fp;
555 #endif
556  char inbuf[MS_LARGE_BUF*4];
557  Meta_Info *current;
558 
559  protox = getprotobyname("tcp");
560  if (protox == NULL) {
561  LOG(LOG_WARNING, "common::metaserver_get_info", "Error getting protobyname (tcp)");
562  pthread_mutex_lock(&ms2_info_mutex);
563  ms1_is_running=0;
564  pthread_mutex_unlock(&ms2_info_mutex);
565  pthread_exit(NULL);
566  }
567 
568  fd = socket(PF_INET, SOCK_STREAM, protox->p_proto);
569  if (fd == -1) {
570  perror("get_metaserver_info: Error on socket command.\n");
571  pthread_mutex_lock(&ms2_info_mutex);
572  ms1_is_running=0;
573  pthread_mutex_unlock(&ms2_info_mutex);
574  pthread_exit(NULL);
575  }
576  insock.sin_family = AF_INET;
577  insock.sin_port = htons((unsigned short)meta_port);
578  if (isdigit(*meta_server))
579  insock.sin_addr.s_addr = inet_addr(meta_server);
580  else {
581  struct hostent *hostbn = gethostbyname(meta_server);
582  if (hostbn == NULL) {
583  LOG(LOG_WARNING, "common::metaserver_get_info", "Unknown metaserver hostname: %s", meta_server);
584  pthread_mutex_lock(&ms2_info_mutex);
585  ms1_is_running=0;
586  pthread_mutex_unlock(&ms2_info_mutex);
587  pthread_exit(NULL);
588  }
589  memcpy(&insock.sin_addr, hostbn->h_addr, hostbn->h_length);
590  }
591  if (connect(fd, (struct sockaddr *)&insock, sizeof(insock)) == -1) {
592  perror("Can't connect to metaserver");
593  draw_info("\nCan't connect to metaserver.", NDI_BLACK);
594  pthread_mutex_lock(&ms2_info_mutex);
595  ms1_is_running=0;
596  pthread_mutex_unlock(&ms2_info_mutex);
597  pthread_exit(NULL);
598  }
599 
600 #ifndef WIN32 /* Windows doesn't support this */
601  /* Turn this into a file handle - this will break it on newlines
602  * for us, which makes our processing much easier - it basically
603  * means one line/server
604  */
605  if ((fp = fdopen(fd, "r")) == NULL) {
606  perror("fdopen failed.");
607  pthread_mutex_lock(&ms2_info_mutex);
608  ms1_is_running=0;
609  pthread_mutex_unlock(&ms2_info_mutex);
610  pthread_exit(NULL);
611  }
612 #endif
613 
614  pthread_mutex_lock(&ms2_info_mutex);
615  if (!meta_servers)
616  meta_servers = calloc(MAX_METASERVER, sizeof(Meta_Info));
617 
618 
619  /* The loop goes through and unpacks the data from the metaserver
620  * into its individual components. We do a little extra work and
621  * put the |'s back in the string after we are done with that section -
622  * this is so if there is a corrupt entry, it gets displayed as
623  * originally received from the server.
624  */
625 #ifndef WIN32 /* Windows doesn't support this */
626  while (fgets(inbuf, MS_LARGE_BUF*4-1, fp) != NULL) {
627 #else
628  while (get_line_from_sock(inbuf, MS_LARGE_BUF*4-1, fd) != NULL) {
629 #endif
630  char *cp, *cp1;
631 
632  cp = strchr(inbuf, '|');
633  if (cp == NULL) {
634  LOG(LOG_WARNING, "common::metaserver_get_info", "Corrupt line from server: %s", inbuf);
635  break;
636  }
637  *cp = 0;
638 
639  current = &meta_servers[meta_numservers];
640 
641  strncpy(current->ip_addr, inbuf, sizeof(current->ip_addr)-1);
642  current->ip_addr[sizeof(current->ip_addr)-1] = '\0';
643  *cp++ = '|';
644 
645  current->idle_time = atoi(cp);
646 
647  cp1 = strchr(cp, '|');
648  if (cp1 == NULL) {
649  LOG(LOG_WARNING, "common::metaserver_get_info", "Corrupt line from server: %s", inbuf);
650  break;
651  }
652  *cp1 = 0;
653 
654  cp = strchr(cp1+1, '|');
655  if (cp == NULL) {
656  LOG(LOG_WARNING, "common::metaserver_get_info", "Corrupt line from server: %s", inbuf);
657  break;
658  }
659  *cp = 0;
660  /* cp1 points at start of comment, cp points at end */
661  strncpy(current->hostname, cp1+1, sizeof(current->hostname)-1);
662  current->hostname[sizeof(current->hostname)-1] = '\0';
663 
664  *cp1++ = '|';
665  *cp++ = '|'; /* cp now points to num players */
666 
667  current->num_players = atoi(cp);
668 
669  cp1 = strchr(cp, '|');
670  if (cp1 == NULL) {
671  LOG(LOG_WARNING, "common::metaserver_get_info", "Corrupt line from server: %s", inbuf);
672  break;
673  }
674  *cp1 = 0;
675 
676  cp = strchr(cp1+1, '|');
677  if (cp == NULL) {
678  LOG(LOG_WARNING, "common::metaserver_get_info", "Corrupt line from server: %s", inbuf);
679  break;
680  }
681  *cp = 0;
682  /* cp1 is start of version, cp is end */
683  strncpy(current->version, cp1+1, sizeof(current->version)-1);
684  current->version[sizeof(current->version)-1] = '\0';
685 
686  *cp1++ = '|';
687  *cp++ = '|'; /* cp now points to comment */
688 
689  cp1 = strchr(cp, '\n');
690  if (cp1 == NULL) {
691  LOG(LOG_WARNING, "common::metaserver_get_info", "Corrupt line from server: %s", inbuf);
692  break;
693  }
694  *cp1 = 0;
695  /* There is extra info included, like the bytes to/from the server
696  * that we dont' care about, so strip them off so they don't show up in
697  * the comment.
698  */
699  cp1 = strchr(cp, '|');
700  if (cp1 != NULL)
701  *cp1 = 0;
702 
703  strncpy(current->text_comment, cp, sizeof(current->text_comment)-1);
704  current->text_comment[sizeof(current->text_comment)-1] = '\0';
705 
706  meta_numservers++;
707  /* has to be 1 less than array size, since array starts counting
708  * at 0.
709  */
710  if (meta_numservers >= MAX_METASERVER-1) {
711  LOG(LOG_WARNING, "common:metaserver_get_info", "Have reached maximum metaserver count\n");
712  break;
713  }
714  }
715 #ifdef WIN32
716  closesocket(fd);
717 #else
718  fclose(fp);
719 #endif
720  qsort(meta_servers, meta_numservers, sizeof(Meta_Info), (int (*)(const void *, const void *))meta_sort);
721  ms1_is_running=0;
722  pthread_mutex_unlock(&ms2_info_mutex);
723  pthread_exit(NULL);
724  // never reached, just to make the compiler happy.
725  return NULL;
726 }
727 
728 
730  pthread_t thread_id;
731  int ret;
732 
733  if (!metaserver_on) {
734  return 0;
735  }
737 
738  pthread_mutex_lock(&ms2_info_mutex);
739  if (!meta_servers)
740  meta_servers = calloc(MAX_METASERVER, sizeof(Meta_Info));
741 
742  ms1_is_running=1;
743  pthread_mutex_unlock(&ms2_info_mutex);
744 
745  ret=pthread_create(&thread_id, NULL, metaserver1_thread, NULL);
746  if (ret) {
747  LOG(LOG_ERROR, "common::metaserver1_get_info", "Thread creation failed.");
748  pthread_mutex_lock(&ms2_info_mutex);
749  ms1_is_running=0;
750  pthread_mutex_unlock(&ms2_info_mutex);
751  }
752 
753  return 0;
754 }
755 /******************************************************************************
756  * End of metaserver1 logic
757  ******************************************************************************/
758 
759 /******************************************************************************
760  * This is start of common logic - the above sections are actually getting
761  * the data. The code below here is just displaying the data we got
762  */
763 
775  int status;
776 
777  pthread_mutex_lock(&ms2_info_mutex);
778  status = ms2_is_running | ms1_is_running;
779  pthread_mutex_unlock(&ms2_info_mutex);
780 
781  return status;
782 }
783 
784 /* This contacts the metaserver and gets the list of servers. returns 0
785  * on success, 1 on failure. Errors will get dumped to stderr,
786  * so most errors should be reasonably clear.
787  * metaserver and meta_port are the server name and port number
788  * to connect to.
789  */
790 
791 int metaserver_get_info(char *metaserver, int meta_port) {
792 
793  meta_numservers = 0;
794 
796 
797  if (metaserver_on) {
799  }
800 
801  return 0;
802 }
803 
804 /* show the metaservers to the player. we use the draw_info to do
805  * that, and also let the player know they can enter their own host name.
806  */
807 void metaserver_show(int show_selection) {
808  int i;
809  char buf[256];
810 
811  if (cached_servers_num) {
812  draw_info("\nLast servers you connected to:\n", NDI_BLACK);
813  for (i = 0; i < cached_servers_num; i++) {
814  snprintf(buf, sizeof(buf), "%2d) %-20.20s %-20.20s", i+1, cached_servers_name[i], cached_servers_ip[i]);
815  draw_info(buf, NDI_BLACK);
816  }
817  draw_info(" ", NDI_BLACK);
818  }
819 
820  while(metaserver_check_status()) {
821  usleep(100);
822  }
823 
824  draw_info(" #) Server # version idle", NDI_BLACK);
825  draw_info(" Name players seconds", NDI_BLACK);
826  pthread_mutex_lock(&ms2_info_mutex);
827 
828  /* Re-sort the data - may get different data from ms1 and ms2, so
829  * order of this is somewhat random.
830  */
831  qsort(meta_servers, meta_numservers, sizeof(Meta_Info), (int (*)(const void *, const void *))meta_sort);
832  for (i = 0; i < meta_numservers; i++) {
833  if (check_server_version(i)) {
834  snprintf(buf, sizeof(buf), "%2d) %-15.15s %2d %-12.12s %2d",
835  i+1+cached_servers_num, meta_servers[i].hostname,
836  meta_servers[i].num_players, meta_servers[i].version,
837  meta_servers[i].idle_time);
838  draw_info(buf, NDI_BLACK);
839  }
840  }
841  if (show_selection) {
842  /* Show default/current server */
843  if (server) {
844  snprintf(buf, sizeof(buf), "%2d) %s (default)", meta_numservers+1+cached_servers_num, server);
845  draw_info(buf, NDI_BLACK);
846  }
847 
848  draw_info("Choose one of the entries above", NDI_BLACK);
849  draw_info("or type in a hostname/ip address", NDI_BLACK);
850  draw_info("Hit enter to re-update this list", NDI_BLACK);
851  draw_info("Enter 0 to exit the program.", NDI_BLACK);
852  }
853  pthread_mutex_unlock(&ms2_info_mutex);
854 }
855 
856 /* String contains the selection that the player made for the metaserver.
857  * this may not be a a selection, but could be a host name or ip address.
858  * this returns 0 on sucessful selection, 1 if failure (invalid selection
859  * or the like.
860  */
861 int metaserver_select(char *sel) {
862  int num = atoi(sel);
863  int port=0;
864  char buf[MAX_BUF], buf2[MAX_BUF];
865  char *server_name = NULL, *server_ip;
866 
867  /* User hit return */
868  if (sel[0] == 0) {
869  metaserver_get_info(meta_server, meta_port);
871  return 1;
872  }
873 
874  /* Special case - player really entered a 0, so exit the
875  * program.
876  */
877  if (num == 0 && sel[0] == '0') {
878 #ifdef WIN32
879  script_killall();
880 #endif
881  exit(0);
882  }
883 
884  pthread_mutex_lock(&ms2_info_mutex);
885 
886  /* if the entry is not a number (selection from the list),
887  * or is a selection but also has a dot (suggesting
888  * a.b.c.d selection), just try to connect with given name.
889  */
890  if (num == 0 || strchr(sel, '.') != NULL) {
891  server_name = sel;
892  server_ip = sel;
893  } else {
894  if (num <= 0 || num > meta_numservers+cached_servers_num+1) {
895  draw_info("Invalid selection. Try again", NDI_BLACK);
896  return 1;
897  }
898 
899  if (num == meta_numservers+cached_servers_num+1) {
900  server_name = server;
901  server_ip = server;
902  } else if (num > cached_servers_num) {
903  server_name = meta_servers[num-cached_servers_num-1 ].hostname;
904  server_ip = meta_servers[num-cached_servers_num-1 ].ip_addr;
905  port = meta_servers[num-cached_servers_num-1 ].port;
906  } else {
907  server_name = cached_servers_name[num-1];
908  server_ip = cached_servers_ip[num-1];
909  }
910  }
911  pthread_mutex_unlock(&ms2_info_mutex);
912  if (!server_name) {
913  draw_info("Bad selection. Try again", NDI_BLACK);
914  return 1;
915  }
916 
917  /* check for :port suffix, and use it */
918  if (!port) {
919  if ((sel = strrchr(server_name, ':')) != NULL && (port = atoi(sel+1)) > 0) {
920  snprintf(buf2, sizeof(buf2), "%s", server_name);
921  buf2[sel-server_name] = '\0';
922  server_name = buf2;
923  }
924  else {
925  port = use_config[CONFIG_PORT];
926  }
927  }
928 
929  snprintf(buf, sizeof(buf), "Trying to connect to %s:%d", server_name, port);
930  draw_info(buf, NDI_BLACK);
931 #ifdef MULTKEYS
932  csocket.fd = init_connection(server_name, port);
933 #else
934  csocket.fd = init_connection(server_ip, port);
935 #endif
936  if (csocket.fd == -1) {
937  draw_info("Unable to connect to server.", NDI_BLACK);
938  return 1;
939  }
940 
941  /* Add server to cache */
942  if ((num <= meta_numservers) && (num != meta_numservers + cached_servers_num + 1)) {
943  int index;
944  for (index = 0; index < cached_servers_num; index++) {
945  if (strcmp(server_name, cached_servers_name[index]) == 0)
946  break;
947  }
948  /* If server is first in cache, no need to re-add id */
949  if (index != 0 || !cached_servers_num) {
950  char *name;
951  char *ip;
952  int copy;
953 
954  if (index == cached_servers_num) {
955  name = strdup(server_name);
956  ip = strdup(server_ip);
957  cached_servers_num++;
958  if (cached_servers_num > CACHED_SERVERS_MAX) {
959  cached_servers_num--;
960  free(cached_servers_name[cached_servers_num-1]);
961  free(cached_servers_ip[cached_servers_num-1]);
962  }
963  } else {
964  name = cached_servers_name[index];
965  ip = cached_servers_ip[index];
966  }
967  for (copy = MIN(index, CACHED_SERVERS_MAX-1); copy > 0; copy--) {
969  cached_servers_ip[copy] = cached_servers_ip[copy-1];
970  }
972  cached_servers_ip[0] = ip;
974  }
975  }
976 
977  return 0;
978 }
979 
980 #ifdef MS_STANDALONE
981 /* This is here just to verify that the code seems to be working
982  * properly
983  * To use this code, compile as:
984  * gcc -o metaserver -I. -DMS_STANDALONE metaserver.c
985  */
986 
987 int main(int argc, char *argv[])
988 {
989  int i;
990 
992  for (i = 0; i < meta_numservers; i++) {
993  printf("%s:%d:%s:%d:%s:%s\n",
994  meta_servers[i].ip_addr,
995  meta_servers[i].idle_time,
996  meta_servers[i].hostname,
997  meta_servers[i].num_players,
998  meta_servers[i].version,
999  meta_servers[i].text_comment);
1000  }
1001 }
1002 
1003 #endif
1004 
1005 #ifdef MS2_STANDALONE
1006 /* This is here just to verify that the code seems to be working
1007  * properly
1008  * To use this code, compile as:
1009  * gcc -o metaserver -I. -DMS2_STANDALONE metaserver.c misc.o -lcurl -lpthread
1010  */
1011 
1012 /* Following lines are to cover external symbols not
1013  * defined - trying to bring in the files the are defined
1014  * in just causes more dependencies, etc.
1015  */
1016 void draw_info(const char *str, int color) { }
1017 int init_connection(char *host, int port) {}
1018 
1019 int metaserver_on=1, meta_port=0;
1020 char *server=NULL, *meta_server;
1023 
1024 int main(int argc, char *argv[])
1025 {
1026  int i;
1027 
1028  init_metaserver();
1030  fprintf(stderr,"Collecting data.");
1031  while (metaserver2_check_status()) {
1032  fprintf(stderr,".");
1033  sleep(1);
1034  }
1035  fprintf(stderr,"\n");
1036  for (i = 0; i < meta_numservers; i++) {
1037  printf("%s:%d:%s:%d:%s:%s\n",
1038  meta_servers[i].ip_addr,
1039  meta_servers[i].idle_time,
1040  meta_servers[i].hostname,
1041  meta_servers[i].num_players,
1042  meta_servers[i].version,
1043  meta_servers[i].text_comment);
1044  }
1045 }
1046 
1047 #endif
#define MS_LARGE_BUF
Definition: metaserver.h:35
#define VERSION_CS
Definition: client.h:46
static void metaserver_load_cache(void)
Definition: metaserver.c:110
signed short sint16
Definition: client-types.h:80
char * cached_servers_ip[CACHED_SERVERS_MAX]
Definition: metaserver.c:105
int sc_version
Definition: metaserver.h:64
int main()
Definition: first.c:1
static char * metaservers[]
Definition: metaserver.c:188
void init_metaserver(void)
Definition: metaserver.c:459
void metaserver_show(int show_selection)
Definition: metaserver.c:807
int metaserver_select(char *sel)
Definition: metaserver.c:861
ClientSocket csocket
Definition: client.c:78
int port
Definition: metaserver.h:49
int cs_version
Definition: metaserver.h:65
int meta_port
Definition: client.c:67
int uptime
Definition: metaserver.h:62
#define META_SERVER
Definition: cconfig.h:73
char hostname[MS_LARGE_BUF]
Definition: metaserver.h:48
#define CACHED_SERVERS_MAX
Definition: metaserver.h:82
char text_comment[MS_LARGE_BUF]
Definition: metaserver.h:51
char * server
Definition: client.c:56
void * metaserver1_thread(void *junk)
Definition: metaserver.c:548
int metaserver2_on
Definition: client.c:69
int init_connection(char *host, int port)
Definition: client.c:205
Meta_Info * meta_servers
Definition: metaserver.c:55
void LOG(LogLevel level, const char *origin, const char *format,...)
Definition: misc.c:178
void * metaserver2_thread(void *junk)
Definition: metaserver.c:397
#define TRUE
Definition: client-types.h:71
size_t metaserver2_writer(void *ptr, size_t size, size_t nmemb, void *data)
Definition: metaserver.c:214
#define CONFIG_NUMS
Definition: client.h:183
#define CONFIG_PORT
Definition: client.h:174
sint16 use_config[CONFIG_NUMS]
Definition: init.c:50
int metaserver1_get_info(void)
Definition: metaserver.c:729
static int meta_sort(Meta_Info *m1, Meta_Info *m2)
Definition: metaserver.h:76
pthread_mutex_t ms2_info_mutex
Definition: metaserver.c:171
int check_server_version(int entry)
Definition: metaserver.c:69
char * name
Definition: image.c:61
int fd
Definition: client.h:97
static void metaserver_save_cache(void)
Definition: metaserver.c:144
static int ms1_is_running
Definition: metaserver.c:478
char ip_addr[MS_SMALL_BUF]
Definition: metaserver.h:47
#define META_PORT
Definition: cconfig.h:74
static int cached_servers_loaded
Definition: metaserver.c:106
int num_players
Definition: metaserver.h:57
#define MAX_BUF
Definition: client-types.h:128
int cached_servers_num
Definition: metaserver.c:103
char * cached_servers_name[CACHED_SERVERS_MAX]
Definition: metaserver.c:104
uint32 out_bytes
Definition: metaserver.h:59
uint32 in_bytes
Definition: metaserver.h:58
int metaserver2_get_info(void)
Definition: metaserver.c:423
static int get_metaserver2_data(char *metaserver2)
Definition: metaserver.c:369
int metaserver_on
Definition: client.c:69
#define VERSION_SC
Definition: client.h:47
const char *const rcsid_common_metaserver_c
Definition: metaserver.c:1
#define MAX_METASERVER
Definition: metaserver.h:31
void draw_info(const char *str, int color)
Definition: gx11.c:1773
int metaserver_get_info(char *metaserver, int meta_port)
Definition: metaserver.c:791
int idle_time
Definition: metaserver.h:60
char version[MS_SMALL_BUF]
Definition: metaserver.h:63
char * meta_server
Definition: client.c:61
#define NDI_BLACK
Definition: newclient.h:201
int meta_numservers
Definition: metaserver.c:57
static int ms2_is_running
Definition: metaserver.c:178
#define MIN(X__, Y__)
Definition: client.h:520
const char * cached_server_file
Definition: metaserver.c:107
int metaserver_check_status(void)
Definition: metaserver.c:774