From c81ed168c54e976d54fdf838b64e0a0ab706ea1a Mon Sep 17 00:00:00 2001 From: Egor Date: Wed, 21 Jan 2026 18:31:38 +0200 Subject: [PATCH] admin: make lua mandatory dependency --- README.md | 41 ++++++++++++++++++++++++++++----------- configure.ac | 2 +- src/admin.cpp | 9 +++++++-- src/admin.hpp | 5 ++--- src/irc.cpp | 18 +---------------- src/main.cpp | 53 ++------------------------------------------------- src/net.cpp | 4 +--- 7 files changed, 44 insertions(+), 88 deletions(-) diff --git a/README.md b/README.md index 7eb227e..6d2f645 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,14 @@ patchy IRC server????? ## How to compile and use +Required dependencies: +- libuv +- lua5.4 +- sqlite3 + +Optional dependencies: +- libtls + Basic compilation: ``` autoreconf -iv # if you're compiling from source control @@ -70,17 +78,28 @@ organization running the server. Third line is a contact email address. You can set these to whatever you want, though setting a contact email is recommended. -When the server starts, it reads the `init.txt` file. A fake oper connection -performs the actions specified in the file. This allows initializing permanent -channels. If you specify source (i.e. `:nick!user@host` before the command), -the oper's nick will change accordingly. You can also fake the current unix time -by prefixing the line with `$123` (`$0` sets the time back to real time). Faking -nick and time is useful for JOIN and TOPIC commands. `chanlog.txt` records all -channels changes in this format. -(NOTE: this feature is deprecated. We only use it for bootstrapping lua: -`LUA eval :dofile(getluafile"init.lua")`. Then in `lua/local.lua` we set up the -channels. Also, be sure to PART any `--autojoin` channels if you want to give -those a specific creation timestamp.) +When the server starts, it initialized the lua subsystem by running the +`init.lua` file. You can put your own code into `local.lua`. In particular, this +allows automatically initializing permanent channels. +```lua +channel_new("#anime", 1652016295) -- name, creation time +local chan = channel_map["#anime"] +channel_mode(chan, "+b", "*!~*@*") -- varargs, like MODE command +-- note that default modes are +ntP +channel_topic(chan, "Anime!", "root", 1751023162) -- topic, who set, when set +``` +The scripts can be reloaded by opers with `/quote LUA reload`, so you probably +want to wrap some of your code like so: +```lua +if not local_processed then + local_processed = true + -- code here +end +``` + +Channel state changes are logged in `chanlog.txt`, in a format similar to IRC +protocol. Lines are prefixed with `$123`, which indicates the unix timestamp for +when the action has occured. ## Command-line options Add TCP listeners with `--listenPlain ip port` and `--listenTls ip port`. If diff --git a/configure.ac b/configure.ac index 777f905..3cb910c 100644 --- a/configure.ac +++ b/configure.ac @@ -56,7 +56,7 @@ AS_IF([test "x$enable_pch" = xyes], PKG_CHECK_MODULES(LIBUV, [libuv]) PKG_HAVE_DEFINE_WITH_MODULES(LIBTLS, [libtls]) -PKG_HAVE_DEFINE_WITH_MODULES(LUA, [lua5.4]) +PKG_CHECK_MODULES(LUA, [lua5.4]) PKG_CHECK_MODULES(SQLITE3, [sqlite3]) AC_OUTPUT diff --git a/src/admin.cpp b/src/admin.cpp index 13f4e0d..bb2be59 100644 --- a/src/admin.cpp +++ b/src/admin.cpp @@ -1,7 +1,6 @@ // Copyright (c) 2023-2026 Egor // SPDX-License-Identifier: GPL-2.0-or-later #include "common.hpp" -#ifdef HAVE_LUA #include "admin.hpp" #include "irc.hpp" #include "ircfmt.hpp" @@ -452,6 +451,13 @@ void admin_fini(void) GL = 0; } } +void admin_dostring(const char *command) +{ + lua_State *L = GL; + if (luaL_dostring(L, command) != LUA_OK) + fprintf(stderr, "%s\n", lua_tostring(L, -1)); + lua_settop(L, 0); +} int admin_command(Client *client, char *source, char *command, int argc, char *args[]) { if (!is_registered(client)) @@ -534,4 +540,3 @@ int admin_hook(const char *hook, Client *cl1, Client *cl2, Channel *chan, char * lua_pop(L, 1); return rv; } -#endif diff --git a/src/admin.hpp b/src/admin.hpp index f2b31c2..f8050c5 100644 --- a/src/admin.hpp +++ b/src/admin.hpp @@ -1,12 +1,11 @@ -// Copyright (c) 2023-2025 Egor +// Copyright (c) 2023-2026 Egor // SPDX-License-Identifier: GPL-2.0-or-later #ifndef ADMIN_H_ #define ADMIN_H_ -#ifdef HAVE_LUA #include "ircdata.hpp" void admin_init(void); void admin_fini(void); +void admin_dostring(const char *command); int admin_command(Client *client, char *source, char *command, int argc, char *args[]); int admin_hook(const char *hook, Client *cl1, Client *cl2, Channel *chan, char *txt); #endif -#endif diff --git a/src/irc.cpp b/src/irc.cpp index 1cde194..c51dff1 100644 --- a/src/irc.cpp +++ b/src/irc.cpp @@ -733,9 +733,7 @@ privmsg_notice_common: return err_needmoreparams(client, command), sendbuf(client), 0; client->lastactivity = get_time(); if (!strcmp(args[0], "cia")) { -#ifdef HAVE_LUA admin_hook(command[0] == 'P' ? "privmsgtoservice" : "noticetoservice", client, 0, 0, args[1]); -#endif return 0; } (command[0] == 'P' ? msg_privmsg : msg_notice)(client, args[0], args[1]); @@ -759,7 +757,6 @@ privmsg_notice_common: return err_cannotsendtochan(client, chan->name), sendbuf(client), 0; if (chan->dmode & DMODE_MODERATED && !chan->v.has(client) && !chan->o.has(client)) return err_cannotsendtochan(client, chan->name), sendbuf(client), 0; -#ifdef HAVE_LUA switch (admin_hook("sendtochannel", client, 0, chan, args[1])) { case -1: msg_quit(client, "Quit: eel bearing atlantean", ""); @@ -769,7 +766,6 @@ privmsg_notice_common: 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)) { @@ -781,7 +777,6 @@ privmsg_notice_common: } } else { Client *cl = findClient(args[0]); -#ifdef HAVE_LUA if (!cl) return err_nosuchnick(client, args[0]), sendbuf(client), 0; switch (admin_hook("sendtoclient", client, cl, 0, args[1])) { @@ -793,7 +788,6 @@ privmsg_notice_common: case 1: return err_cantsendtouser(client, cl), sendbuf(client), 0; } -#endif sendbuf(cl); if (cl->away) rpl_away(client, cl), sendbuf(client); @@ -1108,9 +1102,7 @@ privmsg_notice_common: rpl_whoismodes(client, cl), sendbuf(client); if (cl->umode & UMODE_BOT) rpl_whoisbot(client, cl), sendbuf(client); -#ifdef HAVE_LUA admin_hook("whois", client, cl, 0, 0); -#endif rpl_endofwhois(client, cl), sendbuf(client); } else { err_nosuchnick(client, nick); @@ -1210,12 +1202,10 @@ privmsg_notice_common: sendbuf_monitor(client->key, cookie, CAP_SETNAME|CAP_EXTENDEDMONITOR, 0); if (client->cookie != cookie) sendbuf(client); -#ifdef HAVE_LUA } MATCH_COMMAND("LUA") { if (argc < 1) return err_needmoreparams(client, command), sendbuf(client), 0; return admin_command(client, source, args[0], argc-1, args+1); -#endif } MATCH_ELSE { unknown: err_unknowncommand(client, command), sendbuf(client); @@ -1274,9 +1264,7 @@ void newUser(Client *client) { sendLUSERS(client); rpl_umodeis(client), sendbuf(client); sendMOTD(client); -#ifdef HAVE_LUA admin_hook("newuser", client, 0, 0, 0); -#endif for (auto key : autojoin) channel_join(client, key, NULL); } @@ -1360,10 +1348,8 @@ void setNick(Client *client, char* nickname) { } // Update nickname - if(client->nick) { -#ifdef HAVE_LUA + if (client->nick) { admin_hook("nickchange", client, 0, 0, key); -#endif msg_nick(client, nickname); sendbuf(client); sendbuf_visible(client); @@ -1377,9 +1363,7 @@ void setNick(Client *client, char* nickname) { delete[] client->nick; delete[] client->key; } else { -#ifdef HAVE_LUA admin_hook("nickadd", client, 0, 0, key); -#endif } client->nick = mystrdup(nickname); diff --git a/src/main.cpp b/src/main.cpp index 80e1aeb..91532fa 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -213,9 +213,7 @@ int main(int argc, char *argv[]) auto dblk = db::init(); netpoll::init(); job_init(); -#ifdef HAVE_LUA admin_init(); -#endif struct tls *tls_ctx = NULL; #ifdef HAVE_LIBTLS @@ -288,53 +286,8 @@ int main(int argc, char *argv[]) netpoll::reg(listener.fd, UV_READABLE); } - FILE *f = fopen(getdatafile("init.txt"), "rbe"); - if (f) { - Client *fake = new Client(); - fake->fd = -1; - fake->umode |= UMODE_OPER | UMODE_SECURE; - fake->hostname = mystrdup("localhost"); - if (!fake->hostname) - abortf("strdup fail (hostname)\n"); - strcpy_lit(fake->rbuf, "NICK :root\r\n"); - irc_parse_line(fake, fake->rbuf, 0); - strcpy_lit(fake->rbuf, "USER root 0 * :Patchouli Knowledge\r\n"); - irc_parse_line(fake, fake->rbuf, 0); - while (fgets(fake->rbuf, sizeof(fake->rbuf), f)) { - char *p = fake->rbuf; - size_t len = strlen(fake->rbuf); - if (!len || fake->rbuf[len-1] != '\n') - break; - unsigned long faketime; - if (sscanf(p, "$%lu", &faketime)) { - set_time((time_t)faketime); - p = strchr(fake->rbuf, ' '); - if (!p) - continue; - len -= ++p - fake->rbuf; - memmove(fake->rbuf, p, len); - } - if (*p == ':') { - char *sp = strchr(p, ' '); - char *bang = strchr(p, '!'); - if (sp && bang && bang < sp) { - size_t nlen = bang - p - 1; - if (memcmp(&p[1], fake->nick, nlen) != 0) { - char *nnick = new char[nlen+1]; - memcpy(nnick, &p[1], nlen); - nnick[nlen] = '\0'; - delete[] fake->nick; - fake->nick = nnick; - } - } - } - irc_parse_line(fake, p, 0); - } - strcpy_lit(fake->rbuf, "QUIT :bye\r\n"); - irc_parse_line(fake, fake->rbuf, 0); - set_time(0); - fclose(f); - } + admin_dostring("dofile(getluafile'init.lua')"); + fchanlog = fopen(getdatafile("chanlog.txt"), "abe"); if (fchanlog) { fprintf(fchanlog, "$0 AAA :server starting %lu\r\n", (unsigned long)get_time()); @@ -406,9 +359,7 @@ int main(int argc, char *argv[]) } } -#ifdef HAVE_LUA admin_fini(); -#endif job_fini(); netpoll::fini(); #ifdef HAVE_LIBTLS diff --git a/src/net.cpp b/src/net.cpp index 7948bff..d718aaf 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2022 Sam Smith -// Copyright (c) 2022-2025 Egor +// Copyright (c) 2022-2026 Egor // SPDX-License-Identifier: GPL-2.0-or-later #include "common.hpp" #include "net.hpp" @@ -77,9 +77,7 @@ void killConnection(Client *cl, int byChoice) channel_part(cl, chan, NULL, NULL); /* Remove from client_map and get rid of the pending invites */ if (cl->key) { -#ifdef HAVE_LUA admin_hook("nickdel", cl, 0, 0, cl->key); -#endif client_map.remove(cl); rpl_monoffline(0, cl->key), sendbuf_monitor(cl->key, 0, 0, 0); for (auto chan : cl->invites) -- 2.56.0