AmendHub

Download:

jcs

/

wallops

/

amendments

/

21

irc: Yet another protocol rewrite, add /whois command and parsing

Instead of trying to map protocol message parts to specific things
like dest/channel/msg, just map them all to arg[0]-arg[5] and let
each command-specific printer use the args it cares about.

jcs made amendment 21 over 2 years ago
--- irc.c Tue Feb 8 08:11:19 2022 +++ irc.c Tue Feb 8 21:13:21 2022 @@ -313,9 +313,9 @@ irc_parse_user(char *str) ret = sscanf(str, "%[^!]!%[^@]@%s", &parsed_user.nick, &parsed_user.username, &parsed_user.hostname); if (ret != 3) { - warn("irc_parse_user: failed parsing \"%s\"", str); - strcpy(parsed_user.nick, "?"); - strcpy(parsed_user.username, "?"); + /* probably just a hostname */ + strlcpy(parsed_user.nick, str, sizeof(parsed_user.nick)); + strlcpy(parsed_user.username, str, sizeof(parsed_user.username)); strlcpy(parsed_user.hostname, str, sizeof(parsed_user.hostname)); } return &parsed_user; @@ -365,7 +365,7 @@ irc_process_server(struct chatter *chatter) struct irc_user *user; char *line, *word; size_t size, n, lastbreak; - short state; + short state, curarg; line = irc_get_line(chatter, &size); if (size == 0 || line == NULL) @@ -373,6 +373,9 @@ irc_process_server(struct chatter *chatter) memset(&msg, 0, sizeof(msg)); + if (strstr(line, "services")) + msg.code = 1; + word = strsep(&line, " "); /* extract source before command */ @@ -391,42 +394,32 @@ irc_process_server(struct chatter *chatter) /* ":jcs!~jcs@jcs JOIN #wallops" -> msg.cmd=JOIN */ strlcpy(msg.cmd, word, sizeof(msg.cmd)); - /* parameters */ - - if (line[0] == ':') { - strlcpy(msg.msg, line + 1, sizeof(msg.msg)); - goto done_parsing; + /* parameters, put into args until we see one starting with : */ + curarg = 0; + while (line != NULL && line[0] != '\0') { + if (line[0] == ':') { + strlcpy(msg.msg, line + 1, sizeof(msg.msg)); + break; + } + + word = strsep(&line, " "); + if (word == NULL) { + strlcpy(msg.msg, line, sizeof(msg.msg)); + break; + } + + strlcpy(msg.arg[curarg], word, sizeof(msg.arg[curarg])); + curarg++; + + if (curarg >= IRC_MSG_MAX_ARGS) { + /* just stick the rest in msg */ + strlcpy(msg.msg, line, sizeof(msg.msg)); + break; + } } - word = strsep(&line, " "); - if (word == NULL) { - strlcpy(msg.msg, line + 1, sizeof(msg.msg)); - goto done_parsing; - } - strlcpy(msg.dest, word, sizeof(msg.dest)); - if (line[0] == ':') { - strlcpy(msg.msg, line + 1, sizeof(msg.msg)); - goto done_parsing; - } - word = strsep(&line, " "); - if (word == NULL) { - strlcpy(msg.msg, line + 1, sizeof(msg.msg)); - goto done_parsing; - } - strlcpy(msg.channel, word, sizeof(msg.channel)); - - if (line[0] == ':') { - strlcpy(msg.msg, line + 1, sizeof(msg.msg)); - goto done_parsing; - } - strlcpy(msg.msg, line, sizeof(msg.msg)); - -done_parsing: if (msg.cmd[0]) { - if (strcmp(msg.cmd, "PING") == 0) { - irc_printf(chatter, "PONG :%s\r\n", msg.msg); - return; - } + /* this one will fire most often, keep at the top */ if (strcmp(msg.cmd, "PRIVMSG") == 0) { user = irc_parse_user(msg.source); size = strlen(msg.msg); @@ -441,10 +434,10 @@ done_parsing: chatter_printf(chatter, "$B*** %s ($0%s@%s$B)$0 " "requested CTCP $BVERSION$0 from $B%s$0", user->nick, user->username, user->hostname, - msg.dest); + msg.arg[0]); /* only respond if it was sent directly to us */ - if (strcmp(msg.dest, chatter->irc_nick) == 0) { + if (strcmp(msg.arg[0], chatter->irc_nick) == 0) { irc_printf(chatter, "NOTICE %s :\1VERSION %s %s on a %s\1\r\n", user->nick, PROGRAM_NAME, get_version(false), @@ -453,11 +446,11 @@ done_parsing: } else { goto unknown; } - } else if (strcmp(msg.dest, chatter->irc_nick) == 0) { + } else if (strcmp(msg.arg[0], chatter->irc_nick) == 0) { chatter_printf(chatter, "$B[$0%s$B($0%s@%s$B)]$0$/ %s", user->nick, user->username, user->hostname, msg.msg); } else if (chatter->irc_channel && - strcmp(msg.dest, chatter->irc_channel->name) == 0) { + strcmp(msg.arg[0], chatter->irc_channel->name) == 0) { size = strlen(chatter->irc_nick); if (strncmp(msg.msg, chatter->irc_nick, size) == 0 && (msg.msg[size] == ' ' || msg.msg[size] == ':' || @@ -472,42 +465,38 @@ done_parsing: msg.msg); } else chatter_printf(chatter, "$B[%s($0%s@%s$B):%s]$0$/ %s", - user->nick, user->username, user->hostname, msg.dest, + user->nick, user->username, user->hostname, msg.arg[0], msg.msg); return; } + if (strcmp(msg.cmd, "ERROR") == 0) { + chatter_printf(chatter, "$B*!* Disconnected$0 from $B%s$0:$/ %s", + chatter->irc_hostname, msg.msg); + irc_abort(chatter); + return; + } if (strcmp(msg.cmd, "JOIN") == 0) { user = irc_parse_user(msg.source); chatter_printf(chatter, "$B*** %s ($0%s@%s$B)$0 has joined " "$B%s$0", user->nick, user->username, user->hostname, - msg.dest); + msg.arg[0]); if (strcmp(user->nick, chatter->irc_nick) == 0) - irc_set_active_channel(chatter, msg.dest); + irc_set_active_channel(chatter, msg.arg[0]); else irc_add_user_to_channel(chatter, user); return; } - if (strcmp(msg.cmd, "PART") == 0) { + if (strcmp(msg.cmd, "KICK") == 0) { user = irc_parse_user(msg.source); - chatter_printf(chatter, "$B*** %s ($0%s@%s$B)$0 has left " - "$B%s$0", user->nick, user->username, user->hostname, - msg.dest); - if (strcmp(user->nick, chatter->irc_nick) == 0) + chatter_printf(chatter, "$B*** %s$0 was kicked " + "from $B%s$0 by $B%s%0:$/ %s", msg.arg[1], msg.arg[0], + user->nick, msg.msg); + if (strcmp(msg.arg[1], chatter->irc_nick) == 0) irc_set_active_channel(chatter, NULL); else - irc_remove_nick_from_channel(chatter, user->nick); + irc_remove_nick_from_channel(chatter, msg.arg[1]); return; } - if (strcmp(msg.cmd, "QUIT") == 0) { - user = irc_parse_user(msg.source); - chatter_printf(chatter, "$B*** %s ($0%s@%s$B)$0 has quit:$/ %s", - user->nick, user->username, user->hostname, msg.msg); - if (strcmp(user->nick, chatter->irc_nick) == 0) - irc_set_active_channel(chatter, NULL); - else - irc_remove_nick_from_channel(chatter, user->nick); - return; - } if (strcmp(msg.cmd, "KILL") == 0) { user = irc_parse_user(msg.source); chatter_printf(chatter, "$B*** %s ($0%s@%s$B)$0 has been " @@ -519,52 +508,75 @@ done_parsing: irc_remove_nick_from_channel(chatter, user->nick); return; } - - if (strcmp(msg.cmd, "NICK") == 0) { - user = irc_parse_user(msg.source); - chatter_printf(chatter, "$B*** %s$0 is now known as $B%s$0", - user->nick, msg.msg); - irc_change_user_nick(chatter, user, msg.msg); - return; - } if (strcmp(msg.cmd, "MODE") == 0) { - if (strcmp(msg.dest, chatter->irc_nick) == 0) + if (strcmp(msg.arg[0], chatter->irc_nick) == 0) chatter_printf(chatter, "$B***$0 Mode change ($B%s$0) " - "for user $B%s$0", msg.msg, msg.dest); + "for user $B%s$0", msg.msg, msg.arg[0]); else { - /* msg.channel is mode */ user = irc_parse_user(msg.source); + /* concatenate nicks */ + msg.msg[0] = '\0'; + if (msg.arg[2][0] != '\0') + strlcat(msg.msg, msg.arg[2], sizeof(msg.msg)); + if (msg.arg[3][0] != '\0') { + strlcat(msg.msg, " ", sizeof(msg.msg)); + strlcat(msg.msg, msg.arg[3], sizeof(msg.msg)); + } + if (msg.arg[4][0] != '\0') { + strlcat(msg.msg, " ", sizeof(msg.msg)); + strlcat(msg.msg, msg.arg[4], sizeof(msg.msg)); + } + if (msg.arg[5][0] != '\0') { + strlcat(msg.msg, " ", sizeof(msg.msg)); + strlcat(msg.msg, msg.arg[5], sizeof(msg.msg)); + } chatter_printf(chatter, "$B***$0 Mode change ($B%s %s$0) " - "on $B%s$0 by $B%s%0", msg.channel, msg.msg, msg.dest, + "on $B%s$0 by $B%s%0", msg.arg[1], msg.msg, msg.arg[0], user->nick); - irc_parse_channel_mode_change(chatter, msg.channel, + irc_parse_channel_mode_change(chatter, msg.arg[1], msg.msg); } return; } - if (strcmp(msg.cmd, "KICK") == 0) { - /* dest and channel are swapped */ + if (strcmp(msg.cmd, "NICK") == 0) { user = irc_parse_user(msg.source); - chatter_printf(chatter, "$B*** %s ($0%s$B)$0 was kicked " - "from $B%s$0 by $B%s%0:$/ %s", msg.channel, msg.dest, + chatter_printf(chatter, "$B*** %s$0 is now known as $B%s$0", user->nick, msg.msg); - if (strcmp(msg.channel, chatter->irc_nick) == 0) + irc_change_user_nick(chatter, user, msg.msg); + return; + } + if (strcmp(msg.cmd, "NOTICE") == 0) { + if (strncmp(msg.msg, "*** ", 4) == 0) + chatter_printf(chatter, "$B***$0 $/%s", msg.msg + 4); + else + chatter_printf(chatter, "$/%s", msg.msg); + return; + } + if (strcmp(msg.cmd, "PART") == 0) { + user = irc_parse_user(msg.source); + chatter_printf(chatter, "$B*** %s ($0%s@%s$B)$0 has left " + "$B%s$0", user->nick, user->username, user->hostname, + msg.arg[0]); + if (strcmp(user->nick, chatter->irc_nick) == 0) irc_set_active_channel(chatter, NULL); else - irc_remove_nick_from_channel(chatter, msg.channel); + irc_remove_nick_from_channel(chatter, user->nick); return; } - if (strcmp(msg.cmd, "NOTICE") == 0) { - chatter_printf(chatter, "%s", msg.msg); + if (strcmp(msg.cmd, "PING") == 0) { + irc_printf(chatter, "PONG :%s\r\n", msg.msg); return; } - if (strcmp(msg.cmd, "ERROR") == 0) { - chatter_printf(chatter, "$B*!* Disconnected from %s:$0$/ %s", - chatter->irc_hostname, msg.msg); - irc_abort(chatter); + if (strcmp(msg.cmd, "QUIT") == 0) { + user = irc_parse_user(msg.source); + chatter_printf(chatter, "$B*** %s ($0%s@%s$B)$0 has quit:$/ %s", + user->nick, user->username, user->hostname, msg.msg); + if (strcmp(user->nick, chatter->irc_nick) == 0) + irc_set_active_channel(chatter, NULL); + else + irc_remove_nick_from_channel(chatter, user->nick); return; } - goto unknown; } @@ -589,21 +601,48 @@ done_parsing: /* server stats, unhelpful */ return; case 311: + /* WHOIS nick: "nick :ident hostname * :name" */ + chatter_printf(chatter, "$B*** %s ($0%s@%s$B)$0", msg.arg[1], + msg.arg[2], msg.arg[3]); + chatter_printf(chatter, "$B*** |$0 Name:$/ %s", msg.msg); + return; case 312: + /* WHOIS server */ + chatter_printf(chatter, "$B*** |$0 Server:$/ %s (%s)", msg.arg[2], + msg.msg); + return; + case 671: + /* WHOIS server */ + chatter_printf(chatter, "$B*** |$0 Connection:$/ %s", msg.msg); + return; case 317: case 318: + /* WHOIS end */ + chatter_printf(chatter, "$B*** `-------$0"); + return; + case 319: + /* WHOIS channels */ + chatter_printf(chatter, "$B*** |$0 Channels:$/ %s", msg.msg); + return; + case 330: + /* WHOIS account */ + chatter_printf(chatter, "$B*** |$0 Account:$/ %s %s", msg.msg, + msg.arg[2]); + return; case 338: - /* WHOIS stuff */ - goto print_msg; + /* WHOIS host */ + chatter_printf(chatter, "$B*** |$0 Host:$/ %s %s", msg.msg, + msg.arg[2]); + return; case 328: /* channel URL, we probably can't do anything with it anyway */ return; case 332: /* TOPIC */ chatter_printf(chatter, "$B***$0 Topic for $B%s$0:$/ %s", - msg.channel, msg.msg); + msg.arg[1], msg.msg); if (chatter->irc_channel && - strcmp(chatter->irc_channel->name, msg.channel) == 0) { + strcmp(chatter->irc_channel->name, msg.arg[1]) == 0) { strlcpy(chatter->irc_channel->topic, msg.msg, sizeof(chatter->irc_channel->topic)); } @@ -618,16 +657,15 @@ done_parsing: /* end of WHO */ goto unknown; case 353: - /* NAMES output, channel is first part of msg.msg, not channel */ + /* NAMES output */ if (chatter->irc_channel && - strncmp(msg.msg, chatter->irc_channel->name, - strlen(chatter->irc_channel->name)) == 0) + strcmp(msg.arg[2], chatter->irc_channel->name) == 0) irc_parse_names(chatter, msg.msg); return; case 366: /* end of NAMES output */ if (chatter->irc_channel && - strcmp(msg.channel, chatter->irc_channel->name) == 0) + strcmp(msg.arg[1], chatter->irc_channel->name) == 0) chatter_sync_nick_list(chatter, true); return; case 372: @@ -635,17 +673,23 @@ done_parsing: case 376: /* MOTD */ goto print_msg; + case 396: + /* Cloak */ + chatter_printf(chatter, "$B***$0$/ %s %s", msg.arg[1], msg.msg); + return; default: goto unknown; } print_msg: - chatter_printf(chatter, "$/%s", msg.msg); + chatter_printf(chatter, "$B***$0$/ %s", msg.msg); return; unknown: - chatter_printf(chatter, "$B[?]$0$/ code:%d cmd:%s source:%s dest:%s " - "msg:%s", msg.code, msg.cmd, msg.source, msg.dest, msg.msg); + chatter_printf(chatter, "$B[?]$0$/ code:%d cmd:%s source:%s " + "arg0:%s arg1:%s arg2:%s arg3:%s arg4:%s msg:%s", msg.code, msg.cmd, + msg.source, msg.arg[0], msg.arg[1], msg.arg[2], msg.arg[3], + msg.arg[4], msg.msg); } void @@ -668,12 +712,10 @@ irc_process_input(struct chatter *chatter, char *str) arg0 = strsep(&str, " "); - if (strcmp(arg0, "msg") == 0) { - arg1 = strsep(&str, " "); - if (arg1 == NULL || str == NULL) + if (strcmp(arg0, "join") == 0) { + if (str == NULL) goto not_enough_params; - chatter_printf(chatter, "$B[$0msg$B($0%s$B)]$0$/ %s", arg1, str); - irc_printf(chatter, "PRIVMSG %s :%s\r\n", arg1, str); + irc_printf(chatter, "JOIN %s\r\n", str); return; } if (strcmp(arg0, "me") == 0) { @@ -686,10 +728,18 @@ irc_process_input(struct chatter *chatter, char *str) chatter->irc_channel->name, str); return; } - if (strcmp(arg0, "join") == 0) { + if (strcmp(arg0, "msg") == 0) { + arg1 = strsep(&str, " "); + if (arg1 == NULL || str == NULL) + goto not_enough_params; + chatter_printf(chatter, "$B[$0msg$B($0%s$B)]$0$/ %s", arg1, str); + irc_printf(chatter, "PRIVMSG %s :%s\r\n", arg1, str); + return; + } + if (strcmp(arg0, "nick") == 0) { if (str == NULL) goto not_enough_params; - irc_printf(chatter, "JOIN %s\r\n", str); + irc_printf(chatter, "NICK %s\r\n", str); return; } if (strcmp(arg0, "part") == 0) { @@ -703,19 +753,18 @@ irc_process_input(struct chatter *chatter, char *str) irc_printf(chatter, "QUIT :%s\r\n", str == NULL ? "&" : str); return; } - if (strcmp(arg0, "nick") == 0) { + if (strcmp(arg0, "quote") == 0) { if (str == NULL) goto not_enough_params; - irc_printf(chatter, "NICK %s\r\n", str); + irc_printf(chatter, "%s\r\n", str); return; } - if (strcmp(arg0, "quote") == 0) { + if (strcmp(arg0, "whois") == 0) { if (str == NULL) goto not_enough_params; - irc_printf(chatter, "%s\r\n", str); + irc_printf(chatter, "WHOIS %s\r\n", str); return; } - chatter_printf(chatter, "unrecognized command \"$B%s$0\"", arg0); return; @@ -755,28 +804,15 @@ irc_parse_names(struct chatter *chatter, char *line) { size_t n; char *nick; - bool didchannel = false, last = false; + bool last = false; struct irc_channel_nick *user; if (!chatter->irc_channel) return; - /* #wallops :jcs[mac] @jcs */ - for (;;) { nick = strsep(&line, " "); - if (!didchannel) { - if (strcmp(chatter->irc_channel->name, nick) != 0) - /* wrong channel */ - return; - didchannel = true; - - /* remove leading : on first nick */ - line++; - continue; - } - chatter->irc_channel->nusers++; chatter->irc_channel->users = xreallocarray( chatter->irc_channel->users, sizeof(struct irc_channel_nick), @@ -815,10 +851,12 @@ void irc_remove_nick_from_channel(struct chatter *chatter, char *nick) { size_t n; + bool found = false; for (n = 0; n < chatter->irc_channel->nusers; n++) { if (strcmp(chatter->irc_channel->users[n].nick, nick) == 0) { LDelRow(1, n, chatter->nick_list); + found = true; /* move everyone else up */ for (; n < chatter->irc_channel->nusers - 1; n++) @@ -828,6 +866,9 @@ irc_remove_nick_from_channel(struct chatter *chatter, } } + if (!found) + return; + chatter->irc_channel->nusers--; chatter->irc_channel->users = xreallocarray(chatter->irc_channel->users, --- irc.h Tue Feb 1 17:21:15 2022 +++ irc.h Tue Feb 8 17:07:09 2022 @@ -25,13 +25,14 @@ enum { IRC_STATE_IDLE }; +#define IRC_MSG_MAX_ARGS 6 + struct irc_msg { short code; char cmd[16]; - char source[256]; - char dest[32]; - char channel[64]; + char source[64]; char msg[512]; + char arg[IRC_MSG_MAX_ARGS][64]; }; struct irc_user {