]> PatchyGit - patchyircd.git/commitdiff
add proper channels
authorEgor <egor@patchysicp.xyz>
Tue, 3 May 2022 18:51:40 +0000 (21:51 +0300)
committerEgor <egor@patchysicp.xyz>
Tue, 3 May 2022 18:51:40 +0000 (21:51 +0300)
common.h
irc.c
irc.h
net.c
trie.c

index b252631..101518a 100644 (file)
--- a/common.h
+++ b/common.h
@@ -3,6 +3,7 @@
 #include "NetSock/netsock.h"
 #include "config.h"
 #include "trie.h"
+#include <time.h>
 
 #define UMODE_INVISIBLE 1
 #define UMODE_OPER 2
@@ -22,8 +23,22 @@ struct client_data {
        size_t bufLen;
        size_t bufCap;
        char   umode;
+       struct trie_node *channels;
+};
+struct channel {
+       char *key;
+       char *name;
+       char *topic;
+       char *topic_who;
+       time_t topic_when;
+       struct trie_node *members;
+       struct trie_node *o;
+       struct trie_node *v;
+       int member_count;
+       char dmode;
 };
 extern struct trie_node *client_map;
+extern struct trie_node *channel_map;
 extern struct client_data  *clients[CLIENTS];
 extern        fd_set       readfds;
 extern struct sockaddr_in  serverAddrV4;
diff --git a/irc.c b/irc.c
index a0bb24c..9501222 100644 (file)
--- a/irc.c
+++ b/irc.c
@@ -3,6 +3,7 @@
 struct dynarr databuf;
 
 struct trie_node *client_map = NULL;
