]> git.lizzy.rs Git - dragonfireclient.git/blobdiff - src/script/lua_api/l_util.cpp
Fix number of tool uses being off by 1..32767 (#11110)
[dragonfireclient.git] / src / script / lua_api / l_util.cpp
index c04f09f9016232e4ebb4fe079e5180deb2244552..53319ccfd2a21f569446181be0ba80a63f5a0096 100644 (file)
@@ -17,26 +17,36 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */
 
+#include "irrlichttypes_extrabloated.h"
 #include "lua_api/l_util.h"
 #include "lua_api/l_internal.h"
+#include "lua_api/l_settings.h"
 #include "common/c_converter.h"
 #include "common/c_content.h"
 #include "cpp_api/s_async.h"
 #include "serialization.h"
-#include "json/json.h"
+#include <json/json.h>
 #include "cpp_api/s_security.h"
-#include "areastore.h"
 #include "porting.h"
+#include "convert_json.h"
+#include "debug.h"
 #include "log.h"
 #include "tool.h"
 #include "filesys.h"
 #include "settings.h"
 #include "util/auth.h"
+#include "util/base64.h"
+#include "config.h"
+#include "version.h"
+#include "util/hex.h"
+#include "util/sha1.h"
+#include "util/png.h"
 #include <algorithm>
+#include <cstdio>
 
 // log([level,] text)
 // Writes a line to the logger.
-// The one-argument version logs to infostream.
+// The one-argument version logs to LL_NONE.
 // The two-argument version accepts a log level.
 // Either the special case "deprecated" for deprecation notices, or any specified in
 // Logger::stringToLevel(name).
@@ -51,7 +61,7 @@ int ModApiUtil::l_log(lua_State *L)
                std::string name = luaL_checkstring(L, 1);
                text = luaL_checkstring(L, 2);
                if (name == "deprecated") {
-                       log_deprecated(L, text);
+                       log_deprecated(L, text, 2);
                        return 0;
                }
                level = Logger::stringToLevel(name);
@@ -73,71 +83,6 @@ int ModApiUtil::l_get_us_time(lua_State *L)
        return 1;
 }
 
