From: Egor Date: Fri, 6 May 2022 18:25:51 +0000 (+0300) Subject: add channel modes (currently they don't do anything) X-Git-Url: https://git.patchysicp.xyz/?a=commitdiff_plain;h=2e8654ad08abd445603f4d46e4104d89a7c15aab;p=patchyircd.git add channel modes (currently they don't do anything) --- diff --git a/common.h b/common.h index 5349cf2..0266b8f 100644 --- a/common.h +++ b/common.h @@ -5,6 +5,7 @@ #include "trie.h" #include +#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 --- 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); } } diff --git a/ircfmt.c b/ircfmt.c index 6e98ae6..f6f9932 100644 --- 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; diff --git a/ircfmt.h b/ircfmt.h index d7b4dd7..91a88f0 100644 --- 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