From 7a7bb6f02cf42bdbe596769a86c4661596c22b9d Mon Sep 17 00:00:00 2001 From: Egor Date: Wed, 11 May 2022 20:05:35 +0300 Subject: [PATCH] add +beIq modes --- common.h | 8 +++-- glob.c | 45 ++++++++++++++++++++++++++ irc.c | 98 ++++++++++++++++++++++++++++++++++++++++++++++++++------ irc.h | 2 ++ ircfmt.c | 6 ++-- ircfmt.h | 2 ++ net.c | 1 + 7 files changed, 148 insertions(+), 14 deletions(-) create mode 100644 glob.c diff --git a/common.h b/common.h index 0e4106b..46fca0b 100644 --- a/common.h +++ b/common.h @@ -28,6 +28,7 @@ struct client_data { char *username; char *realname; char *hostname; + char *nuh; // case-mapped nick!user@host char *away; char *nick_reg; char *pass_reg; @@ -54,9 +55,10 @@ struct channel { struct trie_node *v; int member_count; char dmode; - //char *ban_mask[20]; - //char *exc_mask[20]; - //char *iex_mask[20]; + struct trie_node *listb; + struct trie_node *liste; + struct trie_node *listI; + struct trie_node *listq; char *chankey; int member_limit; }; diff --git a/glob.c b/glob.c new file mode 100644 index 0000000..69a0248 --- /dev/null +++ b/glob.c @@ -0,0 +1,45 @@ +// https://research.swtch.com/glob +int irc_glob(const char *key, const char *mask) +{ + const char *key2 = 0, *mask2 = 0; + while (*key || *mask) { + if (*mask) { + switch (*mask) { + case '\\': + if (*key == *mask) { + key++, mask++; + if (!*mask) + continue; + if (*key == *mask) { + key++, mask++; + continue; + } + } + break; + default: + if (*key == *mask) { + key++, mask++; + continue; + } + break; + case '?': + if (*key) { + key++, mask++; + continue; + } + break; + case '*': + key2 = key2 + 1; + mask2 = mask++; + continue; + } + } + if (key2 && *key2) { + key = key2; + mask = mask2; + continue; + } + return 0; + } + return 1; +} diff --git a/irc.c b/irc.c index f079b49..9a5c5eb 100644 --- a/irc.c +++ b/irc.c @@ -92,6 +92,18 @@ const char *format_umodes(struct client_data *cl) { *p++ = '\0'; return umodes; } +int match_list(struct client_data *cl, struct trie_node **list) +{ + struct trie_find *f = NULL; + if (trie_findfirst(&f, list) == 1) + do { + if (irc_glob(cl->nuh, f->key)) { + free(f); + return 1; + } + } while (trie_findnext(&f) == 1); + return 0; +} int parsePacket(struct client_data *client) { char *source = NULL; char *command = NULL; @@ -362,7 +374,13 @@ int processPacket(struct client_data *client, char *source, char *command, int a struct channel *chan = findChannel(args[0]+is_modmsg); if (!chan) return err_nosuchchannel(client, args[0]), sendbuf(client), 0; - // TODO: check bans + if (match_list(client, &chan->listq)) + return err_cannotsendtochan(client, chan->name), sendbuf(client), 0; + if (match_list(client, &chan->listb) && !match_list(client, &chan->liste)) + return err_cannotsendtochan(client, chan->name), sendbuf(client), 0; + // TODO: actual invites + if (chan->dmode & DMODE_INVITEONLY && !match_list(client, &chan->listI)) + return err_cannotsendtochan(client, chan->name), sendbuf(client), 0; if (chan->dmode & DMODE_NOEXTERNAL && !trie_get(&chan->members, client->key)) return err_cannotsendtochan(client, chan->name), sendbuf(client), 0; if (chan->dmode & DMODE_MODERATED && !trie_get(&chan->v, client->key) && !trie_get(&chan->o, client->key)) @@ -525,18 +543,44 @@ int processPacket(struct client_data *client, char *source, char *command, int a if (!trie_get(&chan->o, client->key)) return 0; // TODO: error if (argc == 2) { // Type A channel modes + struct trie_find *f = NULL; if (!strcmp(args[1], "+b")) { - copy_strf(&databuf, ":%s 368 %s %s :End of channel ban list\n", hostname, client->nick, args[1]); + if (trie_findfirst(&f, &chan->listb) == 1) + do { + copy_strf(&databuf, ":%s 367 %s %s %s\n", hostname, client->nick, chan->name, f->key); + sendbuf(client); + } while(trie_findnext(&f) == 1); + copy_strf(&databuf, ":%s 368 %s %s :End of channel ban list\n", hostname, client->nick, chan->name); sendbuf(client); 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]); + if (trie_findfirst(&f, &chan->liste) == 1) + do { + copy_strf(&databuf, ":%s 348 %s %s %s\n", hostname, client->nick, chan->name, f->key); + sendbuf(client); + } while(trie_findnext(&f) == 1); + copy_strf(&databuf, ":%s 349 %s %s :End of channel exception list\n", hostname, client->nick, chan->name); sendbuf(client); 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]); + if (trie_findfirst(&f, &chan->listI) == 1) + do { + copy_strf(&databuf, ":%s 346 %s %s %s\n", hostname, client->nick, chan->name, f->key); + sendbuf(client); + } while(trie_findnext(&f) == 1); + copy_strf(&databuf, ":%s 347 %s %s :End of Channel Invite Exception List\n", hostname, client->nick, chan->name); + sendbuf(client); + return 0; + } + if (!strcmp(args[1], "+q")) { + if (trie_findfirst(&f, &chan->listq) == 1) + do { + copy_strf(&databuf, ":%s 728 %s %s q %s\n", hostname, client->nick, chan->name, f->key); + sendbuf(client); + } while(trie_findnext(&f) == 1); + copy_strf(&databuf, ":%s 729 %s %s q :End of channel quiet list\n", hostname, client->nick, chan->name); sendbuf(client); return 0; } @@ -548,6 +592,7 @@ int processPacket(struct client_data *client, char *source, char *command, int a case '-': is_plus = 0; break; default: goto unknown; } + struct trie_node **list; char **argp = &args[2]; while (*p) { switch (*p) { @@ -639,11 +684,35 @@ int processPacket(struct client_data *client, char *source, char *command, int a chanlog(), sendbuf_chan(chan); break; case 'b': + list = &chan->listb; + goto set_list; case 'e': + list = &chan->liste; + goto set_list; case 'I': + list = &chan->listI; + goto set_list; + case 'q': + list = &chan->listq; + goto set_list; + set_list: { + if(!*argp) + break; + char *mask = *argp++; + if (strchr(mask, ' ')) + break; // TODO: error msg + stringlower(mask); + trie_set(list, mask, is_plus ? mask /* dummy */ : NULL, NULL); + char which[2] = {*p, 0}; + copy_strf(&databuf, ":%s!%s@%s MODE %s %s%s %s\r\n", + client->nick, client->username, client->hostname, + chan->name, is_plus?"+":"-", which, mask); + // TODO: limit to ops + chanlog(), sendbuf_chan(chan); break; + } case 'v': - case 'o': + case 'o': { if(!*argp) break; const char *nick = *argp++; @@ -663,6 +732,7 @@ int processPacket(struct client_data *client, char *source, char *command, int a chan->name, is_plus?"+":"-", which, cl->nick); sendbuf_chan(chan); break; + } default: goto unknown; } @@ -938,6 +1008,11 @@ void setNick(struct client_data *client, char* nickname) { client->nick = mystrdup(nickname); if (!client->nick) abort(); + client->nuh = malloc(strlen(client->nick) + strlen(client->username) + strlen(client->hostname) + 3); + if (!client->nuh) + abort(); + sprintf(client->nuh, "%s!%s@%s", client->nick, client->username, client->hostname); + stringlower(client->nuh); client->key = mystrdup(key); if (!client->key) abort(); @@ -1010,10 +1085,11 @@ void channel_join(struct client_data *client, const char *channel, const char *c return; // error 475 if (chan->member_limit != -1 && chan->member_count >= chan->member_limit) return; // error 471 - // TODO: check ban - // TODO: check ban exemption - // TODO: check invite - // TODO: check invite exemption + if (match_list(client, &chan->listb) && !match_list(client, &chan->liste)) + return; // error 474 + // TODO: actual invites + if (chan->dmode & DMODE_INVITEONLY && !match_list(client, &chan->listI)) + return; // error 474 } } if (trie_set(&chan->members, client->key, client, NULL) == -1) @@ -1053,6 +1129,10 @@ void channel_part(struct client_data *client, struct channel *chan, const char * trie_free(&chan->members); trie_free(&chan->o); trie_free(&chan->v); + trie_free(&chan->listb); + trie_free(&chan->liste); + trie_free(&chan->listI); + trie_free(&chan->listq); free(chan->chankey); free(chan); } diff --git a/irc.h b/irc.h index f9100b9..d75225f 100644 --- a/irc.h +++ b/irc.h @@ -19,4 +19,6 @@ void channel_join(struct client_data *client, const char *channel, const char *c void channel_part(struct client_data *client, struct channel *chan, const char *reason); extern struct dynarr databuf; +// glob.c +int irc_glob(const char *key, const char *mask); #endif diff --git a/ircfmt.c b/ircfmt.c index ebf4a0a..6d6148f 100644 --- a/ircfmt.c +++ b/ircfmt.c @@ -100,11 +100,11 @@ void rpl_created(struct client_data *client) // 003 } void rpl_myinfo(struct client_data *client) // 004 { - copy_strf(&databuf, ":%s 004 %s PatchyIRCD 0.0.1 iworZ beIklPimnst\n", hostname, client->nick); + copy_strf(&databuf, ":%s 004 %s PatchyIRCD 0.0.1 iworZ beIqklPimnst\n", hostname, client->nick); } void rpl_isupport(struct client_data *client) // 005 { - copy_strf(&databuf, ":%s 005 %s CASEMAPPING=ascii NICKLEN=%u EXCEPTS=e INVEX=I MAXTARGETS=1 CHANMODES=beI,k,l,Pimnst CHANTYPES=#& PREFIX=(ov)@+ STATUSMSG=@+ :are supported by this server\n", hostname, client->nick, MAX_NICKLEN-1); + copy_strf(&databuf, ":%s 005 %s CASEMAPPING=ascii NICKLEN=%u EXCEPTS=e INVEX=I MAXTARGETS=1 CHANMODES=beIq,k,l,Pimnst CHANTYPES=#& PREFIX=(ov)@+ STATUSMSG=@+ :are supported by this server\n", hostname, client->nick, MAX_NICKLEN-1); } #if 0 @@ -392,6 +392,8 @@ void err_notregistered(struct client_data *client) // 451 // 705 RPL_HELPTXT // 706 RPL_ENDOFHELP // 723 ERR_NOPRIVS +// 728 RPL_QUIETLIST +// 729 RPL_ENDOFQUIETLIST // 900 RPL_LOGGEDIN // 901 RPL_LOGGEDOUT // 902 ERR_NICKLOCKED diff --git a/ircfmt.h b/ircfmt.h index cb68b00..af2ee03 100644 --- a/ircfmt.h +++ b/ircfmt.h @@ -129,6 +129,8 @@ void err_notregistered(struct client_data *client); // 451 // 705 RPL_HELPTXT // 706 RPL_ENDOFHELP // 723 ERR_NOPRIVS +// 728 RPL_QUIETLIST +// 729 RPL_ENDOFQUIETLIST // 900 RPL_LOGGEDIN // 901 RPL_LOGGEDOUT // 902 ERR_NICKLOCKED diff --git a/net.c b/net.c index d027f88..a534f3e 100644 --- a/net.c +++ b/net.c @@ -59,6 +59,7 @@ void killConnection(struct client_data *cl, bool byChoice) { free(cl->username); free(cl->realname); free(cl->hostname); + free(cl->nuh); free(cl->away); free(cl->nick_reg); if (cl->pass_reg) -- 2.56.0