AmendHub

Download:

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 about 1 month 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 };