aboutsummaryrefslogtreecommitdiff
path: root/src/script/common/c_internal.cpp
Commit message (Expand)AuthorAge
* Clean up/improve some scriptapi error handling codesfan52021-09-10
* HUD: Reject and warn on invalid stat types (#11548)SmallJoker2021-08-21
* Fix segfault in deprecation logging due to tail call, log by default (#10174)rubenwardy2020-10-31
* Fix configuration caching in log_deprecated (#9697)HybridDog2020-04-22
* Fix 'the the' typos in comments (#9554)LNJ2020-04-04
* Refactor Script API's log_deprecatedsfan52020-02-23
* Clean up stack after script_get_backtrace (#7854)Michael Muller2018-11-28
* Add a MSVC / Windows compatible snprintf function (#7353)nOOb31672018-07-22
* Fix segfault in player migration and crash in log_deprecatedSmallJoker2018-05-14
* Hint at problematic code when logging deprecated callssfan52017-11-27
* Create a filesystem abstraction layer for CSM and only allow accessing files ...red-0012017-06-30
* Fix crash regression when invsize formspec gets usedest312015-10-17
* Use warningstream for deprecated field messages and refactor log_deprecatedShadowNinja2015-10-15
* Push error handler afresh each time lua_pcall is usedKahrl2015-08-27
* SAPI: Track last executed mod and include in error messageskwolekr2015-08-12
* Display Lua memory usage at the time of Out-of-Memory errorkwolekr2015-08-10
* Improve Script CPP API diagnosticskwolekr2015-08-05
* Add mod securityShadowNinja2015-05-16
* Move globals from main.cpp to more sane locationsCraig Robbins2015-04-01
* For usages of assert() that are meant to persist in Release builds (when NDEB...Craig Robbins2015-03-07
* Fix LuaJIT exception wrapperKahrl2014-08-23
* Use "core" namespace internallyShadowNinja2014-05-08
* Add proper lua api deprecated handlingsapier2014-04-29
* Remove lua_State parameter from LuaError::LuaErrorShadowNinja2014-03-15
* Revert "Make sure we get a stacktrace for as many lua errors as possible"ShadowNinja2014-03-15
* Make sure we get a stacktrace for as many lua errors as possibleSfan52014-03-15
* Handle LuaErrors in Lua -> C++ calls on LuaJITShadowNinja2013-12-18
* Move script_run_callbacks to LuaShadowNinja2013-12-07
* Fix possible implicit conversion of NULL to std::stringkwolekr2013-11-21
* Pass a errfunc to lua_pcall to get a tracebackShadowNinja2013-11-15
* Omnicleanup: header cleanup, add ModApiUtil shared between game and mainmenuKahrl2013-08-14
* Move scriptapi to separate folder (by sapier)sapier2013-05-25
6 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446
/*
Minetest
Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

#include "lua_api/l_util.h"
#include "lua_api/l_internal.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 "cpp_api/s_security.h"
#include "debug.h"
#include "porting.h"
#include "log.h"
#include "tool.h"
#include "filesys.h"
#include "settings.h"
#include "util/auth.h"
#include <algorithm>

// debug(...)
// Writes a line to dstream
int ModApiUtil::l_debug(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;
	// Handle multiple parameters to behave like standard lua print()
	int n = lua_gettop(L);
	lua_getglobal(L, "tostring");
	for (int i = 1; i <= n; i++) {
		/*
			Call tostring(i-th argument).
			This is what print() does, and it behaves a bit
			differently from directly calling lua_tostring.
		*/
		lua_pushvalue(L, -1);  /* function to be called */
		lua_pushvalue(L, i);   /* value to print */
		lua_call(L, 1, 1);
		size_t len;
		const char *s = lua_tolstring(L, -1, &len);
		if (i > 1)
			dstream << "\t";
		if (s)
			dstream << std::string(s, len);
		lua_pop(L, 1);
	}
	dstream << std::endl;
	return 0;
}

// log([level,] text)
// Writes a line to the logger.
// The one-argument version logs to infostream.
// The two-argument version accept a log level: error, action, info, or verbose.
int ModApiUtil::l_log(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;
	std::string text;
	LogMessageLevel level = LMT_INFO;
	if (lua_isnone(L, 2)) {
		text = lua_tostring(L, 1);
	}
	else {
		std::string levelname = luaL_checkstring(L, 1);
		text = luaL_checkstring(L, 2);
		if(levelname == "error")
			level = LMT_ERROR;
		else if(levelname == "action")
			level = LMT_ACTION;
		else if(levelname == "verbose")
			level = LMT_VERBOSE;
		else if (levelname == "deprecated") {
			log_deprecated(L,text);
			return 0;
		}

	}
	log_printline(level, text);
	return 0;
}

#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)
{
	NO_MAP_LOCK_REQUIRED;

	const char *jsonstr = luaL_checkstring(L, 1);

	// Use passed nullvalue or default to nil
	int nullindex = 2;
	if (lua_isnone(L, nullindex)) {
		lua_pushnil(L);
		nullindex = lua_gettop(L);
	}

	Json::Value root;

	{
		Json::Reader reader;
		std::istringstream stream(jsonstr);

		if (!reader.parse(stream, root)) {
			errorstream << "Failed to parse json data "
				<< reader.getFormattedErrorMessages();
			errorstream << "data: \"" << jsonstr << "\""
				<< std::endl;
			lua_pushnil(L);
			return 1;
		}
	}

	if (!push_json_value(L, root, nullindex)) {
		errorstream << "Failed to parse json data, "
			<< "depth exceeds lua stack limit" << std::endl;
		errorstream << "data: \"" << jsonstr << "\"" << std::endl;
		lua_pushnil(L);
	}
	return 1;
}

// write_json(data[, styled]) -> string or nil and error message
int ModApiUtil::l_write_json(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;

	bool styled = false;
	if (!lua_isnone(L, 2)) {
		styled = lua_toboolean(L, 2);
		lua_pop(L, 1);
	}

	Json::Value root;
	try {
		read_json_value(L, root, 1);
	} catch (SerializationError &e) {
		lua_pushnil(L);
		lua_pushstring(L, e.what());
		return 2;
	}

	std::string out;
	if (styled) {
		Json::StyledWriter writer;
		out = writer.write(root);
	} else {
		Json::FastWriter writer;
		out = writer.write(root);
	}
	lua_pushlstring(L, out.c_str(), out.size());
	return 1;
}

// get_dig_params(groups, tool_capabilities[, time_from_last_punch])
int ModApiUtil::l_get_dig_params(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;
	std::map<std::string, int> groups;
	read_groups(L, 1, groups);
	ToolCapabilities tp = read_tool_capabilities(L, 2);
	if(lua_isnoneornil(L, 3))
		push_dig_params(L, getDigParams(groups, &tp));
	else
		push_dig_params(L, getDigParams(groups, &tp,
					luaL_checknumber(L, 3)));
	return 1;
}

// get_hit_params(groups, tool_capabilities[, time_from_last_punch])
int ModApiUtil::l_get_hit_params(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;
	std::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)));
	return 1;
}

// get_password_hash(name, raw_password)
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);
	lua_pushstring(L, hash.c_str());
	return 1;
}

