Stop sharing requirement_unit_state_ereq().
[freeciv.git] / client / connectdlg_common.c
blob5cb1be148b0cb3f5395b423aa0709bc6be069909
1 /***********************************************************************
2 Freeciv - Copyright (C) 2004 - The Freeciv Project
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 ***********************************************************************/
13 #ifdef HAVE_CONFIG_H
14 #include <fc_config.h>
15 #endif
17 #include "fc_prehdrs.h"
19 #include <fcntl.h>
20 #include <stdio.h>
21 #include <signal.h> /* SIGTERM and kill */
22 #include <string.h>
23 #include <time.h>
25 #ifdef FREECIV_MSWINDOWS
26 #include <windows.h>
27 #endif
29 #ifdef FREECIV_HAVE_SYS_TYPES_H
30 #include <sys/types.h> /* fchmod */
31 #endif
33 #ifdef HAVE_SYS_STAT_H
34 #include <sys/stat.h> /* fchmod */
35 #endif
37 #ifdef HAVE_SYS_WAIT_H
38 #include <sys/wait.h>
39 #endif
41 /* utility */
42 #include "astring.h"
43 #include "capability.h"
44 #include "deprecations.h"
45 #include "fciconv.h"
46 #include "fcintl.h"
47 #include "ioz.h"
48 #include "log.h"
49 #include "mem.h"
50 #include "netintf.h"
51 #include "rand.h"
52 #include "registry.h"
53 #include "shared.h"
54 #include "support.h"
56 /* client */
57 #include "client_main.h"
58 #include "climisc.h"
59 #include "clinet.h" /* connect_to_server() */
60 #include "packhand_gen.h"
62 #include "chatline_common.h"
63 #include "connectdlg_g.h"
64 #include "connectdlg_common.h"
65 #include "tilespec.h"
67 #define WAIT_BETWEEN_TRIES 100000 /* usecs */
68 #define NUMBER_OF_TRIES 500
70 #if defined(HAVE_WORKING_FORK) && !defined(FREECIV_MSWINDOWS)
71 /* We are yet to see FREECIV_MSWINDOWS setup where even HAVE_WORKING_FORK would
72 * mean fork() that actually works for us. */
73 #define HAVE_USABLE_FORK
74 #endif
76 #ifdef HAVE_USABLE_FORK
77 static pid_t server_pid = -1;
78 #elif FREECIV_MSWINDOWS
79 HANDLE server_process = INVALID_HANDLE_VALUE;
80 HANDLE loghandle = INVALID_HANDLE_VALUE;
81 #endif /* HAVE_USABLE_FORK || FREECIV_MSWINDOWS */
82 bool server_quitting = FALSE;
84 static char challenge_fullname[MAX_LEN_PATH];
85 static bool client_has_hack = FALSE;
87 int internal_server_port;
89 /**************************************************************************
90 The general chain of events:
92 Two distinct paths are taken depending on the choice of mode:
94 if the user selects the multi- player mode, then a packet_req_join_game
95 packet is sent to the server. It is either successful or not. The end.
97 If the user selects a single- player mode (either a new game or a save game)
98 then:
99 1. the packet_req_join_game is sent.
100 2. on receipt, if we can join, then a challenge packet is sent to the
101 server, so we can get hack level control.
102 3. if we can't get hack, then we get dumped to multi- player mode. If
103 we can, then:
104 a. for a new game, we send a series of packet_generic_message packets
105 with commands to start the game.
106 b. for a saved game, we send the load command with a
107 packet_generic_message, then we send a PACKET_PLAYER_LIST_REQUEST.
108 the response to this request will tell us if the game was loaded or
109 not. if not, then we send another load command. if so, then we send
110 a series of packet_generic_message packets with commands to start
111 the game.
112 **************************************************************************/
114 /**************************************************************************
115 Tests if the client has started the server.
116 **************************************************************************/
117 bool is_server_running(void)
119 if (server_quitting) {
120 return FALSE;
122 #ifdef HAVE_USABLE_FORK
123 return (server_pid > 0);
124 #elif FREECIV_MSWINDOWS
125 return (server_process != INVALID_HANDLE_VALUE);
126 #else
127 return FALSE; /* We've been unable to start one! */
128 #endif
131 /**************************************************************************
132 Returns TRUE if the client has hack access.
133 **************************************************************************/
134 bool can_client_access_hack(void)
136 return client_has_hack;
139 /****************************************************************************
140 Kills the server if the client has started it.
142 If the 'force' parameter is unset, we just do a /quit. If it's set, then
143 we'll send a signal to the server to kill it (use this when the socket
144 is disconnected already).
145 ****************************************************************************/
146 void client_kill_server(bool force)
148 #ifdef HAVE_USABLE_FORK
149 if (server_quitting && server_pid > 0) {
150 /* Already asked to /quit.
151 * If it didn't do that, kill it. */
152 if (waitpid(server_pid, NULL, WUNTRACED) <= 0) {
153 kill(server_pid, SIGTERM);
154 waitpid(server_pid, NULL, WUNTRACED);
156 server_pid = -1;
157 server_quitting = FALSE;
159 #elif FREECIV_MSWINDOWS
160 if (server_quitting && server_process != INVALID_HANDLE_VALUE) {
161 /* Already asked to /quit.
162 * If it didn't do that, kill it. */
163 TerminateProcess(server_process, 0);
164 CloseHandle(server_process);
165 if (loghandle != INVALID_HANDLE_VALUE) {
166 CloseHandle(loghandle);
168 server_process = INVALID_HANDLE_VALUE;
169 loghandle = INVALID_HANDLE_VALUE;
170 server_quitting = FALSE;
172 #endif /* FREECIV_MSWINDOWS || HAVE_USABLE_FORK */
174 if (is_server_running()) {
175 if (client.conn.used && client_has_hack) {
176 /* This does a "soft" shutdown of the server by sending a /quit.
178 * This is useful when closing the client or disconnecting because it
179 * doesn't kill the server prematurely. In particular, killing the
180 * server in the middle of a save can have disastrous results. This
181 * method tells the server to quit on its own. This is safer from a
182 * game perspective, but more dangerous because if the kill fails the
183 * server will be left running.
185 * Another potential problem is because this function is called atexit
186 * it could potentially be called when we're connected to an unowned
187 * server. In this case we don't want to kill it. */
188 send_chat("/quit");
189 server_quitting = TRUE;
190 } else if (force) {
191 /* Either we already disconnected, or we didn't get control of the
192 * server. In either case, the only thing to do is a "hard" kill of
193 * the server. */
194 #ifdef HAVE_USABLE_FORK
195 kill(server_pid, SIGTERM);
196 waitpid(server_pid, NULL, WUNTRACED);
197 server_pid = -1;
198 #elif FREECIV_MSWINDOWS
199 TerminateProcess(server_process, 0);
200 CloseHandle(server_process);
201 if (loghandle != INVALID_HANDLE_VALUE) {
202 CloseHandle(loghandle);
204 server_process = INVALID_HANDLE_VALUE;
205 loghandle = INVALID_HANDLE_VALUE;
206 #endif /* FREECIV_MSWINDOWS || HAVE_USABLE_FORK */
207 server_quitting = FALSE;
210 client_has_hack = FALSE;
213 /****************************************************************
214 Forks a server if it can. Returns FALSE if we find we
215 couldn't start the server.
216 *****************************************************************/
217 bool client_start_server(void)
219 #if !defined(HAVE_USABLE_FORK) && !defined(FREECIV_MSWINDOWS)
220 /* Can't do much without fork */
221 return FALSE;
222 #else /* HAVE_USABLE_FORK || FREECIV_MSWINDOWS */
223 char buf[512];
224 int connect_tries = 0;
225 char savesdir[MAX_LEN_PATH];
226 char scensdir[MAX_LEN_PATH];
227 char *storage;
228 char *ruleset;
230 #if !defined(HAVE_USABLE_FORK)
231 /* Above also implies that this is FREECIV_MSWINDOWS ->
232 * Win32 that can't use fork() */
233 STARTUPINFO si;
234 PROCESS_INFORMATION pi;
236 char options[1024];
237 char *depr;
238 char rsparam[256];
239 #ifdef FREECIV_DEBUG
240 char cmdline1[512];
241 #ifndef FREECIV_WEB
242 char cmdline2[512];
243 #endif /* FREECIV_WEB */
244 #endif /* FREECIV_DEBUG */
245 char cmdline3[512];
246 char cmdline4[512];
247 char logcmdline[512];
248 char scriptcmdline[512];
249 char savefilecmdline[512];
250 char savescmdline[512];
251 char scenscmdline[512];
252 #endif /* !HAVE_USABLE_FORK -> FREECIV_MSWINDOWS */
254 #ifdef FREECIV_IPV6_SUPPORT
255 enum fc_addr_family family = FC_ADDR_ANY;
256 #else
257 enum fc_addr_family family = FC_ADDR_IPV4;
258 #endif /* FREECIV_IPV6_SUPPORT */
260 /* only one server (forked from this client) shall be running at a time */
261 /* This also resets client_has_hack. */
262 client_kill_server(TRUE);
264 output_window_append(ftc_client, _("Starting local server..."));
266 /* find a free port */
267 /* Mitigate the risk of ending up with the port already
268 * used by standalone server on Windows where this is known to be buggy
269 * by not starting from DEFAULT_SOCK_PORT but from one higher. */
270 internal_server_port = find_next_free_port(DEFAULT_SOCK_PORT + 1,
271 DEFAULT_SOCK_PORT + 1 + 10000,
272 family, "localhost", TRUE);
274 if (internal_server_port < 0) {
275 output_window_append(ftc_client, _("Couldn't start the server."));
276 output_window_append(ftc_client,
277 _("You'll have to start one manually. Sorry..."));
278 return FALSE;
281 storage = freeciv_storage_dir();
282 if (storage == NULL) {
283 output_window_append(ftc_client, _("Cannot find freeciv storage directory"));
284 output_window_append(ftc_client,
285 _("You'll have to start server manually. Sorry..."));
286 return FALSE;
289 ruleset = tileset_what_ruleset(tileset);
291 #ifdef HAVE_USABLE_FORK
293 int argc = 0;
294 const int max_nargs = 25;
295 char *argv[max_nargs + 1];
296 char port_buf[32];
297 char dbg_lvl_buf[32]; /* Do not move this inside the block where it gets filled,
298 * it's needed via the argv[x] pointer later on, so must
299 * remain in scope. */
301 /* Set up the command-line parameters. */
302 fc_snprintf(port_buf, sizeof(port_buf), "%d", internal_server_port);
303 fc_snprintf(savesdir, sizeof(savesdir), "%s" DIR_SEPARATOR "saves", storage);
304 fc_snprintf(scensdir, sizeof(scensdir), "%s" DIR_SEPARATOR "scenarios", storage);
305 #ifdef FREECIV_WEB
306 argv[argc++] = "freeciv-web";
307 #else /* FREECIV_WEB */
308 argv[argc++] = "freeciv-server";
309 #endif /* FREECIV_WEB */
310 argv[argc++] = "-p";
311 argv[argc++] = port_buf;
312 argv[argc++] = "--bind";
313 argv[argc++] = "localhost";
314 argv[argc++] = "-q";
315 argv[argc++] = "1";
316 argv[argc++] = "-e";
317 argv[argc++] = "--saves";
318 argv[argc++] = savesdir;
319 argv[argc++] = "--scenarios";
320 argv[argc++] = scensdir;
321 argv[argc++] = "-A";
322 argv[argc++] = "none";
323 if (logfile) {
324 enum log_level llvl = log_get_level();
326 argv[argc++] = "--debug";
327 fc_snprintf(dbg_lvl_buf, sizeof(dbg_lvl_buf), "%d", llvl);
328 argv[argc++] = dbg_lvl_buf;
329 argv[argc++] = "--log";
330 argv[argc++] = logfile;
332 if (scriptfile) {
333 argv[argc++] = "--read";
334 argv[argc++] = scriptfile;
336 if (savefile) {
337 argv[argc++] = "--file";
338 argv[argc++] = savefile;
340 if (are_deprecation_warnings_enabled()) {
341 argv[argc++] = "--warnings";
343 if (ruleset != NULL) {
344 argv[argc++] = "--ruleset";
345 argv[argc++] = ruleset;
347 argv[argc] = NULL;
348 fc_assert(argc <= max_nargs);
351 struct astring srv_cmdline_opts = ASTRING_INIT;
352 int i;
354 for (i = 1; i < argc; i++) {
355 astr_add(&srv_cmdline_opts, i == 1 ? "%s" : " %s", argv[i]);
357 log_verbose("Arguments to spawned server: %s",
358 astr_str(&srv_cmdline_opts));
359 astr_free(&srv_cmdline_opts);
362 server_pid = fork();
363 server_quitting = FALSE;
365 if (server_pid == 0) {
366 int fd;
368 /* inside the child */
370 /* avoid terminal spam, but still make server output available */
371 fclose(stdout);
372 fclose(stderr);
374 /* FIXME: include the port to avoid duplication? */
375 if (logfile) {
376 fd = open(logfile, O_WRONLY | O_CREAT | O_APPEND, 0644);
378 if (fd != 1) {
379 dup2(fd, 1);
381 if (fd != 2) {
382 dup2(fd, 2);
384 fchmod(1, 0644);
387 /* If it's still attatched to our terminal, things get messed up,
388 but freeciv-server needs *something* */
389 fclose(stdin);
390 fd = open("/dev/null", O_RDONLY);
391 if (fd != 0) {
392 dup2(fd, 0);
395 /* these won't return on success */
396 #ifdef FREECIV_DEBUG
397 /* Search under current directory (what ever that happens to be)
398 * only in debug builds. This allows running freeciv directly from build
399 * tree, but could be considered security risk in release builds. */
400 #ifdef FREECIV_WEB
401 execvp("./server/freeciv-web", argv);
402 #else /* FREECIV_WEB */
403 execvp("./fcser", argv);
404 execvp("./server/freeciv-server", argv);
405 #endif /* FREECIV_WEB */
406 #endif /* FREECIV_DEBUG */
407 #ifdef FREECIV_WEB
408 execvp(BINDIR "/freeciv-web", argv);
409 execvp("freeciv-web", argv);
410 #else /* FREECIV_WEB */
411 execvp(BINDIR "/freeciv-server", argv);
412 execvp("freeciv-server", argv);
413 #endif /* FREECIV_WEB */
415 /* This line is only reached if freeciv-server cannot be started,
416 * so we kill the forked process.
417 * Calling exit here is dangerous due to X11 problems (async replies) */
418 _exit(1);
421 #else /* HAVE_USABLE_FORK */
422 #ifdef FREECIV_MSWINDOWS
423 if (logfile) {
424 loghandle = CreateFile(logfile, GENERIC_WRITE,
425 FILE_SHARE_READ | FILE_SHARE_WRITE,
426 NULL,
427 OPEN_ALWAYS, 0, NULL);
430 ZeroMemory(&si, sizeof(si));
431 si.cb = sizeof(si);
432 si.hStdOutput = loghandle;
433 si.hStdInput = INVALID_HANDLE_VALUE;
434 si.hStdError = loghandle;
435 si.dwFlags = STARTF_USESTDHANDLES;
437 /* Set up the command-line parameters. */
438 logcmdline[0] = 0;
439 scriptcmdline[0] = 0;
440 savefilecmdline[0] = 0;
442 /* the server expects command line arguments to be in local encoding */
443 if (logfile) {
444 char *logfile_in_local_encoding =
445 internal_to_local_string_malloc(logfile);
446 enum log_level llvl = log_get_level();
448 fc_snprintf(logcmdline, sizeof(logcmdline), " --debug %d --log %s", llvl,
449 logfile_in_local_encoding);
450 free(logfile_in_local_encoding);
452 if (scriptfile) {
453 char *scriptfile_in_local_encoding =
454 internal_to_local_string_malloc(scriptfile);
456 fc_snprintf(scriptcmdline, sizeof(scriptcmdline), " --read %s",
457 scriptfile_in_local_encoding);
458 free(scriptfile_in_local_encoding);
460 if (savefile) {
461 char *savefile_in_local_encoding =
462 internal_to_local_string_malloc(savefile);
464 fc_snprintf(savefilecmdline, sizeof(savefilecmdline), " --file %s",
465 savefile_in_local_encoding);
466 free(savefile_in_local_encoding);
469 fc_snprintf(savesdir, sizeof(savesdir), "%s" DIR_SEPARATOR "saves", storage);
470 internal_to_local_string_buffer(savesdir, savescmdline, sizeof(savescmdline));
472 fc_snprintf(scensdir, sizeof(scensdir), "%s" DIR_SEPARATOR "scenarios", storage);
473 internal_to_local_string_buffer(scensdir, scenscmdline, sizeof(scenscmdline));
475 if (are_deprecation_warnings_enabled()) {
476 depr = " --warnings";
477 } else {
478 depr = "";
480 if (ruleset != NULL) {
481 fc_snprintf(rsparam, sizeof(rsparam), " --ruleset %s", ruleset);
482 } else {
483 rsparam[0] = '\0';
486 fc_snprintf(options, sizeof(options),
487 "-p %d --bind localhost -q 1 -e%s%s%s --saves \"%s\" "
488 "--scenarios \"%s\" -A none %s%s",
489 internal_server_port, logcmdline, scriptcmdline, savefilecmdline,
490 savescmdline, scenscmdline, rsparam, depr);
491 #ifdef FREECIV_DEBUG
492 #ifdef FREECIV_WEB
493 fc_snprintf(cmdline1, sizeof(cmdline1),
494 "./server/freeciv-web %s", options);
495 #else /* FREECIV_WEB */
496 fc_snprintf(cmdline1, sizeof(cmdline1), "./fcser %s", options);
497 fc_snprintf(cmdline2, sizeof(cmdline2),
498 "./server/freeciv-server %s", options);
499 #endif /* FREECIV_WEB */
500 #endif /* FREECIV_DEBUG */
501 #ifdef FREECIV_WEB
502 fc_snprintf(cmdline3, sizeof(cmdline2),
503 BINDIR "/freeciv-web %s", options);
504 fc_snprintf(cmdline4, sizeof(cmdline3),
505 "freeciv-web %s", options);
506 #else /* FREECIV_WEB */
507 fc_snprintf(cmdline3, sizeof(cmdline3),
508 BINDIR "/freeciv-server %s", options);
509 fc_snprintf(cmdline4, sizeof(cmdline4),
510 "freeciv-server %s", options);
511 #endif /* FREECIV_WEB */
513 if (
514 #ifdef FREECIV_DEBUG
515 !CreateProcess(NULL, cmdline1, NULL, NULL, TRUE,
516 DETACHED_PROCESS | NORMAL_PRIORITY_CLASS,
517 NULL, NULL, &si, &pi)
518 #ifndef FREECIV_WEB
519 && !CreateProcess(NULL, cmdline2, NULL, NULL, TRUE,
520 DETACHED_PROCESS | NORMAL_PRIORITY_CLASS,
521 NULL, NULL, &si, &pi)
522 #endif /* FREECIV_WEB */
524 #endif /* FREECIV_DEBUG */
525 !CreateProcess(NULL, cmdline3, NULL, NULL, TRUE,
526 DETACHED_PROCESS | NORMAL_PRIORITY_CLASS,
527 NULL, NULL, &si, &pi)
528 && !CreateProcess(NULL, cmdline4, NULL, NULL, TRUE,
529 DETACHED_PROCESS | NORMAL_PRIORITY_CLASS,
530 NULL, NULL, &si, &pi)) {
531 log_error("Failed to start server process.");
532 #ifdef FREECIV_DEBUG
533 log_verbose("Tried with commandline: '%s'", cmdline1);
534 log_verbose("Tried with commandline: '%s'", cmdline2);
535 #endif /* FREECIV_DEBUG */
536 log_verbose("Tried with commandline: '%s'", cmdline3);
537 log_verbose("Tried with commandline: '%s'", cmdline4);
538 output_window_append(ftc_client, _("Couldn't start the server."));
539 output_window_append(ftc_client,
540 _("You'll have to start one manually. Sorry..."));
541 return FALSE;
544 log_verbose("Arguments to spawned server: %s", options);
546 server_process = pi.hProcess;
548 #endif /* FREECIV_MSWINDOWS */
549 #endif /* HAVE_USABLE_FORK */
551 /* a reasonable number of tries */
552 while (connect_to_server(user_name, "localhost", internal_server_port,
553 buf, sizeof(buf)) == -1) {
554 fc_usleep(WAIT_BETWEEN_TRIES);
555 #ifdef HAVE_USABLE_FORK
556 #ifndef FREECIV_MSWINDOWS
557 if (waitpid(server_pid, NULL, WNOHANG) != 0) {
558 break;
560 #endif /* FREECIV_MSWINDOWS */
561 #endif /* HAVE_USABLE_FORK */
562 if (connect_tries++ > NUMBER_OF_TRIES) {
563 log_error("Last error from connect attempts: '%s'", buf);
564 break;
568 /* weird, but could happen, if server doesn't support new startup stuff
569 * capabilities won't help us here... */
570 if (!client.conn.used) {
571 /* possible that server is still running. kill it */
572 client_kill_server(TRUE);
574 log_error("Failed to connect to spawned server!");
575 output_window_append(ftc_client, _("Couldn't connect to the server."));
576 output_window_append(ftc_client,
577 _("We probably couldn't start it from here."));
578 output_window_append(ftc_client,
579 _("You'll have to start one manually. Sorry..."));
581 return FALSE;
584 /* We set the topology to match the view.
586 * When a typical player launches a game, he wants the map orientation to
587 * match the tileset orientation. So if you use an isometric tileset you
588 * get an iso-map and for a classic tileset you get a classic map. In
589 * both cases the map wraps in the X direction by default.
591 * This works with hex maps too now. A hex map always has
592 * tileset_is_isometric(tileset) return TRUE. An iso-hex map has
593 * tileset_hex_height(tileset) != 0, while a non-iso hex map
594 * has tileset_hex_width(tileset) != 0.
596 * Setting the option here is a bit of a hack, but so long as the client
597 * has sufficient permissions to do so (it doesn't have HACK access yet) it
598 * is safe enough. Note that if you load a savegame the topology will be
599 * set but then overwritten during the load.
601 * Don't send it now, it will be sent to the server when receiving the
602 * server setting infos. */
604 char topobuf[16];
606 fc_strlcpy(topobuf, "WRAPX", sizeof(topobuf));
607 if (tileset_is_isometric(tileset) && 0 == tileset_hex_height(tileset)) {
608 fc_strlcat(topobuf, "|ISO", sizeof(topobuf));
610 if (0 < tileset_hex_width(tileset) || 0 < tileset_hex_height(tileset)) {
611 fc_strlcat(topobuf, "|HEX", sizeof(topobuf));
613 desired_settable_option_update("topology", topobuf, FALSE);
616 return TRUE;
617 #endif /* HAVE_USABLE_FORK || FREECIV_MSWINDOWS */
620 /*************************************************************************
621 generate a random string.
622 *************************************************************************/
623 static void randomize_string(char *str, size_t n)
625 const char chars[] =
626 "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
627 int i;
629 for (i = 0; i < n - 1; i++) {
630 str[i] = chars[fc_rand(sizeof(chars) - 1)];
632 str[i] = '\0';
635 /****************************************************************
636 if the client is capable of 'wanting hack', then the server will
637 send the client a filename in the packet_join_game_reply packet.
639 this function creates the file with a suitably random string in it
640 and then sends the string to the server. If the server can open
641 and read the string, then the client is given hack access.
642 *****************************************************************/
643 void send_client_wants_hack(const char *filename)
645 if (filename[0] != '\0') {
646 struct packet_single_want_hack_req req;
647 struct section_file *file;
648 const char *sdir = freeciv_storage_dir();
650 if (sdir == NULL) {
651 return;
654 if (!is_safe_filename(filename)) {
655 return;
658 make_dir(sdir);
660 fc_snprintf(challenge_fullname, sizeof(challenge_fullname),
661 "%s" DIR_SEPARATOR "%s", sdir, filename);
663 /* generate an authentication token */
664 randomize_string(req.token, sizeof(req.token));
666 file = secfile_new(FALSE);
667 secfile_insert_str(file, req.token, "challenge.token");
668 if (!secfile_save(file, challenge_fullname, 0, FZ_PLAIN)) {
669 log_error("Couldn't write token to temporary file: %s",
670 challenge_fullname);
672 secfile_destroy(file);
674 /* tell the server what we put into the file */
675 send_packet_single_want_hack_req(&client.conn, &req);
679 /****************************************************************
680 handle response (by the server) if the client has got hack or not.
681 *****************************************************************/
682 void handle_single_want_hack_reply(bool you_have_hack)
684 /* remove challenge file */
685 if (challenge_fullname[0] != '\0') {
686 if (fc_remove(challenge_fullname) == -1) {
687 log_error("Couldn't remove temporary file: %s", challenge_fullname);
689 challenge_fullname[0] = '\0';
692 if (you_have_hack) {
693 output_window_append(ftc_client,
694 _("Established control over the server. "
695 "You have command access level 'hack'."));
696 client_has_hack = TRUE;
697 } else if (is_server_running()) {
698 /* only output this if we started the server and we NEED hack */
699 output_window_append(ftc_client,
700 _("Failed to obtain the required access "
701 "level to take control of the server. "
702 "Attempting to shut down server."));
703 client_kill_server(TRUE);
707 /****************************************************************
708 send server command to save game.
709 *****************************************************************/
710 void send_save_game(const char *filename)
712 if (filename) {
713 send_chat_printf("/save %s", filename);
714 } else {
715 send_chat("/save");
719 /**************************************************************************
720 Handle the list of rulesets sent by the server.
721 **************************************************************************/
722 void handle_ruleset_choices(const struct packet_ruleset_choices *packet)
724 char *rulesets[packet->ruleset_count];
725 int i;
726 size_t suf_len = strlen(RULESET_SUFFIX);
728 for (i = 0; i < packet->ruleset_count; i++) {
729 size_t len = strlen(packet->rulesets[i]);
731 rulesets[i] = fc_strdup(packet->rulesets[i]);
733 if (len > suf_len
734 && strcmp(rulesets[i] + len - suf_len, RULESET_SUFFIX) == 0) {
735 rulesets[i][len - suf_len] = '\0';
738 set_rulesets(packet->ruleset_count, rulesets);
740 for (i = 0; i < packet->ruleset_count; i++) {
741 free(rulesets[i]);
745 /**************************************************************************
746 Called by the GUI code when the user sets the ruleset. The ruleset
747 passed in here should match one of the strings given to set_rulesets().
748 **************************************************************************/
749 void set_ruleset(const char *ruleset)
751 char buf[4096];
753 fc_snprintf(buf, sizeof(buf), "/read %s%s", ruleset, RULESET_SUFFIX);
754 log_debug("Executing '%s'", buf);
755 send_chat(buf);