Replaced deprecated gtk_menu_popup() calls with modern constructs in gtk3.22-client
[freeciv.git] / client / servers.c
blob06b1639ac6382079b2938abf1dd8d8d58fe57ade
1 /***********************************************************************
2 Freeciv - Copyright (C) 1996-2005 - Freeciv Development Team
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; either version 2, or (at your option)
6 any later version.
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12 ***********************************************************************/
14 #ifdef HAVE_CONFIG_H
15 #include <fc_config.h>
16 #endif
18 #include "fc_prehdrs.h"
20 #include <errno.h>
21 #include <stdlib.h>
23 #ifdef FREECIV_HAVE_SYS_TYPES_H
24 #include <sys/types.h>
25 #endif
26 #ifdef HAVE_SYS_SOCKET_H
27 #include <sys/socket.h>
28 #endif
29 #ifdef HAVE_NETINET_IN_H
30 #include <netinet/in.h>
31 #endif
32 #ifdef HAVE_ARPA_INET_H
33 #include <arpa/inet.h>
34 #endif
35 #ifdef HAVE_NETDB_H
36 #include <netdb.h>
37 #endif
38 #ifdef HAVE_PWD_H
39 #include <pwd.h>
40 #endif
41 #ifdef HAVE_SYS_SELECT_H
42 #include <sys/select.h>
43 #endif
44 #ifdef HAVE_SYS_TIME_H
45 #include <sys/time.h>
46 #endif
47 #ifdef HAVE_SYS_UIO_H
48 #include <sys/uio.h>
49 #endif
50 #ifdef HAVE_UNISTD_H
51 #include <unistd.h>
52 #endif
54 /* dependencies */
55 #include "cvercmp.h"
57 /* utility */
58 #include "fcintl.h"
59 #include "fcthread.h"
60 #include "log.h"
61 #include "mem.h"
62 #include "netfile.h"
63 #include "netintf.h"
64 #include "rand.h" /* fc_rand() */
65 #include "registry.h"
66 #include "support.h"
68 /* common */
69 #include "capstr.h"
70 #include "dataio.h"
71 #include "game.h"
72 #include "packets.h"
73 #include "version.h"
75 /* client */
76 #include "chatline_common.h"
77 #include "chatline_g.h"
78 #include "client_main.h"
79 #include "servers.h"
81 #include "gui_main_g.h"
83 struct server_scan {
84 enum server_scan_type type;
85 ServerScanErrorFunc error_func;
87 struct srv_list srvrs;
88 int sock;
90 /* Only used for metaserver */
91 struct {
92 enum server_scan_status status;
94 fc_thread thr;
95 fc_mutex mutex;
97 const char *urlpath;
98 struct netfile_write_cb_data mem;
99 } meta;
102 extern enum announce_type announce;
104 static bool begin_metaserver_scan(struct server_scan *scan);
105 static void delete_server_list(struct server_list *server_list);
107 /**************************************************************************
108 The server sends a stream in a registry 'ini' type format.
109 Read it using secfile functions and fill the server_list structs.
110 **************************************************************************/
111 static struct server_list *parse_metaserver_data(fz_FILE *f)
113 struct server_list *server_list;
114 struct section_file *file;
115 int nservers, i, j;
116 const char *latest_ver;
117 const char *comment;
119 /* This call closes f. */
120 if (!(file = secfile_from_stream(f, TRUE))) {
121 return NULL;
124 latest_ver = secfile_lookup_str_default(file, NULL, "versions." FOLLOWTAG);
125 comment = secfile_lookup_str_default(file, NULL, "version_comments." FOLLOWTAG);
127 if (latest_ver != NULL) {
128 const char *my_comparable = fc_comparable_version();
129 char vertext[2048];
131 log_verbose("Metaserver says latest '" FOLLOWTAG "' version is '%s'; we have '%s'",
132 latest_ver, my_comparable);
133 if (cvercmp_greater(latest_ver, my_comparable)) {
134 const char *const followtag = "?vertag:" FOLLOWTAG;
135 fc_snprintf(vertext, sizeof(vertext),
136 /* TRANS: Type is version tag name like "stable", "S2_4",
137 * "win32" (which can also be localised -- msgids start
138 * '?vertag:') */
139 _("Latest %s release of Freeciv is %s, this is %s."),
140 Q_(followtag), latest_ver, my_comparable);
142 version_message(vertext);
143 } else if (comment == NULL) {
144 fc_snprintf(vertext, sizeof(vertext),
145 _("There is no newer %s release of Freeciv available."),
146 FOLLOWTAG);
148 version_message(vertext);
152 if (comment != NULL) {
153 log_verbose("Mesaserver comment about '" FOLLOWTAG "': %s", comment);
154 version_message(comment);
157 server_list = server_list_new();
158 nservers = secfile_lookup_int_default(file, 0, "main.nservers");
160 for (i = 0; i < nservers; i++) {
161 const char *host, *port, *version, *state, *message, *nplayers, *nhumans;
162 int n;
163 struct server *pserver = (struct server*)fc_malloc(sizeof(struct server));
165 host = secfile_lookup_str_default(file, "", "server%d.host", i);
166 pserver->host = fc_strdup(host);
168 port = secfile_lookup_str_default(file, "", "server%d.port", i);
169 pserver->port = atoi(port);
171 version = secfile_lookup_str_default(file, "", "server%d.version", i);
172 pserver->version = fc_strdup(version);
174 state = secfile_lookup_str_default(file, "", "server%d.state", i);
175 pserver->state = fc_strdup(state);
177 message = secfile_lookup_str_default(file, "", "server%d.message", i);
178 pserver->message = fc_strdup(message);
180 nplayers = secfile_lookup_str_default(file, "0", "server%d.nplayers", i);
181 n = atoi(nplayers);
182 pserver->nplayers = n;
184 nhumans = secfile_lookup_str_default(file, "-1", "server%d.humans", i);
185 n = atoi(nhumans);
186 pserver->humans = n;
188 if (pserver->nplayers > 0) {
189 pserver->players = fc_malloc(pserver->nplayers * sizeof(*pserver->players));
190 } else {
191 pserver->players = NULL;
194 for (j = 0; j < pserver->nplayers ; j++) {
195 const char *name, *nation, *type, *plrhost;
197 name = secfile_lookup_str_default(file, "",
198 "server%d.player%d.name", i, j);
199 pserver->players[j].name = fc_strdup(name);
201 type = secfile_lookup_str_default(file, "",
202 "server%d.player%d.type", i, j);
203 pserver->players[j].type = fc_strdup(type);
205 plrhost = secfile_lookup_str_default(file, "",
206 "server%d.player%d.host", i, j);
207 pserver->players[j].host = fc_strdup(plrhost);
209 nation = secfile_lookup_str_default(file, "",
210 "server%d.player%d.nation", i, j);
211 pserver->players[j].nation = fc_strdup(nation);
214 server_list_append(server_list, pserver);
217 secfile_destroy(file);
219 return server_list;
222 /****************************************************************************
223 Read the reply string from the metaserver.
224 ****************************************************************************/
225 static bool meta_read_response(struct server_scan *scan)
227 fz_FILE *f;
228 char str[4096];
229 struct server_list *srvrs;
231 f = fz_from_memory(scan->meta.mem.mem, scan->meta.mem.size, TRUE);
232 if (NULL == f) {
233 fc_snprintf(str, sizeof(str),
234 _("Failed to read the metaserver data from %s."),
235 metaserver);
236 scan->error_func(scan, str);
238 return FALSE;
241 /* parse message body */
242 fc_allocate_mutex(&scan->srvrs.mutex);
243 srvrs = parse_metaserver_data(f);
244 scan->srvrs.servers = srvrs;
245 fc_release_mutex(&scan->srvrs.mutex);
247 /* 'f' (hence 'meta.mem.mem') was closed in parse_metaserver_data(). */
248 scan->meta.mem.mem = NULL;
250 if (NULL == srvrs) {
251 fc_snprintf(str, sizeof(str),
252 _("Failed to parse the metaserver data from %s:\n"
253 "%s."),
254 metaserver, secfile_error());
255 scan->error_func(scan, str);
257 return FALSE;
260 return TRUE;
263 /****************************************************************************
264 Metaserver scan thread entry point
265 ****************************************************************************/
266 static void metaserver_scan(void *arg)
268 struct server_scan *scan = arg;
270 if (!begin_metaserver_scan(scan)) {
271 fc_allocate_mutex(&scan->meta.mutex);
272 scan->meta.status = SCAN_STATUS_ERROR;
273 } else {
274 if (!meta_read_response(scan)) {
275 fc_allocate_mutex(&scan->meta.mutex);
276 scan->meta.status = SCAN_STATUS_ERROR;
277 } else {
278 fc_allocate_mutex(&scan->meta.mutex);
279 if (scan->meta.status == SCAN_STATUS_WAITING) {
280 scan->meta.status = SCAN_STATUS_DONE;
285 fc_release_mutex(&scan->meta.mutex);
288 /****************************************************************************
289 Begin a metaserver scan for servers.
291 Returns FALSE on error (in which case errbuf will contain an error
292 message).
293 ****************************************************************************/
294 static bool begin_metaserver_scan(struct server_scan *scan)
296 struct netfile_post *post;
297 bool retval = TRUE;
299 post = netfile_start_post();
300 netfile_add_form_str(post, "client_cap", our_capability);
302 if (!netfile_send_post(metaserver, post, NULL, &scan->meta.mem, NULL)) {
303 scan->error_func(scan, _("Error connecting to metaserver"));
304 retval = FALSE;
307 netfile_close_post(post);
309 return retval;
312 /**************************************************************************
313 Frees everything associated with a server list including
314 the server list itself (so the server_list is no longer
315 valid after calling this function)
316 **************************************************************************/
317 static void delete_server_list(struct server_list *server_list)
319 if (!server_list) {
320 return;
323 server_list_iterate(server_list, ptmp) {
324 int i;
325 int n = ptmp->nplayers;
327 free(ptmp->host);
328 free(ptmp->version);
329 free(ptmp->state);
330 free(ptmp->message);
332 if (ptmp->players) {
333 for (i = 0; i < n; i++) {
334 free(ptmp->players[i].name);
335 free(ptmp->players[i].type);
336 free(ptmp->players[i].host);
337 free(ptmp->players[i].nation);
339 free(ptmp->players);
342 free(ptmp);
343 } server_list_iterate_end;
345 server_list_destroy(server_list);
348 /**************************************************************************
349 Broadcast an UDP package to all servers on LAN, requesting information
350 about the server. The packet is send to all Freeciv servers in the same
351 multicast group as the client.
352 **************************************************************************/
353 static bool begin_lanserver_scan(struct server_scan *scan)
355 union fc_sockaddr addr;
356 struct data_out dout;
357 int send_sock, opt = 1;
358 #ifndef FREECIV_HAVE_WINSOCK
359 unsigned char buffer[MAX_LEN_PACKET];
360 #else /* FREECIV_HAVE_WINSOCK */
361 char buffer[MAX_LEN_PACKET];
362 #endif /* FREECIV_HAVE_WINSOCK */
363 #ifdef HAVE_IP_MREQN
364 struct ip_mreqn mreq4;
365 #else
366 struct ip_mreq mreq4;
367 #endif
368 const char *group;
369 size_t size;
370 int family;
372 #ifdef FREECIV_IPV6_SUPPORT
373 struct ipv6_mreq mreq6;
374 #endif
376 #ifndef FREECIV_HAVE_WINSOCK
377 unsigned char ttl;
378 #endif
380 if (announce == ANNOUNCE_NONE) {
381 /* Succeeded in doing nothing */
382 return TRUE;
385 #ifdef FREECIV_IPV6_SUPPORT
386 if (announce == ANNOUNCE_IPV6) {
387 family = AF_INET6;
388 } else
389 #endif /* IPv6 support */
391 family = AF_INET;
394 /* Set the UDP Multicast group IP address. */
395 group = get_multicast_group(announce == ANNOUNCE_IPV6);
397 /* Create a socket for listening for server packets. */
398 if ((scan->sock = socket(family, SOCK_DGRAM, 0)) < 0) {
399 char errstr[2048];
401 fc_snprintf(errstr, sizeof(errstr),
402 _("Opening socket to listen LAN announcements failed:\n%s"),
403 fc_strerror(fc_get_errno()));
404 scan->error_func(scan, errstr);
406 return FALSE;
409 fc_nonblock(scan->sock);
411 if (setsockopt(scan->sock, SOL_SOCKET, SO_REUSEADDR,
412 (char *)&opt, sizeof(opt)) == -1) {
413 log_error("SO_REUSEADDR failed: %s", fc_strerror(fc_get_errno()));
416 memset(&addr, 0, sizeof(addr));
418 #ifdef FREECIV_IPV6_SUPPORT
419 if (family == AF_INET6) {
420 addr.saddr.sa_family = AF_INET6;
421 addr.saddr_in6.sin6_port = htons(SERVER_LAN_PORT + 1);
422 addr.saddr_in6.sin6_addr = in6addr_any;
423 } else
424 #endif /* IPv6 support */
425 if (family == AF_INET) {
426 addr.saddr.sa_family = AF_INET;
427 addr.saddr_in4.sin_port = htons(SERVER_LAN_PORT + 1);
428 addr.saddr_in4.sin_addr.s_addr = htonl(INADDR_ANY);
429 } else {
430 /* This is not only error situation worth assert() This
431 * is error situation that has check (with assert) against
432 * earlier already. */
433 fc_assert(FALSE);
435 return FALSE;
438 if (bind(scan->sock, &addr.saddr, sockaddr_size(&addr)) < 0) {
439 char errstr[2048];
441 fc_snprintf(errstr, sizeof(errstr),
442 _("Binding socket to listen LAN announcements failed:\n%s"),
443 fc_strerror(fc_get_errno()));
444 scan->error_func(scan, errstr);
446 return FALSE;
449 #ifndef FREECIV_IPV6_SUPPORT
451 #ifdef HAVE_INET_ATON
452 inet_aton(group, &mreq4.imr_multiaddr);
453 #else /* HAVE_INET_ATON */
454 mreq4.imr_multiaddr.s_addr = inet_addr(group);
455 #endif /* HAVE_INET_ATON */
456 #else /* IPv6 support */
457 if (family == AF_INET6) {
458 inet_pton(AF_INET6, group, &mreq6.ipv6mr_multiaddr.s6_addr);
459 mreq6.ipv6mr_interface = 0; /* TODO: Interface selection */
461 if (setsockopt(scan->sock, IPPROTO_IPV6, FC_IPV6_ADD_MEMBERSHIP,
462 (const char*)&mreq6, sizeof(mreq6)) < 0) {
463 char errstr[2048];
465 fc_snprintf(errstr, sizeof(errstr),
466 _("Adding membership for IPv6 LAN announcement group failed:\n%s"),
467 fc_strerror(fc_get_errno()));
468 scan->error_func(scan, errstr);
470 } else {
471 inet_pton(AF_INET, group, &mreq4.imr_multiaddr.s_addr);
472 #endif /* IPv6 support */
473 #ifdef HAVE_IP_MREQN
474 mreq4.imr_address.s_addr = htonl(INADDR_ANY);
475 mreq4.imr_ifindex = 0;
476 #else
477 mreq4.imr_interface.s_addr = htonl(INADDR_ANY);
478 #endif
480 if (setsockopt(scan->sock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
481 (const char*)&mreq4, sizeof(mreq4)) < 0) {
482 char errstr[2048];
484 fc_snprintf(errstr, sizeof(errstr),
485 _("Adding membership for IPv4 LAN announcement group failed:\n%s"),
486 fc_strerror(fc_get_errno()));
487 scan->error_func(scan, errstr);
489 return FALSE;
493 /* Create a socket for broadcasting to servers. */
494 if ((send_sock = socket(family, SOCK_DGRAM, 0)) < 0) {
495 log_error("socket failed: %s", fc_strerror(fc_get_errno()));
496 return FALSE;
499 memset(&addr, 0, sizeof(addr));
501 #ifndef FREECIV_IPV6_SUPPORT
502 if (family == AF_INET) {
503 #ifdef HAVE_INET_ATON
504 inet_aton(group, &addr.saddr_in4.sin_addr);
505 #else /* HAVE_INET_ATON */
506 addr.saddr_in4.sin_addr.s_addr = inet_addr(group);
507 #endif /* HAVE_INET_ATON */
508 #else /* IPv6 support */
509 if (family == AF_INET6) {
510 addr.saddr.sa_family = AF_INET6;
511 inet_pton(AF_INET6, group, &addr.saddr_in6.sin6_addr);
512 addr.saddr_in6.sin6_port = htons(SERVER_LAN_PORT);
513 } else if (family == AF_INET) {
514 inet_pton(AF_INET, group, &addr.saddr_in4.sin_addr);
515 #endif /* IPv6 support */
516 addr.saddr.sa_family = AF_INET;
517 addr.saddr_in4.sin_port = htons(SERVER_LAN_PORT);
518 } else {
519 fc_assert(FALSE);
521 log_error("Unsupported address family in begin_lanserver_scan()");
523 return FALSE;
526 /* this setsockopt call fails on Windows 98, so we stick with the default
527 * value of 1 on Windows, which should be fine in most cases */
528 #ifndef FREECIV_HAVE_WINSOCK
529 /* Set the Time-to-Live field for the packet */
530 ttl = SERVER_LAN_TTL;
531 if (setsockopt(send_sock, IPPROTO_IP, IP_MULTICAST_TTL, (const char*)&ttl,
532 sizeof(ttl))) {
533 log_error("setsockopt failed: %s", fc_strerror(fc_get_errno()));
534 return FALSE;
536 #endif /* FREECIV_HAVE_WINSOCK */
538 if (setsockopt(send_sock, SOL_SOCKET, SO_BROADCAST, (const char*)&opt,
539 sizeof(opt))) {
540 log_error("setsockopt failed: %s", fc_strerror(fc_get_errno()));
541 return FALSE;
544 dio_output_init(&dout, buffer, sizeof(buffer));
545 dio_put_uint8(&dout, SERVER_LAN_VERSION);
546 size = dio_output_used(&dout);
549 if (sendto(send_sock, buffer, size, 0, &addr.saddr,
550 sockaddr_size(&addr)) < 0) {
551 /* This can happen when there's no network connection - it should
552 * give an in-game message. */
553 log_error("lanserver scan sendto failed: %s",
554 fc_strerror(fc_get_errno()));
555 return FALSE;
556 } else {
557 log_debug("Sending request for server announcement on LAN.");
560 fc_closesocket(send_sock);
562 fc_allocate_mutex(&scan->srvrs.mutex);
563 scan->srvrs.servers = server_list_new();
564 fc_release_mutex(&scan->srvrs.mutex);
566 return TRUE;
569 /**************************************************************************
570 Listens for UDP packets broadcasted from a server that responded
571 to the request-packet sent from the client.
572 **************************************************************************/
573 static enum server_scan_status
574 get_lan_server_list(struct server_scan *scan)
576 socklen_t fromlen;
577 union fc_sockaddr fromend;
578 char msgbuf[128];
579 int type;
580 struct data_in din;
581 char servername[512];
582 char portstr[256];
583 int port;
584 char version[256];
585 char status[256];
586 char players[256];
587 char humans[256];
588 char message[1024];
589 bool found_new = FALSE;
591 while (TRUE) {
592 struct server *pserver;
593 bool duplicate = FALSE;
595 dio_input_init(&din, msgbuf, sizeof(msgbuf));
596 fromlen = sizeof(fromend);
598 /* Try to receive a packet from a server. No select loop is needed;
599 * we just keep on reading until recvfrom returns -1. */
600 if (recvfrom(scan->sock, msgbuf, sizeof(msgbuf), 0,
601 &fromend.saddr, &fromlen) < 0) {
602 break;
605 dio_get_uint8(&din, &type);
606 if (type != SERVER_LAN_VERSION) {
607 continue;
609 dio_get_string(&din, servername, sizeof(servername));
610 dio_get_string(&din, portstr, sizeof(portstr));
611 port = atoi(portstr);
612 dio_get_string(&din, version, sizeof(version));
613 dio_get_string(&din, status, sizeof(status));
614 dio_get_string(&din, players, sizeof(players));
615 dio_get_string(&din, humans, sizeof(humans));
616 dio_get_string(&din, message, sizeof(message));
618 if (!fc_strcasecmp("none", servername)) {
619 bool nameinfo = FALSE;
620 #ifdef FREECIV_IPV6_SUPPORT
621 char dst[INET6_ADDRSTRLEN];
622 char host[NI_MAXHOST], service[NI_MAXSERV];
624 if (!getnameinfo(&fromend.saddr, fromlen, host, NI_MAXHOST,
625 service, NI_MAXSERV, NI_NUMERICSERV)) {
626 nameinfo = TRUE;
628 if (!nameinfo) {
629 if (fromend.saddr.sa_family == AF_INET6) {
630 inet_ntop(AF_INET6, &fromend.saddr_in6.sin6_addr,
631 dst, sizeof(dst));
632 } else if (fromend.saddr.sa_family == AF_INET) {
633 inet_ntop(AF_INET, &fromend.saddr_in4.sin_addr, dst, sizeof(dst));;
634 } else {
635 fc_assert(FALSE);
637 log_error("Unsupported address family in get_lan_server_list()");
639 fc_snprintf(dst, sizeof(dst), "Unknown");
642 #else /* IPv6 support */
643 const char *dst = NULL;
644 struct hostent *from;
645 const char *host = NULL;
647 from = gethostbyaddr((char *) &fromend.saddr_in4.sin_addr,
648 sizeof(fromend.saddr_in4.sin_addr), AF_INET);
649 if (from) {
650 host = from->h_name;
651 nameinfo = TRUE;
653 if (!nameinfo) {
654 dst = inet_ntoa(fromend.saddr_in4.sin_addr);
656 #endif /* IPv6 support */
658 sz_strlcpy(servername, nameinfo ? host : dst);
661 /* UDP can send duplicate or delayed packets. */
662 fc_allocate_mutex(&scan->srvrs.mutex);
663 server_list_iterate(scan->srvrs.servers, aserver) {
664 if (0 == fc_strcasecmp(aserver->host, servername)
665 && aserver->port == port) {
666 duplicate = TRUE;
667 break;
669 } server_list_iterate_end;
671 if (duplicate) {
672 fc_release_mutex(&scan->srvrs.mutex);
673 continue;
676 log_debug("Received a valid announcement from a server on the LAN.");
678 pserver = fc_malloc(sizeof(*pserver));
679 pserver->host = fc_strdup(servername);
680 pserver->port = port;
681 pserver->version = fc_strdup(version);
682 pserver->state = fc_strdup(status);
683 pserver->nplayers = atoi(players);
684 pserver->humans = atoi(humans);
685 pserver->message = fc_strdup(message);
686 pserver->players = NULL;
687 found_new = TRUE;
689 server_list_prepend(scan->srvrs.servers, pserver);
690 fc_release_mutex(&scan->srvrs.mutex);
693 if (found_new) {
694 return SCAN_STATUS_PARTIAL;
696 return SCAN_STATUS_WAITING;
699 /****************************************************************************
700 Creates a new server scan and returns it, or NULL if impossible.
702 Depending on 'type' the scan will look for either local or internet
703 games.
705 error_func provides a callback to be used in case of error; this
706 callback probably should call server_scan_finish.
708 NB: You must call server_scan_finish() when you are done with the
709 scan to free the memory and resources allocated by it.
710 ****************************************************************************/
711 struct server_scan *server_scan_begin(enum server_scan_type type,
712 ServerScanErrorFunc error_func)
714 struct server_scan *scan;
715 bool ok = FALSE;
717 scan = fc_calloc(1, sizeof(*scan));
718 scan->type = type;
719 scan->error_func = error_func;
720 scan->sock = -1;
721 fc_init_mutex(&scan->srvrs.mutex);
723 switch (type) {
724 case SERVER_SCAN_GLOBAL:
726 int thr_ret;
728 fc_init_mutex(&scan->meta.mutex);
729 scan->meta.status = SCAN_STATUS_WAITING;
730 thr_ret = fc_thread_start(&scan->meta.thr, metaserver_scan, scan);
731 if (thr_ret) {
732 ok = FALSE;
733 } else {
734 ok = TRUE;
737 break;
738 case SERVER_SCAN_LOCAL:
739 ok = begin_lanserver_scan(scan);
740 break;
741 default:
742 break;
745 if (!ok) {
746 server_scan_finish(scan);
747 scan = NULL;
750 return scan;
753 /****************************************************************************
754 A simple query function to determine the type of a server scan (previously
755 allocated in server_scan_begin).
756 ****************************************************************************/
757 enum server_scan_type server_scan_get_type(const struct server_scan *scan)
759 if (!scan) {
760 return SERVER_SCAN_LAST;
762 return scan->type;
765 /****************************************************************************
766 A function to query servers of the server scan. This will check any
767 pending network data and update the server list.
769 The return value indicates the status of the server scan:
770 SCAN_STATUS_ERROR - The scan failed and should be aborted.
771 SCAN_STATUS_WAITING - The scan is in progress (continue polling).
772 SCAN_STATUS_PARTIAL - The scan received some data, with more expected.
773 Get the servers with server_scan_get_list(), and
774 continue polling.
775 SCAN_STATUS_DONE - The scan received all data it expected to receive.
776 Get the servers with server_scan_get_list(), and
777 stop calling this function.
778 SCAN_STATUS_ABORT - The scan has been aborted
779 ****************************************************************************/
780 enum server_scan_status server_scan_poll(struct server_scan *scan)
782 if (!scan) {
783 return SCAN_STATUS_ERROR;
786 switch (scan->type) {
787 case SERVER_SCAN_GLOBAL:
789 enum server_scan_status status;
791 fc_allocate_mutex(&scan->meta.mutex);
792 status = scan->meta.status;
793 fc_release_mutex(&scan->meta.mutex);
795 return status;
797 break;
798 case SERVER_SCAN_LOCAL:
799 return get_lan_server_list(scan);
800 break;
801 default:
802 break;
805 return SCAN_STATUS_ERROR;
808 /**************************************************************************
809 Returns the srv_list currently held by the scan (may be NULL).
810 **************************************************************************/
811 struct srv_list *
812 server_scan_get_list(struct server_scan *scan)
814 if (!scan) {
815 return NULL;
818 return &scan->srvrs;
821 /**************************************************************************
822 Closes the socket listening on the scan, frees the list of servers, and
823 frees the memory allocated for 'scan' by server_scan_begin().
824 **************************************************************************/
825 void server_scan_finish(struct server_scan *scan)
827 if (!scan) {
828 return;
831 if (scan->type == SERVER_SCAN_GLOBAL) {
832 /* Signal metaserver scan thread to stop */
833 fc_allocate_mutex(&scan->meta.mutex);
834 scan->meta.status = SCAN_STATUS_ABORT;
835 fc_release_mutex(&scan->meta.mutex);
837 /* Wait thread to stop */
838 fc_thread_wait(&scan->meta.thr);
839 fc_destroy_mutex(&scan->meta.mutex);
841 /* This mainly duplicates code from below "else" block.
842 * That's intentional, since they will be completely different in future versions.
843 * We are better prepared for that by having them separately already. */
844 if (scan->sock >= 0) {
845 fc_closesocket(scan->sock);
846 scan->sock = -1;
849 if (scan->srvrs.servers) {
850 fc_allocate_mutex(&scan->srvrs.mutex);
851 delete_server_list(scan->srvrs.servers);
852 scan->srvrs.servers = NULL;
853 fc_release_mutex(&scan->srvrs.mutex);
856 if (scan->meta.mem.mem) {
857 FC_FREE(scan->meta.mem.mem);
858 scan->meta.mem.mem = NULL;
860 } else {
861 if (scan->sock >= 0) {
862 fc_closesocket(scan->sock);
863 scan->sock = -1;
866 if (scan->srvrs.servers) {
867 delete_server_list(scan->srvrs.servers);
868 scan->srvrs.servers = NULL;
872 fc_destroy_mutex(&scan->srvrs.mutex);
874 free(scan);