--- /dev/null
+function commands.reload(client, source, command, args)
+ client_notice(client, "reloading...")
+ dofile "lua/init.lua"
+ client_notice(client, "done")
+end
+if not managed_hooks then
+ managed_hooks = {}
+end
+function hook_setfold(name, fn)
+ if not managed_hooks[name] then
+ managed_hooks[name] = {n=0,fns={}}
+ end
+ managed_hooks[name].fold = fn
+end
+function hook_add(name, id, fn)
+ if not managed_hooks[name] then error("unknown hook "..tostring(name)) end
+ local oldn = managed_hooks[name].n
+ local newn = oldn + (fn and 1 or 0)-(managed_hooks[name].fns[id] and 1 or 0)
+ managed_hooks[name].fns[id] = fn or nil
+ if oldn ~= newn and (oldn == 0 or newn == 0) then
+ if newn ~= 0 then
+ hooks[name] = function(...)
+ local r, exit
+ r = 0
+ for k, v in pairs(managed_hooks[name].fns) do
+ r, exit = managed_hooks[name].fold(r, v(...))
+ if exit then break end
+ end
+ return r
+ end
+ else
+ hooks[name] = nil
+ end
+ end
+end
+local function fold_sendtox(a, b)
+ b = b or 0
+ if b == -1 then
+ return -1, true
+ end
+ return a > b and a or b
+end
+hook_setfold("sendtochannel", fold_sendtox)
+hook_setfold("sendtoclient", fold_sendtox)
+
+function commands.echo(client, source, command, args)
+ client_notice(client, "client = "..tostring(client))
+ client_notice(client, "source = "..tostring(source))
+ client_notice(client, "command = "..tostring(command))
+ client_notice(client, "args.n = "..tostring(args.n))
+ for i = 1, args.n do
+ client_notice(client, "args["..i.."] = "..tostring(args[i]))
+ end
+end
+function commands.joinall(client, source, command, args)
+ for k, v in pairs(ircd.channel_map) do
+ channel_join(client, k, true)
+ end
+end
+
+if not spamlevel then
+ spamlevel = 0
+end
+function setspamlevel(newlevel)
+ spamlevel = newlevel
+ hook_add("sendtochannel", "supernets_filter", spamlevel >= 10 and function(hook, cl1, cl2, chan, txt)
+ if cl1.umode & 2 ~= 0 then -- UMODE_OPER
+ return 0
+ end
+ if not chan.v[cl1.key] and not chan.o[cl1.key] and
+ (txt:find("irc.supernets.org", 1, true) or txt:find("IRC.SUPERNETS.ORG", 1, true)) then
+ return -1
+ end
+ end)
+ hook_add("sendtoclient", "no_unsolicited_pm", spamlevel >= 20 and function(hook, cl1, cl2, chan, txt)
+ if cl1.umode & 2 ~= 0 then -- UMODE_OPER
+ return 0
+ end
+ if cl1.umode & 8 ~= 0 then -- UMODE_REGISTERED
+ return 0
+ end
+ -- allow is cl1 has perms in any of the channels cl2 is in
+ for k, v in pairs(cl2.channels) do
+ if v.v[cl1.key] or v.o[cl1.key] then
+ return 0
+ end
+ end
+ return -1
+ end)
+ hook_add("sendtochannel", "globally_moderated", spamlevel >= 30 and function(hook, cl1, cl2, chan, txt)
+ if cl1.umode & 2 ~= 0 then -- UMODE_OPER
+ return 0
+ end
+ if cl1.umode & 8 ~= 0 then -- UMODE_REGISTERED
+ return 0
+ end
+ if not chan.v[cl1.key] and not chan.o[cl1.key] then
+ return 1
+ end
+ end)
+end
+setspamlevel(spamlevel)
+
+function commands.spam(client, source, command, args)
+ if args.n == 0 then
+ client_notice(client, "current spam protection level: "..tostring(spamlevel))
+ else
+ setspamlevel(tonumber(args[1]))
+ client_notice(client, "new spam protection level: "..tostring(spamlevel))
+ end
+end
--- /dev/null
+#include "admin.h"
+#include "irc.h"
+#include "ircfmt.h"
+#include <lauxlib.h>
+#include <lualib.h>
+static lua_State *GL = 0;
+
+#define MAPTYPE_STRINGSET 0
+#define MAPTYPE_CLIENT 1
+#define MAPTYPE_CHANNEL 2
+static int map_index(lua_State *L)
+{
+ map_t *m = *(map_t **)luaL_checkudata(L, 1, "map");
+ const char *key = luaL_checkstring(L, 2);
+ void *value = map_get(m, key);
+ if (!value)
+ return 0;
+ lua_getiuservalue(L, 1, 1);
+ switch (lua_tointeger(L, -1)) {
+ case MAPTYPE_STRINGSET:
+ lua_pushvalue(L, 2);
+ return 1;
+ case MAPTYPE_CLIENT:
+ *(struct client_data **)lua_newuserdatauv(L, sizeof(struct client_data *), 0) = value;
+ luaL_setmetatable(L, "client");
+ return 1;
+ case MAPTYPE_CHANNEL:
+ *(struct channel **)lua_newuserdatauv(L, sizeof(struct channel *), 0) = value;
+ luaL_setmetatable(L, "channel");
+ return 1;
+ }
+ return 0;
+}
+
+static int map_newindex(lua_State *L)
+{
+ map_t *m = *(map_t **)luaL_checkudata(L, 1, "map");
+ const char *key = luaL_checkstring(L, 2);
+ luaL_checkany(L, 3);
+ if (lua_isnil(L, 3)) {
+ (void)map_set(m, key, 0, 0);
+ return 0;
+ }
+ lua_getiuservalue(L, 1, 1);
+ switch (lua_tointeger(L, -1)) {
+ case MAPTYPE_STRINGSET:
+ (void)map_set(m, key, key, 0);
+ break;
+ case MAPTYPE_CLIENT: {
+ void **p = luaL_checkudata(L, 3, "client");
+ (void)map_set(m, key, *p, 0);
+ break;
+ }
+ case MAPTYPE_CHANNEL: {
+ void **p = luaL_checkudata(L, 3, "channel");
+ (void)map_set(m, key, *p, 0);
+ break;
+ }
+ }
+ return 0;
+}
+static int map_iter_next(lua_State *L)
+{
+ map_iter_t *it = luaL_checkudata(L, 1, "map_iter");
+ lua_getiuservalue(L, 1, 2);
+ const char *key = lua_touserdata(L, -1);
+ lua_pop(L, 1);
+ void *value;
+ if (!map_foriter(it, &key, &value))
+ return 0;
+ lua_pushlightuserdata(L, (void *)key);
+ lua_setiuservalue(L, 1, 2);
+ lua_getiuservalue(L, 1, 1);
+ switch (lua_tointeger(L, -1)) {
+ case MAPTYPE_STRINGSET:
+ lua_pushstring(L, key);
+ lua_pushstring(L, key);
+ return 2;
+ case MAPTYPE_CLIENT:
+ lua_pushstring(L, key);
+ *(struct client_data **)lua_newuserdatauv(L, sizeof(struct client_data *), 0) = value;
+ luaL_setmetatable(L, "client");
+ return 2;
+ case MAPTYPE_CHANNEL:
+ lua_pushstring(L, key);
+ *(struct channel **)lua_newuserdatauv(L, sizeof(struct channel *), 0) = value;
+ luaL_setmetatable(L, "channel");
+ return 2;
+ }
+ return 0;
+}
+static int map_pairs(lua_State *L)
+{
+ map_t *m = *(map_t **)luaL_checkudata(L, 1, "map");
+ lua_pushcfunction(L, map_iter_next);
+ map_iter_t *it = lua_newuserdatauv(L, sizeof(map_iter_t), 2);
+ memset(it, 0, sizeof(*it));
+ it->root = m;
+ luaL_setmetatable(L, "map_iter");
+ lua_getiuservalue(L, 1, 1);
+ lua_setiuservalue(L, -2, 1);
+ lua_pushlightuserdata(L, 0);
+ lua_setiuservalue(L, -2, 2);
+ lua_pushnil(L);
+ return 3;
+}
+static void map_push(lua_State *L, map_t *map, int type)
+{
+ *(map_t **)lua_newuserdatauv(L, sizeof(map_t *), 1) = map;
+ luaL_setmetatable(L, "map");
+ lua_pushinteger(L, type);
+ lua_setiuservalue(L, -2, 1);
+}
+static int client_index(lua_State *L)
+{
+ struct client_data *cl = *(struct client_data **)luaL_checkudata(L, 1, "client");
+ const char *key = luaL_checkstring(L, 2);
+ if (!cl)
+ return 0;
+#define string_getter(c, s) if (!strcmp(key, #s)) return lua_pushstring(L, c->s), 1
+#define integer_getter(c, s) if (!strcmp(key, #s)) return lua_pushinteger(L, c->s), 1
+#define map_getter(c, s, t) if (!strcmp(key, #s)) return map_push(L, &c->s, t), 1
+ integer_getter(cl, FD);
+ /* cctx */
+ integer_getter(cl, type);
+ if (!strcmp(key, "next")) {
+ if (!cl->next)
+ return 0;
+ *(struct client_data **)lua_newuserdatauv(L, sizeof(struct client_data *), 0) = cl->next;
+ luaL_setmetatable(L, "client");
+ return 1;
+ }
+ string_getter(cl, nick);
+ string_getter(cl, key);
+ string_getter(cl, username);
+ string_getter(cl, realname);
+ string_getter(cl, hostname);
+ string_getter(cl, nuh);
+ string_getter(cl, away);
+ string_getter(cl, nick_reg);
+ /* string_getter(cl, pass_reg); */
+ string_getter(cl, peername);
+ integer_getter(cl, peerport);
+ /* buf/bufLen/wbuf/wbufLen */
+ integer_getter(cl, umode);
+ integer_getter(cl, regstate);
+ map_getter(cl, channels, MAPTYPE_CHANNEL);
+ map_getter(cl, invites, MAPTYPE_CHANNEL);
+ integer_getter(cl, cookie);
+ integer_getter(cl, signon);
+ integer_getter(cl, lastactivity);
+ integer_getter(cl, caps);
+ integer_getter(cl, want_write_for_read);
+ integer_getter(cl, want_read_for_write);
+ integer_getter(cl, want_write_for_write);
+ integer_getter(cl, want_read_for_close);
+ integer_getter(cl, want_write_for_close);
+ integer_getter(cl, dying);
+ integer_getter(cl, write_error);
+ integer_getter(cl, poll_reg);
+ return 0;
+}
+static int channel_index(lua_State *L)
+{
+ struct channel *chan = *(struct channel **)luaL_checkudata(L, 1, "channel");
+ const char *key = luaL_checkstring(L, 2);
+ if (!chan)
+ return 0;
+ string_getter(chan, key);
+ string_getter(chan, name);
+ string_getter(chan, topic);
+ string_getter(chan, topic_who);
+ integer_getter(chan, topic_when);
+ integer_getter(chan, creation);
+ map_getter(chan, members, MAPTYPE_CLIENT);
+ map_getter(chan, o, MAPTYPE_CLIENT);
+ map_getter(chan, v, MAPTYPE_CLIENT);
+ integer_getter(chan, member_count);
+ integer_getter(chan, dmode);
+ map_getter(chan, listb, MAPTYPE_STRINGSET);
+ map_getter(chan, liste, MAPTYPE_STRINGSET);
+ map_getter(chan, listI, MAPTYPE_STRINGSET);
+ map_getter(chan, listq, MAPTYPE_STRINGSET);
+ map_getter(chan, listwo, MAPTYPE_STRINGSET);
+ map_getter(chan, listwv, MAPTYPE_STRINGSET);
+ map_getter(chan, invitees, MAPTYPE_CLIENT);
+ string_getter(chan, chankey);
+ integer_getter(chan, member_limit);
+ return 0;
+}
+static int globals_index(lua_State *L)
+{
+ const char *key = luaL_checkstring(L, 2);
+ if (!strcmp(key, "client_map"))
+ return map_push(L, &client_map, MAPTYPE_CLIENT), 1;
+ if (!strcmp(key, "channel_map"))
+ return map_push(L, &channel_map, MAPTYPE_CHANNEL), 1;
+ if (!strcmp(key, "autojoin"))
+ return map_push(L, &autojoin, MAPTYPE_STRINGSET), 1;
+ if (!strcmp(key, "clients")) {
+ *(struct client_data **)lua_newuserdatauv(L, sizeof(struct client_data *), 0) = clients;
+ luaL_setmetatable(L, "client");
+ return 1;
+ }
+ if (!strcmp(key, "dyingclients")) {
+ *(struct client_data **)lua_newuserdatauv(L, sizeof(struct client_data *), 0) = dyingclients;
+ luaL_setmetatable(L, "client");
+ return 1;
+ }
+ return 0;
+}
+static int command_eval(lua_State *L)
+{
+ luaL_checktype(L, 4, LUA_TTABLE);
+ lua_geti(L, 4, 1);
+ if (LUA_OK != luaL_dostring(L, lua_tostring(L, -1))) {
+ fprintf(stderr, "%s\n", lua_tostring(L, -1));
+ }
+ return 0;
+}
+static int fn_client_notice(lua_State *L)
+{
+ extern const char *hostname;
+ struct client_data *cl = *(struct client_data **)luaL_checkudata(L, 1, "client");
+ const char *msg = luaL_checkstring(L, 2);
+ msg_notice_server(hostname, cl->nick, msg), sendbuf(cl);
+ return 0;
+}
+static int fn_channel_join(lua_State *L)
+{
+ struct client_data *cl = *(struct client_data **)luaL_checkudata(L, 1, "client");
+ const char *channel = 0;
+ struct channel *chan = 0;
+ if (lua_isuserdata(L, 2)) {
+ chan = *(struct channel **)luaL_checkudata(L, 2, "channel");
+ channel = chan->key;
+ } else {
+ channel = luaL_checkstring(L, 2);
+ }
+ const char *chankey = lua_isboolean(L, 3) && lua_toboolean(L, 3) ? (chan ? chan->chankey : 0) : luaL_optstring(L, 3, 0);
+ channel_join(cl, channel, chankey);
+ return 0;
+}
+static int fn_channel_part(lua_State *L)
+{
+ struct client_data *cl = *(struct client_data **)luaL_checkudata(L, 1, "client");
+ struct channel *chan = *(struct channel **)luaL_checkudata(L, 2, "channel");
+ const char *reason = luaL_optstring(L, 3, 0);
+ struct client_data *kicker = 0;
+ if (lua_isuserdata(L, 4))
+ kicker = *(struct client_data **)luaL_checkudata(L, 4, "client");
+ channel_part(cl, chan, reason, kicker);
+ return 0;
+}
+void admin_init(void)
+{
+ lua_State *L = luaL_newstate();
+ GL = L;
+ luaL_openlibs(L);
+ if (luaL_newmetatable(L, "map")) {
+ luaL_Reg funcs[] = {
+ {"__index", map_index},
+ {"__newindex", map_newindex},
+ {"__pairs", map_pairs},
+ {0, 0},
+ };
+ luaL_setfuncs(L, funcs, 0);
+ }
+ lua_pop(L, 1);
+ luaL_newmetatable(L, "map_iter");
+ lua_pop(L, 1);
+ if (luaL_newmetatable(L, "client")) {
+ luaL_Reg funcs[] = {
+ {"__index", client_index},
+ {0, 0},
+ };
+ luaL_setfuncs(L, funcs, 0);
+ }
+ lua_pop(L, 1);
+ if (luaL_newmetatable(L, "channel")) {
+ luaL_Reg funcs[] = {
+ {"__index", channel_index},
+ {0, 0},
+ };
+ luaL_setfuncs(L, funcs, 0);
+ }
+ lua_pop(L, 1);
+ if (luaL_newmetatable(L, "globals")) {
+ luaL_Reg funcs[] = {
+ {"__index", globals_index},
+ {0, 0},
+ };
+ luaL_setfuncs(L, funcs, 0);
+ }
+ lua_pop(L, 1);
+ lua_newuserdatauv(L, 0, 0);
+ luaL_setmetatable(L, "globals");
+ lua_setglobal(L, "ircd");
+ lua_newtable(L);
+ lua_pushcfunction(L, command_eval);
+ lua_setfield(L, -2, "eval");
+ lua_setglobal(L, "commands");
+ lua_newtable(L);
+ lua_setglobal(L, "hooks");
+ lua_pushcfunction(L, fn_client_notice);
+ lua_setglobal(L, "client_notice");
+ lua_pushcfunction(L, fn_channel_join);
+ lua_setglobal(L, "channel_join");
+ lua_pushcfunction(L, fn_channel_part);
+ lua_setglobal(L, "channel_part");
+}
+void admin_fini(void)
+{
+ if (GL) {
+ lua_close(GL);
+ GL = 0;
+ }
+}
+int admin_command(struct client_data *client, char *source, char *command, int argc, char *args[])
+{
+ if (!is_registered(client))
+ return err_notregistered(client), sendbuf(client), 0;
+ if (!(client->umode & UMODE_OPER))
+ return err_noprivileges(client), sendbuf(client), 0;
+ lua_State *L = GL;
+
+ if (lua_getglobal(L, "commands") != LUA_TTABLE) {
+ fprintf(stderr, "no command table\n");
+ lua_pop(L, 1);
+ return 0;
+ }
+ lua_getfield(L, -1, command);
+ lua_remove(L, -2);
+ if (!lua_isfunction(L, -1)) {
+ fprintf(stderr, "command not found: %s\n", command);
+ lua_pop(L, 1);
+ return 0;
+ }
+ *(struct client_data **)lua_newuserdatauv(L, sizeof(struct client_data *), 0) = client;
+ luaL_setmetatable(L, "client");
+ lua_pushstring(L, source);
+ lua_pushstring(L, command);
+ lua_createtable(L, argc, 1);
+ for (int i = 0; i < argc; i++) {
+ lua_pushstring(L, args[i]);
+ lua_seti(L, -2, i+1);
+ }
+ lua_pushinteger(L, argc);
+ lua_setfield(L, -2, "n");
+ if (LUA_OK != lua_pcall(L, 4, 0, 0)) {
+ fprintf(stderr, "%s\n", lua_tostring(L, -1));
+ lua_pop(L, 1);
+ }
+ return 0;
+}
+int admin_hook(const char *hook, struct client_data *cl1, struct client_data *cl2, struct channel *chan, char *txt)
+{
+ lua_State *L = GL;
+ if (lua_getglobal(L, "hooks") != LUA_TTABLE) {
+ lua_pop(L, 1);
+ return 0;
+ }
+ lua_getfield(L, -1, hook);
+ lua_remove(L, -2);
+ if (!lua_isfunction(L, -1)) {
+ lua_pop(L, 1);
+ return 0;
+ }
+ lua_pushstring(L, hook);
+ if (cl1) {
+ *(struct client_data **)lua_newuserdatauv(L, sizeof(struct client_data *), 0) = cl1;
+ luaL_setmetatable(L, "client");
+ } else {
+ lua_pushnil(L);
+ }
+ if (cl2) {
+ *(struct client_data **)lua_newuserdatauv(L, sizeof(struct client_data *), 0) = cl2;
+ luaL_setmetatable(L, "client");
+ } else {
+ lua_pushnil(L);
+ }
+ if (chan) {
+ *(struct channel **)lua_newuserdatauv(L, sizeof(struct client_data *), 0) = chan;
+ luaL_setmetatable(L, "channel");
+ } else {
+ lua_pushnil(L);
+ }
+ txt ? lua_pushstring(L, txt) : lua_pushnil(L);
+ if (LUA_OK != lua_pcall(L, 5, 1, 0)) {
+ fprintf(stderr, "%s\n", lua_tostring(L, -1));
+ lua_pop(L, 1);
+ return 0;
+ }
+ int rv = lua_tointeger(L, 1);
+ lua_pop(L, 1);
+ return rv;
+}