]> PatchyGit - patchyircd.git/commitdiff
add lua scripting
authorEgor <egor@patchysicp.xyz>
Fri, 27 Oct 2023 15:43:57 +0000 (18:43 +0300)
committerEgor <egor@patchysicp.xyz>
Fri, 27 Oct 2023 15:43:57 +0000 (18:43 +0300)
12 files changed:
Makefile
config.example.mk
lua/channeldump.lua [new file with mode: 0644]
lua/init.lua [new file with mode: 0644]
src/admin.c [new file with mode: 0644]
src/admin.h [new file with mode: 0644]
src/common.h
src/irc.c
src/irc.h
src/ircfmt.c
src/ircfmt.h
src/main.c

index ad27027..bc88915 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -22,6 +22,13 @@ HEADERS := \
 
 -include config.mk
 
+ifdef USE_LUA
+SOURCES += src/admin.c
+HEADERS += src/admin.h
+CFLAGS += -DHAS_LUA $(shell pkg-config --cflags lua5.4)
+LDLIBS += $(shell pkg-config --libs lua5.4)
+endif
+
 ifndef USE_MAP_DYNARR
 SOURCES += src/map_trie.c
 else
index a19c626..5c2efc8 100644 (file)
@@ -6,3 +6,4 @@ CC.w32=i686-w64-mingw32-gcc
 CFLAGS.w32=-Wall -Wextra -DNETPOLL_USE_SELECT
 LDLIBS.w32=-lws2_32
 USE_MAP_DYNARR=1
+USE_LUA=1
diff --git a/lua/channeldump.lua b/lua/channeldump.lua
new file mode 100644 (file)
index 0000000..2aa0d4f
--- /dev/null
@@ -0,0 +1,49 @@
+local function showlist(name, ls)
+       local x
+       for k,v in pairs(ls) do
+               if not x then print(name) x = 1 end
+               print("\t\t"..k)
+       end
+end
+for k,v in pairs(ircd.channel_map) do
+       print("Channel "..k)
+       if k ~= v.name then
+               print("\tName: "..tostring(v.name))
+       end
+       if v.topic then
+               print("\tTopic: "..v.topic)
+               if v.topic_who then
+                       print("\t\tSet by: "..v.topic_who)
+               end
+               if v.topic_when ~= 0 then
+                       print("\t\tSet on: "..tostring(v.topic_when))
+               end
+       end
+       if v.creation ~= 0 then
+               print("\tCreated on: "..tostring(v.creation))
+       end
+       showlist("\tCurrent members ("..v.member_count.."):", v.members)
+       showlist("\tCurrent ops:", v.o)
+       showlist("\tCurrent voices:", v.v)
+       local mode = ""
+       if v.dmode & 1  ~= 0 then mode = mode .. "P" end
+       if v.dmode & 2  ~= 0 then mode = mode .. "i" end
+       if v.dmode & 4  ~= 0 then mode = mode .. "m" end
+       if v.dmode & 8  ~= 0 then mode = mode .. "n" end
+       if v.dmode & 16 ~= 0 then mode = mode .. "s" end
+       if v.dmode & 32 ~= 0 then mode = mode .. "t" end
+       print("\tMode: +"..mode)
+       showlist("\tBans:", v.listb)
+       showlist("\tBan exceptions:", v.liste)
+       showlist("\tInvite exceptions:", v.listI)
+       showlist("\tQuiets:", v.listq)
+       showlist("\tAuto-ops:", v.listwo)
+       showlist("\tAuto-voices:", v.listwv)
+       showlist("\tCurrent invitees:", v.invitees)
+       if v.chankey then
+               print("\tKey: "..v.chankey)
+       end
+       if v.member_limit ~= -1 then
+               print("\tMember limit: "..tostring(v.member_limit))
+       end
+end
diff --git a/lua/init.lua b/lua/init.lua
new file mode 100644 (file)
index 0000000..64b41c8
--- /dev/null
@@ -0,0 +1,111 @@
+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
diff --git a/src/admin.c b/src/admin.c
new file mode 100644 (file)
index 0000000..568e048
--- /dev/null
@@ -0,0 +1,397 @@
+#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;
+}
diff --git a/src/admin.h b/src/admin.h
new file mode 100644 (file)
index 0000000..57cd93d
--- /dev/null
@@ -0,0 +1,8 @@
+#ifndef ADMIN_H_
+#define ADMIN_H_
+#include "common.h"
+void admin_init(void);
+void admin_fini(void);
+int admin_command(struct client_data *client, char *source, char *command, int argc, char *args[]);
+int admin_hook(const char *hook, struct client_data *cl1, struct client_data *cl2, struct channel *chan, char *txt);
+#endif
index 7062f1d..0b8e1fc 100644 (file)
@@ -99,4 +99,7 @@ extern map_t autojoin;
 extern struct client_data  *clients;
 extern struct client_data  *dyingclients;
 
+#ifdef HAS_LUA
+#include "admin.h"
+#endif
 #endif
index 8fa0ecb..c02c367 100644 (file)
--- a/src/irc.c
+++ b/src/irc.c
@@ -9,7 +9,6 @@ map_t channel_map = {0};
 map_t autojoin = {0};
 
 const char *hostname = IRCDHOSTNAME;
-#define is_registered(cl) ((cl)->regstate == REG_REGISTERED)
 void sendbuf(struct client_data *client) {
        sendClient(client, databuf.buf, databuf.len);
 }
