]> PatchyGit - patchyircd.git/commitdiff
add multi-prefix cap
authorEgor <egor@patchysicp.xyz>
Fri, 3 Feb 2023 16:14:31 +0000 (18:14 +0200)
committerEgor <egor@patchysicp.xyz>
Fri, 3 Feb 2023 16:14:31 +0000 (18:14 +0200)
common.h
irc.c
ircfmt.c

index 2d4acd6..387f3f9 100644 (file)
--- a/common.h
+++ b/common.h
@@ -19,6 +19,8 @@ struct tls;
 #define REG_INIT 0
 #define REG_CAPLS 1
 #define REG_REGISTERED 2
+#define CAP_302 1
+#define CAP_MULTIPREFIX 2
 struct client_data {
        SOCKET FD;
        struct tls *cctx;
@@ -44,6 +46,7 @@ struct client_data {
        int    cookie;
        time_t signon;
        time_t lastactivity;
+       unsigned int caps;
 };
 struct channel {
        char *key;
@@ -73,8 +76,8 @@ struct channel {
 #define DMODE_NOEXTERNAL 8
 #define DMODE_SECRET 16
 #define DMODE_TOPICPROT 32
-#define is_chanop(chan, client) (trie_get(&(chan)->o, (client)->key))
-#define is_chanvoice(chan, client) (trie_get(&(chan)->v, (client)->key))
+#define is_chanop(chan, client) (!!trie_get(&(chan)->o, (client)->key))
+#define is_chanvoice(chan, client) (!!trie_get(&(chan)->v, (client)->key))
 extern struct trie_node *client_map;
 extern struct trie_node *channel_map;
 extern struct trie_node *autojoin;
diff --git a/irc.c b/irc.c
index 55d5aa9..b1e0a96 100644 (file)
--- a/irc.c
+++ b/irc.c
@@ -178,28 +178,61 @@ int processPacket(struct client_data *client, char *source, char *command, int a
                msg_pong(args[0]);
                sendbuf(client);
        } else if(strcmp(command, "CAP") == 0) {
+               static const struct cap {
+                       const char *name;
+                       unsigned caps;
+               } caps[] = {
+                       { "multi-prefix", CAP_MULTIPREFIX },
+                       { "cap-notify", 0 },
+                       { 0, 0 }
+               };
+
                if (!args[0])
                        return err_needmoreparams(client, command), sendbuf(client), 0;
                stringupper(args[0]);
-               // don't enable CAP command until we actually have CAPs
-               // because clients can't handle NAKs
-               goto unknown;
-#if 0
                if (strcmp(args[0], "LS") == 0) {
-                       // at least one client hangs if it doesn't see a known extension
-                       copy_strf(&databuf, ":%s CAP * LS :multi-prefix\n", hostname);
+                       if (args[1] && atoi(args[1]) >= 302)
+                               client->caps |= CAP_302;
+                       copy_strf(&databuf, ":%s CAP * LS :multi-prefix\r\n", hostname);
                        sendbuf(client);
                        if (!is_registered(client)) {
                                client->regstate = REG_CAPLS;
                        }
                } else if (strcmp(args[0], "LIST") == 0) {
-                       copy_strf(&databuf, ":%s CAP * LIST :\n", hostname);
+                       copy_strf(&databuf, ":%s CAP * LIST :\r\n", hostname);
+                       for (const struct cap *c = caps; c->name; c++) {
+                               if (client->caps & c->caps)
+                               append_strf(&databuf, "%s ", c->name);
+                       }
+                       if (databuf.buf[databuf.len-1] == ' ')
+                               databuf.len--;
+                       append_strf(&databuf, "\r\n");
                        sendbuf(client);
                } else if (strcmp(args[0], "REQ") == 0) {
                        if (!args[1]) // FIXME: error message
                                return 0;
-                       copy_strf(&databuf, ":%s CAP * NAK :%s\n", hostname, args[1]);
-                       sendbuf(client);
+                       char *p;
+                       if ((p = strtok(args[1], " "))) {
+                               do {
+                                       int invert = *p == '-';
+                                       p += invert;
+                                       const struct cap *c;
+                                       for (c = caps; c->name; c++) {
+                                               if (strcmp(p, c->name) == 0) {
+                                                       copy_strf(&databuf, ":%s CAP * ACK :%s%s\r\n", hostname, invert?"-":"", c->name);
+                                                       if (!invert)
+                                                               client->caps |= c->caps;
+                                                       else
+                                                               client->caps &= ~c->caps;
+                                                       break;
+                                               }
+                                       }
+                                       if (!c->name)
+                                               copy_strf(&databuf, ":%s CAP * NAK :%s\r\n", hostname, p);
+                                       sendbuf(client);
+                               } while ((p = strtok(NULL, " ")));
+                       }
+                       // FIXME: else error?
                } else if (strcmp(args[0], "END") == 0) {
                        if (!is_registered(client)) {
                                client->regstate = REG_INIT;
@@ -208,7 +241,6 @@ int processPacket(struct client_data *client, char *source, char *command, int a
                } else {
                        goto unknown;
                }
-#endif
        } else if(strcmp(command, "NICK") == 0) {
                // User now registered
                if (!args[0])
index 61a6790..ad70f48 100644 (file)
--- a/ircfmt.c
+++ b/ircfmt.c
@@ -2,6 +2,19 @@
 extern struct dynarr databuf;
 extern const char *hostname;
 extern const char *format_umodes(struct client_data *cl);
+const char *format_prefixes(struct client_data *client, struct client_data *cl, struct channel *chan) {
+       if (!chan) return "";
+       int o = is_chanop(chan, cl);
+       int v = is_chanvoice(chan, cl);
+       if (client->caps & CAP_MULTIPREFIX && o && v)
+               return "@+";
+       else if (o)
+               return "@";
+       else if (v)
+               return "+";
+       else
+               return "";
+}
 
 void msg_nick(struct client_data *src, const char *newnick)
 {
@@ -223,16 +236,9 @@ void rpl_nowaway(struct client_data *client) // 306
 }
 void rpl_whoreply(struct client_data *client, struct client_data *cl, struct channel *chan) // 352
 {
-       char prefix[2] = {0,0};
-       if (chan) {
-               if (trie_get(&chan->o, cl->key))
-                       prefix[0] = '@';
-               else if (trie_get(&chan->v, cl->key))
-                       prefix[0] = '+';
-       }
        copy_strf(&databuf, ":%s 352 %s %s %s %s %s %s %s%s%s :1 %s\r\n",
                        hostname, client->nick, chan ? chan->name : "*", cl->username, cl->hostname, hostname, cl->nick,
-                       cl->away ? "G" : "H", cl->umode&UMODE_OPER ? "*" : "", prefix, cl->realname);
+                       cl->away ? "G" : "H", cl->umode&UMODE_OPER ? "*" : "", format_prefixes(client, cl, chan), cl->realname);
 }
 void rpl_endofwho(struct client_data *client, const char *query) // 315
 {
@@ -267,14 +273,7 @@ void rpl_endofwhois(struct client_data *client, struct client_data *cl) // 318
 }
 void rpl_whoischannels(struct client_data *client, struct client_data *cl, struct channel *chan) // 319
 {
-       char prefix[2] = {0,0};
-       if (chan) {
-               if (trie_get(&chan->o, cl->key))
-                       prefix[0] = '@';
-               else if (trie_get(&chan->v, cl->key))
-                       prefix[0] = '+';
-       }
-       copy_strf(&databuf, ":%s 319 %s %s :%s%s\r\n", hostname, client->nick, cl->nick, prefix, chan->name);
+       copy_strf(&databuf, ":%s 319 %s %s :%s%s\r\n", hostname, client->nick, cl->nick, format_prefixes(client, cl, chan), chan->name);
 }
 #if 0
 void rpl_whoisspecial(struct client_data *client, struct client_data *cl, const char *info) // 320
@@ -382,16 +381,10 @@ int rpl_namreply(struct client_data *client, struct channel *chan, struct client
                        return 0;
                }
        }
-       const char *prefix = NULL;
-       if (is_chanop(chan, cl))
-               prefix = "@";
-       else if (is_chanvoice(chan, cl))
-               prefix = "+";
-       else
-               prefix = "";
+       const char *prefix = format_prefixes(client, cl, chan);
        if (databuf.len == 0) {
-               // TODO: secret channel "@"
-               copy_strf(&databuf, ":%s 353 %s = %s :", hostname, client->nick, chan->name);
+               const char *chanchar = chan->dmode & DMODE_SECRET ? "@" : "=";
+               copy_strf(&databuf, ":%s 353 %s %s %s :", hostname, client->nick, chanchar, chan->name);
        } else {
                if (databuf.len + 1 + strlen(prefix) + strlen(cl->nick) > 510) {
                        append_str(&databuf, "\r\n");