From 0e2bca2c907deaa6cb45cb2cfdd3cdf8694716f0 Mon Sep 17 00:00:00 2001
From: Marko Lindqvist <cazfi74@gmail.com>
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 <cazfi74@gmail.com>
---
 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