]> git.lizzy.rs Git - minetest.git/blob - src/script/lua_api/l_util.cpp
f990dacf960b535a089c69e50265cc56f6467757
[minetest.git] / src / script / lua_api / l_util.cpp
1 /*
2 Minetest
3 Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
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_util.h"
21 #include "lua_api/l_internal.h"
22 #include "common/c_converter.h"
23 #include "common/c_content.h"
24 #include "cpp_api/s_async.h"
25 #include "serialization.h"
26 #include "json/json.h"
27 #include "cpp_api/s_security.h"
28 #include "porting.h"
29 #include "debug.h"
30 #include "log.h"
31 #include "tool.h"
32 #include "filesys.h"
33 #include "settings.h"
34 #include "util/auth.h"
35 #include <algorithm>
36
37 // log([level,] text)
38 // Writes a line to the logger.
39 // The one-argument version logs to infostream.
40 // The two-argument version accepts a log level.
41 // Either the special case "deprecated" for deprecation notices, or any specified in
42 // Logger::stringToLevel(name).
43 int ModApiUtil::l_log(lua_State *L)
44 {
45         NO_MAP_LOCK_REQUIRED;
46         std::string text;
47         LogLevel level = LL_NONE;
48         if (lua_isnone(L, 2)) {
49                 text = luaL_checkstring(L, 1);
50         } else {
51                 std::string name = luaL_checkstring(L, 1);
52                 text = luaL_checkstring(L, 2);
53                 if (name == "deprecated") {
54                         log_deprecated(L, text);
55                         return 0;
56                 }
57                 level = Logger::stringToLevel(name);
58                 if (level == LL_MAX) {
59                         warningstream << "Tried to log at unknown level '" << name
60                                 << "'.  Defaulting to \"none\"." << std::endl;
61                         level = LL_NONE;
62                 }
63         }
64         g_logger.log(level, text);
65         return 0;
66 }
67
68 // get_us_time()
69 int ModApiUtil::l_get_us_time(lua_State *L)
70 {
71         NO_MAP_LOCK_REQUIRED;
72         lua_pushnumber(L, porting::getTimeUs());
73         return 1;
74 }
75
76 #define CHECK_SECURE_SETTING(L, name) \
77         if (ScriptApiSecurity::isSecure(L) && \
78                         name.compare(0, 7, "secure.") == 0) { \
79                 throw LuaError("Attempt to set secure setting."); \
80         }
81
82 // setting_set(name, value)
83 int ModApiUtil::l_setting_set(lua_State *L)
84 {
85         NO_MAP_LOCK_REQUIRED;
86         std::string name = luaL_checkstring(L, 1);
87         std::string value = luaL_checkstring(L, 2);
88         CHECK_SECURE_SETTING(L, name);
89         g_settings->set(name, value);
90         return 0;
91 }
92
93 // setting_get(name)
94 int ModApiUtil::l_setting_get(lua_State *L)
95 {
96         NO_MAP_LOCK_REQUIRED;
97         const char *name = luaL_checkstring(L, 1);
98         try{
99                 std::string value = g_settings->get(name);
100                 lua_pushstring(L, value.c_str());
101         } catch(SettingNotFoundException &e){
102                 lua_pushnil(L);
103         }
104         return 1;
105 }
106
107 // setting_setbool(name)
108 int ModApiUtil::l_setting_setbool(lua_State *L)
109 {
110         NO_MAP_LOCK_REQUIRED;
111         std::string name = luaL_checkstring(L, 1);
112         bool value = lua_toboolean(L, 2);
113         CHECK_SECURE_SETTING(L, name);
114         g_settings->setBool(name, value);
115         return 0;
116 }
117
118 // setting_getbool(name)
119 int ModApiUtil::l_setting_getbool(lua_State *L)
120 {
121         NO_MAP_LOCK_REQUIRED;
122         const char *name = luaL_checkstring(L, 1);
123         try{
124                 bool value = g_settings->getBool(name);
125                 lua_pushboolean(L, value);
126         } catch(SettingNotFoundException &e){
127                 lua_pushnil(L);
128         }
129         return 1;
130 }
131
132 // setting_save()
133 int ModApiUtil::l_setting_save(lua_State *L)
134 {
135         NO_MAP_LOCK_REQUIRED;
136         if(g_settings_path != "")
137                 g_settings->updateConfigFile(g_settings_path.c_str());
138         return 0;
139 }
140
141 // parse_json(str[, nullvalue])
142 int ModApiUtil::l_parse_json(lua_State *L)
143 {
144         NO_MAP_LOCK_REQUIRED;
145
146         const char *jsonstr = luaL_checkstring(L, 1);
147
148         // Use passed nullvalue or default to nil
149         int nullindex = 2;
150         if (lua_isnone(L, nullindex)) {
151                 lua_pushnil(L);
152                 nullindex = lua_gettop(L);
153         }
154
155         Json::Value root;
156
157         {
158                 Json::Reader reader;
159                 std::istringstream stream(jsonstr);
160
161                 if (!reader.parse(stream, root)) {
162                         errorstream << "Failed to parse json data "
163                                 << reader.getFormattedErrorMessages();
164                         size_t jlen = strlen(jsonstr);
165                         if (jlen > 100) {
166                                 errorstream << "Data (" << jlen
167                                         << " bytes) printed to warningstream." << std::endl;
168                                 warningstream << "data: \"" << jsonstr << "\"" << std::endl;
169                         } else {
170                                 errorstream << "data: \"" << jsonstr << "\"" << std::endl;
171                         }
172                         lua_pushnil(L);
173                         return 1;
174                 }
175         }
176
177         if (!push_json_value(L, root, nullindex)) {
178                 errorstream << "Failed to parse json data, "
179                         << "depth exceeds lua stack limit" << std::endl;
180                 errorstream << "data: \"" << jsonstr << "\"" << std::endl;
181                 lua_pushnil(L);
182         }
183         return 1;
184 }
185
186 // write_json(data[, styled]) -> string or nil and error message
187 int ModApiUtil::l_write_json(lua_State *L)
188 {
189         NO_MAP_LOCK_REQUIRED;
190
191         bool styled = false;
192         if (!lua_isnone(L, 2)) {
193                 styled = lua_toboolean(L, 2);
194                 lua_pop(L, 1);
195         }
196
197         Json::Value root;
198         try {
199                 read_json_value(L, root, 1);
200         } catch (SerializationError &e) {
201                 lua_pushnil(L);
202                 lua_pushstring(L, e.what());
203                 return 2;
204         }
205
206         std::string out;
207         if (styled) {
208                 Json::StyledWriter writer;
209                 out = writer.write(root);
210         } else {
211                 Json::FastWriter writer;
212                 out = writer.write(root);
213         }
214         lua_pushlstring(L, out.c_str(), out.size());
215         return 1;
216 }
217
218 // get_dig_params(groups, tool_capabilities[, time_from_last_punch])
219 int ModApiUtil::l_get_dig_params(lua_State *L)
220 {
221         NO_MAP_LOCK_REQUIRED;
222         std::map<std::string, int> groups;
223         read_groups(L, 1, groups);
224         ToolCapabilities tp = read_tool_capabilities(L, 2);
225         if(lua_isnoneornil(L, 3))
226                 push_dig_params(L, getDigParams(groups, &tp));
227         else
228                 push_dig_params(L, getDigParams(groups, &tp,
229                                         luaL_checknumber(L, 3)));
230         return 1;
231 }
232
233 // get_hit_params(groups, tool_capabilities[, time_from_last_punch])
234 int ModApiUtil::l_get_hit_params(lua_State *L)
235 {
236         NO_MAP_LOCK_REQUIRED;
237         std::map<std::string, int> groups;
238         read_groups(L, 1, groups);
239         ToolCapabilities tp = read_tool_capabilities(L, 2);
240         if(lua_isnoneornil(L, 3))
241                 push_hit_params(L, getHitParams(groups, &tp));
242         else
243                 push_hit_params(L, getHitParams(groups, &tp,
244                                         luaL_checknumber(L, 3)));
245         return 1;
246 }
247
248 // get_password_hash(name, raw_password)
249 int ModApiUtil::l_get_password_hash(lua_State *L)
250 {
251         NO_MAP_LOCK_REQUIRED;
252         std::string name = luaL_checkstring(L, 1);
253         std::string raw_password = luaL_checkstring(L, 2);
254         std::string hash = translatePassword(name, raw_password);
255         lua_pushstring(L, hash.c_str());
256         return 1;
257 }
258
259 // is_yes(arg)
260 int ModApiUtil::l_is_yes(lua_State *L)
261 {
262         NO_MAP_LOCK_REQUIRED;
263
264         lua_getglobal(L, "tostring"); // function to be called
265         lua_pushvalue(L, 1); // 1st argument
266         lua_call(L, 1, 1); // execute function
267         std::string str(lua_tostring(L, -1)); // get result
268         lua_pop(L, 1);
269
270         bool yes = is_yes(str);
271         lua_pushboolean(L, yes);
272         return 1;
273 }
274
275 int ModApiUtil::l_get_builtin_path(lua_State *L)
276 {
277         NO_MAP_LOCK_REQUIRED;
278
279         std::string path = porting::path_share + DIR_DELIM + "builtin";
280         lua_pushstring(L, path.c_str());
281         return 1;
282 }
283
284 // compress(data, method, level)
285 int ModApiUtil::l_compress(lua_State *L)
286 {
287         NO_MAP_LOCK_REQUIRED;
288
289         size_t size;
290         const char *data = luaL_checklstring(L, 1, &size);
291
292         int level = -1;
293         if (!lua_isnone(L, 3) && !lua_isnil(L, 3))
294                 level = luaL_checknumber(L, 3);
295
296         std::ostringstream os;
297         compressZlib(std::string(data, size), os, level);
298
299         std::string out = os.str();
300
301         lua_pushlstring(L, out.data(), out.size());
302         return 1;
303 }
304
305 // decompress(data, method)
306 int ModApiUtil::l_decompress(lua_State *L)
307 {
308         NO_MAP_LOCK_REQUIRED;
309
310         size_t size;
311         const char *data = luaL_checklstring(L, 1, &size);
312
313         std::istringstream is(std::string(data, size));
314         std::ostringstream os;
315         decompressZlib(is, os);
316
317         std::string out = os.str();
318
319         lua_pushlstring(L, out.data(), out.size());
320         return 1;
321 }
322
323 // mkdir(path)
324 int ModApiUtil::l_mkdir(lua_State *L)
325 {
326         NO_MAP_LOCK_REQUIRED;
327         const char *path = luaL_checkstring(L, 1);
328         CHECK_SECURE_PATH_OPTIONAL(L, path);
329         lua_pushboolean(L, fs::CreateAllDirs(path));
330         return 1;
331 }
332
333 // get_dir_list(path, is_dir)
334 int ModApiUtil::l_get_dir_list(lua_State *L)
335 {
336         NO_MAP_LOCK_REQUIRED;
337         const char *path = luaL_checkstring(L, 1);
338         short is_dir = lua_isboolean(L, 2) ? lua_toboolean(L, 2) : -1;
339
340         CHECK_SECURE_PATH_OPTIONAL(L, path);
341
342         std::vector<fs::DirListNode> list = fs::GetDirListing(path);
343
344         int index = 0;
345         lua_newtable(L);
346
347         for (size_t i = 0; i < list.size(); i++) {
348                 if (is_dir == -1 || is_dir == list[i].dir) {
349                         lua_pushstring(L, list[i].name.c_str());
350                         lua_rawseti(L, -2, ++index);
351                 }
352         }
353
354         return 1;
355 }
356
357 int ModApiUtil::l_request_insecure_environment(lua_State *L)
358 {
359         NO_MAP_LOCK_REQUIRED;
360
361         // Just return _G if security is disabled
362         if (!ScriptApiSecurity::isSecure(L)) {
363                 lua_getglobal(L, "_G");
364                 return 1;
365         }
366
367         // We have to make sure that this function is being called directly by
368         // a mod, otherwise a malicious mod could override this function and
369         // steal its return value.
370         lua_Debug info;
371         // Make sure there's only one item below this function on the stack...
372         if (lua_getstack(L, 2, &info)) {
373                 return 0;
374         }
375         FATAL_ERROR_IF(!lua_getstack(L, 1, &info), "lua_getstack() failed");
376         FATAL_ERROR_IF(!lua_getinfo(L, "S", &info), "lua_getinfo() failed");
377         // ...and that that item is the main file scope.
378         if (strcmp(info.what, "main") != 0) {
379                 return 0;
380         }
381
382         // Get mod name
383         lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_CURRENT_MOD_NAME);
384         if (!lua_isstring(L, -1)) {
385                 return 0;
386         }
387
388         // Check secure.trusted_mods
389         const char *mod_name = lua_tostring(L, -1);
390         std::string trusted_mods = g_settings->get("secure.trusted_mods");
391         trusted_mods.erase(std::remove(trusted_mods.begin(),
392                         trusted_mods.end(), ' '), trusted_mods.end());
393         std::vector<std::string> mod_list = str_split(trusted_mods, ',');
394         if (std::find(mod_list.begin(), mod_list.end(), mod_name) ==
395                         mod_list.end()) {
396                 return 0;
397         }
398
399         // Push insecure environment
400         lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_GLOBALS_BACKUP);
401         return 1;
402 }
403
404
405 void ModApiUtil::Initialize(lua_State *L, int top)
406 {
407         API_FCT(log);
408
409         API_FCT(get_us_time);
410
411         API_FCT(setting_set);
412         API_FCT(setting_get);
413         API_FCT(setting_setbool);
414         API_FCT(setting_getbool);
415         API_FCT(setting_save);
416
417         API_FCT(parse_json);
418         API_FCT(write_json);
419
420         API_FCT(get_dig_params);
421         API_FCT(get_hit_params);
422
423         API_FCT(get_password_hash);
424
425         API_FCT(is_yes);
426
427         API_FCT(get_builtin_path);
428
429         API_FCT(compress);
430         API_FCT(decompress);
431
432         API_FCT(mkdir);
433         API_FCT(get_dir_list);
434
435         API_FCT(request_insecure_environment);
436 }
437
438 void ModApiUtil::InitializeAsync(AsyncEngine& engine)
439 {
440         ASYNC_API_FCT(log);
441
442         ASYNC_API_FCT(get_us_time);
443
444         //ASYNC_API_FCT(setting_set);
445         ASYNC_API_FCT(setting_get);
446         //ASYNC_API_FCT(setting_setbool);
447         ASYNC_API_FCT(setting_getbool);
448         //ASYNC_API_FCT(setting_save);
449
450         ASYNC_API_FCT(parse_json);
451         ASYNC_API_FCT(write_json);
452
453         ASYNC_API_FCT(is_yes);
454
455         ASYNC_API_FCT(get_builtin_path);
456
457         ASYNC_API_FCT(compress);
458         ASYNC_API_FCT(decompress);
459
460         ASYNC_API_FCT(mkdir);
461         ASYNC_API_FCT(get_dir_list);
462 }
463