[Freeciv-Dev] Re: (PR#13262) pubserver-in-a-diff
[Top] [All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
Subject: |
[Freeciv-Dev] Re: (PR#13262) pubserver-in-a-diff |
From: |
"Per I. Mathisen" <per@xxxxxxxxxxx> |
Date: |
Wed, 22 Jun 2005 13:40:40 -0700 |
Reply-to: |
bugs@xxxxxxxxxxx |
<URL: http://bugs.freeciv.org/Ticket/Display.html?id=13262 >
On Wed, 22 Jun 2005, Per I. Mathisen wrote:
> On Mon, 13 Jun 2005, Per I. Mathisen wrote:
> > This patch implements the ./configure switch --enable-pubserver.
New version. This one is quite similar in features, except that it does
not use a compile time switch, but a command line switch --Pubserver (or
-P) instead, and does not try to find game number on its own, but takes
that from the command line.
- Per
Index: common/game.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/game.c,v
retrieving revision 1.220
diff -u -r1.220 game.c
--- common/game.c 21 Jun 2005 16:21:01 -0000 1.220
+++ common/game.c 22 Jun 2005 20:34:23 -0000
@@ -233,7 +233,11 @@
game.info.cooling = 0;
game.info.watchtower_extra_vision = GAME_DEFAULT_WATCHTOWER_EXTRA_VISION;
game.info.allowed_city_names = GAME_DEFAULT_ALLOWED_CITY_NAMES;
- game.info.save_nturns = 10;
+ if (game.info.pubserver > 0) {
+ game.info.save_nturns = 1;
+ } else {
+ game.info.save_nturns = GAME_DEFAULT_SAVETURNS;
+ }
#ifdef HAVE_LIBZ
game.info.save_compress_level = GAME_DEFAULT_COMPRESS_LEVEL;
#else
Index: common/game.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/game.h,v
retrieving revision 1.194
diff -u -r1.194 game.h
--- common/game.h 14 Jun 2005 18:49:08 -0000 1.194
+++ common/game.h 22 Jun 2005 20:34:23 -0000
@@ -333,6 +333,7 @@
#define GAME_DEFAULT_RULESETDIR "default"
#define GAME_DEFAULT_SAVE_NAME "civgame"
+#define GAME_DEFAULT_SAVETURNS 10
#define GAME_DEFAULT_SKILL_LEVEL 3 /* easy */
#define GAME_OLD_DEFAULT_SKILL_LEVEL 5 /* normal; for old save games */
Index: common/packets.def
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/packets.def,v
retrieving revision 1.136
diff -u -r1.136 packets.def
--- common/packets.def 14 Jun 2005 18:49:08 -0000 1.136
+++ common/packets.def 22 Jun 2005 20:34:23 -0000
@@ -343,6 +343,7 @@
UINT32 tech;
UINT32 skill_level;
+ UINT32 pubserver; # game number, if running on a pubserver
BOOL is_new_game; # TRUE only in pregame for "new" (not loaded) games
FLOAT seconds_to_phasedone;
UINT32 timeout;
Index: server/civserver.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/civserver.c,v
retrieving revision 1.230
diff -u -r1.230 civserver.c
--- server/civserver.c 3 Feb 2005 08:04:55 -0000 1.230
+++ server/civserver.c 22 Jun 2005 20:34:23 -0000
@@ -145,6 +145,12 @@
free(option);
} else if ((option = get_option_malloc("--bind", argv, &inx, argc))) {
srvarg.bind_addr = option; /* Never freed. */
+ } else if ((option = get_option_malloc("--Pubserver", argv, &inx, argc))) {
+ if (sscanf(option, "%d", &game.info.pubserver) != 1) {
+ fc_fprintf(stderr, _("Bad pubserver game number \"%s\"\n"), option);
+ exit(EXIT_FAILURE);
+ }
+ free(option);
} else if ((option = get_option_malloc("--read", argv, &inx, argc)))
srvarg.script_filename = option; /* Never freed. */
else if ((option = get_option_malloc("--quitidle", argv, &inx, argc))) {
@@ -173,7 +179,8 @@
sz_strlcpy(srvarg.serverid, option);
free(option);
} else if ((option = get_option_malloc("--saves", argv, &inx, argc))) {
- srvarg.saves_pathname = option; /* Never freed. */
+ srvarg.saves_pathname = option; /* Base path. Never freed. */
+ sz_strlcpy(srvarg.final_savepath, option); /* Possibly changing path. */
} else if (is_option("--version", argv[inx]))
showvers = TRUE;
else {
@@ -184,6 +191,11 @@
inx++;
}
+ if (game.info.pubserver > 0 && !srvarg.exit_on_end) {
+ fc_fprintf(stderr, _("Pubserver mode requires --exit-on-end\n"));
+ exit(EXIT_FAILURE);
+ }
+
if (showvers && !showhelp) {
fc_fprintf(stderr, "%s \n", freeciv_name_version());
exit(EXIT_SUCCESS);
@@ -221,6 +233,8 @@
fc_fprintf(stderr, _(" -p, --port PORT\tListen for clients on "
"port PORT\n"));
+ fc_fprintf(stderr, _(" -P, --Pubserver NUM\tLaunch in pubserver "
+ "mode with game number NUM\n"));
fc_fprintf(stderr, _(" -q, --quitidle TIME\tQuit if no players "
"for TIME seconds\n"));
fc_fprintf(stderr, _(" -e, --exit-on-end\t"
Index: server/commands.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/commands.c,v
retrieving revision 1.12
diff -u -r1.12 commands.c
--- server/commands.c 21 May 2005 19:40:25 -0000 1.12
+++ server/commands.c 22 Jun 2005 20:34:23 -0000
@@ -345,7 +345,7 @@
N_("Load a game from <file-name>. Any current data including players, "
"rulesets and server options are lost.\n")
},
- {"read", ALLOW_HACK,
+ {"read", ALLOW_CTRL,
/* TRANS: translate text between <> only */
N_("read <file-name>"),
N_("Process server commands from file."), NULL
Index: server/connecthand.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/connecthand.c,v
retrieving revision 1.52
diff -u -r1.52 connecthand.c
--- server/connecthand.c 14 Jun 2005 18:49:09 -0000 1.52
+++ server/connecthand.c 22 Jun 2005 20:34:23 -0000
@@ -99,6 +99,13 @@
notify_conn(dest, _("Welcome to the %s Server at port %d."),
freeciv_name_version(), srvarg.port);
}
+ if (game.info.pubserver > 0) {
+ notify_conn(dest, _("Server is running in public mode. Some "
+ "commands will be restricted."));
+ notify_conn(dest, _("This is game %d. Use this number "
+ "to load a saved game or report problems."),
+ game.info.pubserver);
+ }
/* FIXME: this (getting messages about others logging on) should be a
* message option for the client with event */
Index: server/console.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/console.c,v
retrieving revision 1.25
diff -u -r1.25 console.c
--- server/console.c 21 Mar 2005 23:01:12 -0000 1.25
+++ server/console.c 22 Jun 2005 20:34:23 -0000
@@ -47,9 +47,8 @@
************************************************************************/
static void con_handle_log(int level, const char *message, bool file_too)
{
- /* Write to console only when not written to file.
- LOG_FATAL messages always to console too. */
- if (! file_too || level <= LOG_FATAL) {
+ /* Write debug/verbose message to console only when not written to file. */
+ if (!file_too || level <= LOG_NORMAL) {
if (console_rfcstyle) {
con_write(C_LOG_BASE + level, "%s", message);
} else {
Index: server/settings.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/settings.c,v
retrieving revision 1.27
diff -u -r1.27 settings.c
--- server/settings.c 14 Jun 2005 18:49:09 -0000 1.27
+++ server/settings.c 22 Jun 2005 20:34:23 -0000
@@ -951,7 +951,7 @@
N_("Turns per auto-save"),
N_("The game will be automatically saved per this number of "
"turns. Zero means never auto-save."), NULL,
- 0, 200, 10)
+ 0, 200, GAME_DEFAULT_SAVETURNS)
/* Could undef entire option if !HAVE_LIBZ, but this way users get to see
* what they're missing out on if they didn't compile with zlib? --dwp
@@ -979,7 +979,6 @@
GAME_NO_COMPRESS_LEVEL, GAME_NO_COMPRESS_LEVEL,
GAME_NO_COMPRESS_LEVEL)
#endif
-
GEN_STRING("savename", game.save_name,
SSET_META, SSET_INTERNAL, SSET_VITAL, SSET_SERVER_ONLY,
N_("Auto-save name prefix"),
@@ -987,7 +986,6 @@
"\"<prefix><year>.sav\". This setting sets "
"the <prefix> part."), NULL,
GAME_DEFAULT_SAVE_NAME)
-
GEN_BOOL("scorelog", game.scorelog,
SSET_META, SSET_INTERNAL, SSET_SITUATIONAL, SSET_SERVER_ONLY,
N_("Whether to log player statistics"),
Index: server/srv_main.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/srv_main.c,v
retrieving revision 1.273
diff -u -r1.273 srv_main.c
--- server/srv_main.c 21 Jun 2005 16:21:02 -0000 1.273
+++ server/srv_main.c 22 Jun 2005 20:34:23 -0000
@@ -17,10 +17,14 @@
#include <assert.h>
#include <ctype.h>
+#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/types.h>
#ifdef HAVE_NETDB_H
#include <netdb.h>
@@ -179,6 +183,7 @@
srvarg.load_filename[0] = '\0';
srvarg.script_filename = NULL;
srvarg.saves_pathname = "";
+ srvarg.final_savepath[0] = '\0';
srvarg.quitidle = 0;
@@ -710,7 +715,7 @@
**************************************************************************/
void save_game(char *orig_filename, const char *save_reason)
{
- char filename[600];
+ char filename[600] = "civgame";
char *dot;
struct section_file file;
struct timer *timer_cpu, *timer_user;
@@ -726,10 +731,16 @@
*dot = '\0';
}
- /* If orig_filename is NULL or empty, use "civgame.info.year>m". */
- if (filename[0] == '\0'){
- my_snprintf(filename, sizeof(filename),
- "%s%+05dm", game.save_name, game.info.year);
+ if (game.info.pubserver > 0) {
+ /* game.savename could have been renamed, and we need to be sure
+ * the game is not, for scripts that may want to find it */
+ sz_strlcpy(filename, "civgame");
+ } else {
+ /* If orig_filename is NULL or empty, use "civgame.info.year>m". */
+ if (filename[0] == '\0'){
+ my_snprintf(filename, sizeof(filename),
+ " %s%+05dm", game.save_name, game.info.year);
+ }
}
timer_cpu = new_timer_start(TIMER_CPU, TIMER_ACTIVE);
@@ -750,9 +761,9 @@
char tmpname[600];
/* Ensure the saves directory exists. */
- make_dir(srvarg.saves_pathname);
+ make_dir(srvarg.final_savepath);
- sz_strlcpy(tmpname, srvarg.saves_pathname);
+ sz_strlcpy(tmpname, srvarg.final_savepath);
if (tmpname[0] != '\0') {
sz_strlcat(tmpname, "/");
}
@@ -760,10 +771,11 @@
sz_strlcpy(filename, tmpname);
}
- if(!section_file_save(&file, filename, game.info.save_compress_level))
+ if (!section_file_save(&file, filename, game.info.save_compress_level)) {
con_write(C_FAIL, _("Failed saving game as %s"), filename);
- else
+ } else {
con_write(C_OK, _("Game saved as %s"), filename);
+ }
section_file_free(&file);
@@ -1334,12 +1346,15 @@
int num_ready = 0, num_unready = 0;
players_iterate(pplayer) {
- if (pplayer->is_connected) {
- if (pplayer->is_ready) {
- num_ready++;
- } else {
- num_unready++;
- }
+ /* If we load a game, some players may be assigned to user accounts,
+ * in which case we should not start until they have joined too.
+ * Otherwise it would be easy to cheat. This will be problematic
+ * for scenarios where creators forget to reset usernames. */
+ if (pplayer->is_ready && pplayer->is_connected) {
+ num_ready++;
+ } else if (pplayer->is_connected
+ || is_valid_username(pplayer->username)) {
+ num_unready++;
}
} players_iterate_end;
if (num_unready > 0) {
@@ -1770,6 +1785,33 @@
/* Run server loop */
while (TRUE) {
+ if (game.info.pubserver > 0) {
+ DIR *dir;
+ char path[20], gamelogfile[40], conlogfile[40];
+
+ /* Create directory */
+ my_snprintf(path, sizeof(path), "%d", game.info.pubserver);
+ dir = opendir(path);
+ if (dir != NULL) {
+ freelog(LOG_FATAL, "Game directory \"%s\" already exists!", path);
+ exit(EXIT_FAILURE);
+ }
+ make_dir(path);
+ dir = opendir(path);
+ if (dir == NULL) {
+ freelog(LOG_FATAL, "Could not create directory \"%s\".", path);
+ exit(EXIT_FAILURE);
+ }
+ sz_strlcpy(srvarg.final_savepath, path);
+ /* Redirect output to saves directory */
+ my_snprintf(gamelogfile, sizeof(gamelogfile), "%s/gamelog.txt",
+ srvarg.final_savepath);
+ my_snprintf(conlogfile, sizeof(conlogfile), "%s/cmdline.txt",
+ srvarg.final_savepath);
+ con_log_init(conlogfile, srvarg.loglevel);
+ gamelog_init(gamelogfile);
+ }
+
srv_loop();
send_game_state(game.game_connections, CLIENT_GAME_OVER_STATE);
@@ -1830,7 +1872,6 @@
{
init_available_nations();
- freelog(LOG_NORMAL, _("Now accepting new client connections."));
while(server_state == PRE_GAME_STATE) {
sniff_packets(); /* Accepting commands. */
}
Index: server/srv_main.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/srv_main.h,v
retrieving revision 1.35
diff -u -r1.35 srv_main.h
--- server/srv_main.h 21 Jun 2005 10:17:03 -0000 1.35
+++ server/srv_main.h 22 Jun 2005 20:34:24 -0000
@@ -22,6 +22,7 @@
BV_DEFINE(bv_draw, MAX_NUM_PLAYERS);
struct server_arguments {
+ int pubserver_serial;
/* metaserver information */
bool metaserver_no_send;
char metaserver_addr[256];
@@ -33,11 +34,12 @@
/* the log level */
int loglevel;
/* filenames */
- char *log_filename;
- char *gamelog_filename;
+ char *log_filename; /* ignored by pubserver */
+ char *gamelog_filename; /* ignored by pubserver */
char load_filename[512]; /* FIXME: may not be long enough? use MAX_PATH? */
char *script_filename;
- char *saves_pathname;
+ char *saves_pathname; /* base path; used by pubserver for all output */
+ char final_savepath[512]; /* may be changed by pubserver code */
char serverid[256];
/* quit if there no players after a given time interval */
int quitidle;
Index: server/stdinhand.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/stdinhand.c,v
retrieving revision 1.422
diff -u -r1.422 stdinhand.c
--- server/stdinhand.c 21 Jun 2005 10:17:03 -0000 1.422
+++ server/stdinhand.c 22 Jun 2005 20:34:24 -0000
@@ -116,6 +116,16 @@
Returns whether the specified server setting (option) should be
sent to the client.
*********************************************************************/
+static bool restricted_filepaths(struct connection *caller)
+{
+ return (game.info.pubserver > 0
+ || (caller && caller->access_level != ALLOW_HACK));
+}
+
+/********************************************************************
+Returns whether the specified server setting (option) should be
+sent to the client.
+*********************************************************************/
static bool sset_is_to_client(int idx)
{
return (settings[idx].to_client == SSET_TO_CLIENT);
@@ -755,6 +765,11 @@
**************************************************************************/
static bool save_command(struct connection *caller, char *arg, bool check)
{
+ if (restricted_filepaths(caller)) {
+ cmd_reply(CMD_SAVE, caller, C_FAIL,
+ _("You cannot save games manually on this server."));
+ return FALSE;
+ }
if (!check) {
save_game(arg, "User request");
}
@@ -970,22 +985,50 @@
/**************************************************************************
Returns FALSE iff there was an error.
+
+ Security: We will look for a file with mandatory extension '.serv',
+ and on public servers we will not look outside the data directories.
+ As long as the user cannot create files with arbitrary names in the
+ root of the data directories, this should ensure that we will not be
+ tricked into loading non-approved content. The script is read with the
+ permissions of the caller, so it will in any case not lead to elevated
+ permissions unless there are other bugs.
**************************************************************************/
bool read_init_script(struct connection *caller, char *script_filename)
{
FILE *script_file;
+ const char extension[] = ".serv";
+ char serv_filename[strlen(extension) + strlen(script_filename) + 2];
char tilde_filename[4096];
char *real_filename;
- interpret_tilde(tilde_filename, sizeof(tilde_filename), script_filename);
+ my_snprintf(serv_filename, sizeof(serv_filename), "%s%s",
+ script_filename, extension);
+
+ if (restricted_filepaths(caller)) {
+ if (!is_ascii_name(serv_filename)) {
+ cmd_reply(CMD_READ_SCRIPT, caller, C_FAIL,
+ _("Name \"%s\" disallowed for security reasons."),
+ serv_filename);
+ return FALSE;
+ }
+ sz_strlcpy(tilde_filename, serv_filename);
+ } else {
+ interpret_tilde(tilde_filename, sizeof(tilde_filename), serv_filename);
+ }
real_filename = datafilename(tilde_filename);
if (!real_filename) {
+ if (restricted_filepaths(caller)) {
+ cmd_reply(CMD_READ_SCRIPT, caller, C_FAIL,
+ _("No command script found by the name \"%s\"."),
+ serv_filename);
+ return FALSE;
+ }
+ /* File is outside data directories */
real_filename = tilde_filename;
}
- /* This used to print out the script_filename, but it seems more useful
- * to show the real_filename. */
freelog(LOG_NORMAL, _("Loading script file: %s"), real_filename);
if (is_reg_file_for_access(real_filename, FALSE)
@@ -993,7 +1036,7 @@
char buffer[MAX_LEN_CONSOLE_LINE];
/* the size is set as to not overflow buffer in handle_stdin_input */
while(fgets(buffer,MAX_LEN_CONSOLE_LINE-1,script_file))
- handle_stdin_input((struct connection *)NULL, buffer, FALSE);
+ handle_stdin_input(caller, buffer, FALSE);
fclose(script_file);
return TRUE;
} else {
@@ -1105,7 +1148,11 @@
**************************************************************************/
static bool write_command(struct connection *caller, char *arg, bool check)
{
- if (!check) {
+ if (restricted_filepaths(caller)) {
+ cmd_reply(CMD_WRITE_SCRIPT, caller, C_OK, _("You cannot use the write "
+ "command on this server for security reasons."));
+ return FALSE;
+ } else if (!check) {
write_init_script(arg);
}
return TRUE;
@@ -3135,20 +3182,53 @@
{
struct timer *loadtimer, *uloadtimer;
struct section_file file;
- char arg[strlen(filename) + 1];
+ char arg[512 + strlen(filename)];
+ int i;
- /* We make a local copy because the parameter might be a pointer to
- * srvarg.load_filename, which we edit down below. */
- sz_strlcpy(arg, filename);
+ if (!filename || filename[0] == '\0') {
+ cmd_reply(CMD_LOAD, caller, C_FAIL, _("Usage: load <game name or "
+ "number>"));
+ return FALSE;
+ }
+ if (!is_ascii_name(filename) && restricted_filepaths(caller)) {
+ cmd_reply(CMD_LOAD, caller, C_FAIL, _("Name \"%s\" disallowed for "
+ "security reasons."), filename);
+ return FALSE;
+ }
+ /* if filename is a valid number, it may be a pubserver savegame */
+ if (sscanf(filename, "%d", &i) == 1) {
+ my_snprintf(arg, sizeof(arg), "%s%s%s/civgame.sav%s",
+ srvarg.saves_pathname,
+ srvarg.saves_pathname[0] == '\0' ? "" : "/",
+ filename, game.info.save_compress_level > 0 ? ".gz" : "");
+ } else {
+ /* otherwise, it is a normal savegame or maybe a scenario */
+ char tmp[256 + strlen(filename)];
- if (!arg || arg[0] == '\0') {
- cmd_reply(CMD_LOAD, caller, C_FAIL, _("Usage: load <filename>"));
- send_load_game_info(FALSE);
- return FALSE;
+ my_snprintf(tmp, sizeof(tmp), "%s.sav", filename);
+ if (!datafilename(tmp)) {
+ my_snprintf(tmp, sizeof(tmp), "%s.sav.gz", filename);
+ if (!datafilename(tmp)) {
+ my_snprintf(tmp, sizeof(tmp), "scenario/%s.sav", filename);
+ if (!datafilename(tmp)) {
+ my_snprintf(tmp, sizeof(tmp), "scenario/%s.sav.gz", filename);
+ if (restricted_filepaths(caller) && !datafilename(tmp)) {
+ cmd_reply(CMD_LOAD, caller, C_FAIL, _("Cannot find savegame or "
+ "scenario with the name \"%s\"."), filename);
+ return FALSE;
+ }
+ }
+ }
+ }
+ if (datafilename(tmp)) {
+ sz_strlcpy(arg, datafilename(tmp));
+ } else {
+ sz_strlcpy(arg, filename);
+ }
}
if (server_state != PRE_GAME_STATE) {
- cmd_reply(CMD_LOAD, caller, C_FAIL, _("Can't load a game while another "
+ cmd_reply(CMD_LOAD, caller, C_FAIL, _("Cannot load a game while another "
"is running."));
send_load_game_info(FALSE);
return FALSE;
@@ -3157,7 +3237,7 @@
/* attempt to parse the file */
if (!section_file_load_nodup(&file, arg)) {
- cmd_reply(CMD_LOAD, caller, C_FAIL, _("Couldn't load savefile: %s"), arg);
+ cmd_reply(CMD_LOAD, caller, C_FAIL, _("Could not load savefile: %s"), arg);
send_load_game_info(FALSE);
return FALSE;
}
@@ -3208,7 +3288,13 @@
}
/**************************************************************************
- ...
+ Load rulesets from a given ruleset directory.
+
+ Security: There are some rudimentary checks in load_rulesets() to see
+ if this directory realls is a viable ruleset directory. For public
+ servers, we check against directory redirection (is_ascii_name) and
+ other bad stuff in the directory name, and will only use directories
+ inside the data directories.
**************************************************************************/
static bool set_rulesetdir(struct connection *caller, char *str, bool check)
{
@@ -3218,6 +3304,13 @@
_("Current ruleset directory is \"%s\""), game.rulesetdir);
return FALSE;
}
+ if (restricted_filepaths(caller)
+ && (!is_ascii_name(str) || strchr(str, '.'))) {
+ cmd_reply(CMD_RULESETDIR, caller, C_SYNTAX,
+ _("Ruleset directory name \"%s\" disallowed for security "
+ "reasons."), str);
+ return FALSE;
+ }
my_snprintf(filename, sizeof(filename), "%s", str);
pfilename = datafilename(filename);
if (!pfilename) {
Index: utility/shared.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/utility/shared.c,v
retrieving revision 1.133
diff -u -r1.133 shared.c
--- utility/shared.c 15 Jun 2005 20:23:00 -0000 1.133
+++ utility/shared.c 22 Jun 2005 20:34:24 -0000
@@ -385,7 +385,8 @@
***************************************************************/
bool is_ascii_name(const char *name)
{
- const char illegal_chars[] = {'|', '%', '"', ',', '*', '<', '>', '\0'};
+ const char illegal_chars[] = { '|', '%', '"', ',', '*', '<', '>',
+ '/', '\\', '$', '`', '\0' };
int i, j;
/* must not be NULL or empty */
- [Freeciv-Dev] Re: (PR#13262) RFC: pubserver-in-a-diff, Mike Kaufman, 2005/06/16
- [Freeciv-Dev] Re: (PR#13262) RFC: pubserver-in-a-diff, Per I. Mathisen, 2005/06/22
- [Freeciv-Dev] Re: (PR#13262) RFC: pubserver-in-a-diff, Mike Kaufman, 2005/06/22
- [Freeciv-Dev] Re: (PR#13262) pubserver-in-a-diff, Per I. Mathisen, 2005/06/22
- [Freeciv-Dev] Re: (PR#13262) pubserver-in-a-diff, Mike Kaufman, 2005/06/22
- [Freeciv-Dev] Re: (PR#13262) pubserver-in-a-diff, Jason Short, 2005/06/22
- [Freeciv-Dev] Re: (PR#13262) pubserver-in-a-diff, Per I. Mathisen, 2005/06/22
- [Freeciv-Dev] Re: (PR#13262) pubserver-in-a-diff, Per I. Mathisen, 2005/06/22
- [Freeciv-Dev] Re: (PR#13262) pubserver-in-a-diff, Mike Kaufman, 2005/06/22
- [Freeciv-Dev] Re: (PR#13262) pubserver-in-a-diff, Jason Short, 2005/06/22
- [Freeciv-Dev] Re: (PR#13262) pubserver-in-a-diff,
Per I. Mathisen <=
- [Freeciv-Dev] Re: (PR#13262) pubserver-in-a-diff, Jason Short, 2005/06/22
- [Freeciv-Dev] Re: (PR#13262) pubserver-in-a-diff, Per I. Mathisen, 2005/06/22
- [Freeciv-Dev] Re: (PR#13262) pubserver-in-a-diff, Jason Short, 2005/06/22
- [Freeciv-Dev] Re: (PR#13262) pubserver-in-a-diff, Jason Short, 2005/06/22
- [Freeciv-Dev] Re: (PR#13262) pubserver-in-a-diff, Per I. Mathisen, 2005/06/23
- [Freeciv-Dev] Re: (PR#13262) pubserver-in-a-diff, Per I. Mathisen, 2005/06/23
- [Freeciv-Dev] Re: (PR#13262) pubserver-in-a-diff, Jason Short, 2005/06/23
- [Freeciv-Dev] Re: (PR#13262) pubserver-in-a-diff, Per I. Mathisen, 2005/06/23
|
|