-#define CHECK_SECURE_SETTING(L, name) \
-       if (name.compare(0, 7, "secure.") == 0) {\
-               lua_pushliteral(L, "Attempt to set secure setting.");\
-               lua_error(L);\
-       }
-
-// setting_set(name, value)
-int ModApiUtil::l_setting_set(lua_State *L)
-{
-       NO_MAP_LOCK_REQUIRED;
-       std::string name = luaL_checkstring(L, 1);
-       std::string value = luaL_checkstring(L, 2);
-       CHECK_SECURE_SETTING(L, name);
-       g_settings->set(name, value);
-       return 0;
-}
-
-// setting_get(name)
-int ModApiUtil::l_setting_get(lua_State *L)
-{
-       NO_MAP_LOCK_REQUIRED;
-       const char *name = luaL_checkstring(L, 1);
-       try{
-               std::string value = g_settings->get(name);
-               lua_pushstring(L, value.c_str());
-       } catch(SettingNotFoundException &e){
-               lua_pushnil(L);
-       }
-       return 1;
-}
-
-// setting_setbool(name)
-int ModApiUtil::l_setting_setbool(lua_State *L)
-{
-       NO_MAP_LOCK_REQUIRED;
-       std::string name = luaL_checkstring(L, 1);
-       bool value = lua_toboolean(L, 2);
-       CHECK_SECURE_SETTING(L, name);
-       g_settings->setBool(name, value);
-       return 0;
-}
-
-// setting_getbool(name)
-int ModApiUtil::l_setting_getbool(lua_State *L)
-{
-       NO_MAP_LOCK_REQUIRED;
-       const char *name = luaL_checkstring(L, 1);
-       try{
-               bool value = g_settings->getBool(name);
-               lua_pushboolean(L, value);
-       } catch(SettingNotFoundException &e){
-               lua_pushnil(L);
-       }
-       return 1;
-}
-
-// setting_save()
-int ModApiUtil::l_setting_save(lua_State *L)
-{
-       NO_MAP_LOCK_REQUIRED;
-       if(g_settings_path != "")
-               g_settings->updateConfigFile(g_settings_path.c_str());
-       return 0;
-}
-
 // parse_json(str[, nullvalue])
 int ModApiUtil::l_parse_json(lua_State *L)
 {
@@ -155,12 +100,14 @@ int ModApiUtil::l_parse_json(lua_State *L)
        Json::Value root;
 
        {
-               Json::Reader reader;
                std::istringstream stream(jsonstr);
 
-               if (!reader.parse(stream, root)) {
-                       errorstream << "Failed to parse json data "
-                               << reader.getFormattedErrorMessages();
+               Json::CharReaderBuilder builder;
+               builder.settings_["collectComments"] = false;
+               std::string errs;
+
+               if (!Json::parseFromStream(builder, stream, &root, &errs)) {
+                       errorstream << "Failed to parse json data " << errs << std::endl;
                        size_t jlen = strlen(jsonstr);
                        if (jlen > 100) {
                                errorstream << "Data (" << jlen
@@ -190,7 +137,7 @@ int ModApiUtil::l_write_json(lua_State *L)
 
        bool styled = false;
        if (!lua_isnone(L, 2)) {
-               styled = lua_toboolean(L, 2);
+               styled = readParam<bool>(L, 2);
                lua_pop(L, 1);
        }
 
@@ -205,43 +152,70 @@ int ModApiUtil::l_write_json(lua_State *L)
 
        std::string out;
        if (styled) {
-               Json::StyledWriter writer;
-               out = writer.write(root);
+               out = root.toStyledString();
        } else {
-               Json::FastWriter writer;
-               out = writer.write(root);
+               out = fastWriteJson(root);
        }
        lua_pushlstring(L, out.c_str(), out.size());
        return 1;
 }
 
-// get_dig_params(groups, tool_capabilities[, time_from_last_punch])
+// get_dig_params(groups, tool_capabilities[, wear])
 int ModApiUtil::l_get_dig_params(lua_State *L)
 {
        NO_MAP_LOCK_REQUIRED;
-       std::map<std::string, int> groups;
+       ItemGroupList groups;
        read_groups(L, 1, groups);
        ToolCapabilities tp = read_tool_capabilities(L, 2);
-       if(lua_isnoneornil(L, 3))
+       if (lua_isnoneornil(L, 3)) {
                push_dig_params(L, getDigParams(groups, &tp));
-       else
-               push_dig_params(L, getDigParams(groups, &tp,
-                                       luaL_checknumber(L, 3)));
+       } else {
+               u16 wear = readParam<int>(L, 3);
+               push_dig_params(L, getDigParams(groups, &tp, wear));
+       }
        return 1;
 }
 
-// get_hit_params(groups, tool_capabilities[, time_from_last_punch])
+// get_hit_params(groups, tool_capabilities[, time_from_last_punch, [, wear]])
 int ModApiUtil::l_get_hit_params(lua_State *L)
 {
        NO_MAP_LOCK_REQUIRED;
-       std::map<std::string, int> groups;
+       std::unordered_map<std::string, int> groups;
        read_groups(L, 1, groups);
        ToolCapabilities tp = read_tool_capabilities(L, 2);
-       if(lua_isnoneornil(L, 3))
-               push_hit_params(L, getHitParams(groups, &tp));
-       else
-               push_hit_params(L, getHitParams(groups, &tp,
-                                       luaL_checknumber(L, 3)));
+       float time_from_last_punch = readParam<float>(L, 3, 1000000);
+       int wear = readParam<int>(L, 4, 0);
+       push_hit_params(L, getHitParams(groups, &tp,
+               time_from_last_punch, wear));
+       return 1;
+}
+
+// check_password_entry(name, entry, password)
+int ModApiUtil::l_check_password_entry(lua_State *L)
+{
+       NO_MAP_LOCK_REQUIRED;
+       std::string name = luaL_checkstring(L, 1);
+       std::string entry = luaL_checkstring(L, 2);
+       std::string password = luaL_checkstring(L, 3);
+
+       if (base64_is_valid(entry)) {
+               std::string hash = translate_password(name, password);
+               lua_pushboolean(L, hash == entry);
+               return 1;
+       }
+
+       std::string salt;
+       std::string verifier;
+
+       if (!decode_srp_verifier_and_salt(entry, &verifier, &salt)) {
+               // invalid format
+               warningstream << "Invalid password format for " << name << std::endl;
+               lua_pushboolean(L, false);
+               return 1;
+       }
+       std::string gen_verifier = generate_srp_verifier(name, password, salt);
+
+       lua_pushboolean(L, gen_verifier == verifier);
        return 1;
 }
 
@@ -251,7 +225,7 @@ int ModApiUtil::l_get_password_hash(lua_State *L)
        NO_MAP_LOCK_REQUIRED;
        std::string name = luaL_checkstring(L, 1);
        std::string raw_password = luaL_checkstring(L, 2);
-       std::string hash = translatePassword(name, raw_password);
+       std::string hash = translate_password(name, raw_password);
        lua_pushstring(L, hash.c_str());
        return 1;
 }
@@ -264,7 +238,7 @@ int ModApiUtil::l_is_yes(lua_State *L)
        lua_getglobal(L, "tostring"); // function to be called
        lua_pushvalue(L, 1); // 1st argument
        lua_call(L, 1, 1); // execute function
-       std::string str(lua_tostring(L, -1)); // get result
+       std::string str = readParam<std::string>(L, -1); // get result
        lua_pop(L, 1);
 
        bool yes = is_yes(str);
@@ -272,12 +246,25 @@ int ModApiUtil::l_is_yes(lua_State *L)
        return 1;
 }
 
+// get_builtin_path()
 int ModApiUtil::l_get_builtin_path(lua_State *L)
 {
        NO_MAP_LOCK_REQUIRED;
 
-       std::string path = porting::path_share + DIR_DELIM + "builtin";
+       std::string path = porting::path_share + DIR_DELIM + "builtin" + DIR_DELIM;
+       lua_pushstring(L, path.c_str());
+
+       return 1;
+}
+
+// get_user_path()
+int ModApiUtil::l_get_user_path(lua_State *L)
+{
+       NO_MAP_LOCK_REQUIRED;
+
+       std::string path = porting::path_user;
        lua_pushstring(L, path.c_str());
+
        return 1;
 }
 
@@ -290,11 +277,11 @@ int ModApiUtil::l_compress(lua_State *L)
        const char *data = luaL_checklstring(L, 1, &size);
 
        int level = -1;
-       if (!lua_isnone(L, 3) && !lua_isnil(L, 3))
-               level = luaL_checknumber(L, 3);
+       if (!lua_isnoneornil(L, 3))
+               level = readParam<int>(L, 3);
 
-       std::ostringstream os;
-       compressZlib(std::string(data, size), os, level);
+       std::ostringstream os(std::ios_base::binary);
+       compressZlib(reinterpret_cast<const u8 *>(data), size, os, level);
 
        std::string out = os.str();
 
@@ -310,8 +297,8 @@ int ModApiUtil::l_decompress(lua_State *L)
        size_t size;
        const char *data = luaL_checklstring(L, 1, &size);
 
-       std::istringstream is(std::string(data, size));
-       std::ostringstream os;
+       std::istringstream is(std::string(data, size), std::ios_base::binary);
+       std::ostringstream os(std::ios_base::binary);
        decompressZlib(is, os);
 
        std::string out = os.str();
@@ -320,12 +307,44 @@ int ModApiUtil::l_decompress(lua_State *L)
        return 1;
 }
 
+// encode_base64(string)
+int ModApiUtil::l_encode_base64(lua_State *L)
+{
+       NO_MAP_LOCK_REQUIRED;
+
+       size_t size;
+       const char *data = luaL_checklstring(L, 1, &size);
+
+       std::string out = base64_encode((const unsigned char *)(data), size);
+
+       lua_pushlstring(L, out.data(), out.size());
+       return 1;
+}
+
+// decode_base64(string)
+int ModApiUtil::l_decode_base64(lua_State *L)
+{
+       NO_MAP_LOCK_REQUIRED;
+
+       size_t size;
+       const char *d = luaL_checklstring(L, 1, &size);
+       const std::string data = std::string(d, size);
+
+       if (!base64_is_valid(data))
+               return 0;
+
+       std::string out = base64_decode(data);
+
+       lua_pushlstring(L, out.data(), out.size());
+       return 1;
+}
+
 // mkdir(path)
 int ModApiUtil::l_mkdir(lua_State *L)
 {
        NO_MAP_LOCK_REQUIRED;
        const char *path = luaL_checkstring(L, 1);
-       CHECK_SECURE_PATH_OPTIONAL(L, path);
+       CHECK_SECURE_PATH(L, path, true);
        lua_pushboolean(L, fs::CreateAllDirs(path));
        return 1;
 }
@@ -335,18 +354,19 @@ int ModApiUtil::l_get_dir_list(lua_State *L)
 {
        NO_MAP_LOCK_REQUIRED;
        const char *path = luaL_checkstring(L, 1);
-       short is_dir = lua_isboolean(L, 2) ? lua_toboolean(L, 2) : -1;
+       bool list_all = !lua_isboolean(L, 2); // if its not a boolean list all
+       bool list_dirs = readParam<bool>(L, 2); // true: list dirs, false: list files
 
-       CHECK_SECURE_PATH_OPTIONAL(L, path);
+       CHECK_SECURE_PATH(L, path, false);
 
        std::vector<fs::DirListNode> list = fs::GetDirListing(path);
 
        int index = 0;
        lua_newtable(L);
 
-       for (size_t i = 0; i < list.size(); i++) {
-               if (is_dir == -1 || is_dir == list[i].dir) {
-                       lua_pushstring(L, list[i].name.c_str());
+       for (const fs::DirListNode &dln : list) {
+               if (list_all || list_dirs == dln.dir) {
+                       lua_pushstring(L, dln.name.c_str());
                        lua_rawseti(L, -2, ++index);
                }
        }
@@ -354,6 +374,23 @@ int ModApiUtil::l_get_dir_list(lua_State *L)
        return 1;
 }
 
+// safe_file_write(path, content)
+int ModApiUtil::l_safe_file_write(lua_State *L)
+{
+       NO_MAP_LOCK_REQUIRED;
+       const char *path = luaL_checkstring(L, 1);
+       size_t size;
+       const char *content = luaL_checklstring(L, 2, &size);
+
+       CHECK_SECURE_PATH(L, path, true);
+
+       bool ret = fs::safeWriteToFile(path, std::string(content, size));
+       lua_pushboolean(L, ret);
+
+       return 1;
+}
+
+// request_insecure_environment()
 int ModApiUtil::l_request_insecure_environment(lua_State *L)
 {
        NO_MAP_LOCK_REQUIRED;
@@ -372,8 +409,8 @@ int ModApiUtil::l_request_insecure_environment(lua_State *L)
        if (lua_getstack(L, 2, &info)) {
                return 0;
        }
-       assert(lua_getstack(L, 1, &info));
-       assert(lua_getinfo(L, "S", &info));
+       FATAL_ERROR_IF(!lua_getstack(L, 1, &info), "lua_getstack() failed");
+       FATAL_ERROR_IF(!lua_getinfo(L, "S", &info), "lua_getinfo() failed");
        // ...and that that item is the main file scope.
        if (strcmp(info.what, "main") != 0) {
                return 0;
@@ -386,10 +423,11 @@ int ModApiUtil::l_request_insecure_environment(lua_State *L)
        }
 
        // Check secure.trusted_mods
-       const char *mod_name = lua_tostring(L, -1);
+       std::string mod_name = readParam<std::string>(L, -1);
        std::string trusted_mods = g_settings->get("secure.trusted_mods");
-       trusted_mods.erase(std::remove(trusted_mods.begin(),
-                       trusted_mods.end(), ' '), trusted_mods.end());
+       trusted_mods.erase(std::remove_if(trusted_mods.begin(),
+                       trusted_mods.end(), static_cast<int(*)(int)>(&std::isspace)),
+                       trusted_mods.end());
        std::vector<std::string> mod_list = str_split(trusted_mods, ',');
        if (std::find(mod_list.begin(), mod_list.end(), mod_name) ==
                        mod_list.end()) {
@@ -401,6 +439,130 @@ int ModApiUtil::l_request_insecure_environment(lua_State *L)
        return 1;
 }
 
+// get_version()
+int ModApiUtil::l_get_version(lua_State *L)
+{
+       lua_createtable(L, 0, 3);
+       int table = lua_gettop(L);
+
+       lua_pushstring(L, PROJECT_NAME_C);
+       lua_setfield(L, table, "project");
+
+       lua_pushstring(L, g_version_string);
+       lua_setfield(L, table, "string");
+
+       if (strcmp(g_version_string, g_version_hash) != 0) {
+               lua_pushstring(L, g_version_hash);
+               lua_setfield(L, table, "hash");
+       }
+
+       return 1;
+}
+
+int ModApiUtil::l_sha1(lua_State *L)
+{
+       NO_MAP_LOCK_REQUIRED;
+       size_t size;
+       const char *data = luaL_checklstring(L, 1, &size);
+       bool hex = !lua_isboolean(L, 2) || !readParam<bool>(L, 2);
+
+       // Compute actual checksum of data
+       std::string data_sha1;
+       {
+               SHA1 ctx;
+               ctx.addBytes(data, size);
+               unsigned char *data_tmpdigest = ctx.getDigest();
+               data_sha1.assign((char*) data_tmpdigest, 20);
+               free(data_tmpdigest);
+       }
+
+       if (hex) {
+               std::string sha1_hex = hex_encode(data_sha1);
+               lua_pushstring(L, sha1_hex.c_str());
+       } else {
+               lua_pushlstring(L, data_sha1.data(), data_sha1.size());
+       }
+
+       return 1;
+}
+
+// colorspec_to_colorstring(colorspec)
+int ModApiUtil::l_colorspec_to_colorstring(lua_State *L)
+{
+       NO_MAP_LOCK_REQUIRED;
+
+       video::SColor color(0);
+       if (read_color(L, 1, &color)) {
+               char colorstring[10];
+               snprintf(colorstring, 10, "#%02X%02X%02X%02X",
+                       color.getRed(), color.getGreen(), color.getBlue(), color.getAlpha());
+               lua_pushstring(L, colorstring);
+               return 1;
+       }
+
+       return 0;
+}
+
+// colorspec_to_bytes(colorspec)
+int ModApiUtil::l_colorspec_to_bytes(lua_State *L)
+{
+       NO_MAP_LOCK_REQUIRED;
+
+       video::SColor color(0);
+       if (read_color(L, 1, &color)) {
+               u8 colorbytes[4] = {
+                       (u8) color.getRed(),
+                       (u8) color.getGreen(),
+                       (u8) color.getBlue(),
+                       (u8) color.getAlpha(),
+               };
+               lua_pushlstring(L, (const char*) colorbytes, 4);
+               return 1;
+       }
+
+       return 0;
+}
+
+// encode_png(w, h, data, level)
+int ModApiUtil::l_encode_png(lua_State *L)
+{
+       NO_MAP_LOCK_REQUIRED;
+
+       // The args are already pre-validated on the lua side.
+       u32 width = readParam<int>(L, 1);
+       u32 height = readParam<int>(L, 2);
+       const char *data = luaL_checklstring(L, 3, NULL);
+       s32 compression = readParam<int>(L, 4);
+
+       std::string out = encodePNG((const u8*)data, width, height, compression);
+
+       lua_pushlstring(L, out.data(), out.size());
+       return 1;
+}
+
+// get_last_run_mod()
+int ModApiUtil::l_get_last_run_mod(lua_State *L)
+{
+       NO_MAP_LOCK_REQUIRED;
+
+       lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_CURRENT_MOD_NAME);
+       std::string current_mod = readParam<std::string>(L, -1, "");
+       if (current_mod.empty()) {
+               lua_pop(L, 1);
+               lua_pushstring(L, getScriptApiBase(L)->getOrigin().c_str());
+       }
+       return 1;
+}
+
+// set_last_run_mod(modname)
+int ModApiUtil::l_set_last_run_mod(lua_State *L)
+{
+       NO_MAP_LOCK_REQUIRED;
+
+       const char *mod = luaL_checkstring(L, 1);
+       getScriptApiBase(L)->setOriginDirect(mod);
+       return 0;
+}
 
 void ModApiUtil::Initialize(lua_State *L, int top)
 {
@@ -408,56 +570,100 @@ void ModApiUtil::Initialize(lua_State *L, int top)
 
        API_FCT(get_us_time);
 
-       API_FCT(setting_set);
-       API_FCT(setting_get);
-       API_FCT(setting_setbool);
-       API_FCT(setting_getbool);
-       API_FCT(setting_save);
-
        API_FCT(parse_json);
        API_FCT(write_json);
 
        API_FCT(get_dig_params);
        API_FCT(get_hit_params);
 
+       API_FCT(check_password_entry);
        API_FCT(get_password_hash);
 
        API_FCT(is_yes);
 
        API_FCT(get_builtin_path);
+       API_FCT(get_user_path);
 
        API_FCT(compress);
        API_FCT(decompress);
 
        API_FCT(mkdir);
        API_FCT(get_dir_list);
+       API_FCT(safe_file_write);
 
        API_FCT(request_insecure_environment);
+
+       API_FCT(encode_base64);
+       API_FCT(decode_base64);
+
+       API_FCT(get_version);
+       API_FCT(sha1);
+       API_FCT(colorspec_to_colorstring);
+       API_FCT(colorspec_to_bytes);
+
+       API_FCT(encode_png);
+
+       API_FCT(get_last_run_mod);
+       API_FCT(set_last_run_mod);
+
+       LuaSettings::create(L, g_settings, g_settings_path);
+       lua_setfield(L, top, "settings");
 }
 
-void ModApiUtil::InitializeAsync(AsyncEngine& engine)
+void ModApiUtil::InitializeClient(lua_State *L, int top)
 {
-       ASYNC_API_FCT(log);
-
-       ASYNC_API_FCT(get_us_time);
+       API_FCT(log);
 
-       //ASYNC_API_FCT(setting_set);
-       ASYNC_API_FCT(setting_get);
-       //ASYNC_API_FCT(setting_setbool);
-       ASYNC_API_FCT(setting_getbool);
-       //ASYNC_API_FCT(setting_save);
+       API_FCT(get_us_time);
 
-       ASYNC_API_FCT(parse_json);
-       ASYNC_API_FCT(write_json);
+       API_FCT(parse_json);
+       API_FCT(write_json);
 
-       ASYNC_API_FCT(is_yes);
+       API_FCT(is_yes);
 
-       ASYNC_API_FCT(get_builtin_path);
+       API_FCT(compress);
+       API_FCT(decompress);
 
-       ASYNC_API_FCT(compress);
-       ASYNC_API_FCT(decompress);
+       API_FCT(encode_base64);
+       API_FCT(decode_base64);
 
-       ASYNC_API_FCT(mkdir);
-       ASYNC_API_FCT(get_dir_list);
+       API_FCT(get_version);
+       API_FCT(sha1);
+       API_FCT(colorspec_to_colorstring);
+       API_FCT(colorspec_to_bytes);
 }
 
+void ModApiUtil::InitializeAsync(lua_State *L, int top)
+{
+       API_FCT(log);
+
+       API_FCT(get_us_time);
+
+       API_FCT(parse_json);
+       API_FCT(write_json);
+
+       API_FCT(is_yes);
+
+       API_FCT(get_builtin_path);
+       API_FCT(get_user_path);
+
+       API_FCT(compress);
+       API_FCT(decompress);
+
+       API_FCT(mkdir);
+       API_FCT(get_dir_list);
+
+       API_FCT(encode_base64);
+       API_FCT(decode_base64);
+
+       API_FCT(get_version);
+       API_FCT(sha1);
+       API_FCT(colorspec_to_colorstring);
+       API_FCT(colorspec_to_bytes);
+
+       API_FCT(get_last_run_mod);
+       API_FCT(set_last_run_mod);
+
+       LuaSettings::create(L, g_settings, g_settings_path);
+       lua_setfield(L, top, "settings");
+}