// is_yes(arg)
int ModApiUtil::l_is_yes(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;

	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
	lua_pop(L, 1);

	bool yes = is_yes(str);
	lua_pushboolean(L, yes);
	return 1;
}

int ModApiUtil::l_get_builtin_path(lua_State *L)
{
	std::string path = porting::path_share + DIR_DELIM + "builtin";
	lua_pushstring(L, path.c_str());
	return 1;
}

// compress(data, method, level)
int ModApiUtil::l_compress(lua_State *L)
{
	size_t size;
	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);

	std::ostringstream os;
	compressZlib(std::string(data, size), os, level);

	std::string out = os.str();

	lua_pushlstring(L, out.data(), out.size());
	return 1;
}

// decompress(data, method)
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;
	decompressZlib(is, os);

	std::string out = os.str();

	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);
	lua_pushboolean(L, fs::CreateAllDirs(path));
	return 1;
}

// get_dir_list(path, is_dir)
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;

	CHECK_SECURE_PATH_OPTIONAL(L, path);

	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());
			lua_rawseti(L, -2, ++index);
		}
	}

	return 1;
}

int ModApiUtil::l_request_insecure_environment(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;
	if (!ScriptApiSecurity::isSecure(L)) {
		lua_getglobal(L, "_G");
		return 1;
	}
	lua_getfield(L, LUA_REGISTRYINDEX, SCRIPT_MOD_NAME_FIELD);
	if (!lua_isstring(L, -1)) {
		lua_pushnil(L);
		return 1;
	}
	const char *mod_name = lua_tostring(L, -1);
	std::string trusted_mods = g_settings->get("secure.trusted_mods");
	std::vector<std::string> mod_list = str_split(trusted_mods, ',');
	if (std::find(mod_list.begin(), mod_list.end(), mod_name) == mod_list.end()) {
		lua_pushnil(L);
		return 1;
	}
	lua_getfield(L, LUA_REGISTRYINDEX, "globals_backup");
	return 1;
}


void ModApiUtil::Initialize(lua_State *L, int top)
{
	API_FCT(debug);
	API_FCT(log);

	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(get_password_hash);

	API_FCT(is_yes);

	API_FCT(get_builtin_path);

	API_FCT(compress);
	API_FCT(decompress);

	API_FCT(mkdir);
	API_FCT(get_dir_list);

	API_FCT(request_insecure_environment);
}

void ModApiUtil::InitializeAsync(AsyncEngine& engine)
{
	ASYNC_API_FCT(debug);
	ASYNC_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);

	ASYNC_API_FCT(parse_json);
	ASYNC_API_FCT(write_json);

	ASYNC_API_FCT(is_yes);

	ASYNC_API_FCT(get_builtin_path);

	ASYNC_API_FCT(compress);
	ASYNC_API_FCT(decompress);

	ASYNC_API_FCT(mkdir);
	ASYNC_API_FCT(get_dir_list);
}