@@ -542,14 +541,17 @@ int processPacket(struct client_data *client, char *source, char *command, int a
                                return err_cannotsendtochan(client, chan->name), sendbuf(client), 0;
                        if (chan->dmode & DMODE_MODERATED && !map_get(&chan->v, client->key) && !map_get(&chan->o, client->key))
                                return err_cannotsendtochan(client, chan->name), sendbuf(client), 0;
-                       if (!map_get(&chan->v, client->key) && !map_get(&chan->o, client->key) &&
-                                       (strstr(args[1], "irc.supernets.org") || strstr(args[1], "IRC.SUPERNETS.ORG"))) {
+#ifdef HAS_LUA
+                       switch (admin_hook("sendtochannel", client, 0, chan, args[1])) {
+                       case -1:
                                msg_quit(client, "Quit: eel bearing atlantean", "");
                                sendbuf_visible(client);
                                killConnection(client, 1);
                                return 1;
+                       case 1:
+                               return err_cannotsendtochan(client, chan->name), sendbuf(client), 0;
                        }
-
+#endif
                        if (!is_modmsg) {
                                sendbuf_list_sans_self(client, &chan->members, 0);
                        } else if (memchr(args[0], '+', is_modmsg)) {
@@ -560,20 +562,23 @@ int processPacket(struct client_data *client, char *source, char *command, int a
                                sendbuf_list_sans_self(client, &chan->o, 0);
                        }
                } else {
-                       if (!(client->umode & UMODE_REGISTERED)) {
+                       struct client_data *cl = findClient(args[0]);
+#ifdef HAS_LUA
+                       if (!cl)
+                               return err_nosuchnick(client, args[0]), sendbuf(client), 0;
+                       switch (admin_hook("sendtoclient", client, cl, 0, args[1])) {
+                       case -1:
                                msg_quit(client, "Quit: eel bearing atlantean", "");
                                sendbuf_visible(client);
                                killConnection(client, 1);
                                return 1;
+                       case 1:
+                               return err_cantsendtouser(client, cl), sendbuf(client), 0;
                        }
-                       struct client_data *cl = findClient(args[0]);
-                       if (!cl) {
-                               return err_nosuchnick(client, args[0]), sendbuf(client), 0;
-                       } else {
-                               sendbuf(cl);
-                               if (cl->away)
-                                       rpl_away(client, cl), sendbuf(client);
-                       }
+#endif
+                       sendbuf(cl);
+                       if (cl->away)
+                               rpl_away(client, cl), sendbuf(client);
                }
        } else if(strcmp(command, "QUIT") == 0) {
                msg_quit(client, "Quit: ", args[0] ? args[0] : "");
@@ -1039,6 +1044,12 @@ int processPacket(struct client_data *client, char *source, char *command, int a
                }
                msg_away(client);
                sendbuf_visible_caps(client, CAP_AWAYNOTIFY, 0);
+#ifdef HAS_LUA
+       } else if(strcmp(command, "LUA") == 0) {
+               if (argc < 1)
+                       return err_needmoreparams(client, command), sendbuf(client), 0;
+               return admin_command(client, source, args[0], argc-1, args+1);
+#endif
        } else {
 unknown:
                err_unknowncommand(client, command), sendbuf(client);
index 9272c82..29bd8c0 100644 (file)
--- a/src/irc.h
+++ b/src/irc.h
@@ -5,6 +5,7 @@
 #include "net.h"
 #include "util.h"
 
+#define is_registered(cl) ((cl)->regstate == REG_REGISTERED)
 int parsePacket(struct client_data *client);
 int processPacket(struct client_data *client, char *source, char *command, int argc, char *args[]);
 void newUser(struct client_data *client);
index ef84f3a..d2b519f 100644 (file)
@@ -597,6 +597,10 @@ void err_invalidkey(struct client_data *client, struct channel *chan) // 525
 {
        copy_strf(&databuf, ":%s 525 %s %s :Key is not well-formed\r\n", hostname, client->nick, chan->name);
 }
+void err_cantsendtouser(struct client_data *client, struct client_data *cl) // 531
+{
+       copy_strf(&databuf, ":%s 531 %s %s :You are not permitted to send private messages to this user\r\n", hostname, client->nick, cl->nick);
+}
 // 670 RPL_STARTTLS
 // 671 RPL_WHOISSECURE
 // 691 ERR_STARTTLS
index 6a5dffc..ad22826 100644 (file)
@@ -126,6 +126,7 @@ void err_umodeunknownflag(struct client_data *client); // 501
 void err_usersdontmatch(struct client_data *client, const char *what); // 502
 void err_helpnotfound(struct client_data *client); // 524
 void err_invalidkey(struct client_data *client, struct channel *chan); // 525
+void err_cantsendtouser(struct client_data *client, struct client_data *cl); // 531
 // 670 RPL_STARTTLS
 // 671 RPL_WHOISSECURE
 // 691 ERR_STARTTLS
index 2703a58..efda7b0 100644 (file)
@@ -203,6 +203,9 @@ int main(int argc, char *argv[])
 
        net_init();
        net_poll_init();
+#ifdef HAS_LUA
+       admin_init();
+#endif
        set_handler();
        databuf.buf = malloc(sizeof(char) * 512);
        databuf.cap = 512;
@@ -411,6 +414,9 @@ int main(int argc, char *argv[])
                net_poll_del(self_sock);
                closesocket(self_sock);
        }
+#endif
+#ifdef HAS_LUA
+       admin_fini();
 #endif
        net_poll_fini();
        net_cleanup();