]> PatchyGit - patchyircd.git/commitdiff
add channel modes (currently they don't do anything)
authorEgor <egor@patchysicp.xyz>
Fri, 6 May 2022 18:25:51 +0000 (21:25 +0300)
committerEgor <egor@patchysicp.xyz>
Fri, 6 May 2022 18:33:12 +0000 (21:33 +0300)
common.h
irc.c
ircfmt.c
ircfmt.h

index 5349cf2..0266b8f 100644 (file)
--- a/common.h
+++ b/common.h
@@ -5,6 +5,7 @@
 #include "trie.h"
 #include <time.h>
 
+#define SETBIT(var, bit, val) ((void)((val) ? ((var)|=(bit)) : ((var)&=~(bit))))
 #define UMODE_INVISIBLE 1
 #define UMODE_OPER 2
 #define UMODE_LOPER 4
@@ -35,12 +36,24 @@ struct channel {
        char *topic;
        char *topic_who;
        time_t topic_when;
+       time_t creation;
        struct trie_node *members;
        struct trie_node *o;
        struct trie_node *v;
        int member_count;
        char dmode;
+       //char *ban_mask[20];
+       //char *exc_mask[20];
+       //char *iex_mask[20];
+       char *chankey;
+       int member_limit;
 };
+#define DMODE_PERSIST 1
+#define DMODE_INVITEONLY 2
+#define DMODE_MODERATED 4
+#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))
 extern struct trie_node *client_map;
diff --git a/irc.c b/irc.c
index 2acbedc..cb2bdf4 100644 (file)
--- a/irc.c
+++ b/irc.c
@@ -11,6 +11,14 @@ const char *hostname = "irc.patchysicp.xyz";
 void sendbuf(struct client_data *client) {
        send_log(client->FD, databuf.buf, databuf.len, 0);
 }
+void sendbuf_chan(struct channel *chan) {
+       struct trie_find *f = NULL;
+       if (trie_findfirst(&f, &chan->members) == 1)
+               do {
+                       struct client_data *cl = f->n->value;
+                       sendbuf(cl);
+               } while (trie_findnext(&f) == 1);
+}
 int parsePacket(struct client_data *client) {
        char *source = NULL;
        char *command = NULL;
@@ -209,12 +217,7 @@ int processPacket(struct client_data *client, char *source, char *command, int a
                                chan->topic_when = time(NULL);
                        }
                        msg_topic(client, chan);
-                       struct trie_find *f = NULL;
-                       if (trie_findfirst(&f, &chan->members) == 1)
-                               do {
-                                       struct client_data *cl = f->n->value;
-                                       sendbuf(cl);
-                               } while (trie_findnext(&f) == 1);
+                       sendbuf_chan(chan);
                }
        } else if(strcmp(command, "PRIVMSG") == 0) {
                if (!is_registered(client))
@@ -227,7 +230,7 @@ int processPacket(struct client_data *client, char *source, char *command, int a
                } else {
                        struct client_data *cl = findClient(args[0]);
                        if (!cl) {
-                               copy_strf(&databuf, ":%s 401 %s %s :No such nick/channel\n", hostname, client->nick, args[0]);
+                               err_nosuchnick(client, args[0]);
                                sendbuf(client);
                                return 0;
                        } else {
@@ -255,11 +258,8 @@ int processPacket(struct client_data *client, char *source, char *command, int a
                if (argc == 1) { // get mode
                        if (*nick != '#' && *nick != '&') {
                                struct client_data *cl = findClient(nick);
-                               if (!cl) {
-                                       copy_strf(&databuf, ":%s 401 %s %s :No such nick/channel\n", hostname, client->nick, nick);
-                                       sendbuf(client);
-                                       return 0;
-                               }
+                               if (!cl)
+                                       return err_nosuchnick(client, nick), sendbuf(client), 0;
                                copy_strf(&databuf, ":%s 221 %s +", hostname, client->nick);
                                if (cl->umode & UMODE_INVISIBLE) append_str(&databuf, "i");
                                if (cl->umode & UMODE_OPER) append_str(&databuf, "o");
@@ -269,39 +269,50 @@ int processPacket(struct client_data *client, char *source, char *command, int a
                                append_str(&databuf, "\r\n");
                                sendbuf(client);
                        } else {
-                               copy_strf(&databuf, ":%s 324 %s %s +nt\n", hostname, client->nick, nick);
+                               struct channel *chan = findChannel(nick);
+                               if (!chan)
+                                       return err_nosuchchannel(client, nick), sendbuf(client), 0;
+                               copy_strf(&databuf, ":%s 324 %s %s +", hostname, client->nick, nick);
+                               if (chan->dmode & DMODE_PERSIST) append_str(&databuf, "P");
+                               if (chan->dmode & DMODE_INVITEONLY) append_str(&databuf, "i");
+                               if (chan->dmode & DMODE_MODERATED) append_str(&databuf, "m");
+                               if (chan->dmode & DMODE_NOEXTERNAL) append_str(&databuf, "n");
+                               if (chan->dmode & DMODE_SECRET) append_str(&databuf, "s");
+                               if (chan->dmode & DMODE_TOPICPROT) append_str(&databuf, "t");
+                               if (chan->member_limit != -1) append_str(&databuf, "l");
+                               if (chan->chankey) append_str(&databuf, "k");
+                               if (chan->member_limit != -1) append_strf(&databuf, " %u", chan->member_limit);
+                               if (chan->chankey) append_strf(&databuf, " %s", chan->chankey);
+                               append_str(&databuf, "\r\n");
+                               sendbuf(client);
+                               // FIXME: 64-bit time_t support
+                               copy_strf(&databuf, ":%s 329 %s %s %u\n", hostname, client->nick, chan->name, (unsigned)chan->creation);
                                sendbuf(client);
                        }
                } else { // set mode
                        if (*nick != '#' && *nick != '&') {
                                struct client_data *cl = findClient(nick);
-                               if (!cl) {
-                                       copy_strf(&databuf, ":%s 401 %s %s :No such nick/channel\n", hostname, client->nick, nick);
-                                       sendbuf(client);
-                                       return 0;
-                               }
+                               if (!cl)
+                                       return err_nosuchnick(client, nick), sendbuf(client), 0;
                                if (client->FD != cl->FD) {
                                        copy_strf(&databuf, ":%s 502 %s :Can't change mode for other users\n", hostname, client->nick);
                                        sendbuf(client);
                                        return 0;
                                }
-                               int off;
+                               int is_plus;
                                char *p = args[1];
                                switch (*p++) {
-                                       case '+': off = 0; break;
-                                       case '-': off = 1; break;
+                                       case '+': is_plus = 1; break;
+                                       case '-': is_plus = 0; break;
                                        default: goto unknown;
                                }
                                while (*p) {
                                        switch (*p) {
+                                       case '+': is_plus = 1; break;
+                                       case '-': is_plus = 0; break;
                                        case 'i':
-                                               if (off) {
-                                                       copy_strf(&databuf, ":%s MODE %s -i\n", hostname, client->nick);
-                                                       cl->umode &= ~UMODE_INVISIBLE;
-                                               } else {
-                                                       copy_strf(&databuf, ":%s MODE %s +i\n", hostname, client->nick);
-                                                       cl->umode |= UMODE_INVISIBLE;
-                                               }
+                                               SETBIT(cl->umode, UMODE_INVISIBLE, is_plus);
+                                               copy_strf(&databuf, ":%s MODE %s %si\n", hostname, client->nick, is_plus?"+":"-");
                                                sendbuf(client);
                                                break;
                                        default:
@@ -311,6 +322,9 @@ int processPacket(struct client_data *client, char *source, char *command, int a
                                        p++;
                                }
                        } else {
+                               struct channel *chan = findChannel(nick);
+                               if (!chan)
+                                       return err_nosuchchannel(client, nick), sendbuf(client), 0;
                                if (argc == 2) { // Type A channel modes
                                        if (!strcmp(args[1], "+b")) {
                                                copy_strf(&databuf, ":%s 368 %s %s :End of channel ban list\n", hostname, client->nick, args[1]);
@@ -328,7 +342,111 @@ int processPacket(struct client_data *client, char *source, char *command, int a
                                                return 0;
                                        }
                                }
-                               goto unknown;
+                               int is_plus;
+                               char *p = args[1];
+                               switch (*p++) {
+                                       case '+': is_plus = 1; break;
+                                       case '-': is_plus = 0; break;
+                                       default: goto unknown;
+                               }
+                               char **argp = &args[2];
+                               while (*p) {
+                                       switch (*p) {
+                                       case '+': is_plus = 1; break;
+                                       case '-': is_plus = 0; break;
+#define MSG_CHANDMODE(m) \
+                                       copy_strf(&databuf, ":%s!%s@%s MODE %s %s"m"\r\n", \
+                                                       client->nick, client->username, client->hostname, \
+                                                       chan->name, is_plus?"+":"-")
+                                       case 'P':
+                                               if (!(client->umode & UMODE_OPER))
+                                                       break;
+                                               SETBIT(chan->dmode, DMODE_PERSIST, is_plus);
+                                               MSG_CHANDMODE("P");
+                                               sendbuf_chan(chan);
+                                               break;
+                                       case 'i':
+                                               SETBIT(chan->dmode, DMODE_INVITEONLY, is_plus);
+                                               MSG_CHANDMODE("i");
+                                               sendbuf_chan(chan);
+                                               break;
+                                       case 'm':
+                                               SETBIT(chan->dmode, DMODE_MODERATED, is_plus);
+                                               MSG_CHANDMODE("m");
+                                               sendbuf_chan(chan);
+                                               break;
+                                       case 'n':
+                                               SETBIT(chan->dmode, DMODE_NOEXTERNAL, is_plus);
+                                               MSG_CHANDMODE("n");
+                                               sendbuf_chan(chan);
+                                               break;
+                                       case 's':
+                                               SETBIT(chan->dmode, DMODE_SECRET, is_plus);
+                                               MSG_CHANDMODE("s");
+                                               sendbuf_chan(chan);
+                                               break;
+                                       case 't':
+                                               SETBIT(chan->dmode, DMODE_TOPICPROT, is_plus);
+                                               MSG_CHANDMODE("t");
+                                               sendbuf_chan(chan);
+                                               break;
+                                       case 'l':
+                                               if (is_plus) {
+                                                       int x = 0;
+                                                       if (!*argp) {
+                                                               // FIXME: not enough args error
+                                                       } else if (sscanf(*argp, "%u", &x) != 1 || x < 0) {
+                                                               // FIXME: bad arg error
+                                                               argp++;
+                                                       } else {
+                                                               chan->member_limit = x;
+                                                               copy_strf(&databuf, ":%s!%s@%s MODE %s +l %u\r\n",
+                                                                               client->nick, client->username, client->hostname,
+                                                                               chan->name, x);
+                                                               sendbuf_chan(chan);
+                                                               argp++;
+                                                       }
+                                               } else {
+                                                       chan->member_limit = -1;
+                                                       MSG_CHANDMODE("l");
+                                                       sendbuf_chan(chan);
+                                               }
+                                               break;
+                                       case 'k':
+                                               if (is_plus) {
+                                                       if (!*argp) {
+                                                               // FIXME: not enough args error
+                                                               break;
+                                                       } else if (strchr(*argp, ',') || strchr(*argp, ' ')) {
+                                                               copy_strf(&databuf, ":%s 525 %s %s :Key is not well-formed\r\n", hostname, client->nick, nick);
+                                                               sendbuf(client);
+                                                               argp++;
+                                                               break;
+                                                       }
+                                                       if (chan->chankey)
+                                                               free(chan->chankey);
+                                                       chan->chankey = mystrdup(*argp);
+                                                       if (!chan->chankey)
+                                                               abort();
+                                               } else {
+                                                       free(chan->chankey);
+                                                       chan->chankey = NULL;
+                                                       if (*argp) // unused argument
+                                                               argp++;
+                                               }
+                                               copy_strf(&databuf, ":%s!%s@%s MODE %s %sk %s\r\n",
+                                                               client->nick, client->username, client->hostname,
+                                                               chan->name, is_plus?"+":"-", is_plus?*argp:"*");
+                                               sendbuf_chan(chan);
+                                               break;
+                                       case 'b':
+                                       case 'e':
+                                       case 'I':
+                                       default:
+                                               goto unknown;
+                                       }
+                                       p++;
+                               }
                        }
                }
        } else if(strcmp(command, "LIST") == 0) {
@@ -427,7 +545,7 @@ int processPacket(struct client_data *client, char *source, char *command, int a
                        copy_strf(&databuf, ":%s 318 %s %s :End of /WHOIS list\n", hostname, client->nick, cl->nick);
                        sendbuf(client);
                } else {
-                       copy_strf(&databuf, ":%s 401 %s %s :No such nick/channel\n", hostname, client->nick, nick);
+                       err_nosuchnick(client, nick);
                        sendbuf(client);
                }
        } else if(strcmp(command, "AWAY") == 0) {
@@ -447,9 +565,7 @@ int processPacket(struct client_data *client, char *source, char *command, int a
                }
        } else {
 unknown:
-               // Inform user of error
-               copy_strf(&databuf, ":%s 421 %s :Unknown command %s\n", hostname, client->nick, command);
-               sendbuf(client);
+               err_unknowncommand(client, command), sendbuf(client);
        }
        return 0;
 }
@@ -483,15 +599,13 @@ void setNick(struct client_data *client, char* nickname) {
        // TODO: perform basic sanitization
 
        if (strlen(nickname) >= MAX_NICKLEN || nickname[0] == '\0') {
-               copy_strf(&databuf, ":%s 432 * %s :Erroneous nickname\n", hostname, nickname);
-               sendbuf(client);
+               err_erroneusnickname(client, nickname), sendbuf(client);
                return;
        }
        const char *forbidden = " ,*?!@$:#&.";
        while (*forbidden)
                if (strchr(nickname, *forbidden++)) {
-                       copy_strf(&databuf, ":%s 432 * %s :Erroneous nickname\n", hostname, nickname);
-                       sendbuf(client);
+                       err_erroneusnickname(client, nickname), sendbuf(client);
                        return;
                }
 
@@ -500,9 +614,7 @@ void setNick(struct client_data *client, char* nickname) {
        stringlower(key);
 
        if (trie_get(&client_map, key)) {
-               // Nickname is already taken
-               copy_strf(&databuf, ":%s 443 * %s :Nickname is already in use\n", hostname, nickname);
-               sendbuf(client);
+               err_nicknameinuse(client, nickname), sendbuf(client);
                return;
        }
 
@@ -572,6 +684,9 @@ void channel_join(struct client_data *client, const char *channel, const char *c
                chan = malloc(sizeof(struct channel));
                if (!chan) abort();
                memset(chan, 0, sizeof(struct channel));
+               chan->creation = time(NULL);
+               chan->member_limit = -1;
+               chan->dmode = DMODE_NOEXTERNAL | DMODE_TOPICPROT;
                chan->key = mystrdup(key);
                if (!chan->key) abort();
                chan->name = mystrdup(channel);
@@ -595,12 +710,7 @@ void channel_join(struct client_data *client, const char *channel, const char *c
        chan->member_count++;
        // send JOIN to everyone
        msg_join(client, chan);
-       struct trie_find *f = NULL;
-       if (trie_findfirst(&f, &chan->members) == 1)
-               do {
-                       struct client_data *cl = f->n->value;
-                       sendbuf(cl);
-               } while(trie_findnext(&f) == 1);
+       sendbuf_chan(chan);
        // send topic
        channel_sendtopic(client, chan, 1);
        // send names
@@ -610,12 +720,7 @@ void channel_part(struct client_data *client, struct channel *chan, const char *
 {
        if (reason) {
                msg_part(client, chan, *reason ? reason : NULL);
-               struct trie_find *f = NULL;
-               if (trie_findfirst(&f, &chan->members) == 1)
-                       do {
-                               struct client_data *cl = f->n->value;
-                               sendbuf(cl);
-                       } while (trie_findnext(&f) == 1);
+               sendbuf_chan(chan);
        }
        trie_set(&client->channels, chan->key, NULL, NULL);
        trie_set(&chan->members, client->key, NULL, NULL);
@@ -631,6 +736,7 @@ void channel_part(struct client_data *client, struct channel *chan, const char *
                trie_free(&chan->members);
                trie_free(&chan->o);
                trie_free(&chan->v);
+               free(chan->chankey);
                free(chan);
        }
 }
index 6e98ae6..f6f9932 100644 (file)
--- a/ircfmt.c
+++ b/ircfmt.c
@@ -102,7 +102,8 @@ void rpl_myinfo(struct client_data *client) // 004
 }
 void rpl_isupport(struct client_data *client) // 005
 {
-       copy_strf(&databuf, ":%s 005 %s CASEMAPPING=rfc1459 NICKLEN=%u CHANTYPES=#& PREFIX=(ov)@+ :are supported by this server\n", hostname, client->nick, MAX_NICKLEN-1);
+       copy_strf(&databuf, ":%s 005 %s CASEMAPPING=rfc1459 NICKLEN=%u CHANMODES=beI,k,l,Pimnst CHANTYPES=#& PREFIX=(ov)@+ :are supported by this server\n", hostname, client->nick, MAX_NICKLEN-1);
+
 }
 // 010 RPL_BOUNC
 // 221 RPL_UMODEIS
@@ -166,7 +167,6 @@ void rpl_topicwhotime(struct client_data *client, struct channel *chan) // 333
 // 348 RPL_EXCEPTLIST
 // 349 RPL_ENDOFEXCEPTLIST
 // 351 RPL_VERSION
-// 353 RPL_NAMREPLY
 int rpl_namreply(struct client_data *client, struct channel *chan, struct client_data *cl) // 353
 {
        if (!cl) {
@@ -216,22 +216,80 @@ void rpl_endofnames(struct client_data *client, const char *channel) // 366
 // 381 RPL_YOUREOPER
 // 382 RPL_REHASHING
 // 391 RPL_TIME
-// 400 ERR_UNKNOWNERROR
-// 401 ERR_NOSUCHNICK
-// 402 ERR_NOSUCHSERVER
-// 403 ERR_NOSUCHCHANNEL
-// 404 ERR_CANNOTSENDTOCHAN
-// 405 ERR_TOOMANYCHANNELS
-// 406 ERR_WASSNOSUCHNICK
-// 409 ERR_NOORIGIN
-// 417 ERR_INPUTTOOLONG
-// 421 ERR_UNKNOWNCOMMAND
-// 422 ERR_NOMOTD
-// 432 ERR_ERRONEUSNICKNAME
-// 433 ERR_NICKNAMEINUSE
-// 441 ERR_USERNOTINCHANNEL
-// 442 ERR_NOTONCHANNEL
-// 443 ERR_USERONCHANNEL
+#if 0
+void err_unknownerror(struct client_data *client, const char *command, const char *msg) // 400
+{
+       copy_strf(&databuf, ":%s 400 %s %s :%s\r\n", hostname, client->nick, command, msg);
+}
+#endif
+void err_nosuchnick(struct client_data *client, const char *nick) // 401
+{
+       copy_strf(&databuf, ":%s 401 %s %s :No such nick/channel\r\n", hostname, client->nick, nick);
+}
+#if 0
+void err_nosuchserver(struct client_data *client, const char *server) // 402
+{
+       copy_strf(&databuf, ":%s 402 %s %s :No such server\r\n", hostname, client->nick, server);
+}
+#endif
+void err_nosuchchannel(struct client_data *client, const char *channel) // 403
+{
+       copy_strf(&databuf, ":%s 403 %s %s :No such channel\r\n", hostname, client->nick, channel);
+}
+#if 0
+void err_cannotsendtochan(struct client_data *client, const char *channel) // 404
+{
+       copy_strf(&databuf, ":%s 404 %s %s :Cannot send to channel\r\n", hostname, client->nick, channel);
+}
+void err_toomanychannels(struct client_data *client, const char *channel) // 405
+{
+       copy_strf(&databuf, ":%s 405 %s %s :You have joined too many channels\r\n", hostname, client->nick, channel);
+}
+void err_wasnosuchnick(struct client_data *client) // 406
+{
+       copy_strf(&databuf, ":%s 406 %s :There was no such nickname\r\n", hostname, client->nick);
+}
+void err_noorigin(struct client_data *client) // 409
+{
+       copy_strf(&databuf, ":%s 409 %s :No origin specified\r\n", hostname, client->nick);
+}
+void err_inputtoolong(struct client_data *client) // 417
+{
+       copy_strf(&databuf, ":%s 417 %s :Input line was too long\r\n", hostname, client->nick);
+}
+#endif
+void err_unknowncommand(struct client_data *client, const char *command) // 421
+{
+       copy_strf(&databuf, ":%s 421 %s %s :Unknown command\r\n", hostname, client->nick, command);
+}
+#if 0
+void err_nomotd(struct client_data *client) // 422
+{
+       copy_strf(&databuf, ":%s 422 %s :MOTD File is missing\r\n", hostname, client->nick);
+}
+#endif
+void err_erroneusnickname(struct client_data *client, const char *nick) // 432
+{
+       copy_strf(&databuf, ":%s 432 %s %s :Erroneus nickname\r\n", hostname, client->nick, nick);
+}
+void err_nicknameinuse(struct client_data *client, const char *nick) // 433
+{
+       copy_strf(&databuf, ":%s 433 %s %s :Nickname is already in use\r\n", hostname, client->nick, nick);
+}
+#if 0
+void err_usernotinchannel(struct client_data *client, const char *nick, struct channel *chan) // 441
+{
+       copy_strf(&databuf, ":%s 441 %s %s %s :They aren't on that channel\r\n", hostname, client->nick, nick, chan->name);
+}
+void err_notonchannel(struct client_data *client, struct channel *chan) // 442
+{
+       copy_strf(&databuf, ":%s 442 %s %s :is already on channel\r\n", hostname, client->nick, chan->name);
+}
+void err_useronchannel(struct client_data *client, const char *nick, struct channel *chan) // 443
+{
+       copy_strf(&databuf, ":%s 443 %s %s %s :is already on channel\r\n", hostname, client->nick, nick, chan->name);
+}
+#endif
 void err_notregistered(struct client_data *client) // 451
 {
        (void)client;
index d7b4dd7..91a88f0 100644 (file)
--- a/ircfmt.h
+++ b/ircfmt.h
@@ -86,22 +86,22 @@ void rpl_endofnames(struct client_data *client, const char *channel); // 366
 // 381 RPL_YOUREOPER
 // 382 RPL_REHASHING
 // 391 RPL_TIME
-// 400 ERR_UNKNOWNERROR
-// 401 ERR_NOSUCHNICK
-// 402 ERR_NOSUCHSERVER
-// 403 ERR_NOSUCHCHANNEL
-// 404 ERR_CANNOTSENDTOCHAN
-// 405 ERR_TOOMANYCHANNELS
-// 406 ERR_WASSNOSUCHNICK
-// 409 ERR_NOORIGIN
-// 417 ERR_INPUTTOOLONG
-// 421 ERR_UNKNOWNCOMMAND
-// 422 ERR_NOMOTD
-// 432 ERR_ERRONEUSNICKNAME
-// 433 ERR_NICKNAMEINUSE
-// 441 ERR_USERNOTINCHANNEL
-// 442 ERR_NOTONCHANNEL
-// 443 ERR_USERONCHANNEL
+void err_unknownerror(struct client_data *client, const char *command, const char *msg); // 400
+void err_nosuchnick(struct client_data *client, const char *nick); // 401
+void err_nosuchserver(struct client_data *client, const char *server); // 402
+void err_nosuchchannel(struct client_data *client, const char *channel); // 403
+void err_cannotsendtochan(struct client_data *client, const char *channel); // 404
+void err_toomanychannels(struct client_data *client, const char *channel); // 405
+void err_wasnosuchnick(struct client_data *client); // 406
+void err_noorigin(struct client_data *client); // 409
+void err_inputtoolong(struct client_data *client); // 417
+void err_unknowncommand(struct client_data *client, const char *command); // 421
+void err_nomotd(struct client_data *client); // 422
+void err_erroneusnickname(struct client_data *client, const char *nick); // 432
+void err_nicknameinuse(struct client_data *client, const char *nick); // 433
+void err_usernotinchannel(struct client_data *client, const char *nick, struct channel *chan); // 441
+void err_notonchannel(struct client_data *client, struct channel *chan); // 442
+void err_useronchannel(struct client_data *client, const char *nick, struct channel *chan); // 443
 void err_notregistered(struct client_data *client); // 451
 // 461 ERR_NEEDMOREPARAMS
 // 462 ERR_ALREADYREGISTERED