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 {