Move shit around, add g_load()

This commit is contained in:
Mm2PL 2023-02-03 22:14:18 +01:00
parent cf7b73f251
commit 3013e73e94
No known key found for this signature in database
GPG key ID: 94AC9B80EFA15ED9
4 changed files with 203 additions and 110 deletions

View file

@ -136,6 +136,8 @@ set(SOURCE_FILES
controllers/pings/MutedChannelModel.cpp controllers/pings/MutedChannelModel.cpp
controllers/pings/MutedChannelModel.hpp controllers/pings/MutedChannelModel.hpp
controllers/plugins/ApiChatterino.cpp
controllers/plugins/ApiChatterino.hpp
controllers/plugins/Plugin.cpp controllers/plugins/Plugin.cpp
controllers/plugins/Plugin.hpp controllers/plugins/Plugin.hpp
controllers/plugins/PluginController.hpp controllers/plugins/PluginController.hpp

View file

@ -0,0 +1,143 @@
#ifdef CHATTERINO_HAVE_PLUGINS
# include "ApiChatterino.hpp"
# include "Application.hpp"
# include "common/QLogging.hpp"
# include "controllers/commands/CommandController.hpp"
# include "controllers/plugins/LuaUtilities.hpp"
# include "controllers/plugins/PluginController.hpp"
# include "messages/MessageBuilder.hpp"
# include "providers/twitch/TwitchIrcServer.hpp"
// lua stuff
# include "lauxlib.h"
# include "lua.h"
# include "lualib.h"
namespace chatterino::lua::api {
int c2_register_command(lua_State *L)
{
auto *pl = getApp()->plugins->getPluginByStatePtr(L);
if (pl == nullptr)
{
luaL_error(L, "internal error: no plugin"); // NOLINT
return 0;
}
QString name;
if (!lua::peek(L, &name, 1))
{
// NOLINTNEXTLINE
luaL_error(L, "cannot get string (1st arg of register_command)");
return 0;
}
if (lua_isnoneornil(L, 2))
{
// NOLINTNEXTLINE
luaL_error(L, "missing argument for register_command: function "
"\"pointer\"");
return 0;
}
auto callbackSavedName = QString("c2commandcb-%1").arg(name);
lua_setfield(L, LUA_REGISTRYINDEX, callbackSavedName.toStdString().c_str());
auto ok = pl->registerCommand(name, callbackSavedName);
// delete both name and callback
lua_pop(L, 2);
lua::push(L, ok);
return 1;
}
int c2_send_msg(lua_State *L)
{
QString text;
QString channel;
lua::pop(L, &text);
lua::pop(L, &channel);
const auto chn = getApp()->twitch->getChannelOrEmpty(channel);
if (chn->isEmpty())
{
qCDebug(chatterinoLua) << "send_msg: no channel" << channel;
lua::push(L, false);
return 1;
}
QString message = text;
message = message.replace('\n', ' ');
QString outText = getApp()->commands->execCommand(message, chn, false);
chn->sendMessage(outText);
lua::push(L, true);
return 1;
}
int c2_system_msg(lua_State *L)
{
if (lua_gettop(L) != 2)
{
qCDebug(chatterinoLua) << "system_msg: need 2 args";
luaL_error(L, "need exactly 2 arguments"); // NOLINT
lua::push(L, false);
return 1;
}
QString channel;
QString text;
lua::pop(L, &text);
lua::pop(L, &channel);
const auto chn = getApp()->twitch->getChannelOrEmpty(channel);
if (chn->isEmpty())
{
qCDebug(chatterinoLua) << "system_msg: no channel" << channel;
lua::push(L, false);
return 1;
}
qCDebug(chatterinoLua) << "system_msg: OK!";
chn->addMessage(makeSystemMessage(text));
lua::push(L, true);
return 1;
}
static const QChar REPLACEMENT_CHARACTER = QChar(0xFFFD);
int g_load(lua_State *L)
{
auto countArgs = lua_gettop(L);
QString str;
if (lua::peek(L, &str, 1))
{
if (str.contains(REPLACEMENT_CHARACTER))
{
// NOLINTNEXTLINE
luaL_error(L, "invalid utf-8 in load() is not allowed");
return 0;
}
}
else
{
// NOLINTNEXTLINE
luaL_error(L, "using reader function in load() is not allowed");
return 0;
}
for (int i = 0; i < countArgs; i++)
{
lua_seti(L, LUA_REGISTRYINDEX, i);
}
// fetch load and call it
lua_getfield(L, LUA_REGISTRYINDEX, "real_load");
for (int i = 0; i < countArgs; i++)
{
lua_geti(L, LUA_REGISTRYINDEX, i);
lua_pushnil(L);
lua_seti(L, LUA_REGISTRYINDEX, i);
}
lua_call(L, countArgs, LUA_MULTRET);
qCDebug(chatterinoLua) << "FDM " << lua_gettop(L);
return lua_gettop(L);
}
} // namespace chatterino::lua::api
#endif

View file

@ -0,0 +1,16 @@
#pragma once
#ifdef CHATTERINO_HAVE_PLUGINS
struct lua_State;
namespace chatterino::lua::api {
// names in this namespace reflect what's visible inside Lua and follow the lua naming scheme
int c2_register_command(lua_State *L); // NOLINT(readability-identifier-naming)
int c2_send_msg(lua_State *L); // NOLINT(readability-identifier-naming)
int c2_system_msg(lua_State *L); // NOLINT(readability-identifier-naming)
int g_load(lua_State *L); // NOLINT(readability-identifier-naming)
} // namespace chatterino::lua::api
#endif

View file

@ -1,13 +1,11 @@
#include "PluginController.hpp" #include "PluginController.hpp"
#ifdef CHATTERINO_HAVE_PLUGINS
#ifdef CHATTERINO_HAVE_PLUGINS
# include "Application.hpp" # include "Application.hpp"
# include "common/QLogging.hpp" # include "common/QLogging.hpp"
# include "controllers/commands/CommandContext.hpp" # include "controllers/commands/CommandContext.hpp"
# include "controllers/plugins/ApiChatterino.hpp"
# include "controllers/plugins/LuaUtilities.hpp" # include "controllers/plugins/LuaUtilities.hpp"
# include "lauxlib.h"
# include "lua.h"
# include "lualib.h"
# include "messages/MessageBuilder.hpp" # include "messages/MessageBuilder.hpp"
# include "providers/twitch/TwitchIrcServer.hpp" # include "providers/twitch/TwitchIrcServer.hpp"
# include "singletons/Paths.hpp" # include "singletons/Paths.hpp"
@ -17,6 +15,11 @@
# include "widgets/splits/Split.hpp" # include "widgets/splits/Split.hpp"
# include "widgets/Window.hpp" # include "widgets/Window.hpp"
// lua stuff
# include "lauxlib.h"
# include "lua.h"
# include "lualib.h"
# include <QJsonDocument> # include <QJsonDocument>
# include <memory> # include <memory>
@ -135,6 +138,41 @@ void PluginController::openLibrariesFor(lua_State *L,
luaL_requiref(L, reg.name, reg.func, int(true)); luaL_requiref(L, reg.name, reg.func, int(true));
lua_pop(L, 1); lua_pop(L, 1);
} }
// NOLINTNEXTLINE
static const luaL_Reg C2LIB[] = {
{"system_msg", lua::api::c2_system_msg},
{"register_command", lua::api::c2_register_command},
{"send_msg", lua::api::c2_send_msg},
{nullptr, nullptr},
};
lua_pushglobaltable(L);
auto global = lua_gettop(L);
// count of elements in C2LIB - 1 (to account for terminator)
lua::pushEmptyTable(L, 3);
luaL_setfuncs(L, C2LIB, 0);
lua_setfield(L, global, "c2");
// ban functions
// Note: this might not be fully secure? some kind of metatable fuckery might come up?
lua_pushglobaltable(L);
auto gtable = lua_gettop(L);
lua_getfield(L, gtable, "load");
// possibly randomize this name at runtime to prevent some attacks?
lua_setfield(L, LUA_REGISTRYINDEX, "real_load");
// NOLINTNEXTLINE
static const luaL_Reg replacementFuncs[] = {
{"load", lua::api::g_load},
{nullptr, nullptr},
};
luaL_setfuncs(L, replacementFuncs, 0);
lua_pop(L, 1);
} }
void PluginController::load(const QFileInfo &index, const QDir &pluginDir, void PluginController::load(const QFileInfo &index, const QDir &pluginDir,
@ -143,7 +181,6 @@ void PluginController::load(const QFileInfo &index, const QDir &pluginDir,
qCDebug(chatterinoLua) << "Running lua file" << index; qCDebug(chatterinoLua) << "Running lua file" << index;
lua_State *l = luaL_newstate(); lua_State *l = luaL_newstate();
PluginController::openLibrariesFor(l, meta); PluginController::openLibrariesFor(l, meta);
PluginController::loadChatterinoLib(l);
auto pluginName = pluginDir.dirName(); auto pluginName = pluginDir.dirName();
auto plugin = std::make_unique<Plugin>(pluginName, l, meta, pluginDir); auto plugin = std::make_unique<Plugin>(pluginName, l, meta, pluginDir);
@ -254,111 +291,6 @@ QString PluginController::tryExecPluginCommand(const QString &commandName,
return ""; return "";
} }
extern "C" {
int luaC2SystemMsg(lua_State *L)
{
if (lua_gettop(L) != 2)
{
qCDebug(chatterinoLua) << "system_msg: need 2 args";
luaL_error(L, "need exactly 2 arguments"); // NOLINT
lua::push(L, false);
return 1;
}
QString channel;
QString text;
lua::pop(L, &text);
lua::pop(L, &channel);
const auto chn = getApp()->twitch->getChannelOrEmpty(channel);
if (chn->isEmpty())
{
qCDebug(chatterinoLua) << "system_msg: no channel" << channel;
lua::push(L, false);
return 1;
}
qCDebug(chatterinoLua) << "system_msg: OK!";
chn->addMessage(makeSystemMessage(text));
lua::push(L, true);
return 1;
}
int luaC2RegisterCommand(lua_State *L)
{
auto *pl = getApp()->plugins->getPluginByStatePtr(L);
if (pl == nullptr)
{
luaL_error(L, "internal error: no plugin"); // NOLINT
return 0;
}
QString name;
if (!lua::peek(L, &name, 1))
{
// NOLINTNEXTLINE
luaL_error(L, "cannot get string (1st arg of register_command)");
return 0;
}
if (lua_isnoneornil(L, 2))
{
// NOLINTNEXTLINE
luaL_error(L, "missing argument for register_command: function "
"\"pointer\"");
return 0;
}
auto callbackSavedName = QString("c2commandcb-%1").arg(name);
lua_setfield(L, LUA_REGISTRYINDEX, callbackSavedName.toStdString().c_str());
auto ok = pl->registerCommand(name, callbackSavedName);
// delete both name and callback
lua_pop(L, 2);
lua::push(L, ok);
return 1;
}
int luaC2SendMsg(lua_State *L)
{
QString text;
QString channel;
lua::pop(L, &text);
lua::pop(L, &channel);
const auto chn = getApp()->twitch->getChannelOrEmpty(channel);
if (chn->isEmpty())
{
qCDebug(chatterinoLua) << "send_msg: no channel" << channel;
lua::push(L, false);
return 1;
}
QString message = text;
message = message.replace('\n', ' ');
QString outText = getApp()->commands->execCommand(message, chn, false);
chn->sendMessage(outText);
lua::push(L, true);
return 1;
}
// NOLINTNEXTLINE
static const luaL_Reg C2LIB[] = {
{"system_msg", luaC2SystemMsg},
{"register_command", luaC2RegisterCommand},
{"send_msg", luaC2SendMsg},
{nullptr, nullptr},
};
}
void PluginController::loadChatterinoLib(lua_State *L)
{
lua_pushglobaltable(L);
auto global = lua_gettop(L);
// count of elements in C2LIB - 1 (to account for terminator)
lua::pushEmptyTable(L, 3);
luaL_setfuncs(L, C2LIB, 0);
lua_setfield(L, global, "c2");
}
bool PluginController::isEnabled(const QString &codename) bool PluginController::isEnabled(const QString &codename)
{ {
if (!getSettings()->enableAnyPlugins) if (!getSettings()->enableAnyPlugins)