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