]> git.lizzy.rs Git - dragonfireclient.git/blob - src/script/lua_api/l_util.cpp
Add helper functions to make tool usable n times (#12047)
[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 "irrlichttypes_extrabloated.h"
21 #include "lua_api/l_util.h"
22 #include "lua_api/l_internal.h"
23 #include "lua_api/l_settings.h"
24 #include "common/c_converter.h"
25 #include "common/c_content.h"
26 #include "cpp_api/s_async.h"
27 #include "serialization.h"
28 #include <json/json.h>
29 #include "cpp_api/s_security.h"
30 #include "porting.h"
31 #include "convert_json.h"
32 #include "debug.h"
33 #include "log.h"
34 #include "tool.h"
35 #include "filesys.h"
36 #include "settings.h"
37 #include "util/auth.h"
38 #include "util/base64.h"
39 #include "config.h"
40 #include "version.h"
41 #include "util/hex.h"
42 #include "util/sha1.h"
43 #include "util/png.h"
44 #include <cstdio>
45
46 // log([level,] text)
47 // Writes a line to the logger.
48 // The one-argument version logs to LL_NONE.
49 // The two-argument version accepts a log level.
50 // Either the special case "deprecated" for deprecation notices, or any specified in
51 // Logger::stringToLevel(name).
52 int ModApiUtil::l_log(lua_State *L)
53 {
54         NO_MAP_LOCK_REQUIRED;
55         std::string text;
56         LogLevel level = LL_NONE;
57         if (lua_isnone(L, 2)) {
58                 text = luaL_checkstring(L, 1);
59         } else {
60                 std::string name = luaL_checkstring(L, 1);
61                 text = luaL_checkstring(L, 2);
62                 if (name == "deprecated") {
63                         log_deprecated(L, text, 2);
64                         return 0;
65                 }
66                 level = Logger::stringToLevel(name);
67                 if (level == LL_MAX) {
68                         warningstream << "Tried to log at unknown level '" << name
69                                 << "'.  Defaulting to \"none\"." << std::endl;
70                         level = LL_NONE;
71                 }
72         }
73         g_logger.log(level, text);
74         return 0;
75 }
76
77 // get_us_time()
78 int ModApiUtil::l_get_us_time(lua_State *L)
79 {
80         NO_MAP_LOCK_REQUIRED;
81         lua_pushnumber(L, porting::getTimeUs());
82         return 1;
83 }
84
85 // parse_json(str[, nullvalue])
86 int ModApiUtil::l_parse_json(lua_State *L)
87 {
88         NO_MAP_LOCK_REQUIRED;
89
90         const char *jsonstr = luaL_checkstring(L, 1);
91
92         // Use passed nullvalue or default to nil
93         int nullindex = 2;
94         if (lua_isnone(L, nullindex)) {
95                 lua_pushnil(L);
96                 nullindex = lua_gettop(L);
97         }
98
99         Json::Value root;
100
101         {
102                 std::istringstream stream(jsonstr);
103
104                 Json::CharReaderBuilder builder;
105                 builder.settings_["collectComments"] = false;
106                 std::string errs;
107
108                 if (!Json::parseFromStream(builder, stream, &root, &errs)) {
109                         errorstream << "Failed to parse json data " << errs << std::endl;
110                         size_t jlen = strlen(jsonstr);
111                         if (jlen > 100) {
112                                 errorstream << "Data (" << jlen
113                                         << " bytes) printed to warningstream." << std::endl;
114                                 warningstream << "data: \"" << jsonstr << "\"" << std::endl;
115                         } else {
116                                 errorstream << "data: \"" << jsonstr << "\"" << std::endl;
117                         }
118                         lua_pushnil(L);
119                         return 1;
120                 }
121         }
122
123         if (!push_json_value(L, root, nullindex)) {
124                 errorstream << "Failed to parse json data, "
125                         << "depth exceeds lua stack limit" << std::endl;
126                 errorstream << "data: \"" << jsonstr << "\"" << std::endl;
127                 lua_pushnil(L);
128         }
129         return 1;
130 }
131
132 // write_json(data[, styled]) -> string or nil and error message
133 int ModApiUtil::l_write_json(lua_State *L)
134 {
135         NO_MAP_LOCK_REQUIRED;
136
137         bool styled = false;
138         if (!lua_isnone(L, 2)) {
139                 styled = readParam<bool>(L, 2);
140                 lua_pop(L, 1);
141         }
142
143         Json::Value root;
144         try {
145                 read_json_value(L, root, 1);
146         } catch (SerializationError &e) {
147                 lua_pushnil(L);
148                 lua_pushstring(L, e.what());
149                 return 2;
150         }
151
152         std::string out;
153         if (styled) {
154                 out = root.toStyledString();
155         } else {
156                 out = fastWriteJson(root);
157         }
158         lua_pushlstring(L, out.c_str(), out.size());
159         return 1;
160 }
161
162 // get_tool_wear_after_use(uses[, initial_wear])
163 int ModApiUtil::l_get_tool_wear_after_use(lua_State *L)
164 {
165         NO_MAP_LOCK_REQUIRED;
166         u32 uses = readParam<int>(L, 1);
167         u16 initial_wear = readParam<int>(L, 2, 0);
168         u16 wear = calculateResultWear(uses, initial_wear);
169         lua_pushnumber(L, wear);
170         return 1;
171 }
172
173 // get_dig_params(groups, tool_capabilities[, wear])
174 int ModApiUtil::l_get_dig_params(lua_State *L)
175 {
176         NO_MAP_LOCK_REQUIRED;
177         ItemGroupList groups;
178         read_groups(L, 1, groups);
179         ToolCapabilities tp = read_tool_capabilities(L, 2);
180         if (lua_isnoneornil(L, 3)) {
181                 push_dig_params(L, getDigParams(groups, &tp));
182         } else {
183                 u16 wear = readParam<int>(L, 3);
184                 push_dig_params(L, getDigParams(groups, &tp, wear));
185         }
186         return 1;
187 }
188
189 // get_hit_params(groups, tool_capabilities[, time_from_last_punch, [, wear]])
190 int ModApiUtil::l_get_hit_params(lua_State *L)
191 {
192         NO_MAP_LOCK_REQUIRED;
193         std::unordered_map<std::string, int> groups;
194         read_groups(L, 1, groups);
195         ToolCapabilities tp = read_tool_capabilities(L, 2);
196         float time_from_last_punch = readParam<float>(L, 3, 1000000);
197         int wear = readParam<int>(L, 4, 0);
198         push_hit_params(L, getHitParams(groups, &tp,
199                 time_from_last_punch, wear));
200         return 1;
201 }
202
203 // check_password_entry(name, entry, password)
204 int ModApiUtil::l_check_password_entry(lua_State *L)
205 {
206         NO_MAP_LOCK_REQUIRED;
207         std::string name = luaL_checkstring(L, 1);
208         std::string entry = luaL_checkstring(L, 2);
209         std::string password = luaL_checkstring(L, 3);
210
211         if (base64_is_valid(entry)) {
212                 std::string hash = translate_password(name, password);
213                 lua_pushboolean(L, hash == entry);
214                 return 1;
215         }
216
217         std::string salt;
218         std::string verifier;
219
220         if (!decode_srp_verifier_and_salt(entry, &verifier, &salt)) {
221                 // invalid format
222                 warningstream << "Invalid password format for " << name << std::endl;
223                 lua_pushboolean(L, false);
224                 return 1;
225         }
226         std::string gen_verifier = generate_srp_verifier(name, password, salt);
227
228         lua_pushboolean(L, gen_verifier == verifier);
229         return 1;
230 }
231
232 // get_password_hash(name, raw_password)
233 int ModApiUtil::l_get_password_hash(lua_State *L)
234 {
235         NO_MAP_LOCK_REQUIRED;
236         std::string name = luaL_checkstring(L, 1);
237         std::string raw_password = luaL_checkstring(L, 2);
238         std::string hash = translate_password(name, raw_password);
239         lua_pushstring(L, hash.c_str());
240         return 1;
241 }
242
243 // is_yes(arg)
244 int ModApiUtil::l_is_yes(lua_State *L)
245 {
246         NO_MAP_LOCK_REQUIRED;
247
248         lua_getglobal(L, "tostring"); // function to be called
249         lua_pushvalue(L, 1); // 1st argument
250         lua_call(L, 1, 1); // execute function
251         std::string str = readParam<std::string>(L, -1); // get result
252         lua_pop(L, 1);
253
254         bool yes = is_yes(str);
255         lua_pushboolean(L, yes);
256         return 1;
257 }
258
259 // get_builtin_path()
260 int ModApiUtil::l_get_builtin_path(lua_State *L)
261 {
262         NO_MAP_LOCK_REQUIRED;
263
264         std::string path = porting::path_share + DIR_DELIM + "builtin" + DIR_DELIM;
265         lua_pushstring(L, path.c_str());
266
267         return 1;
268 }
269
270 // get_user_path()
271 int ModApiUtil::l_get_user_path(lua_State *L)
272 {
273         NO_MAP_LOCK_REQUIRED;
274
275         std::string path = porting::path_user;
276         lua_pushstring(L, path.c_str());
277
278         return 1;
279 }
280
281 // compress(data, method, level)
282 int ModApiUtil::l_compress(lua_State *L)
283 {
284         NO_MAP_LOCK_REQUIRED;
285
286         size_t size;
287         const char *data = luaL_checklstring(L, 1, &size);
288
289         int level = -1;
290         if (!lua_isnoneornil(L, 3))
291                 level = readParam<int>(L, 3);
292
293         std::ostringstream os(std::ios_base::binary);
294         compressZlib(reinterpret_cast<const u8 *>(data), size, os, level);
295
296         std::string out = os.str();
297
298         lua_pushlstring(L, out.data(), out.size());
299         return 1;
300 }
301
302 // decompress(data, method)
303 int ModApiUtil::l_decompress(lua_State *L)
304 {
305         NO_MAP_LOCK_REQUIRED;
306
307         size_t size;
308         const char *data = luaL_checklstring(L, 1, &size);
309
310         std::istringstream is(std::string(data, size), std::ios_base::binary);
311         std::ostringstream os(std::ios_base::binary);
312         decompressZlib(is, os);
313
314         std::string out = os.str();
315
316         lua_pushlstring(L, out.data(), out.size());
317         return 1;
318 }
319
320 // encode_base64(string)
321 int ModApiUtil::l_encode_base64(lua_State *L)
322 {
323         NO_MAP_LOCK_REQUIRED;
324
325         size_t size;
326         const char *data = luaL_checklstring(L, 1, &size);
327
328         std::string out = base64_encode((const unsigned char *)(data), size);
329
330         lua_pushlstring(L, out.data(), out.size());
331         return 1;
332 }
333
334 // decode_base64(string)
335 int ModApiUtil::l_decode_base64(lua_State *L)
336 {
337         NO_MAP_LOCK_REQUIRED;
338
339         size_t size;
340         const char *d = luaL_checklstring(L, 1, &size);
341         const std::string data = std::string(d, size);
342
343         if (!base64_is_valid(data))
344                 return 0;
345
346         std::string out = base64_decode(data);
347
348         lua_pushlstring(L, out.data(), out.size());
349         return 1;
350 }
351
352 // mkdir(path)
353 int ModApiUtil::l_mkdir(lua_State *L)
354 {
355         NO_MAP_LOCK_REQUIRED;
356         const char *path = luaL_checkstring(L, 1);
357         CHECK_SECURE_PATH(L, path, true);
358         lua_pushboolean(L, fs::CreateAllDirs(path));
359         return 1;
360 }
361
362 // rmdir(path, recursive)
363 int ModApiUtil::l_rmdir(lua_State *L)
364 {
365         NO_MAP_LOCK_REQUIRED;
366         const char *path = luaL_checkstring(L, 1);
367         CHECK_SECURE_PATH(L, path, true);
368
369         bool recursive = readParam<bool>(L, 2, false);
370
371         if (recursive)
372                 lua_pushboolean(L, fs::RecursiveDelete(path));
373         else
374                 lua_pushboolean(L, fs::DeleteSingleFileOrEmptyDirectory(path));
375
376         return 1;
377 }
378
379 // cpdir(source, destination)
380 int ModApiUtil::l_cpdir(lua_State *L)
381 {
382         NO_MAP_LOCK_REQUIRED;
383         const char *source = luaL_checkstring(L, 1);
384         const char *destination = luaL_checkstring(L, 2);
385         CHECK_SECURE_PATH(L, source, false);
386         CHECK_SECURE_PATH(L, destination, true);
387
388         lua_pushboolean(L, fs::CopyDir(source, destination));
389         return 1;
390 }
391
392 // mpdir(source, destination)
393 int ModApiUtil::l_mvdir(lua_State *L)
394 {
395         NO_MAP_LOCK_REQUIRED;
396         const char *source = luaL_checkstring(L, 1);
397         const char *destination = luaL_checkstring(L, 2);
398         CHECK_SECURE_PATH(L, source, true);
399         CHECK_SECURE_PATH(L, destination, true);
400
401         lua_pushboolean(L, fs::MoveDir(source, destination));
402         return 1;
403 }
404
405 // get_dir_list(path, is_dir)
406 int ModApiUtil::l_get_dir_list(lua_State *L)
407 {
408         NO_MAP_LOCK_REQUIRED;
409         const char *path = luaL_checkstring(L, 1);
410         bool list_all = !lua_isboolean(L, 2); // if its not a boolean list all
411         bool list_dirs = readParam<bool>(L, 2); // true: list dirs, false: list files
412
413         CHECK_SECURE_PATH(L, path, false);
414
415         std::vector<fs::DirListNode> list = fs::GetDirListing(path);
416
417         int index = 0;
418         lua_newtable(L);
419
420         for (const fs::DirListNode &dln : list) {
421                 if (list_all || list_dirs == dln.dir) {
422                         lua_pushstring(L, dln.name.c_str());
423                         lua_rawseti(L, -2, ++index);
424                 }
425         }
426
427         return 1;
428 }
429
430 // safe_file_write(path, content)
431 int ModApiUtil::l_safe_file_write(lua_State *L)
432 {
433         NO_MAP_LOCK_REQUIRED;
434         const char *path = luaL_checkstring(L, 1);
435         size_t size;
436         const char *content = luaL_checklstring(L, 2, &size);
437
438         CHECK_SECURE_PATH(L, path, true);
439
440         bool ret = fs::safeWriteToFile(path, std::string(content, size));
441         lua_pushboolean(L, ret);
442
443         return 1;
444 }
445
446 // request_insecure_environment()
447 int ModApiUtil::l_request_insecure_environment(lua_State *L)
448 {
449         NO_MAP_LOCK_REQUIRED;
450
451         // Just return _G if security is disabled
452         if (!ScriptApiSecurity::isSecure(L)) {
453                 lua_getglobal(L, "_G");
454                 return 1;
455         }
456
457         if (!ScriptApiSecurity::checkWhitelisted(L, "secure.trusted_mods")) {
458                 return 0;
459         }
460
461         // Push insecure environment
462         lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_GLOBALS_BACKUP);
463         return 1;
464 }
465
466 // get_version()
467 int ModApiUtil::l_get_version(lua_State *L)
468 {
469         lua_createtable(L, 0, 3);
470         int table = lua_gettop(L);
471
472         lua_pushstring(L, PROJECT_NAME_C);
473         lua_setfield(L, table, "project");
474
475         lua_pushstring(L, g_version_string);
476         lua_setfield(L, table, "string");
477
478         if (strcmp(g_version_string, g_version_hash) != 0) {
479                 lua_pushstring(L, g_version_hash);
480                 lua_setfield(L, table, "hash");
481         }
482
483         return 1;
484 }
485
486 int ModApiUtil::l_sha1(lua_State *L)
487 {
488         NO_MAP_LOCK_REQUIRED;
489         size_t size;
490         const char *data = luaL_checklstring(L, 1, &size);
491         bool hex = !lua_isboolean(L, 2) || !readParam<bool>(L, 2);
492
493         // Compute actual checksum of data
494         std::string data_sha1;
495         {
496                 SHA1 ctx;
497                 ctx.addBytes(data, size);
498                 unsigned char *data_tmpdigest = ctx.getDigest();
499                 data_sha1.assign((char*) data_tmpdigest, 20);
500                 free(data_tmpdigest);
501         }
502
503         if (hex) {
504                 std::string sha1_hex = hex_encode(data_sha1);
505                 lua_pushstring(L, sha1_hex.c_str());
506         } else {
507                 lua_pushlstring(L, data_sha1.data(), data_sha1.size());
508         }
509
510         return 1;
511 }
512
513 // colorspec_to_colorstring(colorspec)
514 int ModApiUtil::l_colorspec_to_colorstring(lua_State *L)
515 {
516         NO_MAP_LOCK_REQUIRED;
517
518         video::SColor color(0);
519         if (read_color(L, 1, &color)) {
520                 char colorstring[10];
521                 snprintf(colorstring, 10, "#%02X%02X%02X%02X",
522                         color.getRed(), color.getGreen(), color.getBlue(), color.getAlpha());
523                 lua_pushstring(L, colorstring);
524                 return 1;
525         }
526
527         return 0;
528 }
529
530 // colorspec_to_bytes(colorspec)
531 int ModApiUtil::l_colorspec_to_bytes(lua_State *L)
532 {
533         NO_MAP_LOCK_REQUIRED;
534
535         video::SColor color(0);
536         if (read_color(L, 1, &color)) {
537                 u8 colorbytes[4] = {
538                         (u8) color.getRed(),
539                         (u8) color.getGreen(),
540                         (u8) color.getBlue(),
541                         (u8) color.getAlpha(),
542                 };
543                 lua_pushlstring(L, (const char*) colorbytes, 4);
544                 return 1;
545         }
546
547         return 0;
548 }
549
550 // encode_png(w, h, data, level)
551 int ModApiUtil::l_encode_png(lua_State *L)
552 {
553         NO_MAP_LOCK_REQUIRED;
554
555         // The args are already pre-validated on the lua side.
556         u32 width = readParam<int>(L, 1);
557         u32 height = readParam<int>(L, 2);
558         const char *data = luaL_checklstring(L, 3, NULL);
559         s32 compression = readParam<int>(L, 4);
560
561         std::string out = encodePNG((const u8*)data, width, height, compression);
562
563         lua_pushlstring(L, out.data(), out.size());
564         return 1;
565 }
566
567 // get_last_run_mod()
568 int ModApiUtil::l_get_last_run_mod(lua_State *L)
569 {
570         NO_MAP_LOCK_REQUIRED;
571
572         lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_CURRENT_MOD_NAME);
573         std::string current_mod = readParam<std::string>(L, -1, "");
574         if (current_mod.empty()) {
575                 lua_pop(L, 1);
576                 lua_pushstring(L, getScriptApiBase(L)->getOrigin().c_str());
577         }
578         return 1;
579 }
580
581 // set_last_run_mod(modname)
582 int ModApiUtil::l_set_last_run_mod(lua_State *L)
583 {
584         NO_MAP_LOCK_REQUIRED;
585
586         const char *mod = luaL_checkstring(L, 1);
587         getScriptApiBase(L)->setOriginDirect(mod);
588         return 0;
589 }
590
591 void ModApiUtil::Initialize(lua_State *L, int top)
592 {
593         API_FCT(log);
594
595         API_FCT(get_us_time);
596
597         API_FCT(parse_json);
598         API_FCT(write_json);
599
600         API_FCT(get_tool_wear_after_use);
601         API_FCT(get_dig_params);
602         API_FCT(get_hit_params);
603
604         API_FCT(check_password_entry);
605         API_FCT(get_password_hash);
606
607         API_FCT(is_yes);
608
609         API_FCT(get_builtin_path);
610         API_FCT(get_user_path);
611
612         API_FCT(compress);
613         API_FCT(decompress);
614
615         API_FCT(mkdir);
616         API_FCT(rmdir);
617         API_FCT(cpdir);
618         API_FCT(mvdir);
619         API_FCT(get_dir_list);
620         API_FCT(safe_file_write);
621
622         API_FCT(request_insecure_environment);
623
624         API_FCT(encode_base64);
625         API_FCT(decode_base64);
626
627         API_FCT(get_version);
628         API_FCT(sha1);
629         API_FCT(colorspec_to_colorstring);
630         API_FCT(colorspec_to_bytes);
631
632         API_FCT(encode_png);
633
634         API_FCT(get_last_run_mod);
635         API_FCT(set_last_run_mod);
636
637         LuaSettings::create(L, g_settings, g_settings_path);
638         lua_setfield(L, top, "settings");
639 }
640
641 void ModApiUtil::InitializeClient(lua_State *L, int top)
642 {
643         API_FCT(log);
644
645         API_FCT(get_us_time);
646
647         API_FCT(parse_json);
648         API_FCT(write_json);
649
650         API_FCT(is_yes);
651
652         API_FCT(compress);
653         API_FCT(decompress);
654
655         API_FCT(encode_base64);
656         API_FCT(decode_base64);
657
658         API_FCT(get_version);
659         API_FCT(sha1);
660         API_FCT(colorspec_to_colorstring);
661         API_FCT(colorspec_to_bytes);
662 }
663
664 void ModApiUtil::InitializeAsync(lua_State *L, int top)
665 {
666         API_FCT(log);
667
668         API_FCT(get_us_time);
669
670         API_FCT(parse_json);
671         API_FCT(write_json);
672
673         API_FCT(is_yes);
674
675         API_FCT(get_builtin_path);
676         API_FCT(get_user_path);
677
678         API_FCT(compress);
679         API_FCT(decompress);
680
681         API_FCT(mkdir);
682         API_FCT(rmdir);
683         API_FCT(cpdir);
684         API_FCT(mvdir);
685         API_FCT(get_dir_list);
686         API_FCT(safe_file_write);
687
688         API_FCT(request_insecure_environment);
689
690         API_FCT(encode_base64);
691         API_FCT(decode_base64);
692
693         API_FCT(get_version);
694         API_FCT(sha1);
695         API_FCT(colorspec_to_colorstring);
696         API_FCT(colorspec_to_bytes);
697
698         API_FCT(encode_png);
699
700         API_FCT(get_last_run_mod);
701         API_FCT(set_last_run_mod);
702
703         LuaSettings::create(L, g_settings, g_settings_path);
704         lua_setfield(L, top, "settings");
705 }