1 /*****************************************************************************
2  Freeciv - Copyright (C) 2005 - 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.
7 
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 
14 #ifdef HAVE_CONFIG_H
15 #include <fc_config.h>
16 #endif
17 
18 #include <stdarg.h>
19 #include <stdlib.h>
20 #include <time.h>
21 
22 /* dependencies/lua */
23 #include "lua.h"
24 #include "lualib.h"
25 
26 /* dependencies/tolua */
27 #include "tolua.h"
28 
29 /* dependencies/luasql */
30 #ifdef HAVE_FCDB_MYSQL
31 #include "ls_mysql.h"
32 #endif
33 #ifdef HAVE_FCDB_POSTGRES
34 #include "ls_postgres.h"
35 #endif
36 #ifdef HAVE_FCDB_SQLITE3
37 #include "ls_sqlite3.h"
38 #endif
39 
40 /* utility */
41 #include "log.h"
42 #include "md5.h"
43 #include "registry.h"
44 #include "string_vector.h"
45 
46 /* common/scriptcore */
47 #include "luascript.h"
48 #include "luascript_types.h"
49 #include "tolua_common_a_gen.h"
50 #include "tolua_common_z_gen.h"
51 
52 /* server */
53 #include "console.h"
54 #include "stdinhand.h"
55 
56 /* server/scripting */
57 #ifdef HAVE_FCDB
58 #include "tolua_fcdb_gen.h"
59 #endif /* HAVE_FCDB */
60 
61 #include "script_fcdb.h"
62 
63 #ifdef HAVE_FCDB
64 
65 #define SCRIPT_FCDB_LUA_FILE "database.lua"
66 
67 static void script_fcdb_functions_define(void);
68 static bool script_fcdb_functions_check(const char *fcdb_luafile);
69 
70 static void script_fcdb_cmd_reply(struct fc_lua *lfcl, enum log_level level,
71                                   const char *format, ...)
72             fc__attribute((__format__ (__printf__, 3, 4)));
73 
74 /*****************************************************************************
75   Lua virtual machine state.
76 *****************************************************************************/
77 static struct fc_lua *fcl = NULL;
78 
79 /*****************************************************************************
80   Add fcdb callback functions; these must be defined in the lua script
81   'database.lua':
82 
83   database_init:
84     - test and initialise the database.
85   database_free:
86     - free the database.
87 
88   user_load(Connection pconn):
89     - check if the user data was successful loaded from the database.
90   user_save(Connection pconn):
91     - check if the user data was successful saved in the database.
92   user_log(Connection pconn, Bool success):
93     - check if the login attempt was successful logged.
94 
95   If a database error did occur, the functions return FCDB_SUCCESS_ERROR.
96   If the request was successful, FCDB_SUCCESS_TRUE is returned.
97   If the request was not successful, FCDB_SUCCESS_FALSE is returned.
98 *****************************************************************************/
script_fcdb_functions_define(void)99 static void script_fcdb_functions_define(void)
100 {
101   luascript_func_add(fcl, "database_init", TRUE, 0);
102   luascript_func_add(fcl, "database_free", TRUE, 0);
103 
104   luascript_func_add(fcl, "user_load", TRUE, 1,
105                      API_TYPE_CONNECTION);
106   luascript_func_add(fcl, "user_save", TRUE, 1,
107                      API_TYPE_CONNECTION);
108   luascript_func_add(fcl, "user_log", TRUE, 2,
109                      API_TYPE_CONNECTION, API_TYPE_BOOL);
110 }
111 
112 /*****************************************************************************
113   Check the existence of all needed functions.
114 *****************************************************************************/
script_fcdb_functions_check(const char * fcdb_luafile)115 static bool script_fcdb_functions_check(const char *fcdb_luafile)
116 {
117   bool ret = TRUE;
118   struct strvec *missing_func_required = strvec_new();
119   struct strvec *missing_func_optional = strvec_new();
120 
121   if (!luascript_func_check(fcl, missing_func_required,
122                             missing_func_optional)) {
123     strvec_iterate(missing_func_required, func_name) {
124       log_error("Database script '%s' does not define the required function "
125                 "'%s'.", fcdb_luafile, func_name);
126       ret = FALSE;
127     } strvec_iterate_end;
128     strvec_iterate(missing_func_optional, func_name) {
129       log_verbose("Database script '%s' does not define the optional "
130                   "function '%s'.", fcdb_luafile, func_name);
131     } strvec_iterate_end;
132   }
133 
134   strvec_destroy(missing_func_required);
135   strvec_destroy(missing_func_optional);
136 
137   return ret;
138 }
139 
140 /*****************************************************************************
141   Send the message via cmd_reply().
142 *****************************************************************************/
script_fcdb_cmd_reply(struct fc_lua * lfcl,enum log_level level,const char * format,...)143 static void script_fcdb_cmd_reply(struct fc_lua *lfcl, enum log_level level,
144                                   const char *format, ...)
145 {
146   va_list args;
147   enum rfc_status rfc_status = C_OK;
148   char buf[1024];
149 
150   va_start(args, format);
151   fc_vsnprintf(buf, sizeof(buf), format, args);
152   va_end(args);
153 
154   switch (level) {
155   case LOG_FATAL:
156     /* Special case - will quit the server. */
157     log_fatal("%s", buf);
158     break;
159   case LOG_ERROR:
160     rfc_status = C_WARNING;
161     break;
162   case LOG_NORMAL:
163     rfc_status = C_COMMENT;
164     break;
165   case LOG_VERBOSE:
166     rfc_status = C_LOG_BASE;
167     break;
168   case LOG_DEBUG:
169     rfc_status = C_DEBUG;
170     break;
171   }
172 
173   cmd_reply(CMD_FCDB, lfcl->caller, rfc_status, "%s", buf);
174 }
175 #endif /* HAVE_FCDB */
176 
177 /*****************************************************************************
178   Initialize the scripting state. Returns the status of the freeciv database
179   lua state.
180 *****************************************************************************/
script_fcdb_init(const char * fcdb_luafile)181 bool script_fcdb_init(const char *fcdb_luafile)
182 {
183 #ifdef HAVE_FCDB
184   if (fcl != NULL) {
185     fc_assert_ret_val(fcl->state != NULL, FALSE);
186 
187     return TRUE;
188   }
189 
190   if (!fcdb_luafile) {
191     /* Use default freeciv database lua file. */
192     fcdb_luafile = FC_CONF_PATH "/" SCRIPT_FCDB_LUA_FILE;
193   }
194 
195   fcl = luascript_new(NULL);
196   if (fcl == NULL) {
197     log_error("Error loading the Freeciv database lua definition.");
198     return FALSE;
199   }
200 
201   tolua_common_a_open(fcl->state);
202   tolua_fcdb_open(fcl->state);
203 #ifdef HAVE_FCDB_MYSQL
204   luaL_requiref(fcl->state, "ls_mysql", luaopen_luasql_mysql, 1);
205   lua_pop(fcl->state, 1);
206 #endif
207 #ifdef HAVE_FCDB_POSTGRES
208   luaL_requiref(fcl->state, "ls_postgres", luaopen_luasql_postgres, 1);
209   lua_pop(fcl->state, 1);
210 #endif
211 #ifdef HAVE_FCDB_SQLITE3
212   luaL_requiref(fcl->state, "ls_sqlite3", luaopen_luasql_sqlite3, 1);
213   lua_pop(fcl->state, 1);
214 #endif
215   tolua_common_z_open(fcl->state);
216 
217   luascript_func_init(fcl);
218 
219   /* Define the prototypes for the needed lua functions. */
220   script_fcdb_functions_define();
221 
222   if (luascript_do_file(fcl, fcdb_luafile)
223       || !script_fcdb_functions_check(fcdb_luafile)) {
224     log_error("Error loading the Freeciv database lua script '%s'.",
225               fcdb_luafile);
226     script_fcdb_free();
227     return FALSE;
228   }
229 
230   if (script_fcdb_call("database_init", 0) != FCDB_SUCCESS_TRUE) {
231     log_error("Error connecting to the database");
232     script_fcdb_free();
233     return FALSE;
234   }
235 #endif /* HAVE_FCDB */
236 
237   return TRUE;
238 }
239 
240 /*****************************************************************************
241   Call a lua function.
242 
243   Example call to the lua function 'user_load()':
244     script_fcdb_call("user_load", 1, API_TYPE_CONNECTION, pconn);
245 *****************************************************************************/
script_fcdb_call(const char * func_name,int nargs,...)246 enum fcdb_status script_fcdb_call(const char *func_name, int nargs, ...)
247 {
248 #ifdef HAVE_FCDB
249   enum fcdb_status status = FCDB_ERROR; /* Default return value. */
250   bool success;
251   int ret;
252 
253   va_list args;
254   va_start(args, nargs);
255   success = luascript_func_call_valist(fcl, func_name, &ret, nargs, args);
256   va_end(args);
257 
258   if (success && fcdb_status_is_valid(ret)) {
259     status = (enum fcdb_status) ret;
260   }
261 
262   return status;
263 #else
264   return FCDB_SUCCESS_TRUE;
265 #endif /* HAVE_FCDB */
266 }
267 
268 /*****************************************************************************
269   Free the scripting data.
270 *****************************************************************************/
script_fcdb_free(void)271 void script_fcdb_free(void)
272 {
273 #ifdef HAVE_FCDB
274   if (script_fcdb_call("database_free", 0) != FCDB_SUCCESS_TRUE) {
275     log_error("Error closing the database connection. Continuing anyway ...");
276   }
277 
278   if (fcl) {
279     /* luascript_func_free() is called by luascript_destroy(). */
280     luascript_destroy(fcl);
281     fcl = NULL;
282   }
283 #endif /* HAVE_FCDB */
284 }
285 
286 /*****************************************************************************
287   Parse and execute the script in str in the lua instance for the freeciv
288   database.
289 *****************************************************************************/
script_fcdb_do_string(struct connection * caller,const char * str)290 bool script_fcdb_do_string(struct connection *caller, const char *str)
291 {
292 #ifdef HAVE_FCDB
293   int status;
294   struct connection *save_caller;
295   luascript_log_func_t save_output_fct;
296 
297   /* Set a log callback function which allows to send the results of the
298    * command to the clients. */
299   save_caller = fcl->caller;
300   save_output_fct = fcl->output_fct;
301   fcl->output_fct = script_fcdb_cmd_reply;
302   fcl->caller = caller;
303 
304   status = luascript_do_string(fcl, str, "cmd");
305 
306   /* Reset the changes. */
307   fcl->caller = save_caller;
308   fcl->output_fct = save_output_fct;
309 
310   return (status == 0);
311 #else
312   return TRUE;
313 #endif /* HAVE_FCDB */
314 }
315