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