jcs
/wallops
/amendments
/115
chatter+irc: Support switching servers or reconnecting
Do registration after connecting so we can get the connection result
before logging in. If we fail to connect to a new server, keep the
connection to the old one.
Remove duplicate conn NULL checking.
jcs made amendment 115 2 months ago
--- chatter.c Mon Sep 16 17:20:33 2024
+++ chatter.c Tue Sep 17 15:40:13 2024
@@ -509,10 +509,6 @@ chatter_update_titlebar(struct chatter *chatter)
if (!tab->conn || tab->conn->state <= IRC_STATE_DISCONNECTED) {
snprintf(title, sizeof(title), "%s: Disconnected", PROGRAM_NAME);
strlcpy(menu_title, "Disconnected", sizeof(menu_title));
- } else if (tab->conn->state == IRC_STATE_CONNECTING) {
- snprintf(title, sizeof(title), "%s: Connecting to %s",
- PROGRAM_NAME, tab->conn->hostname);
- strlcpy(menu_title, tab->conn->hostname, sizeof(menu_title));
} else {
snprintf(title, sizeof(title), "%s: %s@%s", PROGRAM_NAME,
tab->conn->nick, tab->conn->hostname);
@@ -690,21 +686,17 @@ chatter_draw_tab_bar(struct chatter *chatter)
ClipRect(&r);
- if (tab->conn && tab->conn->state >= IRC_STATE_CONNECTED) {
- if (tab->channel) {
- if (tab->channel->mode[0])
- len = snprintf(label, sizeof(label), "%s (%s)",
- tab->channel->name, tab->channel->mode);
- else
- len = strlcpy(label, tab->channel->name,
- sizeof(label));
- } else if (tab->query_nick[0])
- len = strlcpy(label, tab->query_nick, sizeof(label));
- else
- len = strlcpy(label, tab->conn->hostname, sizeof(label));
-
- tlabel = label;
- } else {
+ tlabel = label;
+ if (tab->channel && tab->channel->mode[0])
+ len = snprintf(label, sizeof(label), "%s (%s)",
+ tab->channel->name, tab->channel->mode);
+ else if (tab->channel)
+ len = strlcpy(label, tab->channel->name, sizeof(label));
+ else if (tab->query_nick[0])
+ len = strlcpy(label, tab->query_nick, sizeof(label));
+ else if (tab->conn->state >= IRC_STATE_UNREGISTERED)
+ len = strlcpy(label, tab->conn->hostname, sizeof(label));
+ else {
tlabel = (char *)&no_connection;
len = sizeof(no_connection) - 1;
}
--- irc.c Mon Sep 16 16:51:26 2024
+++ irc.c Tue Sep 17 15:56:38 2024
@@ -22,7 +22,6 @@
#include "irc.h"
#include "settings.h"
-short irc_verify_state(struct irc_connection *conn, short state);
short irc_recv(struct irc_connection *conn);
size_t irc_send(struct irc_connection *conn, char *line, size_t size);
size_t irc_printf(struct irc_connection *conn, const char *format, ...);
@@ -31,6 +30,7 @@ struct irc_user * irc_parse_user(char *str);
bool irc_can_send(struct irc_connection *conn);
struct irc_channel * irc_find_channel(struct irc_connection *conn,
char *channame);
+void irc_register(struct irc_connection *conn);
bool irc_process_server(struct irc_connection *conn);
struct irc_channel * irc_create_channel(struct irc_connection *conn,
char *channame);
@@ -92,7 +92,7 @@ irc_connect(struct chatter *chatter, const char *serve
conn->port);
if ((err = _TCPCreate(&conn->send_pb, &conn->stream, (Ptr)conn->tcp_buf,
- sizeof(conn->tcp_buf), nil, nil, nil, false)) != noErr) {
+ sizeof(conn->tcp_buf), NULL, NULL, NULL, false)) != noErr) {
chatter_printf(conn->chatter, conn, NULL,
"$B*!* TCPCreate failed: %d$0", err);
return conn;
@@ -108,7 +108,7 @@ irc_connect(struct chatter *chatter, const char *serve
long2ip(ip, ip_str);
if ((err = _TCPActiveOpen(&conn->send_pb, conn->stream, ip,
- conn->port, &local_ip, &local_port, nil, nil, false)) != noErr) {
+ conn->port, &local_ip, &local_port, NULL, NULL, false)) != noErr) {
chatter_printf(conn->chatter, conn, NULL,
"$B*!* Failed connecting to %s (%s) port %d: %d$0",
conn->hostname, ip_str, conn->port, err);
@@ -120,22 +120,8 @@ irc_connect(struct chatter *chatter, const char *serve
"$B***$0 Connected to $B%s$0 (%s) port $B%d$0", conn->hostname,
ip_str, conn->port);
- conn->state = IRC_STATE_CONNECTED;
+ conn->state = IRC_STATE_UNREGISTERED;
- if (conn->password && conn->password[0]) {
- len = snprintf(conn->line, sizeof(conn->line),
- "PASS %s\r\n", conn->password);
- irc_send(conn, conn->line, len);
- }
-
- len = snprintf(conn->line, sizeof(conn->line),
- "NICK %s\r\n", conn->nick);
- irc_send(conn, conn->line, len);
-
- len = snprintf(conn->line, sizeof(conn->line),
- "USER %s 0 * :%s\r\n", conn->ident, conn->realname);
- irc_send(conn, conn->line, len);
-
return conn;
}
@@ -146,8 +132,8 @@ irc_close_connection(struct irc_connection *conn)
irc_quit_with_message(conn, NULL);
if (conn->stream) {
- _TCPAbort(&conn->close_pb, conn->stream, nil, nil, false);
- _TCPRelease(&conn->close_pb, conn->stream, nil, nil, false);
+ _TCPAbort(&conn->close_pb, conn->stream, NULL, NULL, false);
+ _TCPRelease(&conn->close_pb, conn->stream, NULL, NULL, false);
conn->stream = 0;
}
@@ -186,7 +172,7 @@ irc_dealloc_connection(struct irc_connection *conn)
void
irc_process(struct irc_connection *conn)
{
- if (conn->state >= IRC_STATE_CONNECTED)
+ if (conn->state >= IRC_STATE_UNREGISTERED)
irc_recv(conn);
switch (conn->state) {
@@ -195,6 +181,9 @@ irc_process(struct irc_connection *conn)
case IRC_STATE_DEAD:
irc_close_connection(conn);
break;
+ case IRC_STATE_UNREGISTERED:
+ irc_register(conn);
+ break;
case IRC_STATE_CONNECTED:
irc_process_server(conn);
break;
@@ -202,28 +191,16 @@ irc_process(struct irc_connection *conn)
}
short
-irc_verify_state(struct irc_connection *conn, short state)
-{
- if (conn->state != state) {
- warn("Bad IRC state (in %d, expected %d)", conn->state, state);
- conn->state = IRC_STATE_DEAD;
- return 0;
- }
-
- return 1;
-}
-
-short
irc_recv(struct irc_connection *conn)
{
unsigned short rlen;
short error, rerror, n;
- if (conn->state < IRC_STATE_CONNECTING)
+ if (conn->state < IRC_STATE_UNREGISTERED)
return 0;
- error = _TCPStatus(&conn->rcv_pb, conn->stream, &conn->status_pb, nil,
- nil, false);
+ error = _TCPStatus(&conn->rcv_pb, conn->stream, &conn->status_pb, NULL,
+ NULL, false);
if (conn->status_pb.amtUnreadData > 0 &&
conn->ibuflen < sizeof(conn->ibuf)) {
@@ -232,7 +209,7 @@ irc_recv(struct irc_connection *conn)
rlen = sizeof(conn->ibuf) - conn->ibuflen;
rerror = _TCPRcv(&conn->rcv_pb, conn->stream,
- (Ptr)(conn->ibuf + conn->ibuflen), &rlen, nil, nil, false);
+ (Ptr)(conn->ibuf + conn->ibuflen), &rlen, NULL, NULL, false);
if (rerror) {
chatter_printf(conn->chatter, conn, NULL,
"$B*!* TCPRecv failed (%d), disconnecting$0", error);
@@ -270,8 +247,8 @@ irc_send(struct irc_connection *conn, char *line, size
if (size > sizeof(conn->obuf))
panic("irc_send: too much data %lu", size);
- if (!irc_verify_state(conn, IRC_STATE_CONNECTED))
- return;
+ if (conn->state < IRC_STATE_UNREGISTERED)
+ return 0;
while (!IRC_CAN_SEND(conn))
SystemTask();
@@ -296,7 +273,7 @@ irc_send(struct irc_connection *conn, char *line, size
conn->wds[0].ptr = (Ptr)&conn->obuf;
conn->wds[0].length = size;
- error = _TCPSend(&conn->send_pb, conn->stream, conn->wds, nil, nil,
+ error = _TCPSend(&conn->send_pb, conn->stream, conn->wds, NULL, NULL,
true);
if (error) {
chatter_printf(conn->chatter, conn, NULL,
@@ -425,6 +402,32 @@ irc_find_channel(struct irc_connection *conn, char *ch
return NULL;
}
+void
+irc_register(struct irc_connection *conn)
+{
+ size_t len;
+
+ if (conn->state == IRC_STATE_CONNECTED)
+ return;
+
+ if (conn->password && conn->password[0]) {
+ len = snprintf(conn->line, sizeof(conn->line),
+ "PASS %s\r\n", conn->password);
+ irc_send(conn, conn->line, len);
+ }
+
+ len = snprintf(conn->line, sizeof(conn->line),
+ "NICK %s\r\n", conn->nick);
+ irc_send(conn, conn->line, len);
+
+ len = snprintf(conn->line, sizeof(conn->line),
+ "USER %s 0 * :%s\r\n", conn->ident, conn->realname);
+ irc_send(conn, conn->line, len);
+
+ if (conn->state == IRC_STATE_UNREGISTERED)
+ conn->state = IRC_STATE_CONNECTED;
+}
+
bool
irc_process_server(struct irc_connection *conn)
{
@@ -433,7 +436,7 @@ irc_process_server(struct irc_connection *conn)
struct irc_channel *channel;
char *line, *word;
size_t size, n, lastbreak;
- short state, curarg;
+ short curarg;
line = irc_get_line(conn, &size);
if (size == 0 || line == NULL)
@@ -1040,6 +1043,62 @@ irc_process_input(struct irc_connection *conn, struct
size_t n;
bool say = false;
+ /* check for commands that don't require a connection */
+ if (strcasecmp(str, "/quit") == 0 ||
+ strncasecmp(str, "/quit ", 6) == 0)
+ {
+ str++;
+ arg0 = strsep(&str, " ");
+
+ if (conn && conn->state == IRC_STATE_CONNECTED) {
+ irc_quit_with_message(conn, str);
+ conn->state = IRC_STATE_DEAD;
+ }
+ if (focusables_quit())
+ ExitToShell();
+ return;
+ }
+ if (strcasecmp(str, "/connect") == 0 ||
+ strncasecmp(str, "/connect ", 9) == 0 ||
+ strcasecmp(str, "/recon") == 0 ||
+ strcasecmp(str, "/reconnect") == 0 ||
+ strcasecmp(str, "/server") == 0 ||
+ strncasecmp(str, "/server ", 8) == 0) {
+ struct irc_connection *new;
+ struct chatter_tab *tab;
+
+ str++;
+ arg0 = strsep(&str, " ");
+ if (strcasecmp(arg0, "server") == 0 && str == NULL)
+ goto not_enough_params;
+
+ new = irc_connect(conn->chatter, str ? str : conn->hostname,
+ conn->port, conn->password, conn->nick, conn->ident,
+ conn->realname, conn->hide_motd, conn->channel_autojoin);
+
+ if (new->state == IRC_STATE_UNREGISTERED) {
+ /* the new connection succeeded, kill the old one */
+ if (conn->state == IRC_STATE_CONNECTED)
+ irc_close_connection(conn);
+
+ SLIST_FOREACH(tab, &conn->chatter->tabs_list, list) {
+ if (tab->conn == conn)
+ tab->conn = new;
+ }
+
+ irc_dealloc_connection(conn);
+
+ conn->chatter->need_tab_bar_redraw = true;
+ } else {
+ /* just keep the old one */
+ irc_dealloc_connection(new);
+ }
+
+ return;
+ }
+
+ /* everything else requires an active connection */
+
if (conn == NULL || conn->state < IRC_STATE_CONNECTED)
goto not_connected;
@@ -1071,8 +1130,6 @@ irc_process_input(struct irc_connection *conn, struct
/* special cases: send as-is */
if (strcasecmp(arg0, "quote") == 0 || strcasecmp(arg0, "raw") == 0) {
- if (conn == NULL)
- goto not_connected;
if (str == NULL)
goto not_enough_params;
irc_printf(conn, "%s\r\n", str);
@@ -1103,16 +1160,12 @@ irc_process_input(struct irc_connection *conn, struct
return;
}
if (strcasecmp(arg0, "deop") == 0) {
- if (conn == NULL)
- goto not_connected;
if (channel_name == NULL)
goto not_in_channel;
irc_do_mode_to_users(conn, channel_name, "-o", str);
return;
}
if (strcasecmp(arg0, "devoice") == 0) {
- if (conn == NULL)
- goto not_connected;
if (channel_name == NULL)
goto not_in_channel;
irc_do_mode_to_users(conn, channel_name, "-v", str);
@@ -1121,14 +1174,10 @@ irc_process_input(struct irc_connection *conn, struct
if (strcasecmp(arg0, "disco") == 0 ||
strcasecmp(arg0, "discon") == 0 ||
strcasecmp(arg0, "disconnect") == 0) {
- if (conn == NULL)
- goto not_connected;
irc_quit_with_message(conn, str);
return;
}
if (strcasecmp(arg0, "join") == 0) {
- if (conn == NULL)
- goto not_connected;
if (channel_name == NULL)
goto not_enough_params;
/*
@@ -1144,8 +1193,6 @@ irc_process_input(struct irc_connection *conn, struct
return;
}
if (strcasecmp(arg0, "me") == 0) {
- if (conn == NULL)
- goto not_connected;
if (str == NULL)
goto not_enough_params;
if (query_nick == NULL && channel == NULL)
@@ -1159,8 +1206,6 @@ irc_process_input(struct irc_connection *conn, struct
return;
}
if (strcasecmp(arg0, "mode") == 0) {
- if (conn == NULL)
- goto not_connected;
if (channel_name == NULL)
goto not_in_channel;
irc_printf(conn, "MODE %s%s%s\r\n", channel_name, (str ? " " : ""),
@@ -1168,8 +1213,6 @@ irc_process_input(struct irc_connection *conn, struct
return;
}
if (strcasecmp(arg0, "msg") == 0) {
- if (conn == NULL)
- goto not_connected;
arg1 = strsep(&str, " ");
if (arg1 == NULL || str == NULL)
goto not_enough_params;
@@ -1180,24 +1223,18 @@ irc_process_input(struct irc_connection *conn, struct
return;
}
if (strcasecmp(arg0, "nick") == 0) {
- if (conn == NULL)
- goto not_connected;
if (str == NULL)
goto not_enough_params;
irc_printf(conn, "NICK %s\r\n", str);
return;
}
if (strcasecmp(arg0, "op") == 0) {
- if (conn == NULL)
- goto not_connected;
if (channel_name == NULL)
goto not_in_channel;
irc_do_mode_to_users(conn, channel_name, "+o", str);
return;
}
if (strcasecmp(arg0, "part") == 0) {
- if (conn == NULL)
- goto not_connected;
if (channel_name == NULL)
goto not_in_channel;
irc_printf(conn, "PART %s%s%s\r\n", channel_name,
@@ -1205,27 +1242,12 @@ irc_process_input(struct irc_connection *conn, struct
return;
}
if (strcasecmp(arg0, "query") == 0) {
- if (conn == NULL)
- goto not_connected;
if (str == NULL)
goto not_enough_params;
chatter_add_tab(conn->chatter, NULL, conn, NULL, str);
return;
}
- if (strcasecmp(arg0, "quit") == 0) {
- if (conn == NULL)
- goto not_connected;
- if (conn->state == IRC_STATE_CONNECTED) {
- irc_quit_with_message(conn, str);
- conn->state = IRC_STATE_DISCONNECTED;
- }
- if (focusables_quit())
- ExitToShell();
- return;
- }
if (strcasecmp(arg0, "topic") == 0) {
- if (conn == NULL)
- goto not_connected;
if (channel_name == NULL)
goto not_in_channel;
if (str)
@@ -1235,31 +1257,23 @@ irc_process_input(struct irc_connection *conn, struct
return;
}
if (strcasecmp(arg0, "umode") == 0) {
- if (conn == NULL)
- goto not_connected;
irc_printf(conn, "MODE %s%s%s\r\n", conn->nick, (str ? " :" : ""),
(str ? str : ""));
return;
}
if (strcasecmp(arg0, "voice") == 0) {
- if (conn == NULL)
- goto not_connected;
if (channel_name == NULL)
goto not_in_channel;
irc_do_mode_to_users(conn, channel_name, "+v", str);
return;
}
if (strcasecmp(arg0, "who") == 0) {
- if (conn == NULL)
- goto not_connected;
if (channel_name == NULL)
goto not_in_channel;
irc_printf(conn, "WHO %s\r\n", channel_name);
return;
}
if (strcasecmp(arg0, "whois") == 0) {
- if (conn == NULL)
- goto not_connected;
if (str == NULL)
goto not_enough_params;
irc_printf(conn, "WHOIS %s\r\n", str);
@@ -1272,11 +1286,11 @@ irc_process_input(struct irc_connection *conn, struct
return;
not_enough_params:
- chatter_printf(conn->chatter, conn, NULL,
+ chatter_printf(conn->chatter, conn, channel ? channel->name : NULL,
"$B*!*$0 Not enough parameters given");
return;
not_connected:
- chatter_printf(conn->chatter, conn, NULL,
+ chatter_printf(conn->chatter, conn, channel ? channel->name : NULL,
"$B*!*$0 Not connected");
return;
not_in_channel:
--- irc.h Fri Sep 13 17:30:04 2024
+++ irc.h Tue Sep 17 15:18:17 2024
@@ -24,7 +24,7 @@
enum {
IRC_STATE_DISCONNECTED = 0,
IRC_STATE_DEAD,
- IRC_STATE_CONNECTING,
+ IRC_STATE_UNREGISTERED,
IRC_STATE_CONNECTED
};