]> git.lizzy.rs Git - dragonfireclient.git/blob - src/script/lua_api/l_settings.cpp
14398dda218485fc5e67097458fa9d8385a3fe0e
[dragonfireclient.git] / src / script / lua_api / l_settings.cpp
1 /*
2 Minetest
3 Copyright (C) 2013 PilzAdam <pilzadam@minetest.net>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "lua_api/l_settings.h"
21 #include "lua_api/l_internal.h"
22 #include "cpp_api/s_security.h"
23 #include "threading/mutex_auto_lock.h"
24 #include "util/string.h" // FlagDesc
25 #include "settings.h"
26 #include "noise.h"
27 #include "log.h"
28
29
30 /* This protects:
31  * 'secure.*' settings from being set
32  * some mapgen settings from being set
33  *   (not security-criticial, just to avoid messing up user configs)
34  */
35 #define CHECK_SETTING_SECURITY(L, name) \
36         if (o->m_settings == g_settings) { \
37                 if (checkSettingSecurity(L, name) == -1) \
38                         return 0; \
39         }
40
41 static inline int checkSettingSecurity(lua_State* L, const std::string &name)
42 {
43         if (ScriptApiSecurity::isSecure(L) && name.compare(0, 7, "secure.") == 0)
44                 throw LuaError("Attempt to set secure setting.");
45
46         bool is_mainmenu = false;
47 #ifndef SERVER
48         is_mainmenu = ModApiBase::getGuiEngine(L) != nullptr;
49 #endif
50         if (!is_mainmenu && (name == "mg_name" || name == "mg_flags")) {
51                 errorstream << "Tried to set global setting " << name << ", ignoring. "
52                         "minetest.set_mapgen_setting() should be used instead." << std::endl;
53                 infostream << script_get_backtrace(L) << std::endl;
54                 return -1;
55         }
56
57         return 0;
58 }
59
60 LuaSettings::LuaSettings(Settings *settings, const std::string &filename) :
61         m_settings(settings),
62         m_filename(filename)
63 {
64 }
65
66 LuaSettings::LuaSettings(const std::string &filename, bool write_allowed) :
67         m_filename(filename),
68         m_is_own_settings(true),
69         m_write_allowed(write_allowed)
70 {
71         m_settings = new Settings();
72         m_settings->readConfigFile(filename.c_str());
73 }
74
75 LuaSettings::~LuaSettings()
76 {
77         if (m_is_own_settings)
78                 delete m_settings;
79 }
80
81
82 void LuaSettings::create(lua_State *L, Settings *settings,
83                 const std::string &filename)
84 {
85         LuaSettings *o = new LuaSettings(settings, filename);
86         *(void **)(lua_newuserdata(L, sizeof(void *))) = o;
87         luaL_getmetatable(L, className);
88         lua_setmetatable(L, -2);
89 }
90
91
92 // garbage collector
93 int LuaSettings::gc_object(lua_State* L)
94 {
95         LuaSettings* o = *(LuaSettings **)(lua_touserdata(L, 1));
96         delete o;
97         return 0;
98 }
99
100
101 // get(self, key) -> value
102 int LuaSettings::l_get(lua_State* L)
103 {
104         NO_MAP_LOCK_REQUIRED;
105         LuaSettings* o = checkobject(L, 1);
106
107         std::string key = std::string(luaL_checkstring(L, 2));
108         if (o->m_settings->exists(key)) {
109                 std::string value = o->m_settings->get(key);
110                 lua_pushstring(L, value.c_str());
111         } else {
112                 lua_pushnil(L);
113         }
114
115         return 1;
116 }
117
118 // get_bool(self, key) -> boolean
119 int LuaSettings::l_get_bool(lua_State* L)
120 {
121         NO_MAP_LOCK_REQUIRED;
122         LuaSettings* o = checkobject(L, 1);
123
124         std::string key = std::string(luaL_checkstring(L, 2));
125         if (o->m_settings->exists(key)) {
126                 bool value = o->m_settings->getBool(key);
127                 lua_pushboolean(L, value);
128         } else {
129                 // Push default value
130                 if (lua_isboolean(L, 3))
131                         lua_pushboolean(L, readParam<bool>(L, 3));
132                 else
133                         lua_pushnil(L);
134         }
135
136         return 1;
137 }
138
139 // get_np_group(self, key) -> value
140 int LuaSettings::l_get_np_group(lua_State *L)
141 {
142         NO_MAP_LOCK_REQUIRED;
143         LuaSettings *o = checkobject(L, 1);
144
145         std::string key = std::string(luaL_checkstring(L, 2));
146         if (o->m_settings->exists(key)) {
147                 NoiseParams np;
148                 o->m_settings->getNoiseParams(key, np);
149                 push_noiseparams(L, &np);
150         } else {
151                 lua_pushnil(L);
152         }
153
154         return 1;
155 }
156
157 // get_flags(self, key) -> table or nil
158 int LuaSettings::l_get_flags(lua_State *L)
159 {
160         NO_MAP_LOCK_REQUIRED;
161         LuaSettings *o = checkobject(L, 1);
162         std::string key = std::string(luaL_checkstring(L, 2));
163
164         u32 flags = 0;
165         auto flagdesc = o->m_settings->getFlagDescFallback(key);
166         if (o->m_settings->getFlagStrNoEx(key, flags, flagdesc)) {
167                 lua_newtable(L);
168                 int table = lua_gettop(L);
169                 for (size_t i = 0; flagdesc[i].name; ++i) {
170                         lua_pushboolean(L, flags & flagdesc[i].flag);
171                         lua_setfield(L, table, flagdesc[i].name);
172                 }
173                 lua_pushvalue(L, table);
174         } else {
175                 lua_pushnil(L);
176         }
177
178         return 1;
179 }
180
181 // set(self, key, value)
182 int LuaSettings::l_set(lua_State* L)
183 {
184         NO_MAP_LOCK_REQUIRED;
185         LuaSettings* o = checkobject(L, 1);
186
187         std::string key = std::string(luaL_checkstring(L, 2));
188         const char* value = luaL_checkstring(L, 3);
189
190         CHECK_SETTING_SECURITY(L, key);
191
192         if (!o->m_settings->set(key, value))
193                 throw LuaError("Invalid sequence found in setting parameters");
194
195         return 0;
196 }
197
198 // set_bool(self, key, value)
199 int LuaSettings::l_set_bool(lua_State* L)
200 {
201         NO_MAP_LOCK_REQUIRED;
202         LuaSettings* o = checkobject(L, 1);
203
204         std::string key = std::string(luaL_checkstring(L, 2));
205         bool value = readParam<bool>(L, 3);
206
207         CHECK_SETTING_SECURITY(L, key);
208
209         o->m_settings->setBool(key, value);
210
211         return 0;
212 }
213
214 // set_np_group(self, key, value)
215 int LuaSettings::l_set_np_group(lua_State *L)
216 {
217         NO_MAP_LOCK_REQUIRED;
218         LuaSettings *o = checkobject(L, 1);
219
220         std::string key = std::string(luaL_checkstring(L, 2));
221         NoiseParams value;
222         read_noiseparams(L, 3, &value);
223
224         CHECK_SETTING_SECURITY(L, key);
225
226         o->m_settings->setNoiseParams(key, value);
227
228         return 0;
229 }
230
231 // remove(self, key) -> success
232 int LuaSettings::l_remove(lua_State* L)
233 {
234         NO_MAP_LOCK_REQUIRED;
235         LuaSettings* o = checkobject(L, 1);
236
237         std::string key = std::string(luaL_checkstring(L, 2));
238
239         CHECK_SETTING_SECURITY(L, key);
240
241         bool success = o->m_settings->remove(key);
242         lua_pushboolean(L, success);
243
244         return 1;
245 }
246
247 // get_names(self) -> {key1, ...}
248 int LuaSettings::l_get_names(lua_State* L)
249 {
250         NO_MAP_LOCK_REQUIRED;
251         LuaSettings* o = checkobject(L, 1);
252
253         std::vector<std::string> keys = o->m_settings->getNames();
254
255         lua_newtable(L);
256         for (unsigned int i=0; i < keys.size(); i++)
257         {
258                 lua_pushstring(L, keys[i].c_str());
259                 lua_rawseti(L, -2, i + 1);
260         }
261
262         return 1;
263 }
264
265 // write(self) -> success
266 int LuaSettings::l_write(lua_State* L)
267 {
268         NO_MAP_LOCK_REQUIRED;
269         LuaSettings* o = checkobject(L, 1);
270
271         if (!o->m_write_allowed) {
272                 throw LuaError("Settings: writing " + o->m_filename +
273                                 " not allowed with mod security on.");
274         }
275
276         bool success = o->m_settings->updateConfigFile(o->m_filename.c_str());
277         lua_pushboolean(L, success);
278
279         return 1;
280 }
281
282 static void push_settings_table(lua_State *L, const Settings *settings)
283 {
284         std::vector<std::string> keys = settings->getNames();
285         lua_newtable(L);
286         for (const std::string &key : keys) {
287                 std::string value;
288                 Settings *group = nullptr;
289
290                 if (settings->getNoEx(key, value)) {
291                         lua_pushstring(L, value.c_str());
292                 } else if (settings->getGroupNoEx(key, group)) {
293                         // Recursively push tables
294                         push_settings_table(L, group);
295                 } else {
296                         // Impossible case (multithreading) due to MutexAutoLock
297                         continue;
298                 }
299
300                 lua_setfield(L, -2, key.c_str());
301         }
302 }
303
304 // to_table(self) -> {[key1]=value1,...}
305 int LuaSettings::l_to_table(lua_State* L)
306 {
307         NO_MAP_LOCK_REQUIRED;
308         LuaSettings* o = checkobject(L, 1);
309
310         MutexAutoLock(o->m_settings->m_mutex);
311         push_settings_table(L, o->m_settings);
312         return 1;
313 }
314
315
316 void LuaSettings::Register(lua_State* L)
317 {
318         lua_newtable(L);
319         int methodtable = lua_gettop(L);
320         luaL_newmetatable(L, className);
321         int metatable = lua_gettop(L);
322
323         lua_pushliteral(L, "__metatable");
324         lua_pushvalue(L, methodtable);
325         lua_settable(L, metatable);  // hide metatable from Lua getmetatable()
326
327         lua_pushliteral(L, "__index");
328         lua_pushvalue(L, methodtable);
329         lua_settable(L, metatable);
330
331         lua_pushliteral(L, "__gc");
332         lua_pushcfunction(L, gc_object);
333         lua_settable(L, metatable);
334
335         lua_pop(L, 1);  // drop metatable
336
337         luaL_register(L, nullptr, methods);  // fill methodtable
338         lua_pop(L, 1);  // drop methodtable
339
340         // Can be created from Lua (Settings(filename))
341         lua_register(L, className, create_object);
342 }
343
344 // LuaSettings(filename)
345 // Creates a LuaSettings and leaves it on top of the stack
346 int LuaSettings::create_object(lua_State* L)
347 {
348         NO_MAP_LOCK_REQUIRED;
349         bool write_allowed = true;
350         const char* filename = luaL_checkstring(L, 1);
351         CHECK_SECURE_PATH_POSSIBLE_WRITE(L, filename, &write_allowed);
352         LuaSettings* o = new LuaSettings(filename, write_allowed);
353         *(void **)(lua_newuserdata(L, sizeof(void *))) = o;
354         luaL_getmetatable(L, className);
355         lua_setmetatable(L, -2);
356         return 1;
357 }
358
359 LuaSettings* LuaSettings::checkobject(lua_State* L, int narg)
360 {
361         NO_MAP_LOCK_REQUIRED;
362         luaL_checktype(L, narg, LUA_TUSERDATA);
363         void *ud = luaL_checkudata(L, narg, className);
364         if (!ud)
365                 luaL_typerror(L, narg, className);
366         return *(LuaSettings**) ud;  // unbox pointer
367 }
368
369 const char LuaSettings::className[] = "Settings";
370 const luaL_Reg LuaSettings::methods[] = {
371         luamethod(LuaSettings, get),
372         luamethod(LuaSettings, get_bool),
373         luamethod(LuaSettings, get_np_group),
374         luamethod(LuaSettings, get_flags),
375         luamethod(LuaSettings, set),
376         luamethod(LuaSettings, set_bool),
377         luamethod(LuaSettings, set_np_group),
378         luamethod(LuaSettings, remove),
379         luamethod(LuaSettings, get_names),
380         luamethod(LuaSettings, write),
381         luamethod(LuaSettings, to_table),
382         {0,0}
383 };