+struct trie_node *channel_map = NULL;
 
 const char *hostname = "irc.patchysicp.xyz";
 int processPacket(struct client_data *client) {
@@ -121,31 +122,73 @@ int processPacket(struct client_data *client) {
                        return 0;
                if (!args[0]) // FIXME: error message
                        return 0;
-               for (char *p = args[0]; *p; p++) // TODO: multiple channels
-                       if (*p == ',') {
-                               *p = '\0';
-                               break;
+               if (!strcmp(args[0], "0")) {
+                       struct trie_find *f = NULL;
+                       if (trie_findfirst(&f, &client->channels) == 1)
+                               do {
+                                       struct channel *chan = f->n->value;
+                                       channel_part(client, chan, "I fell for a classic prank");
+                               } while(trie_findfirst(&f, &client->channels) == 1);
+                       free(f);
+                       return 0;
+               }
+               char *p = strchr(args[0], ','); // TODO: multiple channels
+               if (p)
+                       *p = '\0';
+               channel_join(client, args[0], NULL);
+       } else if(strcmp(command, "PART") == 0) {
+               if (!client->nick) // FIXME: error message
+                       return 0;
+               if (!args[0]) // FIXME: error message
+                       return 0;
+               char *p = strchr(args[0], ','); // TODO: multiple channels
+               if (p)
+                       *p = '\0';
+               struct channel *chan = findChannel(args[0]);
+               if (!chan) // TODO: error message
+                       return 0;
+               channel_part(client, chan, args[1] ? args[1] : "");
+       } else if(strcmp(command, "TOPIC") == 0) {
+               if (!client->nick) // FIXME: error message
+                       return 0;
+               if (!args[0]) // FIXME: error message
+                       return 0;
+               struct channel *chan = findChannel(args[0]);
+               if (!chan) // TODO: error message
+                       return 0;
+               if (!args[1]) {
+                       // TODO: +s
+                       channel_sendtopic(client, chan);
+               } else {
+                       // TODO: +t check, membership check
+                       free(chan->topic);
+                       free(chan->topic_who);
+                       if (!*args[1]) {
+                               chan->topic = NULL;
+                               chan->topic_who = NULL;
+                               chan->topic_when = 0;
+                       } else {
+                               chan->topic = mystrdup(args[1]);
+                               if (!chan->topic) abort();
+                               chan->topic_who = mystrdup(client->nick);
+                               if (!chan->topic_who) abort();
+                               chan->topic_when = time(NULL);
                        }
-               copy_strf(&databuf, ":%s!%s@%s JOIN :%s\n", client->nick, client->username, client->hostname, args[0]);
-               // End GitHub Copilot
-               send_log(client->FD, databuf.buf, databuf.len, 0);
-               sendall_log(client->FD, databuf.buf, databuf.len, 0);
-               struct trie_find *f;
-               if (trie_findfirst(&f, &client_map) == 1)
-                       do {
-                               struct client_data *cl = f->n->value;
-                               copy_strf(&databuf, ":%s 353 %s = %s :%s\n", hostname, client->nick, args[0], cl->nick);
-                               send_log(client->FD, databuf.buf, databuf.len, 0);
-                       } while(trie_findnext(&f) == 1);
-               copy_strf(&databuf, ":%s 366 %s :End of /NAMES list\n", hostname, client->nick);
-               send_log(client->FD, databuf.buf, databuf.len, 0);
+                       copy_strf(&databuf, ":%s!%s@%s TOPIC %s :%s\n", client->nick, client->username, client->hostname, chan->name, args[1]);
+                       struct trie_find *f = NULL;
+                       if (trie_findfirst(&f, &chan->members) == 1)
+                               do {
+                                       struct client_data *cl = f->n->value;
+                                       send_log(cl->FD, databuf.buf, databuf.len, 0);
+                               } while (trie_findnext(&f) == 1);
+               }
        } else if(strcmp(command, "PRIVMSG") == 0) {
                if (!client->nick) // FIXME: error message
                        return 0;
                if (argc < 2) // FIXME: error message
                        return 0;
                copy_strf(&databuf, ":%s!%s@%s PRIVMSG %s :%s\n", client->nick, client->username, client->hostname, args[0], args[1]);
-               if (args[0][0] == '#') {
+               if (args[0][0] == '#' || args[0][0] == '&') {
                        sendall_log(client->FD, databuf.buf, databuf.len, 0);
                } else {
                        struct client_data *cl = findClient(args[0]);
@@ -176,7 +219,7 @@ int processPacket(struct client_data *client) {
                        return 0;
                char *nick = args[0];
                if (argc == 1) { // get mode
-                       if (*nick != '#') {
+                       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);
@@ -196,7 +239,7 @@ int processPacket(struct client_data *client) {
                                send_log(client->FD, databuf.buf, databuf.len, 0);
                        }
                } else { // set mode
-                       if (*nick != '#') {
+                       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);
@@ -234,6 +277,23 @@ int processPacket(struct client_data *client) {
                                        p++;
                                }
                        } else {
+                               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]);
+                                               send_log(client->FD, databuf.buf, databuf.len, 0);
+                                               return 0;
+                                       }
+                                       if (!strcmp(args[1], "+e")) {
+                                               copy_strf(&databuf, ":%s 349 %s %s :End of channel exception list\n", hostname, client->nick, args[1]);
+                                               send_log(client->FD, databuf.buf, databuf.len, 0);
+                                               return 0;
+                                       }
+                                       if (!strcmp(args[1], "+I")) {
+                                               copy_strf(&databuf, ":%s 347 %s %s :End of Channel Invite Exception List\n", hostname, client->nick, args[1]);
+                                               send_log(client->FD, databuf.buf, databuf.len, 0);
+                                               return 0;
+                                       }
+                               }
                                goto unknown;
                        }
                }
@@ -243,7 +303,7 @@ int processPacket(struct client_data *client) {
                        return 0;
                if (argc == 0) // FIXME: error message
                        return 0;
-               struct trie_find *f;
+               struct trie_find *f = NULL;
                if (trie_findfirst(&f, &client_map) == 1)
                        do {
                                struct client_data *cl = f->n->value;
@@ -351,7 +411,7 @@ void newUser(struct client_data *client) {
        copy_strf(&databuf, ":%s 004 %s PatchyIRCD 0.0.1 i nt\n", hostname, client->nick);
        send_log(client->FD, databuf.buf, databuf.len, 0);
        // FIXME: output nicklen from the actual macro
-       copy_strf(&databuf, ":%s 005 %s CASEMAPPING=rfc1459 NICKLEN=31 CHANTYPES=# :are supported by this server\n", hostname, client->nick);
+       copy_strf(&databuf, ":%s 005 %s CASEMAPPING=rfc1459 NICKLEN=31 CHANTYPES=#& PREFIX=(ov)@+ :are supported by this server\n", hostname, client->nick);
        send_log(client->FD, databuf.buf, databuf.len, 0);
        copy_strf(&databuf, ":%s 251 %s :This server exists\n", hostname, client->nick);
        send_log(client->FD, databuf.buf, databuf.len, 0);
@@ -419,6 +479,117 @@ void setNick(struct client_data *client, char* nickname) {
 
        return;
 }
+void channel_sendtopic(struct client_data *client, struct channel *chan)
+{
+       if (chan->topic) {
+               copy_strf(&databuf, ":%s 332 %s %s :%s\n", hostname, client->nick, chan->name, chan->topic);
+               send_log(client->FD, databuf.buf, databuf.len, 0);
+               // FIXME: needs %d
+               //copy_strf(&databuf, ":%s 333 %s %s %s %d\n", hostname, client->nick, chan->name, chan->topic_who, chan->topic_when);
+               copy_strf(&databuf, ":%s 333 %s %s %s 0\n", hostname, client->nick, chan->name, chan->topic_who);
+               send_log(client->FD, databuf.buf, databuf.len, 0);
+       } else {
+               copy_strf(&databuf, ":%s 331 %s %s :No topic is set\n", hostname, client->nick, chan->name);
+               send_log(client->FD, databuf.buf, databuf.len, 0);
+       }
+}
+void channel_join(struct client_data *client, const char *channel, const char *chankey)
+{
+       (void)chankey;
+       // TODO: check channel limit?
+       if (channel[0] != '#' && channel[0] != '&')
+               return; // FIXME: error
+       if (strchr(channel, '\7') || strchr(channel, ' ') || strchr(channel, ','))
+               return; // FIXME: error
+       if (strlen(channel) > 32)
+               return; // FIXME: error
+       char key[33];
+       strcpy(key, channel);
+       stringlower(key);
+       struct channel *chan = trie_get(&channel_map, key);
+       if (!chan) {
+               chan = malloc(sizeof(struct channel));
+               if (!chan) abort();
+               memset(chan, 0, sizeof(struct channel));
+               chan->key = mystrdup(key);
+               if (!chan->key) abort();
+               chan->name = mystrdup(channel);
+               if (!chan->name) abort();
+               if (trie_set(&channel_map, key, chan, NULL) == -1)
+                       abort();
+               if (trie_set(&chan->o, client->key, client, NULL) == -1)
+                       abort();
+       } else {
+               // TODO: check key
+               // TODO: check limit
+               // TODO: check ban
+               // TODO: check ban exemption
+               // TODO: check invite
+               // TODO: check invite exemption
+       }
+       if (trie_set(&chan->members, client->key, client, NULL) == -1)
+               abort();
+       if (trie_set(&client->channels, key, chan, NULL) == -1)
+               abort();
+       chan->member_count++;
+       // send JOIN to everyone
+       copy_strf(&databuf, ":%s!%s@%s JOIN :%s\n", client->nick, client->username, client->hostname, chan->name);
+       struct trie_find *f = NULL;
+       if (trie_findfirst(&f, &chan->members) == 1)
+               do {
+                       struct client_data *cl = f->n->value;
+                       send_log(cl->FD, databuf.buf, databuf.len, 0);
+               } while(trie_findnext(&f) == 1);
+       // send topic
+       channel_sendtopic(client, chan);
+       // send names
+       if (trie_findfirst(&f, &client_map) == 1)
+               do {
+                       struct client_data *cl = f->n->value;
+                       const char *prefix = NULL;
+                       if (trie_get(&chan->o, cl->key))
+                               prefix = "@";
+                       else if (trie_get(&chan->v, cl->key))
+                               prefix = "+";
+                       else
+                               prefix = "";
+                       copy_strf(&databuf, ":%s 353 %s = %s :%s%s\n", hostname, client->nick, chan->name, prefix, cl->nick);
+                       send_log(client->FD, databuf.buf, databuf.len, 0);
+               } while(trie_findnext(&f) == 1);
+       copy_strf(&databuf, ":%s 366 %s %s :End of /NAMES list\n", hostname, client->nick, chan->name);
+       send_log(client->FD, databuf.buf, databuf.len, 0);
+}
+void channel_part(struct client_data *client, struct channel *chan, const char *reason)
+{
+       if (reason) {
+               if (*reason)
+                       copy_strf(&databuf, ":%s!%s@%s PART %s :%s\n", client->nick, client->username, client->hostname, chan->name, reason);
+               else
+                       copy_strf(&databuf, ":%s!%s@%s PART %s\n", client->nick, client->username, client->hostname, chan->name);
+               struct trie_find *f = NULL;
+               if (trie_findfirst(&f, &chan->members) == 1)
+                       do {
+                               struct client_data *cl = f->n->value;
+                               send_log(cl->FD, databuf.buf, databuf.len, 0);
+                       } while (trie_findnext(&f) == 1);
+       }
+       trie_set(&client->channels, chan->key, NULL, NULL);
+       trie_set(&chan->members, client->key, NULL, NULL);
+       trie_set(&chan->o, client->key, NULL, NULL);
+       trie_set(&chan->v, client->key, NULL, NULL);
+       chan->member_count--;
+       if (!chan->member_count) {
+               trie_set(&channel_map, chan->key, NULL, NULL);
+               free(chan->key);
+               free(chan->name);
+               free(chan->topic);
+               free(chan->topic_who);
+               trie_free(&chan->members);
+               trie_free(&chan->o);
+               trie_free(&chan->v);
+               free(chan);
+       }
+}
 struct client_data *findClient(const char *nickname)
 {
        char key[MAX_NICKLEN];
@@ -429,3 +600,12 @@ struct client_data *findClient(const char *nickname)
        stringlower(key);
        return trie_get(&client_map, key);
 }
+struct channel *findChannel(const char *channel)
+{
+       if (strlen(channel) > 32)
+               return NULL;
+       char key[33];
+       strcpy(key, channel);
+       stringlower(key);
+       return trie_get(&channel_map, key);
+}
diff --git a/irc.h b/irc.h
index 9d583b9..699ab10 100644 (file)
--- a/irc.h
+++ b/irc.h
@@ -10,6 +10,10 @@ void newUser(struct client_data *client);
 void sendMOTD(struct client_data *client);
 void setNick(struct client_data *client, char* nickname);
 struct client_data *findClient(const char *nickname);
+struct channel *findChannel(const char *channel);
+void channel_sendtopic(struct client_data *client, struct channel *chan);
+void channel_join(struct client_data *client, const char *channel, const char *chankey);
+void channel_part(struct client_data *client, struct channel *chan, const char *reason);
 extern struct dynarr databuf;
 
 #endif
diff --git a/net.c b/net.c
index 323ed34..9ecb584 100644 (file)
--- a/net.c
+++ b/net.c
@@ -62,6 +62,13 @@ void killConnection(struct client_data *cl, bool byChoice) {
        // End GitHub Copilot
        shutdown(cl->FD, SHUT_RDWR);
        closesocket(cl->FD);
+       struct trie_find *f = NULL;
+       if (trie_findfirst(&f, &cl->channels) == 1)
+               do {
+                       struct channel *chan = f->n->value;
+                       channel_part(cl, chan, NULL);
+               } while(trie_findfirst(&f, &cl->channels) == 1);
+       free(f);
        if (cl->key)
                trie_set(&client_map, cl->key, NULL, NULL);
        free(cl->nick);
diff --git a/trie.c b/trie.c
index 32d5ba8..efb7fa9 100644 (file)
--- a/trie.c
+++ b/trie.c
@@ -168,12 +168,17 @@ int trie_findnext(struct trie_find **pf) {
 int trie_findfirst(struct trie_find **pf, struct trie_node **root) {
        if (!*root)
                return 0;
-       struct trie_find *f = *pf = malloc(sizeof(struct trie_find)+16);
-       if (!f)
-               return -1;
+       struct trie_find *f;
+       if (*pf) {
+               f = *pf;
+       } else {
+               f = *pf = malloc(sizeof(struct trie_find)+16);
+               if (!f)
+                       return -1;
+               f->len = 0;
+               f->cap = 16;
+       }
        f->n = *root;
-       f->len = 0;
-       f->cap = 16;
        if (f->n->value) {
                f->key[f->len] = '\0';
                return 1;