From 9052c86d1e5cee9aad2baacf4f02f20b81dadefd Mon Sep 17 00:00:00 2001 From: Egor Date: Tue, 3 May 2022 21:51:40 +0300 Subject: [PATCH] add proper channels --- common.h | 15 ++++ irc.c | 224 +++++++++++++++++++++++++++++++++++++++++++++++++------ irc.h | 4 + net.c | 7 ++ trie.c | 15 ++-- 5 files changed, 238 insertions(+), 27 deletions(-) diff --git a/common.h b/common.h index b252631..101518a 100644 --- a/common.h +++ b/common.h @@ -3,6 +3,7 @@ #include "NetSock/netsock.h" #include "config.h" #include "trie.h" +#include #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 --- 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 --- 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 --- 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 --- 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; -- 2.56.0