1 -- Freeciv - Copyright (C) 2011 - The Freeciv Project
2 -- This program is free software; you can redistribute it and/or modify
3 -- it under the terms of the GNU General Public License as published by
4 -- the Free Software Foundation; either version 2, or (at your option)
7 -- This program is distributed in the hope that it will be useful,
8 -- but WITHOUT ANY WARRANTY; without even the implied warranty of
9 -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 -- GNU General Public License for more details.
12 -- This file is the Freeciv server`s interface to the database backend
13 -- when authentication is enabled. See doc/README.fcdb.
15 -- **************************************************************************
16 -- basic mysql functions
17 -- **************************************************************************
21 -- connect to a MySQL database (or stop with an error)
22 local function mysql_connect()
23 local err
-- error message
29 local sql
= luasql
.mysql()
31 log.verbose('MySQL database version is %s.', luasql
._MYSQLVERSION
)
33 -- Load the database parameters.
34 local database
= fcdb
.option(fcdb
.param
.DATABASE
)
35 local user
= fcdb
.option(fcdb
.param
.USER
)
36 local password
= fcdb
.option(fcdb
.param
.PASSWORD
)
37 local host
= fcdb
.option(fcdb
.param
.HOST
)
38 local port
= fcdb
.option(fcdb
.param
.PORT
)
40 dbh
, err
= sql
:connect(database
, user
, password
, host
, port
)
42 log.error('[mysql:connect]: %s', err
)
43 return fcdb
.status
.ERROR
45 return fcdb
.status
.TRUE
49 -- open a SQLite database (or stop with an error)
50 local function sqlite_connect()
51 local err
-- error message
57 local sql
= luasql
.sqlite3()
59 -- Load the database parameters.
60 local database
= fcdb
.option(fcdb
.param
.DATABASE
)
61 -- USER/PASSWORD/HOST/PORT ignored for SQLite
63 dbh
, err
= sql
:connect(database
)
65 log.error('[sqlite:connect]: %s', err
)
66 return fcdb
.status
.ERROR
68 return fcdb
.status
.TRUE
72 -- execute a sql query
73 local function execute(query
)
74 local res
-- result handle
75 local err
-- error message
78 return fcdb
.status
.ERROR
, "[execute] Invalid database handle."
81 -- log.verbose("Database query: %s", query)
83 res
, err
= dbh
:execute(query
)
85 log.error("[luasql:execute]: %s", err
)
86 return fcdb
.status
.ERROR
, err
88 return fcdb
.status
.TRUE
, res
92 -- DIRTY: return a string to put in a database query which gets the
93 -- current time (in seconds since the epoch, UTC).
94 -- (This should be replaced with Lua os.time() once the script has access
95 -- to this, see <http://gna.org/bugs/?19729>.)
97 local backend
= fcdb
.option(fcdb
.param
.BACKEND
)
98 if backend
== 'mysql' then
99 return 'UNIX_TIMESTAMP()'
100 elseif backend
== 'sqlite' then
101 return 'strftime(\'%s\',\'now\')'
103 log.error('Don\'t know how to do timestamps for database backend \'%s\'', backend
)
108 -- Set up tables for an SQLite database.
109 -- (Since there`s no concept of user rights, we can do this directly from Lua,
110 -- without needing a separate script like MySQL. The server operator can do
111 -- "/fcdb lua sqlite_createdb()" from the server prompt.)
112 function sqlite_createdb()
116 if fcdb
.option(fcdb
.param
.BACKEND
) ~= 'sqlite' then
117 log.error("'backend' in configuration file must be 'sqlite'")
118 return fcdb
.status
.ERROR
121 local table_user
= fcdb
.option(fcdb
.param
.TABLE_USER
)
122 local table_log
= fcdb
.option(fcdb
.param
.TABLE_LOG
)
125 log.error("Missing database connection...")
126 return fcdb
.status
.ERROR
129 query
= string.format([[
131 id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
132 name VARCHAR(48) default NULL UNIQUE,
133 password VARCHAR(32) default NULL,
134 email VARCHAR default NULL,
135 createtime INTEGER default NULL,
136 accesstime INTEGER default NULL,
137 address VARCHAR default NULL,
138 createaddress VARCHAR default NULL,
139 logincount INTEGER default '0'
142 status
, res
= execute(query
)
143 if status
== fcdb
.status
.TRUE
then
144 log.normal("Successfully created user table '%s'", table_user
)
146 log.error("Error creating user table '%s'", table_user
)
147 return fcdb
.status
.ERROR
150 query
= string.format([[
152 id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
153 name VARCHAR(48) default NULL,
154 logintime INTEGER default NULL,
155 address VARCHAR default NULL,
156 succeed TEXT default 'S'
158 status
, res
= execute(query
)
159 if status
== fcdb
.status
.TRUE
then
160 log.normal("Successfully created log table '%s'", table_log
)
162 log.error("Error creating log table '%s'", table_log
)
163 return fcdb
.status
.ERROR
166 return fcdb
.status
.TRUE
170 -- **************************************************************************
171 -- For MySQL, the following shapes of tables are expected
172 -- (scripts/setup_auth_server.sh automates this):
174 -- CREATE TABLE fcdb_auth (
175 -- id int(11) NOT NULL auto_increment,
176 -- name varchar(48) default NULL,
177 -- password varchar(32) default NULL,
178 -- email varchar(128) default NULL,
179 -- createtime int(11) default NULL,
180 -- accesstime int(11) default NULL,
181 -- address varchar(255) default NULL,
182 -- createaddress varchar(255) default NULL,
183 -- logincount int(11) default '0',
185 -- UNIQUE KEY name (name)
188 -- CREATE TABLE fcdb_log (
189 -- id int(11) NOT NULL auto_increment,
190 -- name varchar(48) default NULL,
191 -- logintime int(11) default NULL,
192 -- address varchar(255) default NULL,
193 -- succeed enum('S','F') default 'S',
197 -- N.B. if the tables are not of this format, then the select, insert,
198 -- and update syntax in the following functions must be changed.
199 -- **************************************************************************
201 -- **************************************************************************
202 -- freeciv user auth functions
203 -- **************************************************************************
206 function user_load(conn
)
207 local status
-- return value (status of the request)
208 local res
-- result handle
209 local row
-- one row of the sql result
210 local query
-- sql query
212 local fields
= 'password'
214 local table_user
= fcdb
.option(fcdb
.param
.TABLE_USER
)
215 local table_log
= fcdb
.option(fcdb
.param
.TABLE_LOG
)
218 log.error("Missing database connection...")
219 return fcdb
.status
.ERROR
222 local username
= dbh
:escape(auth
.get_username(conn
))
223 local ipaddr
= dbh
:escape(auth
.get_ipaddr(conn
))
225 -- get the password for this user
226 query
= string.format([[SELECT %s FROM %s WHERE name = '%s']],
227 fields
, table_user
, username
)
228 status
, res
= execute(query
)
229 if status
~= fcdb
.status
.TRUE
then
230 return fcdb
.status
.ERROR
233 row
= res
:fetch({}, 'a')
237 return fcdb
.status
.FALSE
240 -- There should be only one result
242 log.error('[user_load]: multiple entries (%d) for user: %s',
245 return fcdb
.status
.FALSE
248 auth
.set_password(conn
, row
.password
)
252 return fcdb
.status
.TRUE
255 -- save a user to the database
256 function user_save(conn
)
257 local status
-- return value (status of the request)
258 local res
-- result handle
259 local query
-- sql query
261 local table_user
= fcdb
.option(fcdb
.param
.TABLE_USER
)
264 log.error("Missing database connection...")
265 return fcdb
.status
.ERROR
268 local username
= dbh
:escape(auth
.get_username(conn
))
269 local password
= dbh
:escape(auth
.get_password(conn
))
270 local ipaddr
= auth
.get_ipaddr(conn
)
273 --local now = os.time()
274 query
= string.format([[INSERT INTO %s VALUES (NULL, '%s', '%s',
275 NULL, %s, %s, '%s', '%s', 0)]],
276 table_user
, username
, password
,
277 sql_time(), sql_time(),
279 status
, res
= execute(query
)
280 if status
~= fcdb
.status
.TRUE
then
281 return fcdb
.status
.ERROR
285 return user_log(conn
, true)
289 function user_log(conn
, success
)
290 local status
-- return value (status of the request)
291 local res
-- result handle
292 local query
-- sql query
295 log.error("Missing database connection...")
296 return fcdb
.status
.ERROR
299 local table_user
= fcdb
.option(fcdb
.param
.TABLE_USER
)
300 local table_log
= fcdb
.option(fcdb
.param
.TABLE_LOG
)
302 local username
= dbh
:escape(auth
.get_username(conn
))
303 local ipaddr
= auth
.get_ipaddr(conn
)
304 local success_str
= success
and 'S' or 'F'
307 --local now = os.time()
308 query
= string.format([[UPDATE %s SET accesstime = %s, address = '%s',
309 logincount = logincount + 1
310 WHERE name = '%s']], table_user
, sql_time(),
312 status
, res
= execute(query
)
313 if status
~= fcdb
.status
.TRUE
then
314 return fcdb
.status
.ERROR
317 -- insert the log row for this user
318 query
= string.format([[INSERT INTO %s (name, logintime, address, succeed)
319 VALUES ('%s', %s, '%s', '%s')]],
320 table_log
, username
, sql_time(), ipaddr
, success_str
)
321 status
, res
= execute(query
)
322 if status
~= fcdb
.status
.TRUE
then
323 return fcdb
.status
.ERROR
326 return fcdb
.status
.TRUE
329 -- **************************************************************************
330 -- freeciv database entry functions
331 -- **************************************************************************
333 -- test and initialise the database connection
334 function database_init()
335 local status
-- return value (status of the request)
337 local backend
= fcdb
.option(fcdb
.param
.BACKEND
)
338 if backend
== 'mysql' then
339 log.verbose('Opening MySQL database connection...')
340 status
= mysql_connect()
341 elseif backend
== 'sqlite' then
342 log.verbose('Opening SQLite database connection...')
343 status
= sqlite_connect()
345 log.error('Database backend \'%s\' not supported by database.lua', backend
)
346 return fcdb
.status
.ERROR
349 if status
== fcdb
.status
.TRUE
then
350 log.verbose('Database connection successful.')
356 -- free the database connection
357 function database_free()
358 log.verbose('Closing database connection...')
364 return fcdb
.status
.TRUE
;