diff --git a/degesch.c b/degesch.c index c21dba7..f1eceb8 100644 --- a/degesch.c +++ b/degesch.c @@ -8322,6 +8322,183 @@ lua_plugin_parse (lua_State *L) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +#define XLUA_USER_METATABLE "user" ///< Identifier for the Lua metatable + +struct lua_user +{ + struct lua_plugin *plugin; ///< The plugin we belong to + struct user *user; ///< The user + struct weak_ref_link *weak_ref; ///< A weak reference link +}; + +static void +lua_user_invalidate (void *object, void *user_data) +{ + struct lua_user *wrapper = user_data; + wrapper->user = NULL; + wrapper->weak_ref = NULL; + // This can in theory call the GC, order isn't arbitrary here + lua_cache_invalidate (wrapper->plugin->L, object); +} + +static void +lua_plugin_push_user (struct lua_plugin *plugin, struct user *user) +{ + lua_State *L = plugin->L; + if (lua_cache_get (L, user)) + return; + + struct lua_user *wrapper = lua_newuserdata (L, sizeof *wrapper); + luaL_setmetatable (L, XLUA_USER_METATABLE); + wrapper->plugin = plugin; + wrapper->user = user; + wrapper->weak_ref = user_weak_ref (user, lua_user_invalidate, wrapper); + lua_cache_store (L, user, -1); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +/// Identifier for the Lua metatable +#define XLUA_CHANNEL_METATABLE "channel" + +struct lua_channel +{ + struct lua_plugin *plugin; ///< The plugin we belong to + struct channel *channel; ///< The channel + struct weak_ref_link *weak_ref; ///< A weak reference link +}; + +static void +lua_channel_invalidate (void *object, void *user_data) +{ + struct lua_channel *wrapper = user_data; + wrapper->channel = NULL; + wrapper->weak_ref = NULL; + // This can in theory call the GC, order isn't arbitrary here + lua_cache_invalidate (wrapper->plugin->L, object); +} + +static void +lua_plugin_push_channel (struct lua_plugin *plugin, struct channel *channel) +{ + lua_State *L = plugin->L; + if (lua_cache_get (L, channel)) + return; + + struct lua_channel *wrapper = lua_newuserdata (L, sizeof *wrapper); + luaL_setmetatable (L, XLUA_CHANNEL_METATABLE); + wrapper->plugin = plugin; + wrapper->channel = channel; + wrapper->weak_ref = channel_weak_ref + (channel, lua_channel_invalidate, wrapper); + lua_cache_store (L, channel, -1); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +static int +lua_user_gc (lua_State *L) +{ + struct lua_user *wrapper = luaL_checkudata (L, 1, XLUA_USER_METATABLE); + if (wrapper->user) + { + lua_cache_invalidate (L, wrapper->user); + user_weak_unref (wrapper->user, &wrapper->weak_ref); + wrapper->user = NULL; + } + return 0; +} + +static int +lua_user_get_nickname (lua_State *L) +{ + struct lua_user *wrapper = luaL_checkudata (L, 1, XLUA_USER_METATABLE); + luaL_argcheck (L, wrapper->user, 1, "dead reference used"); + lua_pushstring (L, wrapper->user->nickname); + return 1; +} + +static int +lua_user_get_channels (lua_State *L) +{ + struct lua_user *wrapper = luaL_checkudata (L, 1, XLUA_USER_METATABLE); + luaL_argcheck (L, wrapper->user, 1, "dead reference used"); + + int i = 1; + lua_newtable (L); + LIST_FOR_EACH (struct user_channel, iter, wrapper->user->channels) + { + lua_plugin_push_channel (wrapper->plugin, iter->channel); + lua_rawseti (L, -2, i++); + } + return 1; +} + +static luaL_Reg lua_user_table[] = +{ + { "__gc", lua_user_gc }, + { "get_nickname", lua_user_get_nickname }, + { "get_channels", lua_user_get_channels }, + { NULL, NULL } +}; + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +static int +lua_channel_gc (lua_State *L) +{ + struct lua_channel *wrapper = + luaL_checkudata (L, 1, XLUA_CHANNEL_METATABLE); + if (wrapper->channel) + { + lua_cache_invalidate (L, wrapper->channel); + channel_weak_unref (wrapper->channel, &wrapper->weak_ref); + wrapper->channel = NULL; + } + return 0; +} + +static int +lua_channel_get_name (lua_State *L) +{ + struct lua_channel *wrapper = + luaL_checkudata (L, 1, XLUA_CHANNEL_METATABLE); + luaL_argcheck (L, wrapper->channel, 1, "dead reference used"); + lua_pushstring (L, wrapper->channel->name); + return 1; +} + +static int +lua_channel_get_users (lua_State *L) +{ + struct lua_channel *wrapper = + luaL_checkudata (L, 1, XLUA_CHANNEL_METATABLE); + luaL_argcheck (L, wrapper->channel, 1, "dead reference used"); + + int i = 1; + lua_newtable (L); + LIST_FOR_EACH (struct channel_user, iter, wrapper->channel->users) + { + lua_createtable (L, 0, 2); + lua_plugin_push_user (wrapper->plugin, iter->user); + lua_setfield (L, -2, "user"); + lua_plugin_kv (L, "prefixes", iter->prefixes.str); + + lua_rawseti (L, -2, i++); + } + return 1; +} + +static luaL_Reg lua_channel_table[] = +{ + { "__gc", lua_channel_gc }, + { "get_name", lua_channel_get_name }, + { "get_users", lua_channel_get_users }, + { NULL, NULL } +}; + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + #define XLUA_BUFFER_METATABLE "buffer" ///< Identifier for the Lua metatable struct lua_buffer @@ -8409,6 +8586,32 @@ lua_buffer_gc (lua_State *L) return 0; } +static int +lua_buffer_get_user (lua_State *L) +{ + struct lua_buffer *wrapper = luaL_checkudata (L, 1, XLUA_BUFFER_METATABLE); + luaL_argcheck (L, wrapper->buffer, 1, "dead reference used"); + + if (wrapper->buffer->user) + lua_plugin_push_user (wrapper->plugin, wrapper->buffer->user); + else + lua_pushnil (L); + return 1; +} + +static int +lua_buffer_get_channel (lua_State *L) +{ + struct lua_buffer *wrapper = luaL_checkudata (L, 1, XLUA_BUFFER_METATABLE); + luaL_argcheck (L, wrapper->buffer, 1, "dead reference used"); + + if (wrapper->buffer->channel) + lua_plugin_push_channel (wrapper->plugin, wrapper->buffer->channel); + else + lua_pushnil (L); + return 1; +} + static int lua_buffer_get_server (lua_State *L) { @@ -8450,11 +8653,13 @@ lua_buffer_execute (lua_State *L) static luaL_Reg lua_buffer_table[] = { // TODO: some useful methods or values - { "__gc", lua_buffer_gc }, - { "get_server", lua_buffer_get_server }, - { "log", lua_buffer_log }, - { "execute", lua_buffer_execute }, - { NULL, NULL } + { "__gc", lua_buffer_gc }, + { "get_user", lua_buffer_get_user }, + { "get_channel", lua_buffer_get_channel }, + { "get_server", lua_buffer_get_server }, + { "log", lua_buffer_log }, + { "execute", lua_buffer_execute }, + { NULL, NULL } }; // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -8472,6 +8677,19 @@ lua_server_gc (lua_State *L) return 0; } +static int +lua_server_get_user (lua_State *L) +{ + struct lua_server *wrapper = luaL_checkudata (L, 1, XLUA_SERVER_METATABLE); + luaL_argcheck (L, wrapper->server, 1, "dead reference used"); + + if (wrapper->server->irc_user) + lua_plugin_push_user (wrapper->plugin, wrapper->server->irc_user); + else + lua_pushnil (L); + return 1; +} + static int lua_server_get_buffer (lua_State *L) { @@ -8500,6 +8718,7 @@ static luaL_Reg lua_server_table[] = { // TODO: some useful methods or values { "__gc", lua_server_gc }, + { "get_user", lua_server_get_user }, { "get_buffer", lua_server_get_buffer }, { "send", lua_server_send }, { NULL, NULL } @@ -9631,6 +9850,8 @@ lua_plugin_load (struct app_context *ctx, const char *filename, // Create metatables for our objects lua_plugin_create_meta (L, XLUA_HOOK_METATABLE, lua_hook_table); + lua_plugin_create_meta (L, XLUA_USER_METATABLE, lua_user_table); + lua_plugin_create_meta (L, XLUA_CHANNEL_METATABLE, lua_channel_table); lua_plugin_create_meta (L, XLUA_BUFFER_METATABLE, lua_buffer_table); lua_plugin_create_meta (L, XLUA_SERVER_METATABLE, lua_server_table); lua_plugin_create_meta (L, XLUA_SCHEMA_METATABLE, lua_schema_table);