Freeciv21
Develop your civilization from humble roots to a global empire
script_fcdb.cpp
Go to the documentation of this file.
1 /*
2  Copyright (c) 1996-2021 Freeciv21 and Freeciv contributors. This file is
3  part of Freeciv21. Freeciv21 is free software: you can redistribute it
4  and/or modify it under the terms of the GNU General Public License as
5  published by the Free Software Foundation, either version 3 of the
6  License, or (at your option) any later version. You should have received
7  a copy of the GNU General Public License along with Freeciv21. If not,
8  see https://www.gnu.org/licenses/.
9  */
10 
11 // Qt
12 #include <QStandardPaths>
13 
14 // sol2
15 #include "sol/sol.hpp"
16 
17 /* dependencies/lua */
18 #include "lua.h"
19 
20 // utility
21 #include "log.h"
22 
23 /* common/scriptcore */
24 #include "luascript.h"
25 #include "luascript_types.h"
26 #include "tolua_common_a_gen.h"
27 #include "tolua_game_gen.h"
28 
29 // server
30 #include "auth.h"
31 #include "console.h"
32 #include "stdinhand.h"
33 
34 #include "script_fcdb.h"
35 
36 #define SCRIPT_FCDB_LUA_FILE "freeciv21/database.lua"
37 
38 static bool script_fcdb_functions_check(const char *fcdb_luafile);
39 
40 static bool script_fcdb_database_init();
41 static bool script_fcdb_database_free();
42 
43 static void script_fcdb_cmd_reply(struct fc_lua *lfcl, QtMsgType level,
44  const char *format, ...)
45  fc__attribute((__format__(__printf__, 3, 4)));
46 
48 static sol::state *fcl = nullptr;
49 
52 
78 static bool script_fcdb_functions_check(const char *fcdb_luafile)
79 {
80  // Mandatory functions
81  bool ret = true;
82  for (const auto &name : {
83  "database_init",
84  "database_free",
85  "user_exists",
86  "user_verify",
87  }) {
88  if (!(*fcl)[name].valid()) {
89  qCritical("Database script '%s' does not define the required function "
90  "'%s'.",
91  fcdb_luafile, name);
92  ret = false;
93  }
94  }
95 
96  // Optional functions
97  for (const auto &name : {
98  "user_save",
99  "user_delegate_to",
100  "user_take",
101  }) {
102  if (!(*fcl)[name].valid()) {
103  qDebug("Database script '%s' does not define the optional "
104  "function '%s'.",
105  fcdb_luafile, name);
106  }
107  }
108 
109  return ret;
110 }
111 
115 static void script_fcdb_cmd_reply(struct fc_lua *lfcl, QtMsgType level,
116  const char *format, ...)
117 {
118  va_list args;
119  enum rfc_status rfc_status = C_OK;
120  char buf[1024];
121 
122  va_start(args, format);
123  fc_vsnprintf(buf, sizeof(buf), format, args);
124  va_end(args);
125 
126  switch (level) {
127  case LOG_FATAL:
128  // Special case - will quit the server.
129  qFatal("%s", buf);
130  break;
131  case LOG_ERROR:
132  case LOG_WARN:
134  break;
135  case LOG_NORMAL:
137  break;
138  case LOG_DEBUG:
140  break;
141  }
142 
143  cmd_reply(CMD_FCDB, lfcl->caller, rfc_status, "%s", buf);
144 }
145 
150 {
151  // "auth" table
152  (*fcl)["auth"] = fcl->create_table_with("get_ipaddr", auth_get_ipaddr,
153  "get_username", auth_get_username);
154  // "fcdb" table
155  (*fcl)["fcdb"] = fcl->create_table_with(
156  "option", fcdb_option_get,
157  // Definitions for backward compatibility with Freeciv 2.4.
158  // Old database.lua scripts might pass fcdb.param.USER etc to
159  // fcdb.option(), but it's deprecated in favour of literal strings, and
160  // the strings listed here are only conventional.
161  // clang-format off
162  "param", fcl->create_table_with("HOST", "host",
163  "USER", "user",
164  "PORT", "port",
165  "PASSWORD", "password",
166  "DATABASE", "database",
167  "TABLE_USER", "table_user",
168  "TABLE_LOG", "table_log",
169  "BACKEND", "backend")
170  // clang-format on
171  );
172 }
173 
178 bool script_fcdb_init(const QString &fcdb_luafile)
179 {
180  if (fcl != nullptr) {
181  return true;
182  }
183 
184  auto fcdb_luafile_resolved =
185  fcdb_luafile.isEmpty() ?
186  // Use default freeciv database lua file.
187  QStandardPaths::locate(QStandardPaths::GenericConfigLocation,
189  : fcdb_luafile;
190  if (fcdb_luafile_resolved.isEmpty()) {
191  qCritical() << "Could not find " SCRIPT_FCDB_LUA_FILE
192  " in the following directories:"
193  << QStandardPaths::standardLocations(
194  QStandardPaths::GenericConfigLocation);
195  return false;
196  }
197 
198  try {
199  fcl = new sol::state();
200  fcl->open_libraries();
201 
202  fcl_compat.state = fcl->lua_state();
203  fcl_compat.output_fct = nullptr;
204  fcl_compat.caller = nullptr;
206 
207  luascript_common_a(fcl->lua_state());
208  tolua_game_open(fcl->lua_state());
210  luascript_common_z(fcl->lua_state());
211  } catch (const std::exception &e) {
212  qCritical() << "Error loading the Freeciv21 database lua definition:"
213  << e.what();
215  return false;
216  }
217 
218  // luascript_func_init(fcl->lua_state());
219 
220  // Define the prototypes for the needed lua functions.
221  if (!fcl->safe_script_file(qUtf8Printable(fcdb_luafile_resolved))
222  .valid()) {
223  qCritical("Error loading the Freeciv21 database lua script '%s'.",
224  qUtf8Printable(fcdb_luafile_resolved));
226  return false;
227  }
228  script_fcdb_functions_check(qUtf8Printable(fcdb_luafile_resolved));
229 
230  if (!script_fcdb_database_init()) {
231  qCritical("Error connecting to the database");
233  return false;
234  }
235  return true;
236 }
237 
242 {
243  if (fcl != nullptr) {
244  if (!script_fcdb_database_free()) {
245  qCritical(
246  "Error closing the database connection. Continuing anyway...");
247  }
248 
249  delete fcl;
250  fcl = nullptr;
251  }
252 }
253 
258 bool script_fcdb_do_string(struct connection *caller, const char *str)
259 {
260  /* Set a log callback function which allows to send the results of the
261  * command to the clients. */
262  auto save_caller = fcl_compat.caller;
263  auto save_output_fct = fcl_compat.output_fct;
265  fcl_compat.caller = caller;
266 
267  auto result = fcl->safe_script(str).valid();
268 
269  // Reset the changes.
270  fcl_compat.caller = save_caller;
271  fcl_compat.output_fct = save_output_fct;
272 
273  return result;
274 }
275 
280 {
281  const sol::protected_function database_init = (*fcl)["database_init"];
282  auto result = database_init();
283  if (result.valid()) {
284  return true;
285  } else {
286  qCritical() << sol::error(result).what();
287  }
288  return false;
289 }
290 
295 {
296  const sol::protected_function database_free = (*fcl)["database_free"];
297  auto result = database_free();
298  if (result.valid()) {
299  return true;
300  } else {
301  qCritical() << sol::error(result).what();
302  }
303  return false;
304 }
305 
310  const char *delegate, bool &success)
311 {
312  const sol::protected_function user_delegate_to =
313  (*fcl)["user_delegate_to"];
314  auto result = user_delegate_to(pconn, pplayer, delegate);
315  if (result.valid()) {
316  success = result;
317  return true;
318  } else {
319  qCritical() << sol::error(result).what();
320  }
321  return false;
322 }
323 
327 bool script_fcdb_user_exists(connection *pconn, bool &exists)
328 {
329  const sol::protected_function user_exists = (*fcl)["user_exists"];
330  auto result = user_exists(pconn);
331  if (result.valid()) {
332  exists = result;
333  return true;
334  } else {
335  qCritical() << sol::error(result).what();
336  }
337  return false;
338 }
339 
343 bool script_fcdb_user_save(connection *pconn, const char *password)
344 {
345  const sol::protected_function user_save = (*fcl)["user_save"];
346  auto result = user_save(pconn, password);
347  if (result.valid()) {
348  return true;
349  } else {
350  qCritical() << sol::error(result).what();
351  }
352  return false;
353 }
354 
359  player *player, bool will_observe, bool &success)
360 {
361  const sol::protected_function user_take = (*fcl)["user_take"];
362  auto result = user_take(requester, taker, player, will_observe);
363  if (result.valid()) {
364  success = result;
365  return true;
366  } else {
367  qCritical() << sol::error(result).what();
368  }
369  return false;
370 }
371 
375 bool script_fcdb_user_verify(connection *pconn, const char *username,
376  bool &success)
377 {
378  const sol::protected_function user_verify = (*fcl)["user_verify"];
379  auto result = user_verify(pconn, username);
380  if (result.valid()) {
381  success = result;
382  return true;
383  } else {
384  qCritical() << sol::error(result).what();
385  success = false;
386  }
387  return false;
388 }
const char * auth_get_username(struct connection *pconn)
Get username for connection.
Definition: auth.cpp:317
const char * auth_get_ipaddr(struct connection *pconn)
Get connection ip address.
Definition: auth.cpp:328
@ CMD_FCDB
Definition: commands.h:99
rfc_status
Definition: console.h:36
@ C_DEBUG
Definition: console.h:39
@ C_OK
Definition: console.h:41
@ C_COMMENT
Definition: console.h:37
@ C_WARNING
Definition: console.h:48
const char * fcdb_option_get(const char *type)
Return the selected fcdb config value.
Definition: fcdb.cpp:133
const char * name
Definition: inputfile.cpp:118
constexpr auto LOG_DEBUG
Definition: log.h:27
constexpr auto LOG_ERROR
Definition: log.h:23
constexpr auto LOG_NORMAL
Definition: log.h:25
constexpr auto LOG_WARN
Definition: log.h:24
constexpr auto LOG_FATAL
Definition: log.h:22
void luascript_init(fc_lua *fcl)
Sets the freeciv lua struct for a lua state.
Definition: luascript.cpp:304
void luascript_common_z(lua_State *L)
Runs tolua_common_z.lua.
Definition: luascript.cpp:420
void luascript_common_a(lua_State *L)
Runs tolua_common_a.lua.
Definition: luascript.cpp:410
static void script_fcdb_register_functions()
Registers FCDB-related functions in the Lua state.
static bool script_fcdb_functions_check(const char *fcdb_luafile)
fcdb callback functions that must be defined in the lua script 'database.lua':
Definition: script_fcdb.cpp:78
bool script_fcdb_user_save(connection *pconn, const char *password)
Save a new user.
bool script_fcdb_user_delegate_to(connection *pconn, player *pplayer, const char *delegate, bool &success)
returns Bool, whether pconn is allowed to delegate player to delegate.
bool script_fcdb_user_exists(connection *pconn, bool &exists)
Check if the user exists.
bool script_fcdb_do_string(struct connection *caller, const char *str)
Parse and execute the script in str in the lua instance for the freeciv database.
bool script_fcdb_user_take(connection *requester, connection *taker, player *player, bool will_observe, bool &success)
returns Bool, whether requester is allowed to attach taker to pplayer.
#define SCRIPT_FCDB_LUA_FILE
Definition: script_fcdb.cpp:36
static void static sol::state * fcl
Lua virtual machine state.
Definition: script_fcdb.cpp:48
static void script_fcdb_cmd_reply(struct fc_lua *lfcl, QtMsgType level, const char *format,...) fc__attribute((__format__(__printf__
Send the message via cmd_reply().
static fc_lua fcl_compat
Tolua compatibility.
Definition: script_fcdb.cpp:51
bool script_fcdb_init(const QString &fcdb_luafile)
Initialize the scripting state.
static bool script_fcdb_database_init()
test and initialise the database.
void script_fcdb_free()
Free the scripting data.
static bool script_fcdb_database_free()
free the database.
bool script_fcdb_user_verify(connection *pconn, const char *username, bool &success)
Check the credentials of the user.
struct setting_list * level[OLEVELS_NUM]
Definition: settings.cpp:167
void cmd_reply(enum command_id cmd, struct connection *caller, enum rfc_status rfc_status, const char *format,...)
var-args version as above, no prefix
Definition: stdinhand.cpp:393
struct connection * caller
Definition: luascript.h:42
luascript_log_func_t output_fct
Definition: luascript.h:40
lua_State * state
Definition: luascript.h:38
Definition: player.h:231
int fc_vsnprintf(char *str, size_t n, const char *format, va_list ap)
Definition: support.cpp:512
int fc__attribute((nonnull(1, 3)))