From 0e2bca2c907deaa6cb45cb2cfdd3cdf8694716f0 Mon Sep 17 00:00:00 2001 From: Marko Lindqvist Date: Sun, 17 Sep 2023 19:57:40 +0300 Subject: [PATCH 22/22] fcdb: Add database capstr Add meta table, with capstr and gamecount values. Check the capstr before trying to access database for established connection. See osdn #48621 Signed-off-by: Marko Lindqvist --- doc/README.fcdb | 4 + lua/database.lua | 33 +++++- scripts/setup_auth_server.sh | 205 +++++++++++++++++++-------------- server/connecthand.c | 12 +- server/scripting/script_fcdb.c | 38 ++++++ server/scripting/script_fcdb.h | 1 + 6 files changed, 201 insertions(+), 92 deletions(-) diff --git a/doc/README.fcdb b/doc/README.fcdb index 7fc73ca062..67adc2672e 100644 --- a/doc/README.fcdb +++ b/doc/README.fcdb @@ -177,6 +177,7 @@ example (for MySQL; SQLite does not need all of these options). database="Freeciv" table_user="auth" table_log="loginlog" + table_meta="fcdb_meta" If that's sufficient for you, it's not necessary to read on. @@ -199,6 +200,9 @@ Freeciv expects the following lua functions to be defined in database.lua: -- Test and initialise the database connection function database_init() + -- Get capstr of the database + function database_capstr() + -- Free the database connection function database_free() diff --git a/lua/database.lua b/lua/database.lua index d837694b73..d0afa5dbd1 100644 --- a/lua/database.lua +++ b/lua/database.lua @@ -36,7 +36,8 @@ local function get_option(name, is_sensitive) local defaults = { backend = "sqlite", table_user = "fcdb_auth", - table_log = "fcdb_log" + table_log = "fcdb_log", + table_meta = "fcdb_meta" } local val = fcdb.option(name) if val then @@ -101,11 +102,19 @@ function sqlite_createdb() local table_user = get_option("table_user") local table_log = get_option("table_log") + local table_meta = get_option("table_meta") if not dbh then error("Missing database connection") end + query = string.format([[ +CREATE TABLE %s ( + capstr VARCHAR(256) default NULL, + gamecount INTEGER default '0' +);]], table_meta) + assert(dbh:execute(query)) + query = string.format([[ CREATE TABLE %s ( id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, @@ -130,12 +139,21 @@ CREATE TABLE %s ( succeed TEXT default 'S' );]], table_log) assert(dbh:execute(query)) + + query = string.format([[ +INSERT INTO %s VALUES ('+fcdb', 0);]], table_meta) + assert(dbh:execute(query)) end -- ************************************************************************** -- For MySQL, the following shapes of tables are expected -- (scripts/setup_auth_server.sh automates this): -- +-- CREATE TABLE fcdb_meta ( +-- capstr varchar(256) default NULL, +-- gamecount int(11) default '0' +-- ); +-- -- CREATE TABLE fcdb_auth ( -- id int(11) NOT NULL auto_increment, -- name varchar(48) default NULL, @@ -282,6 +300,19 @@ function user_log(conn, success) assert(dbh:execute(query)) end +function database_capstr() + local table_meta = get_option("table_meta") + + query = string.format([[SELECT capstr FROM %s]], table_meta) + local res = assert(dbh:execute(query)) + + local caps = res:fetch({}, "a") + + res:close() + + return string.format('%s', caps.capstr) +end + -- ************************************************************************** -- freeciv database entry functions -- ************************************************************************** diff --git a/scripts/setup_auth_server.sh b/scripts/setup_auth_server.sh index dedc6f2ffa..285b78d3dd 100755 --- a/scripts/setup_auth_server.sh +++ b/scripts/setup_auth_server.sh @@ -24,7 +24,7 @@ then else PROGRAM_NAME="setup_auth_server.sh" fi -PROGRAM_VERSION="0.10" +PROGRAM_VERSION="0.15" ############################################################################# # @@ -49,14 +49,14 @@ ask_yes_no() { read ANSWER - if test "x$ANSWER" = "xy" || test "x$ANSWER" = "xyes" || - test "x$ANSWER" = "xY" || test "x$ANSWER" = "xYES" || - test "x$ANSWER" = "xYes" + if test "${ANSWER}" = "y" || test "${ANSWER}" = "yes" || + test "${ANSWER}" = "Y" || test "${ANSWER}" = "YES" || + test "${ANSWER}" = "Yes" then return 1 - elif test "x$ANSWER" = "xn" || test "x$ANSWER" = "xno" || - test "x$ANSWER" = "xN" || test "x$ANSWER" = "xNO" || - test "x$ANSWER" = "xNo" + elif test "${ANSWER}" = "n" || test "${ANSWER}" = "no" || + test "${ANSWER}" = "N" || test "${ANSWER}" = "NO" || + test "${ANSWER}" = "No" then return 0 else @@ -77,58 +77,58 @@ ask_yes_no() { make_query() { declare -i MYSQL_RETURN - if test "x$MYSQL_SERVER" != "x" + if test "${MYSQL_SERVER}" != "" then - MYSQL_SERVER_PARAM="-h$MYSQL_SERVER" + MYSQL_SERVER_PARAM="-h${MYSQL_SERVER}" else MYSQL_SERVER_PARAM= fi - if test "x$MYSQL_PORT" != "x" + if test "${MYSQL_PORT}" != "" then - MYSQL_PORT_PARAM="-P$MYSQL_PORT" + MYSQL_PORT_PARAM="-P${MYSQL_PORT}" else MYSQL_PORT_PARAM= fi - if test "x$MYSQL_DATABASE" != "x" + if test "${MYSQL_DATABASE}" != "" then - MYSQL_DATABASE_PARAM="-D$MYSQL_DATABASE" + MYSQL_DATABASE_PARAM="-D${MYSQL_DATABASE}" else MYSQL_DATABASE_PARAM= fi - if test "x$MYSQL_USER" != "x" + if test "${MYSQL_USER}" != "" then - MYSQL_USER_PARAM="-u$MYSQL_USER" + MYSQL_USER_PARAM="-u${MYSQL_USER}" else MYSQL_USER_PARAM= fi - if test "x$MYSQL_PASSWORD" = "x*" + if test "${MYSQL_PASSWORD}" = "*" then MYSQL_PASSWORD_PARAM="-p" - elif test "x$MYSQL_PASSWORD" != "x" + elif test "${MYSQL_PASSWORD}" != "" then - MYSQL_PASSWORD_PARAM="-p$MYSQL_PASSWORD" + MYSQL_PASSWORD_PARAM="-p${MYSQL_PASSWORD}" else MYSQL_PASSWORD_PARAM= fi - if test "x$1" != "x-" + if test "$1" != "-" then - echo "$1" | mysql $MYSQL_SERVER_PARAM $MYSQL_PORT_PARAM \ - $MYSQL_USER_PARAM $MYSQL_PASSWORD_PARAM \ - $MYSQL_DATABASE_PARAM + echo "$1" | mysql ${MYSQL_SERVER_PARAM} ${MYSQL_PORT_PARAM} \ + ${MYSQL_USER_PARAM} ${MYSQL_PASSWORD_PARAM} \ + ${MYSQL_DATABASE_PARAM} else - mysql $MYSQL_SERVER_PARAM $MYSQL_PORT_PARAM \ - $MYSQL_USER_PARAM $MYSQL_PASSWORD_PARAM \ - $MYSQL_DATABASE_PARAM + mysql ${MYSQL_SERVER_PARAM} ${MYSQL_PORT_PARAM} \ + ${MYSQL_USER_PARAM} ${MYSQL_PASSWORD_PARAM} \ + ${MYSQL_DATABASE_PARAM} fi MYSQL_RETURN=$? - return $MYSQL_RETURN + return ${MYSQL_RETURN} } print_usage() { - echo "Usage: $PROGRAM_NAME [-v|--version] [-h|--help]" + echo "Usage: ${PROGRAM_NAME} [-v|--version] [-h|--help]" } ############################################################################# @@ -137,24 +137,24 @@ print_usage() { # Check for commandline parameters -if test "x$1" = "x-v" || test "x$1" = "x--version" +if test "$1" = "-v" || test "$1" = "--version" then - echo "$PROGRAM_NAME version $PROGRAM_VERSION" + echo "${PROGRAM_NAME} version ${PROGRAM_VERSION}" # If we have several parameters fall through to usage, otherwise exit - if test "x$2" = "x" + if test "$2" = "" then exit 0 fi fi -if test "x$1" != "x" +if test "$1" != "" then print_usage - if test "x$2" = "x" + if test "$2" = "" then - if test "x$1" = "x-h" || test "x$1" = "x--help" + if test "$1" = "-h" || test "$1" = "--help" then exit 0 fi @@ -207,11 +207,11 @@ MYSQL_DATABASE="" while test $CONNECTED = no do echo "Please answer questions determining how to contact MySQL server." - echo -n "server ($MYSQL_SERVER)> " + echo -n "server (${MYSQL_SERVER})> " read MYSQL_SERVER_NEW - echo -n "port ($MYSQL_PORT)> " + echo -n "port (${MYSQL_PORT})> " read MYSQL_PORT_NEW - echo -n "username ($MYSQL_USER)> " + echo -n "username (${MYSQL_USER})> " read MYSQL_USER_NEW echo "If password is not required, say \"-\"." echo "If you want MySQL server to prompt it, say \"*\"." @@ -219,21 +219,21 @@ do echo -n "password > " read MYSQL_PASSWORD_NEW - if test "x$MYSQL_SERVER_NEW" != "x" + if test "${MYSQL_SERVER_NEW}" != "" then - MYSQL_SERVER="$MYSQL_SERVER_NEW" + MYSQL_SERVER="${MYSQL_SERVER_NEW}" fi - if test "x$MYSQL_PORT_NEW" != "x" + if test "${MYSQL_PORT_NEW}" != "" then - MYSQL_PORT="$MYSQL_PORT_NEW" + MYSQL_PORT="${MYSQL_PORT_NEW}" fi - if test "x$MYSQL_USER_NEW" != "x" + if test "${MYSQL_USER_NEW}" != "" then - export MYSQL_USER="$MYSQL_USER_NEW" + export MYSQL_USER="${MYSQL_USER_NEW}" fi - if test "x$MYSQL_PASSWORD_NEW" != "x" + if test "${MYSQL_PASSWORD_NEW}" != "" then - if test "x$MYSQL_PASSWORD_NEW" == "x-" + if test "${MYSQL_PASSWORD_NEW}" == "-" then # No password MYSQL_PASSWORD="" @@ -266,37 +266,37 @@ do # Make sure that make_query doesn't try to select any database # for this query - MYSQL_DATABASE_TMP="$MYSQL_DATABASE" + MYSQL_DATABASE_TMP="${MYSQL_DATABASE}" MYSQL_DATABASE="" # List Databases - remove header and internal database. DBLIST="$(make_query 'show databases' | grep -v '^Database$' | grep -v '^information_schema$')" - echo "$DBLIST" | grep "$MYSQL_DATABASE_TMP" > /dev/null + echo "${DBLIST}" | grep "${MYSQL_DATABASE_TMP}" > /dev/null GREPRESULT=$? # See if automatically proposed database is in the list if test $GREPRESULT -eq 0 then # Keep current default - MYSQL_DATABASE_TMP="$MYSQL_DATABASE_TMP" + MYSQL_DATABASE_TMP="${MYSQL_DATABASE_TMP}" else # Select first one from the list - MYSQL_DATABASE_TMP=$(echo "$DBLIST" | head -n 1) + MYSQL_DATABASE_TMP=$(echo "${DBLIST}" | head -n 1) fi # Start lines with " -" - echo "$DBLIST" | sed 's/^/ -/' + echo "${DBLIST}" | sed 's/^/ -/' echo echo "Please select which one to use." - echo -n "($MYSQL_DATABASE_TMP)> " + echo -n "(${MYSQL_DATABASE_TMP})> " read MYSQL_DATABASE_NEW - if test "x$MYSQL_DATABASE_NEW" != "x" + if test "${MYSQL_DATABASE_NEW}" != "" then - MYSQL_DATABASE="$MYSQL_DATABASE_NEW" + MYSQL_DATABASE="${MYSQL_DATABASE_NEW}" else - MYSQL_DATABASE="$MYSQL_DATABASE_TMP" + MYSQL_DATABASE="${MYSQL_DATABASE_TMP}" fi # Try to connect using that database @@ -322,14 +322,15 @@ done # These are hardcoded here, and not prompted TABLE_USER="auth" TABLE_LOG="loginlog" +TABLE_META="table_meta" TABLELIST="$(make_query 'show tables' | grep -v '^Tables_in_')" -if test "x$TABLELIST" != "x" +if test "${TABLELIST}" != "" then echo "This database already contains some tables." - echo "$TABLELIST" | grep "$TABLE_USER" > /dev/null + echo "${TABLELIST}" | grep "${TABLE_USER}" > /dev/null GREPRESULT=$? if test $GREPRESULT -eq 0 @@ -339,7 +340,7 @@ then USER_TABLE_PRESENT=no fi - echo "$TABLELIST" | grep "$TABLE_LOG" > /dev/null + echo "${TABLELIST}" | grep "${TABLE_LOG}" > /dev/null GREPRESULT=$? if test $GREPRESULT -eq 0 @@ -349,17 +350,32 @@ then LOG_TABLE_PRESENT=no fi - if test $LOG_TABLE_PRESENT = yes || - test $USER_TABLE_PRESENT = yes + echo "${TABLELIST}" | grep "${TABLE_META}" > /dev/null + GREPRESULT=$? + + if test $GREPRESULT -eq 0 + then + META_TABLE_PRESENT=yes + else + META_TABLE_PRESENT=no + fi + + if test "${LOG_TABLE_PRESENT}" = yes || + test "${USER_TABLE_PRESENT}" = yes || + test "${META_TABLE_PRESENT}" = yes then echo "There are even tables with the names Freeciv would use." - if test $USER_TABLE_PRESENT = yes + if test "${USER_TABLE_PRESENT}" = yes + then + echo " -${TABLE_USER}" + fi + if test "${LOG_TABLE_PRESENT}" = yes then - echo " -$TABLE_USER" + echo " -${TABLE_LOG}" fi - if test $LOG_TABLE_PRESENT = yes + if test "${META_TABLE_PRESENT}" = yes then - echo " -$TABLE_LOG" + echo " -${TABLE_META}" fi echo "Maybe you have already attempted to create Freeciv tables?" @@ -376,20 +392,29 @@ then fi # Drop tables - if test $USER_TABLE_PRESENT = yes + if test "${USER_TABLE_PRESENT}" = yes then - if ! make_query "drop table $TABLE_USER" + if ! make_query "drop table ${TABLE_USER}" then - echo "Dropping table $TABLE_USER failed!" + echo "Dropping table ${TABLE_USER} failed!" echo "Aborting!" exit 1 fi fi - if test $LOG_TABLE_PRESENT = yes + if test "${LOG_TABLE_PRESENT}" = yes then - if ! make_query "drop table $TABLE_LOG" + if ! make_query "drop table ${TABLE_LOG}" then - echo "Dropping table $TABLE_LOG failed!" + echo "Dropping table ${TABLE_LOG} failed!" + echo "Aborting!" + exit 1 + fi + fi + if test "${META_TABLE_PRESENT}" = yes + then + if ! make_query "drop table ${TABLE_META}" + then + echo "Dropping table ${TABLE_META} failed!" echo "Aborting!" exit 1 fi @@ -397,17 +422,17 @@ then # Updated tablelist TABLELIST="$(make_query 'show tables' | grep -v '^Tables_in_')" - if test "x$TABLELIST" != "x" + if test "${TABLELIST}" != "" then echo "After dropping Freeciv tables, others remain." fi fi # Do we still have tables in that database? - if test "x$TABLELIST" != "x" + if test "${TABLELIST}" != "" then # Print them, each line starting with " -" - echo "$TABLELIST" | sed 's/^/ -/' + echo "${TABLELIST}" | sed 's/^/ -/' echo "Table names do not conflict with tables Freeciv would use." if ask_yes_no "\nDo you really want to use this database for Freeciv\nplayer authentication?" @@ -424,7 +449,12 @@ echo "Now we create the Freeciv tables." # Maybe we should read it from separate file in the future. # The tables here are as the supplied data/database.lua expects to find them. (echo \ - "CREATE TABLE $TABLE_USER ( \ + "CREATE TABLE ${TABLE_META} ( \ + capstr varchar(256) default NULL, \ + gamecount int(11) default '0' \ + );" + echo \ + "CREATE TABLE ${TABLE_USER} ( \ id int(11) NOT NULL auto_increment, \ name varchar(48) default NULL, \ password varchar(32) default NULL, \ @@ -438,7 +468,7 @@ echo "Now we create the Freeciv tables." UNIQUE KEY name (name) \ );" echo \ - "CREATE TABLE $TABLE_LOG ( \ + "CREATE TABLE ${TABLE_LOG} ( \ id int(11) NOT NULL auto_increment, \ name varchar(48) default NULL, \ logintime int(11) default NULL, \ @@ -446,6 +476,8 @@ echo "Now we create the Freeciv tables." succeed enum('S','F') default 'S', \ PRIMARY KEY (id) \ );" + echo \ + "INSERT INTO ${TABLE_META} VALUES ('+fcdb', 0);" ) | make_query "-" QUERYRESULT=$? @@ -466,22 +498,22 @@ do echo "Give name for Freeciv server authentication config file we" echo "are about to generate next." - echo -n "($CONFIG_FILE) > " + echo -n "(${CONFIG_FILE}) > " read CONFIG_FILE_NEW - if test "x$CONFIG_FILE_NEW" != "x" + if test "${CONFIG_FILE_NEW}" != "" then - CONFIG_FILE="$CONFIG_FILE_NEW" + CONFIG_FILE="${CONFIG_FILE_NEW}" fi # Default is to test file creation TRY_FILE=yes - if test -e "$CONFIG_FILE" + if test -e "${CONFIG_FILE}" then echo "$CONFIG_FILE already exists" # Default is not to test overwriting TRY_FILE=no - if test -d "$CONFIG_FILE" + if test -d "${CONFIG_FILE}" then echo "and it's a directory. Can't overwrite with file." else @@ -494,7 +526,7 @@ do if test $TRY_FILE = "yes" then - if touch "$CONFIG_FILE" + if touch "${CONFIG_FILE}" then ACCEPTABLE_FILE=yes else @@ -510,9 +542,9 @@ do done SAVE_PASSWORD=no -if test "x$MYSQL_PASSWORD" != "x" && - test "x$MYSQL_PASSWORD" != "x-" && - test "x$MYSQL_PASSWORD" != "x*" +if test "${MYSQL_PASSWORD}" != "" && + test "${MYSQL_PASSWORD}" != "-" && + test "${MYSQL_PASSWORD}" != "*" then # User has given password for this script, should it also # go to config script? If user has not given password even @@ -551,11 +583,12 @@ fi echo "database=\"$MYSQL_DATABASE\"" echo echo "; Table names" - echo "table_user=\"$TABLE_USER\"" - echo "table_log=\"$TABLE_LOG\"" -) > $CONFIG_FILE + echo "table_user=\"${TABLE_USER}\"" + echo "table_log=\"${TABLE_LOG}\"" + echo "table_meta=\"${TABLE_META}\"" +) > "${CONFIG_FILE}" echo "Config file generated." echo "Auth server setup finished." echo "To use the newly created database, run" -echo " freeciv-server --Database $CONFIG_FILE --auth" +echo " freeciv-server --Database \"${CONFIG_FILE}\" --auth" diff --git a/server/connecthand.c b/server/connecthand.c index 654ddc0061..e1f001d2ea 100644 --- a/server/connecthand.c +++ b/server/connecthand.c @@ -111,7 +111,7 @@ static void restore_access_level(struct connection *pconn) /**********************************************************************//** This is used when a new player joins a server, before the game - has started. If pconn is NULL, is an AI, else a client. + has started. If pconn is NULL, is an AI, else a client. N.B. this only attachs a connection to a player if pconn->username == player->username @@ -137,10 +137,10 @@ void establish_new_connection(struct connection *pconn) bool delegation_error = FALSE; struct packet_set_topology topo_packet; - /* zero out the password */ + /* Zero out the password */ memset(pconn->server.password, 0, sizeof(pconn->server.password)); - /* send join_reply packet */ + /* Send join_reply packet */ packet.you_can_join = TRUE; sz_strlcpy(packet.capability, our_capability); fc_snprintf(packet.message, sizeof(packet.message), _("%s Welcome"), @@ -166,7 +166,7 @@ void establish_new_connection(struct connection *pconn) (void) send_server_info_to_metaserver(META_INFO); } - /* introduce the server to the connection */ + /* Introduce the server to the connection */ if (fc_gethostname(hostname, sizeof(hostname)) == 0) { notify_conn(dest, NULL, E_CONNECTION, ftc_any, _("Welcome to the %s Server running at %s port %d."), @@ -184,7 +184,9 @@ void establish_new_connection(struct connection *pconn) log_normal(_("%s has connected from %s."), pconn->username, pconn->addr); if (srvarg.fcdb_enabled) { - script_fcdb_call("conn_established", pconn); + if (script_fcdb_capstr()) { + script_fcdb_call("conn_established", pconn); + } } conn_compression_freeze(pconn); diff --git a/server/scripting/script_fcdb.c b/server/scripting/script_fcdb.c index d65bf12414..7b88683c4d 100644 --- a/server/scripting/script_fcdb.c +++ b/server/scripting/script_fcdb.c @@ -41,6 +41,7 @@ #endif /* utility */ +#include "capability.h" #include "log.h" #include "md5.h" #include "registry.h" @@ -70,6 +71,8 @@ #define SCRIPT_FCDB_LUA_FILE "database.lua" +#define FCDB_CAPS "+fcdb" + static void script_fcdb_functions_define(void); static bool script_fcdb_functions_check(const char *fcdb_luafile); @@ -88,6 +91,8 @@ static struct fc_lua *fcl = NULL; database_init(): - test and initialise the database. + database_capstr(): + - get database capstr database_free(): - free the database. @@ -114,6 +119,7 @@ static struct fc_lua *fcl = NULL; static void script_fcdb_functions_define(void) { luascript_func_add(fcl, "database_init", TRUE, 0, 0); + luascript_func_add(fcl, "database_capstr", TRUE, 0, 1, API_TYPE_STRING); luascript_func_add(fcl, "database_free", TRUE, 0, 0); luascript_func_add(fcl, "user_exists", TRUE, 1, 1, API_TYPE_CONNECTION, @@ -299,6 +305,38 @@ bool script_fcdb_init(const char *fcdb_luafile) return TRUE; } +/**********************************************************************//** + Check database capabilities for compatibility +**************************************************************************/ +bool script_fcdb_capstr(void) +{ +#ifdef HAVE_FCDB + static int checked = 0; + const char *fcdb_caps; + + if (checked) { + return checked > 0; + } + + script_fcdb_call("database_capstr", &fcdb_caps); + + log_verbose("Server caps: %s", FCDB_CAPS); + log_verbose("DB caps: %s", fcdb_caps); + + if (!has_capabilities(FCDB_CAPS, fcdb_caps) + || !has_capabilities(fcdb_caps, FCDB_CAPS)) { + log_error(_("Database not compatible. Freeciv caps: %s, DB caps: %s"), + FCDB_CAPS, fcdb_caps); + checked = -1; /* Negative */ + return FALSE; + } + + checked = 1; /* Positive */ +#endif /* HAVE_FCDB */ + + return TRUE; +} + /**********************************************************************//** Call a lua function. diff --git a/server/scripting/script_fcdb.h b/server/scripting/script_fcdb.h index bb15d16f65..23d12d032b 100644 --- a/server/scripting/script_fcdb.h +++ b/server/scripting/script_fcdb.h @@ -22,6 +22,7 @@ /* fcdb script functions. */ bool script_fcdb_init(const char *fcdb_luafile); +bool script_fcdb_capstr(void); bool script_fcdb_call(const char *func_name, ...); void script_fcdb_free(void); -- 2.40.1