jcs
/wallops
/amendments
/27
irc: Print dying gasps in irc_recv, catch nick list trailing space
An Unreal3.2.10.2 server returned a trailing space at the end of
nick lists when joining a channel, causing an infinite loop in
sorting.
jcs made amendment 27 over 2 years ago
--- irc.c Thu Feb 10 10:57:31 2022
+++ irc.c Thu Feb 10 11:33:08 2022
@@ -30,7 +30,7 @@ char * irc_get_line(struct chatter *chatter, size_t *r
struct irc_user * irc_parse_user(char *str);
bool irc_can_send(struct chatter *chatter);
void irc_login(struct chatter *chatter);
-void irc_process_server(struct chatter *chatter);
+bool irc_process_server(struct chatter *chatter);
void irc_set_active_channel(struct chatter *chatter, char *channame);
void irc_parse_names(struct chatter *chatter, char *line);
void irc_add_nick_to_channel(struct chatter *chatter, char *nick,
@@ -160,36 +160,38 @@ short
irc_recv(struct chatter *chatter)
{
unsigned short rlen;
- short error, n;
-
- if (chatter->irc_ibuflen >= sizeof(chatter->irc_ibuf))
- return 0;
-
+ short error, rerror, n;
+
error = _TCPStatus(&chatter->irc_rcv_pb, chatter->irc_stream,
&chatter->irc_status_pb, nil, nil, false);
- if (error) {
- chatter_printf(chatter, "$B*!* TCPStatus failed: %d$0", error);
- irc_abort(chatter);
- return 0;
- }
- if (chatter->irc_status_pb.amtUnreadData == 0)
- return 0;
+ if (chatter->irc_status_pb.amtUnreadData > 0 &&
+ chatter->irc_ibuflen < sizeof(chatter->irc_ibuf)) {
+ rlen = chatter->irc_status_pb.amtUnreadData;
+ if (chatter->irc_ibuflen + rlen > sizeof(chatter->irc_ibuf))
+ rlen = sizeof(chatter->irc_ibuf) - chatter->irc_ibuflen;
+
+ rerror = _TCPRcv(&chatter->irc_rcv_pb, chatter->irc_stream,
+ (Ptr)(chatter->irc_ibuf + chatter->irc_ibuflen), &rlen, nil, nil,
+ false);
+ if (rerror) {
+ chatter_printf(chatter, "$B*!* TCPRecv failed: %d$0", error);
+ irc_abort(chatter);
+ return 0;
+ }
- rlen = chatter->irc_status_pb.amtUnreadData;
- if (chatter->irc_ibuflen + rlen > sizeof(chatter->irc_ibuf))
- rlen = sizeof(chatter->irc_ibuf) - chatter->irc_ibuflen;
+ chatter->irc_ibuflen += rlen;
+ }
- error = _TCPRcv(&chatter->irc_rcv_pb, chatter->irc_stream,
- (Ptr)(chatter->irc_ibuf + chatter->irc_ibuflen), &rlen, nil, nil,
- false);
if (error) {
- chatter_printf(chatter, "$B*!* TCPRecv failed: %d$0", error);
- irc_close(chatter);
+ /* let already-consumed buffer finish processing */
+ while (irc_process_server(chatter))
+ ;
+ chatter_printf(chatter, "$B*!* TCPStatus failed: %d$0", error);
+ irc_abort(chatter);
return 0;
}
-
- chatter->irc_ibuflen += rlen;
+
return rlen;
}
@@ -355,7 +357,7 @@ irc_get_line(struct chatter *chatter, size_t *retsize)
return NULL;
}
-void
+bool
irc_process_server(struct chatter *chatter)
{
struct irc_msg msg;
@@ -366,7 +368,7 @@ irc_process_server(struct chatter *chatter)
line = irc_get_line(chatter, &size);
if (size == 0 || line == NULL)
- return;
+ return false;
memset(&msg, 0, sizeof(msg));
@@ -468,13 +470,13 @@ irc_process_server(struct chatter *chatter)
chatter_printf(chatter, "$B[%s($0%s@%s$B):%s]$0$/ %s",
user->nick, user->username, user->hostname, msg.arg[0],
msg.msg);
- return;
+ return true;
}
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;
+ return true;
}
if (strcmp(msg.cmd, "JOIN") == 0) {
user = irc_parse_user(msg.source);
@@ -485,7 +487,7 @@ irc_process_server(struct chatter *chatter)
irc_set_active_channel(chatter, msg.arg[0]);
else
irc_add_nick_to_channel(chatter, user->nick, 0);
- return;
+ return true;
}
if (strcmp(msg.cmd, "KICK") == 0) {
user = irc_parse_user(msg.source);
@@ -496,7 +498,7 @@ irc_process_server(struct chatter *chatter)
irc_set_active_channel(chatter, NULL);
else
irc_remove_nick_from_channel(chatter, msg.arg[1]);
- return;
+ return true;
}
if (strcmp(msg.cmd, "KILL") == 0) {
user = irc_parse_user(msg.source);
@@ -507,7 +509,7 @@ irc_process_server(struct chatter *chatter)
irc_set_active_channel(chatter, NULL);
else
irc_remove_nick_from_channel(chatter, user->nick);
- return;
+ return true;
}
if (strcmp(msg.cmd, "MODE") == 0) {
if (strcmp(msg.arg[0], chatter->irc_nick) == 0)
@@ -537,21 +539,21 @@ irc_process_server(struct chatter *chatter)
irc_parse_channel_mode_change(chatter, msg.arg[1],
msg.msg);
}
- return;
+ return true;
}
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;
+ return true;
}
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;
+ return true;
}
if (strcmp(msg.cmd, "PART") == 0) {
user = irc_parse_user(msg.source);
@@ -562,11 +564,11 @@ irc_process_server(struct chatter *chatter)
irc_set_active_channel(chatter, NULL);
else
irc_remove_nick_from_channel(chatter, user->nick);
- return;
+ return true;
}
if (strcmp(msg.cmd, "PING") == 0) {
irc_printf(chatter, "PONG :%s\r\n", msg.msg);
- return;
+ return true;
}
if (strcmp(msg.cmd, "QUIT") == 0) {
user = irc_parse_user(msg.source);
@@ -576,7 +578,7 @@ irc_process_server(struct chatter *chatter)
irc_set_active_channel(chatter, NULL);
else
irc_remove_nick_from_channel(chatter, user->nick);
- return;
+ return true;
}
goto unknown;
}
@@ -600,49 +602,53 @@ irc_process_server(struct chatter *chatter)
case 265:
case 266:
/* server stats, unhelpful */
- return;
+ return true;
+ case 307:
+ /* WHOIS regnick */
+ chatter_printf(chatter, "$B*** |$0 Authenticated:$/ %s", msg.msg);
+ return true;
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;
+ return true;
case 312:
/* WHOIS server */
chatter_printf(chatter, "$B*** |$0 Server:$/ %s (%s)", msg.arg[2],
msg.msg);
- return;
+ return true;
case 671:
/* WHOIS server */
chatter_printf(chatter, "$B*** |$0 Connection:$/ %s", msg.msg);
- return;
+ return true;
case 317:
/* WHOIS idle */
chatter_printf(chatter, "$B*** |$0 Idle:$/ %s %s", msg.arg[2],
msg.msg);
- return;
+ return true;
case 318:
/* WHOIS end */
chatter_printf(chatter, "$B*** `-------$0");
- return;
+ return true;
case 319:
/* WHOIS channels */
chatter_printf(chatter, "$B*** |$0 Channels:$/ %s", msg.msg);
- return;
+ return true;
case 330:
/* WHOIS account */
chatter_printf(chatter, "$B*** |$0 Account:$/ %s %s", msg.msg,
msg.arg[2]);
- return;
+ return true;
case 338:
case 378:
/* WHOIS host */
chatter_printf(chatter, "$B*** |$0 Host:$/ %s %s", msg.msg,
msg.arg[2]);
- return;
+ return true;
case 328:
/* channel URL, we probably can't do anything with it anyway */
- return;
+ return true;
case 332:
/* TOPIC */
chatter_printf(chatter, "$B***$0 Topic for $B%s$0:$/ %s",
@@ -652,10 +658,10 @@ irc_process_server(struct chatter *chatter)
strlcpy(chatter->irc_channel->topic, msg.msg,
sizeof(chatter->irc_channel->topic));
}
- return;
+ return true;
case 333:
/* TOPIC creator */
- return;
+ return true;
case 352:
/* WHO output */
goto unknown;
@@ -667,13 +673,13 @@ irc_process_server(struct chatter *chatter)
if (chatter->irc_channel &&
strcmp(msg.arg[2], chatter->irc_channel->name) == 0)
irc_parse_names(chatter, msg.msg);
- return;
+ return true;
case 366:
/* end of NAMES output */
if (chatter->irc_channel &&
strcmp(msg.arg[1], chatter->irc_channel->name) == 0)
chatter_sync_nick_list(chatter, true);
- return;
+ return true;
case 372:
case 375:
case 376:
@@ -682,20 +688,21 @@ irc_process_server(struct chatter *chatter)
case 396:
/* Cloak */
chatter_printf(chatter, "$B***$0$/ %s %s", msg.arg[1], msg.msg);
- return;
+ return true;
default:
goto unknown;
}
print_msg:
chatter_printf(chatter, "$B***$0$/ %s", msg.msg);
- return;
+ return true;
unknown:
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);
+ return true;
}
void
@@ -825,6 +832,9 @@ irc_parse_names(struct chatter *chatter, char *line)
} else if (nick[0] == '+') {
flags = IRC_NICK_FLAG_VOICE;
nick++;
+ } else if (nick[0] == '\0') {
+ /* some servers send a trailing space */
+ break;
} else
flags = 0;