urbackup_backend/luaplugin/LuaInterpreter.cpp
2020-02-04 23:21:02 +01:00

613 lines
13 KiB
C++

#include "LuaInterpreter.h"
#ifdef NO_EMBEDDED_LUA
#include <lua.hpp>
#else
#include "src/lua.hpp"
#endif
#include "../Interface/Server.h"
#include "../common/data.h"
#include <assert.h>
#define MINIZ_NO_ZLIB_COMPATIBLE_NAMES
#include "../common/miniz.h"
#include "../stringtools.h"
#ifndef NO_EMBEDDED_LUA
extern "C" {
LUAMOD_API int luaopen_os_custom(lua_State *L);
}
#endif
namespace
{
#include "lua/dkjson_lua.h"
std::string get_lua(const unsigned char* data, size_t data_len)
{
size_t out_len;
void* cdata = tinfl_decompress_mem_to_heap(data, data_len, &out_len, TINFL_FLAG_PARSE_ZLIB_HEADER | TINFL_FLAG_COMPUTE_ADLER32);
if (cdata == NULL)
{
return std::string();
}
std::string ret(reinterpret_cast<char*>(cdata), reinterpret_cast<char*>(cdata) + out_len);
mz_free(cdata);
return ret;
}
}
namespace
{
int time_monotonic_ms(lua_State *L) {
lua_pushnumber(L, static_cast<lua_Number>(Server->getTimeMS()));
return 1;
}
int time_unix_seconds(lua_State *L) {
lua_pushnumber(L, static_cast<lua_Number>(Server->getTimeSeconds()));
return 1;
}
static const luaL_Reg lib_time[] = {
{ "monotonic_ms", time_monotonic_ms },
{ "unix_seconds", time_unix_seconds },
{ NULL, NULL }
};
LUAMOD_API int luaopen_time(lua_State *L) {
luaL_newlib(L, lib_time);
return 1;
}
class ScopedLuaState
{
lua_State* state;
public:
ScopedLuaState(lua_State* state)
: state(state)
{}
~ScopedLuaState() {
lua_close(state);
}
};
#define LUA_SERIALIZE_STOP 100
bool unserialize_val(lua_State* state, char t, CRData& data)
{
switch (t)
{
case LUA_TNIL:
{
lua_pushnil(state);
} break;
case LUA_TBOOLEAN:
{
int64 b;
if (!data.getVarInt(&b))
return false;
lua_pushboolean(state, static_cast<int>(b));
} break;
case LUA_TNUMBER:
{
double num;
if (!data.getDouble(&num))
return false;
lua_pushnumber(state, num);
} break;
case LUA_TSTRING:
{
std::string str;
if (!data.getStr2(&str))
return false;
lua_pushstring(state, str.c_str());
} break;
default:
return false;
}
return true;
}
bool unserialize_table(lua_State* state, CRData& data)
{
lua_newtable(state);
char t;
while (data.getChar(&t))
{
if (t == LUA_SERIALIZE_STOP)
return true;
if (!unserialize_val(state, t, data))
{
return false;
}
if (t == LUA_TNUMBER || t == LUA_TSTRING)
{
char t2;
if (!data.getChar(&t2))
return false;
if (t2 == LUA_TTABLE)
{
if (!unserialize_table(state, data))
{
lua_rawset(state, -3);
return false;
}
}
else if (!unserialize_val(state, t2, data))
{
lua_pushnil(state);
lua_rawset(state, -3);
return false;
}
lua_rawset(state, -3);
}
else
{
lua_rawset(state, -2);
}
}
return false;
}
bool unserialize_table(lua_State* state, const std::string& data_in)
{
CRData data(data_in.data(), data_in.size());
return unserialize_table(state, data);
}
bool serialize_val(lua_State* state, int idx, CWData& data)
{
if (lua_isboolean(state, idx))
{
data.addChar(LUA_TBOOLEAN);
data.addVarInt(lua_toboolean(state, idx));
}
else if (lua_isnil(state, idx))
{
data.addChar(LUA_TNIL);
}
else if (lua_isnumber(state, idx))
{
data.addChar(LUA_TNUMBER);
data.addDouble(lua_tonumber(state, idx));
}
else if (lua_isstring(state, idx))
{
data.addChar(LUA_TSTRING);
data.addString2(lua_tostring(state, idx));
}
else
{
return false;
}
return true;
}
bool serialize_table(lua_State* state, CWData& data)
{
for (lua_pushnil(state); lua_next(state, -2) != 0; lua_pop(state, 1))
{
if (!serialize_val(state, -2, data))
return false;
if (lua_istable(state, -1))
{
data.addChar(LUA_TTABLE);
if (!serialize_table(state, data))
return false;
}
else if (!serialize_val(state, -1, data))
{
return false;
}
}
data.addChar(LUA_SERIALIZE_STOP);
return true;
}
std::string serialize_table(lua_State* state)
{
CWData data;
if (!serialize_table(state, data))
return std::string();
return std::string(data.getDataPtr(), data.getDataSize());
}
int lua_write(lua_State *L, const void *p, size_t sz, void *ud)
{
try
{
std::string* output = reinterpret_cast<std::string*>(ud);
output->insert(output->end(), reinterpret_cast<const char*>(p), reinterpret_cast<const char*>(p) + sz);
}
catch (...)
{
return 1;
}
return 0;
}
static const luaL_Reg loadedlibs[] = {
{ "_G", luaopen_base },
{ LUA_TABLIBNAME, luaopen_table },
{ LUA_STRLIBNAME, luaopen_string },
{ LUA_MATHLIBNAME, luaopen_math },
{ LUA_UTF8LIBNAME, luaopen_utf8 },
#ifndef NO_EMBEDDED_LUA
{ LUA_OSLIBNAME, luaopen_os_custom},
#endif
{ "time", luaopen_time },
{ NULL, NULL }
};
LUALIB_API void luaL_openlibs_custom(lua_State *L) {
const luaL_Reg *lib;
/* "require" functions from 'loadedlibs' and set results to global table */
for (lib = loadedlibs; lib->func; lib++) {
luaL_requiref(L, lib->name, lib->func, 1);
lua_pop(L, 1); /* remove lib */
}
}
LUALIB_API void luaL_openlibs_all(lua_State *L) {
luaL_openlibs(L);
luaL_requiref(L, "time", luaopen_time, 1);
lua_pop(L, 1);
}
ILuaInterpreter::SInterpreterFunctions* get_funcs(lua_State* L)
{
lua_getglobal(L, "_g_funcs");
ILuaInterpreter::SInterpreterFunctions* ret
= reinterpret_cast<ILuaInterpreter::SInterpreterFunctions*>(lua_touserdata(L, -1));
lua_pop(L, 1);
if (ret == NULL)
{
luaL_error(L, "Internal functions not present");
}
return ret;
}
int l_mail(lua_State *L) {
std::string to = luaL_checkstring(L, 1);
std::string subj = luaL_checkstring(L, 2);
std::string msg = luaL_checkstring(L, 3);
ILuaInterpreter::SInterpreterFunctions* funcs = get_funcs(L);
bool b = funcs->mail_func->mail(to, subj, msg);
lua_pushboolean(L, b ? 1 : 0);
return 1;
}
int l_request_url(lua_State *L)
{
std::string url = luaL_checkstring(L, 1);
str_map params;
if (lua_istable(L, 2))
{
lua_gettable(L, 2);
for (lua_pushnil(L); lua_next(L, -2) != 0; lua_pop(L, 1))
{
std::string key = luaL_checkstring(L, -2);
std::string val;
if (lua_isboolean(L, -1))
{
if (lua_toboolean(L, -1))
val = "1";
else
val = "0";
}
else if (lua_isnil(L, -1))
{
val = "null";
}
else if (lua_isstring(L, -1))
{
val = lua_tostring(L, -1);
}
else if (lua_isnumber(L, -1))
{
val = convert(lua_tonumber(L, -1));
}
else
{
const char* msg = lua_pushfstring(L, "%s expected, got %s", "boolean/number/string", luaL_typename(L, -1));
luaL_argerror(L, -1, msg);
}
params[key] = val;
}
}
ILuaInterpreter::SInterpreterFunctions* funcs = get_funcs(L);
std::string errmsg;
std::string ret;
long http_code;
bool b = funcs->url_func->requestUrl(url, params, ret, http_code, &errmsg);
lua_pushboolean(L, b ? 1 : 0);
lua_pushlstring(L, ret.c_str(), ret.size());
lua_pushinteger(L, http_code);
if (!errmsg.empty())
{
lua_pushlstring(L, errmsg.c_str(), errmsg.size());
return 4;
}
return 3;
}
void set_param(lua_State* state, const ILuaInterpreter::Param& param)
{
if (param.tag == ILuaInterpreter::Param::PARAM_VEC)
{
lua_newtable(state);
for (ILuaInterpreter::Param::params_map::const_iterator it = param.u.params->begin();
it!=param.u.params->end();++it)
{
set_param(state, it->first);
set_param(state, it->second);
lua_rawset(state, -3);
}
}
else if (param.tag == ILuaInterpreter::Param::PARAM_STR)
{
lua_pushstring(state, param.u.str->c_str());
}
else if (param.tag == ILuaInterpreter::Param::PARAM_NUM)
{
lua_pushnumber(state, param.u.num);
}
else if (param.tag == ILuaInterpreter::Param::PARAM_INT)
{
lua_pushinteger(state, param.u.i);
}
else if (param.tag == ILuaInterpreter::Param::PARAM_BOOL)
{
lua_pushboolean(state, param.u.b);
}
else
{
assert(false);
}
}
int l_require(lua_State* state)
{
std::string name = luaL_checkstring(state, 1);
std::string code;
if (name == "dkjson")
{
code = get_lua(dkjson_lua_z, dkjson_lua_z_len);
//code = getFile("luaplugin/lua/dkjson.lua");
}
if (code.empty())
{
return 0;
}
else
{
luaL_loadbuffer(state, code.data(), code.size(), name.c_str());
}
lua_call(state, 0, LUA_MULTRET);
return 1;
}
int l_log(lua_State* state)
{
std::string msg = luaL_checkstring(state, 1);
int loglevel = LL_INFO;
if (lua_isinteger(state, 2))
{
loglevel = static_cast<int>(lua_tointeger(state, 2));
if (loglevel<LL_DEBUG || loglevel>LL_ERROR)
loglevel = LL_INFO;
}
Server->Log(msg, loglevel);
return 0;
}
}
std::string LuaInterpreter::compileScript(const std::string & script)
{
lua_State* state = luaL_newstate();
if (state == NULL)
{
return std::string();
}
ScopedLuaState scoped_state(state);
int rc = luaL_loadbuffer(state, script.c_str(), script.size(), "script");
if (rc) {
Server->Log(std::string("Error loading lua script: ") + lua_tostring(state, -1), LL_ERROR);
return std::string();
}
std::string ret;
rc = lua_dump(state, lua_write, &ret, 0);
if (rc)
{
Server->Log(std::string("Error dumping lua state"), LL_ERROR);
return std::string();
}
return ret;
}
int64 LuaInterpreter::runScript(const std::string& script, const Param& params, int64& ret2,
std::string& state_data, std::string& state_data_mem,
std::string& global_data,
std::string& global_data_mem, SInterpreterFunctions& funcs)
{
ret2 = -1;
lua_State* state = luaL_newstate();
if (state == NULL)
{
return -1;
}
ScopedLuaState scoped_state(state);
if (Server->getServerParameter("lua_sandbox") == "true")
{
luaL_openlibs_custom(state);
}
else
{
luaL_openlibs_all(state);
}
int rc = luaL_loadbuffer(state, script.c_str(), script.size(), "script");
if (rc) {
Server->Log(std::string("Error loading lua script: ") + lua_tostring(state, -1), LL_ERROR);
return -1;
}
set_param(state, params);
lua_setglobal(state, "params");
lua_pushlightuserdata(state, const_cast<ILuaInterpreter::SInterpreterFunctions*>(&funcs));
lua_setglobal(state, "_g_funcs");
lua_pushcfunction(state, l_mail);
lua_setglobal(state, "mail");
lua_pushcfunction(state, l_require);
lua_setglobal(state, "require");
lua_pushcfunction(state, l_request_url);
lua_setglobal(state, "request_url");
lua_pushcfunction(state, l_log);
lua_setglobal(state, "log");
lua_pushinteger(state, LL_DEBUG);
lua_setglobal(state,"LL_DEBUG");
lua_pushinteger(state, LL_INFO);
lua_setglobal(state, "LL_INFO");
lua_pushinteger(state, LL_WARNING);
lua_setglobal(state, "LL_WARNING");
lua_pushinteger(state, LL_ERROR);
lua_setglobal(state, "LL_ERROR");
if (state_data.empty())
{
lua_newtable(state);
}
else if (!unserialize_table(state, state_data))
{
Server->Log("Error unserializing state data", LL_ERROR);
assert(false);
}
lua_setglobal(state, "state");
if (state_data_mem.empty())
{
lua_newtable(state);
}
else if (!unserialize_table(state, state_data_mem))
{
Server->Log("Error unserializing state data mem", LL_ERROR);
assert(false);
}
lua_setglobal(state, "state_mem");
if (global_data.empty())
{
lua_newtable(state);
}
else if (!unserialize_table(state, global_data))
{
Server->Log("Error unserializing global data", LL_ERROR);
assert(false);
}
lua_setglobal(state, "global");
if (global_data_mem.empty())
{
lua_newtable(state);
}
else if (!unserialize_table(state, global_data_mem))
{
Server->Log("Error unserializing global data mem", LL_ERROR);
assert(false);
}
lua_setglobal(state, "global_mem");
rc = lua_pcall(state, 0, LUA_MULTRET, 0);
if (rc) {
Server->Log(std::string("Error running lua script: ") + lua_tostring(state, -1), LL_ERROR);
return -1;
}
if (lua_gettop(state) > 1)
{
ret2 = lua_tointeger(state, -1);
lua_pop(state, 1);
}
int64 ret = lua_tointeger(state, -1);
lua_pop(state, 1);
lua_getglobal(state, "state");
state_data = serialize_table(state);
if (state_data.empty())
{
Server->Log("Error serializing state data", LL_ERROR);
assert(false);
}
lua_getglobal(state, "state_mem");
state_data_mem = serialize_table(state);
if (state_data_mem.empty())
{
Server->Log("Error serializing state mem data", LL_ERROR);
assert(false);
}
lua_getglobal(state, "global");
global_data = serialize_table(state);
if (global_data.empty())
{
Server->Log("Error serializing global data", LL_ERROR);
assert(false);
}
lua_getglobal(state, "global_mem");
global_data_mem = serialize_table(state);
if (global_data_mem.empty())
{
Server->Log("Error serializing global mem data", LL_ERROR);
assert(false);
}
return ret;
}