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