Do not return NULL as boolean from wonder_is_lost() nor wonder_is_built()
[freeciv.git] / client / clinet.c
blobe586dff392ca6cfee59dbc54995bf3cbea202003
1 /***********************************************************************
2 Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold
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 <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
25 #ifdef FREECIV_HAVE_SYS_TYPES_H
26 #include <sys/types.h>
27 #endif
28 #ifdef HAVE_SYS_SOCKET_H
29 #include <sys/socket.h>
30 #endif
31 #ifdef HAVE_NETINET_IN_H
32 #include <netinet/in.h>
33 #endif
34 #ifdef HAVE_ARPA_INET_H
35 #include <arpa/inet.h>
36 #endif
37 #ifdef HAVE_NETDB_H
38 #include <netdb.h>
39 #endif
40 #ifdef HAVE_PWD_H
41 #include <pwd.h>
42 #endif
43 #ifdef HAVE_SYS_SELECT_H
44 #include <sys/select.h>
45 #endif
46 #ifdef HAVE_SYS_TIME_H
47 #include <sys/time.h>
48 #endif
49 #ifdef HAVE_SYS_UIO_H
50 #include <sys/uio.h>
51 #endif
52 #ifdef HAVE_SYS_UTSNAME_H
53 #include <sys/utsname.h>
54 #endif
55 #ifdef HAVE_UNISTD_H
56 #include <unistd.h>
57 #endif
59 /* utility */
60 #include "capstr.h"
61 #include "dataio.h"
62 #include "fcintl.h"
63 #include "log.h"
64 #include "mem.h"
65 #include "netintf.h"
66 #include "registry.h"
67 #include "support.h"
69 /* common */
70 #include "game.h"
71 #include "packets.h"
72 #include "version.h"
74 /* client */
75 #include "agents.h"
76 #include "attribute.h"
77 #include "chatline_g.h"
78 #include "client_main.h"
79 #include "climisc.h"
80 #include "connectdlg_common.h"
81 #include "connectdlg_g.h"
82 #include "dialogs_g.h" /* popdown_races_dialog() */
83 #include "gui_main_g.h" /* add_net_input(), remove_net_input() */
84 #include "mapview_common.h" /* unqueue_mapview_update */
85 #include "menu_g.h"
86 #include "messagewin_g.h"
87 #include "options.h"
88 #include "packhand.h"
89 #include "pages_g.h"
90 #include "plrdlg_g.h"
91 #include "repodlgs_g.h"
93 #include "clinet.h"
95 /* In autoconnect mode, try to connect to once a second */
96 #define AUTOCONNECT_INTERVAL 500
98 /* In autoconnect mode, try to connect 100 times */
99 #define MAX_AUTOCONNECT_ATTEMPTS 100
101 static struct fc_sockaddr_list *list = NULL;
102 static int name_count;
104 /*************************************************************************
105 Close socket and cleanup. This one doesn't print a message, so should
106 do so before-hand if necessary.
107 **************************************************************************/
108 static void close_socket_nomessage(struct connection *pc)
110 connection_common_close(pc);
111 remove_net_input();
112 popdown_races_dialog();
113 close_connection_dialog();
115 set_client_state(C_S_DISCONNECTED);
118 /****************************************************************************
119 Client connection close socket callback. It shouldn't be called directy.
120 Use connection_close() instead.
121 ****************************************************************************/
122 static void client_conn_close_callback(struct connection *pconn)
124 char reason[256];
126 if (NULL != pconn->closing_reason) {
127 fc_strlcpy(reason, pconn->closing_reason, sizeof(reason));
128 } else {
129 fc_strlcpy(reason, _("unknown reason"), sizeof(reason));
132 close_socket_nomessage(pconn);
133 /* If we lost connection to the internal server - kill it. */
134 client_kill_server(TRUE);
135 log_error("Lost connection to server: %s.", reason);
136 output_window_printf(ftc_client, _("Lost connection to server (%s)!"),
137 reason);
140 /**************************************************************************
141 Get ready to [try to] connect to a server:
142 - translate HOSTNAME and PORT (with defaults of "localhost" and
143 DEFAULT_SOCK_PORT respectively) to a raw IP address and port number, and
144 store them in the `names' variable
145 - return 0 on success
146 or put an error message in ERRBUF and return -1 on failure
147 **************************************************************************/
148 static int get_server_address(const char *hostname, int port,
149 char *errbuf, int errbufsize)
151 if (port == 0) {
152 port = DEFAULT_SOCK_PORT;
155 /* use name to find TCP/IP address of server */
156 if (!hostname) {
157 hostname = "localhost";
160 if (list != NULL) {
161 fc_sockaddr_list_destroy(list);
164 /* Any supported family will do */
165 list = net_lookup_service(hostname, port, FC_ADDR_ANY);
167 name_count = fc_sockaddr_list_size(list);
169 if (name_count <= 0) {
170 (void) fc_strlcpy(errbuf, _("Failed looking up host."), errbufsize);
171 return -1;
174 return 0;
177 /**************************************************************************
178 Try to connect to a server (get_server_address() must be called first!):
179 - try to create a TCP socket and connect it to `names'
180 - if successful:
181 - start monitoring the socket for packets from the server
182 - send a "login request" packet to the server
183 and - return 0
184 - if unable to create the connection, close the socket, put an error
185 message in ERRBUF and return the Unix error code (ie., errno, which
186 will be non-zero).
187 **************************************************************************/
188 static int try_to_connect(const char *username, char *errbuf, int errbufsize)
190 int sock = -1;
191 fc_errno err = 0;
193 connections_set_close_callback(client_conn_close_callback);
195 /* connection in progress? wait. */
196 if (client.conn.used) {
197 (void) fc_strlcpy(errbuf, _("Connection in progress."), errbufsize);
198 return -1;
201 /* Try all (IPv4, IPv6, ...) addresses until we have a connection. */
202 sock = -1;
203 fc_sockaddr_list_iterate(list, paddr) {
204 if ((sock = socket(paddr->saddr.sa_family, SOCK_STREAM, 0)) == -1) {
205 if (err == 0) {
206 err = fc_get_errno();
208 /* Probably EAFNOSUPPORT or EPROTONOSUPPORT. */
209 continue;
212 if (fc_connect(sock, &paddr->saddr,
213 sockaddr_size(paddr)) == -1) {
214 err = fc_get_errno(); /* Save errno value before calling anything */
215 fc_closesocket(sock);
216 sock = -1;
217 continue;
218 } else {
219 /* We have a connection! */
220 break;
222 } fc_sockaddr_list_iterate_end;
224 client.conn.sock = sock;
225 if (client.conn.sock == -1) {
226 (void) fc_strlcpy(errbuf, fc_strerror(err), errbufsize);
227 #ifdef FREECIV_HAVE_WINSOCK
228 return -1;
229 #else
230 return err;
231 #endif /* FREECIV_HAVE_WINSOCK */
234 make_connection(client.conn.sock, username);
236 return 0;
239 /**************************************************************************
240 Connect to a freeciv-server instance -- or at least try to. On success,
241 return 0; on failure, put an error message in ERRBUF and return -1.
242 **************************************************************************/
243 int connect_to_server(const char *username, const char *hostname, int port,
244 char *errbuf, int errbufsize)
246 if (errbufsize > 0 && errbuf != NULL) {
247 errbuf[0] = '\0';
250 if (0 != get_server_address(hostname, port, errbuf, errbufsize)) {
251 return -1;
254 if (0 != try_to_connect(username, errbuf, errbufsize)) {
255 return -1;
258 if (gui_options.use_prev_server) {
259 sz_strlcpy(gui_options.default_server_host, hostname);
260 gui_options.default_server_port = port;
263 return 0;
266 /**************************************************************************
267 Called after a connection is completed (e.g., in try_to_connect).
268 **************************************************************************/
269 void make_connection(int sock, const char *username)
271 struct packet_server_join_req req;
273 connection_common_init(&client.conn);
274 client.conn.sock = sock;
275 client.conn.client.last_request_id_used = 0;
276 client.conn.client.last_processed_request_id_seen = 0;
277 client.conn.client.request_id_of_currently_handled_packet = 0;
278 client.conn.incoming_packet_notify = notify_about_incoming_packet;
279 client.conn.outgoing_packet_notify = notify_about_outgoing_packet;
281 /* call gui-dependent stuff in gui_main.c */
282 add_net_input(client.conn.sock);
284 /* now send join_request package */
286 req.major_version = MAJOR_VERSION;
287 req.minor_version = MINOR_VERSION;
288 req.patch_version = PATCH_VERSION;
289 sz_strlcpy(req.version_label, VERSION_LABEL);
290 sz_strlcpy(req.capability, our_capability);
291 sz_strlcpy(req.username, username);
293 send_packet_server_join_req(&client.conn, &req);
296 /**************************************************************************
297 Get rid of server connection. This also kills internal server if it's
298 used.
299 **************************************************************************/
300 void disconnect_from_server(void)
302 const bool force = !client.conn.used;
304 attribute_flush();
306 stop_turn_change_wait();
308 /* If it's internal server - kill him
309 * We assume that we are always connected to the internal server */
310 if (!force) {
311 client_kill_server(FALSE);
313 close_socket_nomessage(&client.conn);
314 if (force) {
315 client_kill_server(TRUE);
317 output_window_append(ftc_client, _("Disconnected from server."));
319 if (gui_options.save_options_on_exit) {
320 options_save(NULL);
324 /****************************************************************************
325 A wrapper around read_socket_data() which also handles the case the
326 socket becomes writeable and there is still data which should be sent
327 to the server.
329 Returns:
330 -1 : an error occurred - you should close the socket
331 -2 : the connection was closed
332 >0 : number of bytes read
333 =0 : no data read, would block
334 ****************************************************************************/
335 static int read_from_connection(struct connection *pc, bool block)
337 for (;;) {
338 fd_set readfs, writefs, exceptfs;
339 int socket_fd = pc->sock;
340 bool have_data_for_server = (pc->used && pc->send_buffer
341 && 0 < pc->send_buffer->ndata);
342 int n;
343 fc_timeval tv;
345 tv.tv_sec = 0;
346 tv.tv_usec = 0;
348 FC_FD_ZERO(&readfs);
349 FD_SET(socket_fd, &readfs);
351 FC_FD_ZERO(&exceptfs);
352 FD_SET(socket_fd, &exceptfs);
354 if (have_data_for_server) {
355 FC_FD_ZERO(&writefs);
356 FD_SET(socket_fd, &writefs);
357 n = fc_select(socket_fd + 1, &readfs, &writefs, &exceptfs,
358 block ? NULL : &tv);
359 } else {
360 n = fc_select(socket_fd + 1, &readfs, NULL, &exceptfs,
361 block ? NULL : &tv);
364 /* the socket is neither readable, writeable nor got an
365 * exception */
366 if (n == 0) {
367 return 0;
370 if (n == -1) {
371 if (errno == EINTR) {
372 /* EINTR can happen sometimes, especially when compiling with -pg.
373 * Generally we just want to run select again. */
374 log_debug("select() returned EINTR");
375 continue;
378 log_error("select() return=%d errno=%d (%s)",
379 n, errno, fc_strerror(fc_get_errno()));
380 return -1;
383 if (FD_ISSET(socket_fd, &exceptfs)) {
384 return -1;
387 if (have_data_for_server && FD_ISSET(socket_fd, &writefs)) {
388 flush_connection_send_buffer_all(pc);
391 if (FD_ISSET(socket_fd, &readfs)) {
392 return read_socket_data(socket_fd, pc->buffer);
397 /**************************************************************************
398 This function is called when the client received a new input from the
399 server.
400 **************************************************************************/
401 void input_from_server(int fd)
403 int nb;
405 fc_assert_ret(fd == client.conn.sock);
407 nb = read_from_connection(&client.conn, FALSE);
408 if (0 <= nb) {
409 agents_freeze_hint();
410 while (client.conn.used) {
411 enum packet_type type;
412 void *packet = get_packet_from_connection(&client.conn, &type);
414 if (NULL != packet) {
415 client_packet_input(packet, type);
416 free(packet);
417 } else {
418 break;
421 if (client.conn.used) {
422 agents_thaw_hint();
424 } else if (-2 == nb) {
425 connection_close(&client.conn, _("server disconnected"));
426 } else {
427 connection_close(&client.conn, _("read error"));
431 /**************************************************************************
432 This function will sniff at the given fd, get the packet and call
433 client_packet_input. It will return if there is a network error or if
434 the PACKET_PROCESSING_FINISHED packet for the given request is
435 received.
436 **************************************************************************/
437 void input_from_server_till_request_got_processed(int fd,
438 int expected_request_id)
440 fc_assert_ret(expected_request_id);
441 fc_assert_ret(fd == client.conn.sock);
443 log_debug("input_from_server_till_request_got_processed("
444 "expected_request_id=%d)", expected_request_id);
446 while (TRUE) {
447 int nb = read_from_connection(&client.conn, TRUE);
449 if (0 <= nb) {
450 enum packet_type type;
452 while (TRUE) {
453 void *packet = get_packet_from_connection(&client.conn, &type);
454 if (NULL == packet) {
455 break;
458 client_packet_input(packet, type);
459 free(packet);
461 if (type == PACKET_PROCESSING_FINISHED) {
462 log_debug("ifstrgp: expect=%d, seen=%d",
463 expected_request_id,
464 client.conn.client.last_processed_request_id_seen);
465 if (client.conn.client.last_processed_request_id_seen >=
466 expected_request_id) {
467 log_debug("ifstrgp: got it; returning");
468 return;
472 } else if (-2 == nb) {
473 connection_close(&client.conn, _("server disconnected"));
474 break;
475 } else {
476 connection_close(&client.conn, _("read error"));
477 break;
482 static bool autoconnecting = FALSE;
483 /**************************************************************************
484 Make an attempt to autoconnect to the server.
485 It returns number of seconds it should be called again.
486 **************************************************************************/
487 double try_to_autoconnect(void)
489 char errbuf[512];
490 static int count = 0;
491 #ifndef WIN32_NATIVE
492 static int warning_shown = 0;
493 #endif
495 if (!autoconnecting) {
496 return FC_INFINITY;
499 count++;
501 if (count >= MAX_AUTOCONNECT_ATTEMPTS) {
502 log_fatal(_("Failed to contact server \"%s\" at port "
503 "%d as \"%s\" after %d attempts"),
504 server_host, server_port, user_name, count);
505 exit(EXIT_FAILURE);
508 switch (try_to_connect(user_name, errbuf, sizeof(errbuf))) {
509 case 0: /* Success! */
510 /* Don't call me again */
511 autoconnecting = FALSE;
512 return FC_INFINITY;
513 #ifndef WIN32_NATIVE
514 /* See PR#4042 for more info on issues with try_to_connect() and errno. */
515 case ECONNREFUSED: /* Server not available (yet) */
516 if (!warning_shown) {
517 log_error("Connection to server refused. Please start the server.");
518 output_window_append(ftc_client, _("Connection to server refused. "
519 "Please start the server."));
520 warning_shown = 1;
522 /* Try again in 0.5 seconds */
523 return 0.001 * AUTOCONNECT_INTERVAL;
524 #endif /* WIN32_NATIVE */
525 default: /* All other errors are fatal */
526 log_fatal(_("Error contacting server \"%s\" at port %d "
527 "as \"%s\":\n %s\n"),
528 server_host, server_port, user_name, errbuf);
529 exit(EXIT_FAILURE);
533 /**************************************************************************
534 Start trying to autoconnect to freeciv-server. Calls
535 get_server_address(), then arranges for try_to_autoconnect(), which
536 calls try_to_connect(), to be called roughly every
537 AUTOCONNECT_INTERVAL milliseconds, until success, fatal error or
538 user intervention.
539 **************************************************************************/
540 void start_autoconnecting_to_server(void)
542 char buf[512];
544 output_window_printf(ftc_client,
545 _("Auto-connecting to server \"%s\" at port %d "
546 "as \"%s\" every %f second(s) for %d times"),
547 server_host, server_port, user_name,
548 0.001 * AUTOCONNECT_INTERVAL,
549 MAX_AUTOCONNECT_ATTEMPTS);
551 if (get_server_address(server_host, server_port, buf, sizeof(buf)) < 0) {
552 log_fatal(_("Error contacting server \"%s\" at port %d "
553 "as \"%s\":\n %s\n"),
554 server_host, server_port, user_name, buf);
555 exit(EXIT_FAILURE);
557 autoconnecting = TRUE;