From 811b0461ce4d96360955b4d08a88a9da648193e8 Mon Sep 17 00:00:00 2001 From: Egor Date: Fri, 27 Oct 2023 23:14:20 +0300 Subject: [PATCH] add MONITOR and extended-monitor --- src/admin.c | 1 + src/common.h | 2 ++ src/irc.c | 93 ++++++++++++++++++++++++++++++++++++++++++++++++++-- src/irc.h | 1 + src/ircfmt.c | 24 +++++++++++++- src/ircfmt.h | 5 +++ src/net.c | 2 ++ 7 files changed, 125 insertions(+), 3 deletions(-) diff --git a/src/admin.c b/src/admin.c index 568e048..431d699 100644 --- a/src/admin.c +++ b/src/admin.c @@ -146,6 +146,7 @@ static int client_index(lua_State *L) integer_getter(cl, regstate); map_getter(cl, channels, MAPTYPE_CHANNEL); map_getter(cl, invites, MAPTYPE_CHANNEL); + map_getter(cl, monitor, MAPTYPE_STRINGSET); integer_getter(cl, cookie); integer_getter(cl, signon); integer_getter(cl, lastactivity); diff --git a/src/common.h b/src/common.h index 0b8e1fc..31d6707 100644 --- a/src/common.h +++ b/src/common.h @@ -26,6 +26,7 @@ struct tls; #define CAP_EXTENDEDJOIN 4 #define CAP_AWAYNOTIFY 8 #define CAP_INVITENOTIFY 16 +#define CAP_EXTENDEDMONITOR 32 struct client_data { SOCKET FD; struct tls *cctx; @@ -50,6 +51,7 @@ struct client_data { char regstate; map_t channels; map_t invites; + map_t monitor; int cookie; time_t signon; time_t lastactivity; diff --git a/src/irc.c b/src/irc.c index 4184026..f790f82 100644 --- a/src/irc.c +++ b/src/irc.c @@ -52,6 +52,21 @@ void sendbuf_list_sans_self_caps(struct client_data *client, map_t *ls, int cook void sendbuf_list_sans_self(struct client_data *client, map_t *ls, int cookie) { sendbuf_list_sans_self_caps(client, ls, cookie, 0, 0); } +void sendbuf_monitor(const char *nick, int cookie, int positive, int negative) +{ + struct client_data *cl; + map_foreach(&client_map, 0, &cl) + if (map_get(&cl->monitor, nick)) { + if (cookie) { + if (cookie != cl->cookie) + cl->cookie = cookie; + else + continue; + } + if (!(~cl->caps & positive) && !(cl->caps & negative)) + sendbuf(cl); + } +} int get_cookie() { static int cookie = 0; if (++cookie == 0) { @@ -63,11 +78,12 @@ int get_cookie() { } return cookie; } -void sendbuf_visible_caps(struct client_data *client, int positive, int negative) { +int sendbuf_visible_caps(struct client_data *client, int positive, int negative) { int cookie = get_cookie(); struct channel *chan; map_foreach(&client->channels, 0, &chan) sendbuf_list_sans_self_caps(client, &chan->members, cookie, positive, negative); + return cookie; } void sendbuf_visible(struct client_data *client) { sendbuf_visible_caps(client, 0, 0); @@ -184,6 +200,7 @@ int processPacket(struct client_data *client, char *source, char *command, int a { "extended-join", CAP_EXTENDEDJOIN }, { "away-notify", CAP_AWAYNOTIFY }, { "invite-notify", CAP_INVITENOTIFY }, + { "extended-monitor", CAP_EXTENDEDMONITOR }, { 0, 0 } }; @@ -1043,7 +1060,77 @@ int processPacket(struct client_data *client, char *source, char *command, int a rpl_unaway(client), sendbuf(client); } msg_away(client); - sendbuf_visible_caps(client, CAP_AWAYNOTIFY, 0); + int cookie = sendbuf_visible_caps(client, CAP_AWAYNOTIFY, 0); + sendbuf_monitor(client->key, cookie, CAP_AWAYNOTIFY|CAP_EXTENDEDMONITOR, 0); + } else if(strcmp(command, "MONITOR") == 0) { + if (!is_registered(client)) + return err_notregistered(client), sendbuf(client), 0; + if (argc == 0) + return err_needmoreparams(client, command), sendbuf(client), 0; + if (!args[0][0] || args[0][1]) + return err_needmoreparams(client, command), sendbuf(client), 0; /* FIXME: wrong error */ + switch (args[0][0]) { + case '+': { + if (argc < 2) + return err_needmoreparams(client, command), sendbuf(client), 0; + if (strchr(args[1], ' ')) + return err_needmoreparams(client, command), sendbuf(client), 0; /* FIXME: wrong error */ + stringlower(args[1]); + char *p = args[1]; + do { + char *nick = p; + if ((p = strchr(nick, ','))) + *p++ = '\0'; + /* TODO: rpl_monlistfull */ + if (map_set(&client->monitor, nick, /* dummy */ nick, 0) == -1) + abort(); + struct client_data *cl = map_get(&client_map, nick); + if (cl) + rpl_mononline(client, cl), sendbuf(client); + else + rpl_monoffline(client, nick), sendbuf(client); + } while (p); + break; + } + case '-': { + if (argc < 2) + return err_needmoreparams(client, command), sendbuf(client), 0; + if (strchr(args[1], ' ')) + return err_needmoreparams(client, command), sendbuf(client), 0; /* FIXME: wrong error */ + stringlower(args[1]); + char *p = args[1]; + do { + char *nick = p; + if ((p = strchr(nick, ','))) + *p++ = '\0'; + map_unset(&client->monitor, nick, 0); + } while (p); + break; + } + case 'C': + map_free(&client->monitor); + break; + case 'L': { + const char *key; + map_foreach(&client->monitor, &key, 0) + rpl_monlist(client, key), sendbuf(client); + rpl_endofmonlist(client), sendbuf(client); + break; + } + case 'S': { + const char *key; + map_foreach(&client->monitor, &key, 0) { + struct client_data *cl = map_get(&client_map, key); + if (cl) + rpl_mononline(client, cl), sendbuf(client); + else + rpl_monoffline(client, key), sendbuf(client); + } + break; + } + default: + return err_needmoreparams(client, command), sendbuf(client), 0; /* FIXME: wrong error */ + } #ifdef HAS_LUA } else if(strcmp(command, "LUA") == 0) { if (argc < 1) @@ -1173,6 +1260,7 @@ void setNick(struct client_data *client, char* nickname) { msg_nick(client, nickname); sendbuf(client); sendbuf_visible(client); + rpl_monoffline(0, client->key), sendbuf_monitor(client->key, 0, 0, 0); map_unset(&client_map, client->key, NULL); struct channel *chan; map_foreach(&client->channels, 0, &chan) { @@ -1206,6 +1294,7 @@ void setNick(struct client_data *client, char* nickname) { abort(); if (map_set(&client_map, client->key, client, 0) == -1) abort(); + rpl_mononline(0, client), sendbuf_monitor(client->key, 0, 0, 0); return; } diff --git a/src/irc.h b/src/irc.h index 29bd8c0..93b2cf8 100644 --- a/src/irc.h +++ b/src/irc.h @@ -6,6 +6,7 @@ #include "util.h" #define is_registered(cl) ((cl)->regstate == REG_REGISTERED) +void sendbuf_monitor(const char *nick, int cookie, int positive, int negative); 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); diff --git a/src/ircfmt.c b/src/ircfmt.c index 202267e..25a89c7 100644 --- a/src/ircfmt.c +++ b/src/ircfmt.c @@ -133,7 +133,7 @@ void rpl_myinfo(struct client_data *client) // 004 } void rpl_isupport(struct client_data *client) // 005 { - copy_strf(&databuf, ":%s 005 %s NETWORK=PatchyIRC CASEMAPPING=ascii NICKLEN=%u EXCEPTS=e INVEX=I MAXTARGETS=1 CHANMODES=beIqw,k,l,Pimnst CHANTYPES=#& PREFIX=(ov)@+ STATUSMSG=@+ BOT=B WHOX :are supported by this server\r\n", hostname, client->nick, MAX_NICKLEN-1); + copy_strf(&databuf, ":%s 005 %s NETWORK=PatchyIRC CASEMAPPING=ascii NICKLEN=%u EXCEPTS=e INVEX=I MAXTARGETS=1 CHANMODES=beIqw,k,l,Pimnst CHANTYPES=#& PREFIX=(ov)@+ STATUSMSG=@+ BOT=B WHOX MONITOR :are supported by this server\r\n", hostname, client->nick, MAX_NICKLEN-1); } #if 0 @@ -685,6 +685,28 @@ void rpl_endofquietlist(struct client_data *client, struct channel *chan) // 729 { copy_strf(&databuf, ":%s 729 %s %s q :End of channel quiet list\r\n", hostname, client->nick, chan->name); } +void rpl_mononline(struct client_data *client, struct client_data *cl) // 730 +{ + copy_strf(&databuf, ":%s 730 %s :%s!%s@%s\r\n", hostname, client ? client->nick : "*", cl->nick, cl->username, cl->hostname); +} +void rpl_monoffline(struct client_data *client, const char *nick) // 731 +{ + copy_strf(&databuf, ":%s 731 %s :%s\r\n", hostname, client ? client->nick : "*", nick); +} +void rpl_monlist(struct client_data *client, const char *nick) // 732 +{ + copy_strf(&databuf, ":%s 732 %s :%s\r\n", hostname, client->nick, nick); +} +void rpl_endofmonlist(struct client_data *client) // 733 +{ + copy_strf(&databuf, ":%s 733 %s :End of MONITOR list\r\n", hostname, client->nick); +} +#if 0 +void rpl_monlistfull(struct client_data *client, int limit, const char *targets) // 734 +{ + copy_strf(&databuf, ":%s 734 %s %d %s :Monitor list is full.\r\n", hostname, client->nick, limit, targets); +} +#endif // 900 RPL_LOGGEDIN // 901 RPL_LOGGEDOUT // 902 ERR_NICKLOCKED diff --git a/src/ircfmt.h b/src/ircfmt.h index f4ab13f..b39a76b 100644 --- a/src/ircfmt.h +++ b/src/ircfmt.h @@ -138,6 +138,11 @@ void rpl_endofhelp(struct client_data *client); // 706 // 723 ERR_NOPRIVS void rpl_quietlist(struct client_data *client, struct channel *chan, const char *mask); // 728 void rpl_endofquietlist(struct client_data *client, struct channel *chan); // 729 +void rpl_mononline(struct client_data *client, struct client_data *cl); // 730 +void rpl_monoffline(struct client_data *client, const char *nick); // 731 +void rpl_monlist(struct client_data *client, const char *nick); // 732 +void rpl_endofmonlist(struct client_data *client); // 733 +void rpl_monlistfull(struct client_data *client, int limit, const char *targets); // 734 // 900 RPL_LOGGEDIN // 901 RPL_LOGGEDOUT // 902 ERR_NICKLOCKED diff --git a/src/net.c b/src/net.c index 3e51cf4..02aa036 100644 --- a/src/net.c +++ b/src/net.c @@ -69,12 +69,14 @@ void killConnection(struct client_data *cl, int byChoice) /* Remove from client_map and get rid of the pending invites */ if (cl->key) { map_unset(&client_map, cl->key, NULL); + rpl_monoffline(0, cl->key), sendbuf_monitor(cl->key, 0, 0, 0); struct channel *chan; map_foreach(&cl->invites, 0, &chan) map_unset(&chan->invitees, cl->key, NULL); } map_free(&cl->channels); map_free(&cl->invites); + map_free(&cl->monitor); killConnectionPart2(cl); } void killConnectionPart2(struct client_data *cl) -- 2.56.0