~aleteoryx/sexchat

a5ac3da4bb20b989218781c720fcc8b96e6611e1 — Aleteoryx 4 months ago 4ce2f29
The wall is built.

Implements: https://todo.amehut.dev/~aleteoryx/sexchat/15
M src/common/dcc.c => src/common/dcc.c +5 -5
@@ 499,7 499,7 @@ dcc_write_chat (char *nick, char *text)
	if (dcc && dcc->dccstat == STAT_ACTIVE)
	{
		len = strlen (text);
		tcp_send_real (NULL, dcc->sok, dcc->serv->write_converter, text, len);
		tcp_send_real (NULL, dcc->sok, dcc->serv->c.write_converter, text, len);
		send (dcc->sok, "\n", 1, 0);
		dcc->size += len;
		fe_dcc_update (dcc);


@@ 521,7 521,7 @@ dcc_chat_line (struct DCC *dcc, char *line)
	char portbuf[32];
	message_tags_data no_tags = MESSAGE_TAGS_DATA_INIT;

	line = text_convert_invalid (line, -1, dcc->serv->read_converter, unicode_fallback_string, NULL);
	line = text_convert_invalid (line, -1, dcc->serv->c.read_converter, unicode_fallback_string, NULL);

	sess = find_dialog (dcc->serv, dcc->nick);
	if (!sess)


@@ 1656,7 1656,7 @@ dcc_listen_init (struct DCC *dcc, session *sess)
	memset (&SAddr, 0, sizeof (struct sockaddr_in));

	len = sizeof (SAddr);
	getsockname (dcc->serv->sok, (struct sockaddr *) &SAddr, &len);
	getsockname (dcc->serv->c.sok, (struct sockaddr *) &SAddr, &len);

	SAddr.sin_family = AF_INET;



@@ 1978,7 1978,7 @@ dcc_change_nick (struct ircconn *serv, char *oldnick, char *newnick)
		dcc = (struct DCC *) list->data;
		if (dcc->serv == serv)
		{
			if (!serv->p_cmp (dcc->nick, oldnick))
			if (!serv->cmp_fn (dcc->nick, oldnick))
			{
				g_free (dcc->nick);
				dcc->nick = g_strdup (newnick);


@@ 2215,7 2215,7 @@ dcc_get_nick (struct session *sess, char *nick)
	while (list)
	{
		dcc = (struct DCC *) list->data;
		if (!sess->server->p_cmp (nick, dcc->nick))
		if (!sess->server->cmp_fn (nick, dcc->nick))
		{
			if (dcc->dccstat == STAT_QUEUED && dcc->type == TYPE_RECV)
			{

M src/common/hexchat.c => src/common/hexchat.c +9 -9
@@ 198,7 198,7 @@ find_dialog (ircconn *serv, char *nick)
		sess = list->data;
		if (sess->server == serv && sess->type == SESS_DIALOG)
		{
			if (!serv->p_cmp (nick, sess->channel))
			if (!serv->cmp_fn (nick, sess->channel))
				return (sess);
		}
		list = list->next;


@@ 216,7 216,7 @@ find_channel (ircconn *serv, char *chan)
		sess = list->data;
		if ((serv == sess->server) && sess->type == SESS_CHANNEL)
		{
			if (!serv->p_cmp (chan, sess->channel))
			if (!serv->cmp_fn (chan, sess->channel))
				return sess;
		}
		list = list->next;


@@ 258,7 258,7 @@ lag_check (void)
	while (list)
	{
		serv = list->data;
		if (serv->connected && serv->end_of_motd)
		if (serv->c.connected && serv->end_of_motd)
		{
			lag = now - serv->ping_recv;
			if (prefs.hex_net_ping_timeout != 0 && lag > prefs.hex_net_ping_timeout && lag > 0)


@@ 267,7 267,7 @@ lag_check (void)
				EMIT_SIGNAL (XP_TE_PINGTIMEOUT, serv->server_session, tbuf, NULL,
								 NULL, NULL, 0);
				if (prefs.hex_net_auto_reconnect)
					serv->auto_reconnect (serv, FALSE, -1);
					irc_auto_reconnect (serv, FALSE, -1);
			}
			else
			{


@@ 304,7 304,7 @@ doover:
	{
		sess = list->data;

		if (sess->server->connected &&
		if (sess->server->c.connected &&
			 sess->type == SESS_CHANNEL &&
			 sess->channel[0] &&
			 (sess->total <= prefs.hex_away_size_max || !prefs.hex_away_size_max))


@@ 600,14 600,14 @@ send_quit_or_part (session * killsess)
	if (hexchat_is_quitting)
		willquit = TRUE;

	if (killserv->connected)
	if (killserv->c.connected)
	{
		if (willquit)
		{
			if (!killserv->sent_quit)
			{
				killserv->flush_queue (killserv);
				killserv->p_quit (killserv);
				server_flush_queue (killserv);
				irc_quit (killserv);
				killserv->sent_quit = TRUE;
			}
		} else


@@ 703,7 703,7 @@ session_free (session *killsess)
		list = list->next;
	}

	server_free (killserv);
	ircconn_free (killserv);
}

static void

M src/common/hexchat.h => src/common/hexchat.h +59 -60
@@ 435,51 435,36 @@ typedef struct session

typedef struct conn
{

} conn;

typedef struct ircconn
{
	struct conn c;

	/*  server control operations (in server*.c) */
	int  (*cleanup)(struct ircconn *);
	void (*flush_queue)(struct ircconn *);
	void (*auto_reconnect)(struct ircconn *, int send_quit, int err);
	
	/* irc protocol functions (in proto*.c) */
	void (*p_inline)(struct ircconn *, char *buf, int len);
	void (*p_connected)(struct ircconn *);
	void (*p_disconnected)(struct ircconn *, int, int);
	void (*p_ssl_message)(struct ircconn *, const char *);
	void (*p_serverlookup)(struct ircconn *, const char *);
	void (*p_connfail)(struct ircconn *, const char *);
	void (*p_connstop)(struct ircconn *, const char *);
	void (*p_connecting)(struct ircconn *, const char *, const char *, const char *);
	void (*p_unknhost)(struct ircconn *);
	void (*p_readerr)(struct ircconn *, int);
	void (*p_log)(struct ircconn *, const char *);
	char *(*p_get_network)(struct ircconn *, gboolean);
	void (*p_rawlog)(struct ircconn *, char *, int, int);
	void (*p_throttle_update)(struct ircconn *);
	void (*p_cleanup)(struct ircconn *);
	void (*p_stopconnect)(struct ircconn *);
	int  (*p_cmp)(const char *s1, const char *s2);
	void (*p_quit)(struct ircconn *);
	
	void (*p_inline)(struct conn *, char *buf, int len);
	void (*p_connected)(struct conn *);
	void (*p_disconnected)(struct conn *, int, int);
	void (*p_ssl_message)(struct conn *, const char *);
	void (*p_serverlookup)(struct conn *, const char *);
	void (*p_connfail)(struct conn *, const char *);
	void (*p_connstop)(struct conn *, const char *);
	void (*p_connecting)(struct conn *, const char *, const char *, const char *);
	void (*p_unknhost)(struct conn *);
	void (*p_readerr)(struct conn *, int);
	void (*p_log)(struct conn *, const char *);
	char *(*p_get_network)(struct conn *, gboolean);
	void (*p_rawlog)(struct conn *, char *, int, int);
	void (*p_throttle_update)(struct conn *);
	gboolean (*p_cleanup)(struct conn *);
	void (*p_stopconnect)(struct conn *);
	void (*p_quit)(struct conn *);
	void (*p_reset)(struct conn *);

	int port;
	int sok;					/* is equal to sok4 or sok6 (the one we are using) */
	int sok4;					/* tcp4 socket */
	int sok6;					/* tcp6 socket */
	int sok;				/* is equal to sok4 or sok6 (the one we are using) */
	int sok4;				/* tcp4 socket */
	int sok6;				/* tcp6 socket */
	int proxy_type;
	int proxy_sok;				/* Additional information for MS Proxy beast */
	int proxy_sok4;
	int proxy_sok6;
	int id;					/* unique ID number (for plugin API) */

	/* dcc_ip moved from hexchatprefs to make it per-server */
	guint32 dcc_ip;

	char hostname[128];			/* real ip number */
#ifdef USE_OPENSSL
	SSL_CTX *ctx;
	SSL *ssl;


@@ 491,15 476,47 @@ typedef struct ircconn
	int childwrite;
	int childpid;
	int iotag;
	int pos;				/* current position in linebuf */
	char linebuf[8704];			/* RFC says 512 chars including \r\n,
						   IRCv3 message tags add 8191, plus the NUL byte */

	GSList *outbound_queue;
	time_t next_send;			/* cptr->since in ircu */
	time_t prev_now;			/* previous now-time */
	int sendq_len;				/* queue size */

	char *encoding;
	GIConv read_converter;			/* iconv converter for converting from server encoding to UTF-8. */
	GIConv write_converter;			/* iconv converter for converting from UTF-8 to server encoding. */

	unsigned int connected:1;
	unsigned int connecting:1;
	unsigned int dont_use_proxy:1;	/* to proxy or not to proxy */
	unsigned int have_cert:1;		/* have loaded a cert */
#ifdef USE_OPENSSL
	unsigned int use_ssl:1;			/* is server SSL capable? */
	unsigned int accept_invalid_cert:1;	/* ignore result of server's cert. verify */
	scram_session *scram_session;		/* session for SASL SCRAM authentication */
#endif
} conn;

typedef struct ircconn
{
	struct conn c;
	
	int (*cmp_fn)(const char *s1, const char *s2);
	
	int id;				/* unique ID number (for plugin API) */

	/* dcc_ip moved from hexchatprefs to make it per-server */
	guint32 dcc_ip;

	int recondelay_tag;				/* reconnect delay timeout */
	int joindelay_tag;				/* waiting before we send JOIN */
	char hostname[128];				/* real ip number */
	char servername[128];			/* what the server says is its name */
	char password[1024];
	char nick[NICKLEN];
	char linebuf[8704];				/* RFC says 512 chars including \r\n, IRCv3 message tags add 8191, plus the NUL byte */
	char *last_away_reason;
	int pos;								/* current position in linebuf */
	int nickcount;
	int loginmethod;					/* see login_types[] */



@@ 512,12 529,6 @@ typedef struct ircconn

	void *network;						/* points to entry in servlist.c or NULL! */

	GSList *outbound_queue;
	time_t next_send;						/* cptr->since in ircu */
	time_t prev_now;					/* previous now-time */
	int sendq_len;						/* queue size */
	int lag;								/* milliseconds */

	char *quitreason;

	struct session *front_session;	/* front-most window/tab */


@@ 531,21 542,16 @@ typedef struct ircconn
	unsigned int msg_counter;	/*counts the msg tab opened in a certain time */
	time_t msg_last_time;

	int lag;				/* milliseconds */
	unsigned long lag_sent;		/* we are still waiting for this ping response*/
	time_t ping_recv;		/* when we last got a ping reply */
	time_t away_time;		/* when we were marked away */

	char *encoding;
	GIConv read_converter;  	/* iconv converter for converting from server encoding to UTF-8. */
	GIConv write_converter; 	/* iconv converter for converting from UTF-8 to server encoding. */

	GSList *favlist;		/* list of channels & keys to join */

	GSList *batches;		/* list of batches */

	unsigned int motd_skipped:1;
	unsigned int connected:1;
	unsigned int connecting:1;
	unsigned int no_login:1;
	unsigned int skip_next_userhost:1;/* used for "get my ip from server" */
	unsigned int skip_next_whois:1;	/* hide whois output */


@@ 556,7 562,6 @@ typedef struct ircconn
	unsigned int use_listargs:1;		/* undernet and dalnet need /list >0,<10000 */
	unsigned int is_away:1;
	unsigned int reconnect_away:1;	/* whether to reconnect in is_away state */
	unsigned int dont_use_proxy:1;	/* to proxy or not to proxy */
	unsigned int supports_watch:1;	/* supports the WATCH command */
	unsigned int supports_monitor:1;	/* supports the MONITOR command */
	unsigned int bad_prefix:1;			/* gave us a bad PREFIX= 005 number */


@@ 574,17 579,11 @@ typedef struct ircconn
	unsigned int have_sasl:1;		/* SASL capability */
	unsigned int have_except:1;	/* ban exemptions +e */
	unsigned int have_invite:1;	/* invite exemptions +I */
	unsigned int have_cert:1;	/* have loaded a cert */
	unsigned int use_who:1;			/* whether to use WHO command to get dcc_ip */
	unsigned int sasl_mech;			/* mechanism for sasl auth */
	unsigned int sent_capend:1;	/* have sent CAP END yet */
	unsigned int waiting_on_cap:1;	/* waiting on another line of CAP LS */
	unsigned int waiting_on_sasl:1; /* waiting on sasl */
#ifdef USE_OPENSSL
	unsigned int use_ssl:1;				  /* is server SSL capable? */
	unsigned int accept_invalid_cert:1;/* ignore result of server's cert. verify */
	scram_session *scram_session; /* session for SASL SCRAM authentication */
#endif
} ircconn;

typedef int (*cmd_callback) (struct session * sess, char *tbuf, char *word[],

M src/common/inbound.c => src/common/inbound.c +31 -31
@@ 498,7 498,7 @@ inbound_newnick (ircconn *serv, char *nick, char *newnick, int quiet,
	session *sess;
	GSList *list = sess_list;

	if (!serv->p_cmp (nick, serv->nick))
	if (!serv->cmp_fn (nick, serv->nick))
	{
		me = TRUE;
		safe_strcpy (serv->nick, newnick, NICKLEN);


@@ 519,7 519,7 @@ inbound_newnick (ircconn *serv, char *nick, char *newnick, int quiet,
						EMIT_SIGNAL_TAGS (XP_TE_CHANGENICK, sess, nick, newnick, NULL, NULL, 0, tags_data);
				}
			}
			if (sess->type == SESS_DIALOG && !serv->p_cmp (sess->channel, nick))
			if (sess->type == SESS_DIALOG && !serv->cmp_fn (sess->channel, nick))
			{
				safe_strcpy (sess->channel, newnick, CHANLEN);
				fe_set_channel (sess);


@@ 565,7 565,7 @@ find_session_from_waitchannel (char *chan, struct ircconn *serv)
		sess = (session *) list->data;
		if (sess->server == serv && sess->channel[0] == 0 && sess->type == SESS_CHANNEL)
		{
			if (!serv->p_cmp (chan, sess->waitchannel))
			if (!serv->cmp_fn (chan, sess->waitchannel))
				return sess;
		}
		list = list->next;


@@ 832,7 832,7 @@ inbound_quit (ircconn *serv, char *nick, char *ip, char *reason,
			{
				EMIT_SIGNAL_TAGS (XP_TE_QUIT, sess, nick, reason, ip, NULL, 0, tags_data);
				userlist_remove_user (sess, user);
			} else if (sess->type == SESS_DIALOG && !serv->p_cmp (sess->channel, nick))
			} else if (sess->type == SESS_DIALOG && !serv->cmp_fn (sess->channel, nick))
			{
				EMIT_SIGNAL_TAGS (XP_TE_QUIT, sess, nick, reason, ip, NULL, 0, tags_data);
			}


@@ 1020,7 1020,7 @@ inbound_notice (ircconn *serv, char *to, char *nick, char *msg, char *ip, int id
void
inbound_away (ircconn *serv, char *nick, char *msg, const message_tags_data *tags_data)
{
	struct away_msg *away = server_away_find_message (serv, nick);
	struct away_msg *away = irc_away_find_message (serv, nick);
	session *sess = NULL;
	GSList *list;



@@ 1030,7 1030,7 @@ inbound_away (ircconn *serv, char *nick, char *msg, const message_tags_data *tag
			return;
	} else
	{
		server_away_save_message (serv, nick, msg);
		irc_away_save_message (serv, nick, msg);
	}

	if (prefs.hex_irc_whois_front)


@@ 1125,7 1125,7 @@ check_autojoin_channels (ircconn *serv)
	favchannel *fav;

	/* shouldn't really happen, the io tag is destroyed in server.c */
	if (!is_server (serv))
	if (!is_server (&serv->c))
	{
		return FALSE;
	}


@@ 1183,7 1183,7 @@ check_autojoin_channels (ircconn *serv)
			irc_join_list (serv, serv->favlist);
			i++;

			/* FIXME this is not going to work and is not needed either. server_free() does the job already. */
			/* FIXME this is not going to work and is not needed either. ircconn_free() does the job already. */
			/* g_slist_free_full (serv->favlist, (GDestroyNotify) servlist_favchan_free); */
		}
	}


@@ 1451,7 1451,7 @@ inbound_user_info (session *sess, char *chan, char *user, char *host,
			{
				userlist_add_hostname (sess, nick, uhost, realname, servname, account, away);
			}
			else if (sess->type == SESS_DIALOG && uhost && !serv->p_cmp (sess->channel, nick))
			else if (sess->type == SESS_DIALOG && uhost && !serv->cmp_fn (sess->channel, nick))
			{
				set_topic (sess, uhost, uhost);
			}


@@ 1671,7 1671,7 @@ inbound_toggle_caps (ircconn *serv, const char *extensions_str, gboolean enable)
					serv->sasl_mech = MECH_SCRAM_SHA_512;
#endif
				/* Mechanism either defaulted to PLAIN or server gave us list */
				tcp_sendf (serv, "AUTHENTICATE %s\r\n", sasl_mechanisms[serv->sasl_mech]);
				tcp_sendf ((struct conn *)serv, "AUTHENTICATE %s\r\n", sasl_mechanisms[serv->sasl_mech]);
			}
		}
	}


@@ 1862,7 1862,7 @@ inbound_cap_ls (ircconn *serv, char *nick, char *extensions_str,
	{
		/* buffer + 9 = emit buffer without "CAP REQ :" */
		EMIT_SIGNAL_TAGS (XP_TE_CAPREQ, serv->server_session, buffer + 9, NULL, NULL, NULL, 0, tags_data);
		tcp_sendf (serv, "%s\r\n", g_strchomp (buffer));
		tcp_sendf ((struct conn *)serv, "%s\r\n", g_strchomp (buffer));
	}
	if (!serv->waiting_on_sasl && !serv->waiting_on_cap)
	{


@@ 1914,7 1914,7 @@ plain_authenticate (ircconn *serv, char *user, char *password)
	if (pass == NULL)
	{
		/* something went wrong abort */
		tcp_sendf (serv, "AUTHENTICATE *\r\n");
		tcp_sendf ((struct conn *)serv, "AUTHENTICATE *\r\n");
		return;
	}



@@ 1922,20 1922,20 @@ plain_authenticate (ircconn *serv, char *user, char *password)
	   https://ircv3.net/specs/extensions/sasl-3.1#the-authenticate-command */
	size_t pass_len = strlen (pass);
	if (pass_len <= 400)
		tcp_sendf (serv, "AUTHENTICATE %s\r\n", pass);
		tcp_sendf ((struct conn *)serv, "AUTHENTICATE %s\r\n", pass);
	else
	{
		size_t sent = 0;
		while (sent < pass_len)
		{
			char *pass_chunk = g_strndup (pass + sent, 400);
			tcp_sendf (serv, "AUTHENTICATE %s\r\n", pass_chunk);
			tcp_sendf ((struct conn *)serv, "AUTHENTICATE %s\r\n", pass_chunk);
			sent += 400;
			g_free (pass_chunk);
		}
	}
	if (pass_len % 400 == 0)
		tcp_sendf (serv, "AUTHENTICATE +\r\n");
		tcp_sendf ((struct conn *)serv, "AUTHENTICATE +\r\n");
}

#ifdef USE_OPENSSL


@@ 1951,49 1951,49 @@ scram_authenticate (ircconn *serv, const char *data, const char *digest,
	size_t output_len;
	gsize decoded_len;

	if (serv->scram_session == NULL)
	if (serv->c.scram_session == NULL)
	{
		serv->scram_session = scram_session_create (digest, user, password);
		serv->c.scram_session = scram_session_create (digest, user, password);

		if (serv->scram_session == NULL)
		if (serv->c.scram_session == NULL)
		{
			PrintTextf (serv->server_session, _("Could not create SCRAM session with digest %s"), digest);
			g_warning ("Could not create SCRAM session with digest %s", digest);
			tcp_sendf (serv, "AUTHENTICATE *\r\n");
			tcp_sendf ((struct conn *)serv, "AUTHENTICATE *\r\n");
			return;
		}
	}

	decoded = g_base64_decode (data, &decoded_len);
	status = scram_process (serv->scram_session, decoded, &output, &output_len);
	status = scram_process (serv->c.scram_session, decoded, &output, &output_len);
	g_free (decoded);

	if (status == SCRAM_IN_PROGRESS)
	{
		// Authentication is still in progress
		encoded = g_base64_encode ((guchar *) output, output_len);
		tcp_sendf (serv, "AUTHENTICATE %s\r\n", encoded);
		tcp_sendf ((struct conn *)serv, "AUTHENTICATE %s\r\n", encoded);
		g_free (encoded);
		g_free (output);
	}
	else if (status == SCRAM_SUCCESS)
	{
		// Authentication succeeded
		tcp_sendf (serv, "AUTHENTICATE +\r\n");
		g_clear_pointer (&serv->scram_session, scram_session_free);
		tcp_sendf ((struct conn *)serv, "AUTHENTICATE +\r\n");
		g_clear_pointer (&serv->c.scram_session, scram_session_free);
	}
	else if (status == SCRAM_ERROR)
	{
		// Authentication failed
		tcp_sendf (serv, "AUTHENTICATE *\r\n");
		tcp_sendf ((struct conn *)serv, "AUTHENTICATE *\r\n");

		if (serv->scram_session->error != NULL)
		if (serv->c.scram_session->error != NULL)
		{
			PrintTextf (serv->server_session, _("SASL SCRAM authentication failed: %s"), serv->scram_session->error);
			g_info ("SASL SCRAM authentication failed: %s", serv->scram_session->error);
			PrintTextf (serv->server_session, _("SASL SCRAM authentication failed: %s"), serv->c.scram_session->error);
			g_info ("SASL SCRAM authentication failed: %s", serv->c.scram_session->error);
		}

		g_clear_pointer (&serv->scram_session, scram_session_free);
		g_clear_pointer (&serv->c.scram_session, scram_session_free);
	}
}
#endif


@@ 2022,7 2022,7 @@ inbound_sasl_authenticate (ircconn *serv, char *data)
			break;
#ifdef USE_OPENSSL
		case MECH_EXTERNAL:
			tcp_sendf (serv, "AUTHENTICATE +\r\n");
			tcp_sendf ((struct conn *)serv, "AUTHENTICATE +\r\n");
			break;
		case MECH_SCRAM_SHA_1:
			scram_authenticate(serv, data, "SHA1", user, serv->password);


@@ 2043,8 2043,8 @@ void
inbound_sasl_error (ircconn *serv)
{
#ifdef USE_OPENSSL
    g_clear_pointer (&serv->scram_session, scram_session_free);
    g_clear_pointer (&serv->c.scram_session, scram_session_free);
#endif
	/* Just abort, not much we can do */
	tcp_sendf (serv, "AUTHENTICATE *\r\n");
	tcp_sendf ((struct conn *)serv, "AUTHENTICATE *\r\n");
}

M src/common/modes.c => src/common/modes.c +2 -2
@@ 541,7 541,7 @@ handle_single_mode (mode_run *mr, char sign, char mode, char *nick,

 genmode:
	/* Received umode +e. If we're waiting to send JOIN then send now! */
	if (mode == 'e' && sign == '+' && !serv->p_cmp (chan, serv->nick))
	if (mode == 'e' && sign == '+' && !serv->cmp_fn (chan, serv->nick))
		inbound_identified (serv);

	if (!quiet)


@@ 886,7 886,7 @@ inbound_005 (ircconn * serv, char *word[], const message_tags_data *tags_data)
		} else if (g_strcmp0 (tokname, "CASEMAPPING") == 0)
		{
			if (g_strcmp0 (tokvalue, "ascii") == 0)
				serv->p_cmp = (void *)g_ascii_strcasecmp;
				serv->cmp_fn = (void *)g_ascii_strcasecmp;
		} else if (g_strcmp0 (tokname, "CHARSET") == 0)
		{
			if (g_ascii_strcasecmp (tokvalue, "UTF-8") == 0)

M src/common/notify.c => src/common/notify.c +7 -7
@@ 196,7 196,7 @@ notify_find (ircconn *serv, char *nick)
			continue;
		}

		if (!serv->p_cmp (notify->name, nick))
		if (!serv->cmp_fn (notify->name, nick))
			return servnot;

		list = list->next;


@@ 362,7 362,7 @@ notify_watch_all (struct notify *notify, int add)
	while (list)
	{
		serv = list->data;
		if (serv->connected && serv->end_of_motd && notify_do_network (notify, serv))
		if (serv->c.connected && serv->end_of_motd && notify_do_network (notify, serv))
			notify_watch (serv, notify->name, add);
		list = list->next;
	}


@@ 466,7 466,7 @@ notify_markonline (ircconn *serv, char *word[], const message_tags_data *tags_da
		seen = FALSE;
		while (*word[i])
		{
			if (!serv->p_cmp (notify->name, word[i]))
			if (!serv->cmp_fn (notify->name, word[i]))
			{
				seen = TRUE;
				notify_announce_online (serv, servnot, notify->name, tags_data);


@@ 534,7 534,7 @@ notify_checklist (void)	/* check ISON list */
	while (list)
	{
		serv = list->data;
		if (serv->connected && serv->end_of_motd && !serv->supports_watch && !serv->supports_monitor)
		if (serv->c.connected && serv->end_of_motd && !serv->supports_watch && !serv->supports_monitor)
		{
			notify_checklist_for_server (serv);
		}


@@ 633,7 633,7 @@ notify_is_in_list (ircconn *serv, char *name)
	while (list)
	{
		notify = (struct notify *) list->data;
		if (!serv->p_cmp (notify->name, name))
		if (!serv->cmp_fn (notify->name, name))
			return TRUE;
		list = list->next;
	}


@@ 651,7 651,7 @@ notify_isnotify (struct session *sess, char *name)
	while (list)
	{
		notify = (struct notify *) list->data;
		if (!sess->server->p_cmp (notify->name, name))
		if (!sess->server->cmp_fn (notify->name, name))
		{
			servnot = notify_find_server_entry (notify, sess->server);
			if (servnot && servnot->ison)


@@ 691,7 691,7 @@ notify_cleanup ()
				serv = (struct ircconn *) srvlist->data;
				if (servnot->server == serv)
				{
					valid = serv->connected;	/* Only valid if server is too */
					valid = serv->c.connected;	/* Only valid if server is too */
					break;
				}
				srvlist = srvlist->next;

M src/common/outbound.c => src/common/outbound.c +38 -39
@@ 242,7 242,7 @@ cmd_allchannels (session *sess, char *tbuf, char *word[], char *word_eol[])
	while (list)
	{
		sess = list->data;
		if (sess->type == SESS_CHANNEL && sess->channel[0] && sess->server->connected)
		if (sess->type == SESS_CHANNEL && sess->channel[0] && sess->server->c.connected)
		{
			handle_command (sess, word_eol[2], FALSE);
		}


@@ 265,7 265,7 @@ cmd_allchannelslocal (session *sess, char *tbuf, char *word[], char *word_eol[])
	{
		sess = list->data;
		if (sess->type == SESS_CHANNEL && sess->channel[0] &&
			 sess->server->connected && sess->server == serv)
			 sess->server->c.connected && sess->server == serv)
		{
			handle_command (sess, word_eol[2], FALSE);
		}


@@ 289,7 289,7 @@ cmd_allservers (struct session *sess, char *tbuf, char *word[],
	while (list)
	{
		serv = list->data;
		if (serv->connected)
		if (serv->c.connected)
			handle_command (serv->front_session, word_eol[2], FALSE);
		list = list->next;
	}


@@ 329,7 329,7 @@ cmd_away (struct session *sess, char *tbuf, char *word[], char *word_eol[])
			sess->server->last_away_reason = reason;
	}

	if (!sess->server->connected)
	if (!sess->server->c.connected)
		sess->server->reconnect_away = 1;

	return TRUE;


@@ 539,13 539,13 @@ cmd_charset (struct session *sess, char *tbuf, char *word[], char *word_eol[])

	if (!word[2 + offset][0])
	{
		PrintTextf (sess, "Current charset: %s\n", serv->encoding);
		PrintTextf (sess, "Current charset: %s\n", serv->c.encoding);
		return TRUE;
	}

	if (servlist_check_encoding (word[2 + offset]))
	{
		server_set_encoding (serv, word[2 + offset]);
		server_set_encoding ((struct conn *)serv, word[2 + offset]);
		if (offset < 1)
			PrintTextf (sess, "Charset changed to: %s\n", word[2 + offset]);
	} else


@@ 846,7 846,7 @@ cmd_debug (struct session *sess, char *tbuf, char *word[], char *word_eol[])
	{
		v = (struct ircconn *) list->data;
		sprintf (tbuf, "%p %-5d %s\n",
					v, v->sok, v->servername);
					v, v->c.sok, v->servername);
		PrintText (sess, tbuf);
		list = list->next;
	}


@@ 1356,7 1356,7 @@ cmd_devoice (struct session *sess, char *tbuf, char *word[], char *word_eol[])
static int
cmd_discon (struct session *sess, char *tbuf, char *word[], char *word_eol[])
{
	if (server_disconnect (sess->server, TRUE, -1))
	if (server_disconnect ((struct conn *)sess->server, TRUE, -1))
		notc_msg (sess);
	return TRUE;
}


@@ 1905,9 1905,9 @@ cmd_exportconf (struct session *sess, char *tbuf, char *word[], char *word_eol[]
static int
cmd_flushq (struct session *sess, char *tbuf, char *word[], char *word_eol[])
{
	sprintf (tbuf, "Flushing server send queue, %d bytes.\n", sess->server->sendq_len);
	sprintf (tbuf, "Flushing server send queue, %d bytes.\n", sess->server->c.sendq_len);
	PrintText (sess, tbuf);
	sess->server->flush_queue (sess->server);
	server_flush_queue ((struct conn *)sess->server);
	return TRUE;
}



@@ 1918,7 1918,7 @@ cmd_quit (struct session *sess, char *tbuf, char *word[], char *word_eol[])

	if (*word_eol[2])
		serv->quitreason = word_eol[2];
	if (server_disconnect (serv, TRUE, -1))
	if (server_disconnect ((struct conn *)serv, TRUE, -1))
		notc_msg (sess);
	serv->quitreason = NULL;
	return 2;


@@ 1933,13 1933,12 @@ cmd_gate (struct session *sess, char *tbuf, char *word[], char *word_eol[])
	{
		char *port = word[3];
#ifdef USE_OPENSSL
		serv->use_ssl = FALSE;
		serv->c.use_ssl = FALSE;
#endif
		server_fill_her_up (serv);
		if (*port)
			server_connect (serv, server_name, atoi (port), TRUE);
			server_connect ((struct conn *)serv, server_name, atoi (port), TRUE);
		else
			server_connect (serv, server_name, 23, TRUE);
			server_connect ((struct conn *)serv, server_name, 23, TRUE);
		return TRUE;
	}
	return FALSE;


@@ 2685,7 2684,7 @@ cmd_me (struct session *sess, char *tbuf, char *word[], char *word_eol[])
	} else
	{
		/* DCC CHAT failed, try through server */
		if (sess->server->connected)
		if (sess->server->c.connected)
		{
			while ((split_text = split_up_text (sess, act + offset, cmd_length, split_text)))
			{


@@ 2802,7 2801,7 @@ cmd_msg (struct session *sess, char *tbuf, char *word[], char *word_eol[])
				}
			} else
			{
				if (!sess->server->connected)
				if (!sess->server->c.connected)
				{
					notc_msg (sess);
					return TRUE;


@@ 2904,7 2903,7 @@ cmd_nick (struct session *sess, char *tbuf, char *word[], char *word_eol[])
	char *nick = word[2];
	if (*nick)
	{
		if (sess->server->connected)
		if (sess->server->c.connected)
			irc_change_nick (sess->server, nick);
		else
		{


@@ 3078,7 3077,7 @@ cmd_query (struct session *sess, char *tbuf, char *word[], char *word_eol[])
		{
			message_tags_data no_tags = MESSAGE_TAGS_DATA_INIT;

			if (!sess->server->connected)
			if (!sess->server->c.connected)
			{
				notc_msg (sess);
				return TRUE;


@@ 3185,8 3184,8 @@ cmd_reconnect (struct session *sess, char *tbuf, char *word[], char *word_eol[])
		while (list)
		{
			serv = list->data;
			if (serv->connected)
				serv->auto_reconnect (serv, TRUE, -1);
			if (serv->c.connected)
				irc_auto_reconnect (serv, TRUE, -1);
			list = list->next;
		}
	}


@@ 3215,20 3214,20 @@ cmd_reconnect (struct session *sess, char *tbuf, char *word[], char *word_eol[])
			use_ssl_noverify = FALSE;
			offset++;	/* args move up by 1 word */
		}
		serv->use_ssl = use_ssl;
		serv->accept_invalid_cert = use_ssl_noverify;
		serv->c.use_ssl = use_ssl;
		serv->c.accept_invalid_cert = use_ssl_noverify;
#endif

		if (*word[4+offset])
			safe_strcpy (serv->password, word[4+offset], sizeof (serv->password));
		if (*word[3+offset])
			serv->port = atoi (word[3+offset]);
		safe_strcpy (serv->hostname, word[2+offset], sizeof (serv->hostname));
		serv->auto_reconnect (serv, TRUE, -1);
			serv->c.port = atoi (word[3+offset]);
		safe_strcpy (serv->c.hostname, word[2+offset], sizeof (serv->c.hostname));
		irc_auto_reconnect (serv, TRUE, -1);
	}
	else
	{
		serv->auto_reconnect (serv, TRUE, -1);
		irc_auto_reconnect (serv, TRUE, -1);
	}
	prefs.hex_net_reconnect_delay = tmp;



@@ 3240,7 3239,7 @@ cmd_recv (struct session *sess, char *tbuf, char *word[], char *word_eol[])
{
	if (*word_eol[2])
	{
		sess->server->p_inline (sess->server, word_eol[2], strlen (word_eol[2]));
		irc_inline (sess->server, word_eol[2], strlen (word_eol[2]));
		return TRUE;
	}



@@ 3275,7 3274,7 @@ cmd_send (struct session *sess, char *tbuf, char *word[], char *word_eol[])
		/* use the one from our connected server socket */
		memset (&SAddr, 0, sizeof (struct sockaddr_in));
		len = sizeof (SAddr);
		getsockname (sess->server->sok, (struct sockaddr *) &SAddr, &len);
		getsockname (sess->server->c.sok, (struct sockaddr *) &SAddr, &len);
		addr = SAddr.sin_addr.s_addr;
	}
	addr = ntohl (addr);


@@ 3501,8 3500,8 @@ cmd_server (struct session *sess, char *tbuf, char *word[], char *word_eol[])
	}

#ifdef USE_OPENSSL
	serv->use_ssl = use_ssl;
	serv->accept_invalid_cert = use_ssl_noverify;
	serv->c.use_ssl = use_ssl;
	serv->c.accept_invalid_cert = use_ssl_noverify;
#endif

	/* try to connect by Network name */


@@ 3511,17 3510,17 @@ cmd_server (struct session *sess, char *tbuf, char *word[], char *word_eol[])

	if (*port)
	{
		server_connect (serv, server_name, atoi (port), FALSE);
		server_connect ((struct conn *)serv, server_name, atoi (port), FALSE);
	} else
	{
		/* -1 for default port */
		server_connect (serv, server_name, -1, FALSE);
		server_connect ((struct conn *)serv, server_name, -1, FALSE);
	}

	/* try to associate this connection with a listed network */
	/* may return NULL, but that's OK */
	if ((serv->network = servlist_net_find_from_server (server_name)))
		server_set_encoding (serv, ((ircnet*)serv->network)->encoding);
		server_set_encoding ((struct conn *)serv, ((ircnet*)serv->network)->encoding);

	return TRUE;
}


@@ 3673,7 3672,7 @@ find_server_from_hostname (char *hostname)
	while (list)
	{
		serv = list->data;
		if (!g_ascii_strcasecmp (hostname, serv->hostname) && serv->connected)
		if (!g_ascii_strcasecmp (hostname, serv->c.hostname) && serv->c.connected)
			return serv;
		list = list->next;
	}


@@ 3690,7 3689,7 @@ find_server_from_net (void *net)
	while (list)
	{
		serv = list->data;
		if (serv->network == net && serv->connected)
		if (serv->network == net && serv->c.connected)
			return serv;
		list = list->next;
	}


@@ 4589,7 4588,7 @@ handle_say (session *sess, char *text, int check_spch)
		}
	}

	if (sess->server->connected)
	if (sess->server->c.connected)
	{
		char *split_text = NULL;
		int cmd_length = 13; /* " PRIVMSG ", " ", :, \r, \n */


@@ 4785,7 4784,7 @@ handle_command (session *sess, char *cmd, int check_spch)

	if (int_cmd)
	{
		if (int_cmd->needserver && !sess->server->connected)
		if (int_cmd->needserver && !sess->server->c.connected)
		{
			notc_msg (sess);
		}


@@ 4808,7 4807,7 @@ handle_command (session *sess, char *cmd, int check_spch)
	}
	else
	{
		if (!sess->server->connected)
		if (!sess->server->c.connected)
		{
			PrintTextf (sess, _("Unknown Command %s. Try /help\n"), word[1]);
		}

M src/common/plugin.c => src/common/plugin.c +9 -9
@@ 923,7 923,7 @@ plugin_find_context (const char *servname, const char *channel, ircconn *current

		if (servname == NULL ||
			 rfc_casecmp (servname, serv->servername) == 0 ||
			 g_ascii_strcasecmp (servname, serv->hostname) == 0 ||
			 g_ascii_strcasecmp (servname, serv->c.hostname) == 0 ||
			 g_ascii_strcasecmp (servname, netname) == 0)
		{
			if (channel == NULL)


@@ 1109,7 1109,7 @@ hexchat_commandf (hexchat_plugin *ph, const char *format, ...)
int
hexchat_nickcmp (hexchat_plugin *ph, const char *s1, const char *s2)
{
	return ((session *)ph->context)->server->p_cmp (s1, s2);
	return ((session *)ph->context)->server->cmp_fn (s1, s2);
}

hexchat_context *


@@ 1190,8 1190,8 @@ hexchat_get_info (hexchat_plugin *ph, const char *id)
		{
			const char *locale;

			if (sess->server->encoding)
				return sess->server->encoding;
			if (sess->server->c.encoding)
				return sess->server->c.encoding;

			locale = NULL;
			g_get_charset (&locale);


@@ 1199,7 1199,7 @@ hexchat_get_info (hexchat_plugin *ph, const char *id)
		}

	case 0x30f5a8: /* host */
		return sess->server->hostname;
		return sess->server->c.hostname;

	case 0x1c0e99c1: /* inputbox */
		return fe_get_inputbox_contents (sess);


@@ 1220,7 1220,7 @@ hexchat_get_info (hexchat_plugin *ph, const char *id)
		return NULL;

	case 0xca022f43: /* server */
		if (!sess->server->connected)
		if (!sess->server->c.connected)
			return NULL;
		return sess->server->servername;



@@ 1614,8 1614,8 @@ hexchat_list_int (hexchat_plugin *ph, hexchat_list *xlist, const char *name)
		case 0xd1b:	/* id */
			return ((struct session *)data)->server->id;
		case 0x5cfee87:	/* flags */
			channel_flags[0] = ((struct session *)data)->server->connected;
			channel_flags[1] = ((struct session *)data)->server->connecting;
			channel_flags[0] = ((struct session *)data)->server->c.connected;
			channel_flags[1] = ((struct session *)data)->server->c.connecting;
			channel_flags[2] = ((struct session *)data)->server->is_away;
			channel_flags[3] = ((struct session *)data)->server->end_of_motd;
			channel_flags[4] = ((struct session *)data)->server->have_whox;


@@ 1651,7 1651,7 @@ hexchat_list_int (hexchat_plugin *ph, hexchat_list *xlist, const char *name)
		case 0x1916144c: /* maxmodes */
			return ((struct session *)data)->server->modes_per_line;
		case 0x66f1911: /* queue */
			return ((struct session *)data)->server->sendq_len;
			return ((struct session *)data)->server->c.sendq_len;
		case 0x368f3a:	/* type */
			return ((struct session *)data)->type;
		case 0x6a68e08: /* users */

M src/common/proto-irc.c => src/common/proto-irc.c +256 -98
@@ 50,20 50,80 @@
#include "servlist.h"
#include "batch.h"

static GSList *away_list = NULL;

struct away_msg *
irc_away_find_message (ircconn *serv, char *nick)
{
	struct away_msg *away;
	GSList *list = away_list;
	while (list)
	{
		away = (struct away_msg *) list->data;
		if (away->server == serv && !serv->cmp_fn (nick, away->nick))
			return away;
		list = list->next;
	}
	return NULL;
}

static void
irc_away_free_messages (ircconn *serv)
{
	GSList *list, *next;
	struct away_msg *away;

	list = away_list;
	while (list)
	{
		away = list->data;
		next = list->next;
		if (away->server == serv)
		{
			away_list = g_slist_remove (away_list, away);
			g_free (away->message);
			g_free (away);
			next = away_list;
		}
		list = next;
	}
}

void
irc_away_save_message (ircconn *serv, char *nick, char *msg)
{
	struct away_msg *away = irc_away_find_message (serv, nick);

	if (away)						  /* Change message for known user */
	{
		g_free (away->message);
		away->message = g_strdup (msg);
	}
	else
	{
		/* Create brand new entry */
		away = g_new(struct away_msg, 1);
		away->server = serv;
		safe_strcpy (away->nick, nick, sizeof (away->nick));
		away->message = g_strdup (msg);
		away_list = g_slist_prepend (away_list, away);
	}
}

static void
irc_login (ircconn *serv, char *user, char *realname)
{
	tcp_sendf (serv, "CAP LS 302\r\n");		/* start with CAP LS as Charybdis sasl.txt suggests */
	tcp_sendf ((struct conn *)serv, "CAP LS 302\r\n");		/* start with CAP LS as Charybdis sasl.txt suggests */
	serv->sent_capend = FALSE;	/* track if we have finished */

	if (serv->password[0] && serv->loginmethod == LOGIN_PASS)
	{
		tcp_sendf (serv, "PASS %s%s\r\n",
		tcp_sendf ((struct conn *)serv, "PASS %s%s\r\n",
			(serv->password[0] == ':' || strchr (serv->password, ' ')) ? ":" : "",
			serv->password);
	}

	tcp_sendf (serv,
	tcp_sendf ((struct conn *)serv,
				  "NICK %s\r\n"
				  "USER %s 0 * :%s\r\n",
				  serv->nick, user, realname);


@@ 76,24 136,24 @@ irc_nickserv (ircconn *serv, char *cmd, char *arg1, char *arg2, char *arg3)
	switch (serv->loginmethod)
	{
		case LOGIN_MSG_NICKSERV:
			tcp_sendf (serv, "PRIVMSG NICKSERV :%s %s%s%s\r\n", cmd, arg1, arg2, arg3);
			tcp_sendf ((struct conn *)serv, "PRIVMSG NICKSERV :%s %s%s%s\r\n", cmd, arg1, arg2, arg3);
			break;
		case LOGIN_NICKSERV:
			tcp_sendf (serv, "NICKSERV %s %s%s%s\r\n", cmd, arg1, arg2, arg3);
			tcp_sendf ((struct conn *)serv, "NICKSERV %s %s%s%s\r\n", cmd, arg1, arg2, arg3);
			break;
		default: /* This may not work but at least it tries something when using /id or /ghost cmd */
			tcp_sendf (serv, "NICKSERV %s %s%s%s\r\n", cmd, arg1, arg2, arg3);
			tcp_sendf ((struct conn *)serv, "NICKSERV %s %s%s%s\r\n", cmd, arg1, arg2, arg3);
			break;
#if 0
		case LOGIN_MSG_NS:
			tcp_sendf (serv, "PRIVMSG NS :%s %s%s%s\r\n", cmd, arg1, arg2, arg3);
			tcp_sendf ((struct conn *)serv, "PRIVMSG NS :%s %s%s%s\r\n", cmd, arg1, arg2, arg3);
			break;
		case LOGIN_NS:
			tcp_sendf (serv, "NS %s %s%s%s\r\n", cmd, arg1, arg2, arg3);
			tcp_sendf ((struct conn *)serv, "NS %s %s%s%s\r\n", cmd, arg1, arg2, arg3);
			break;
		case LOGIN_AUTH:
			/* why couldn't QuakeNet implement one of the existing ones? */
			tcp_sendf (serv, "AUTH %s %s\r\n", arg1, arg2);
			tcp_sendf ((struct conn *)serv, "AUTH %s %s\r\n", arg1, arg2);
			break;
#endif
	}


@@ 105,7 165,7 @@ irc_ns_identify (ircconn *serv, char *pass)
	switch (serv->loginmethod)
	{
		case LOGIN_CHALLENGEAUTH:
			tcp_sendf (serv, "PRIVMSG %s :CHALLENGE\r\n", CHALLENGEAUTH_NICK);	/* request a challenge from Q */
			tcp_sendf ((struct conn *)serv, "PRIVMSG %s :CHALLENGE\r\n", CHALLENGEAUTH_NICK);	/* request a challenge from Q */
			break;
#if 0
		case LOGIN_AUTH:


@@ 130,9 190,9 @@ void
irc_join (ircconn *serv, char *channel, char *key)
{
	if (key[0])
		tcp_sendf (serv, "JOIN %s %s\r\n", channel, key);
		tcp_sendf ((struct conn *)serv, "JOIN %s %s\r\n", channel, key);
	else
		tcp_sendf (serv, "JOIN %s\r\n", channel);
		tcp_sendf ((struct conn *)serv, "JOIN %s\r\n", channel);
}

static void


@@ 146,11 206,11 @@ irc_join_list_flush (ircconn *serv, GString *channels, GString *keys, int send_k

	if (send_keys)
	{
		tcp_sendf (serv, "JOIN %s %s\r\n", chanstr, keystr);	/* send the actual command */
		tcp_sendf ((struct conn *)serv, "JOIN %s %s\r\n", chanstr, keystr);	/* send the actual command */
	}
	else
	{
		tcp_sendf (serv, "JOIN %s\r\n", chanstr);	/* send the actual command */
		tcp_sendf ((struct conn *)serv, "JOIN %s\r\n", chanstr);	/* send the actual command */
	}

	g_free (chanstr);


@@ 233,12 293,12 @@ void
irc_part (ircconn *serv, char *channel, char *reason)
{
	if (reason[0])
		tcp_sendf (serv, "PART %s :%s\r\n", channel, reason);
		tcp_sendf ((struct conn *)serv, "PART %s :%s\r\n", channel, reason);
	else
		tcp_sendf (serv, "PART %s\r\n", channel);
		tcp_sendf ((struct conn *)serv, "PART %s\r\n", channel);
}

static void
void
irc_quit (ircconn *serv)
{
	char *reason, *colrea = NULL;


@@ 252,9 312,9 @@ irc_quit (ircconn *serv)
		reason = serv->quitreason;

	if (reason[0])
		tcp_sendf (serv, "QUIT :%s\r\n", reason);
		tcp_sendf ((struct conn *)serv, "QUIT :%s\r\n", reason);
	else
		tcp_send_len (serv, "QUIT\r\n", 6);
		tcp_send_len ((struct conn *)serv, "QUIT\r\n", 6);
	
	if (colrea)
	{


@@ 266,7 326,7 @@ irc_quit (ircconn *serv)
void
irc_set_back (ircconn *serv)
{
	tcp_send_len (serv, "AWAY\r\n", 6);
	tcp_send_len ((struct conn *)serv, "AWAY\r\n", 6);
}

void


@@ 282,46 342,46 @@ irc_set_away (ircconn *serv, char *reason)
		reason = " ";
	}

	tcp_sendf (serv, "AWAY :%s\r\n", reason);
	tcp_sendf ((struct conn *)serv, "AWAY :%s\r\n", reason);
}

void
irc_ctcp (ircconn *serv, char *to, char *msg)
{
	tcp_sendf (serv, "PRIVMSG %s :\001%s\001\r\n", to, msg);
	tcp_sendf ((struct conn *)serv, "PRIVMSG %s :\001%s\001\r\n", to, msg);
}

void
irc_nctcp (ircconn *serv, char *to, char *msg)
{
	tcp_sendf (serv, "NOTICE %s :\001%s\001\r\n", to, msg);
	tcp_sendf ((struct conn *)serv, "NOTICE %s :\001%s\001\r\n", to, msg);
}

void
irc_cycle (ircconn *serv, char *channel, char *key)
{
	tcp_sendf (serv, "PART %s\r\nJOIN %s %s\r\n", channel, channel, key);
	tcp_sendf ((struct conn *)serv, "PART %s\r\nJOIN %s %s\r\n", channel, channel, key);
}

void
irc_kick (ircconn *serv, char *channel, char *nick, char *reason)
{
	if (reason[0])
		tcp_sendf (serv, "KICK %s %s :%s\r\n", channel, nick, reason);
		tcp_sendf ((struct conn *)serv, "KICK %s %s :%s\r\n", channel, nick, reason);
	else
		tcp_sendf (serv, "KICK %s %s\r\n", channel, nick);
		tcp_sendf ((struct conn *)serv, "KICK %s %s\r\n", channel, nick);
}

void
irc_invite (ircconn *serv, char *channel, char *nick)
{
	tcp_sendf (serv, "INVITE %s %s\r\n", nick, channel);
	tcp_sendf ((struct conn *)serv, "INVITE %s %s\r\n", nick, channel);
}

void
irc_mode (ircconn *serv, char *target, char *mode)
{
	tcp_sendf (serv, "MODE %s %s\r\n", target, mode);
	tcp_sendf ((struct conn *)serv, "MODE %s %s\r\n", target, mode);
}

/* find channel info when joined */


@@ 329,16 389,16 @@ irc_mode (ircconn *serv, char *target, char *mode)
void
irc_join_info (ircconn *serv, char *channel)
{
	tcp_sendf (serv, "MODE %s\r\n", channel);
	tcp_sendf ((struct conn *)serv, "MODE %s\r\n", channel);
}

void
irc_who (ircconn *serv, char *channel)
{
	if (serv->have_whox)
		tcp_sendf (serv, "WHO %s %%chtsunfra,152\r\n", channel);
		tcp_sendf ((struct conn *)serv, "WHO %s %%chtsunfra,152\r\n", channel);
	else
		tcp_sendf (serv, "WHO %s\r\n", channel);
		tcp_sendf ((struct conn *)serv, "WHO %s\r\n", channel);
}

/* userhost */


@@ 346,7 406,7 @@ irc_who (ircconn *serv, char *channel)
void
irc_userhost (ircconn *serv, char *nick)
{
	tcp_sendf (serv, "USERHOST %s\r\n", nick);
	tcp_sendf ((struct conn *)serv, "USERHOST %s\r\n", nick);
}




@@ 357,36 417,36 @@ irc_userhost (ircconn *serv, char *nick)
void
irc_user_whois (ircconn *serv, char *nicks)
{
	tcp_sendf (serv, "WHOIS %s\r\n", nicks);
	tcp_sendf ((struct conn *)serv, "WHOIS %s\r\n", nicks);
}

void
irc_message (ircconn *serv, char *channel, char *text)
{
	tcp_sendf (serv, "PRIVMSG %s :%s\r\n", channel, text);
	tcp_sendf ((struct conn *)serv, "PRIVMSG %s :%s\r\n", channel, text);
}

void
irc_action (ircconn *serv, char *channel, char *act)
{
	tcp_sendf (serv, "PRIVMSG %s :\001ACTION %s\001\r\n", channel, act);
	tcp_sendf ((struct conn *)serv, "PRIVMSG %s :\001ACTION %s\001\r\n", channel, act);
}

void
irc_notice (ircconn *serv, char *channel, char *text)
{
	tcp_sendf (serv, "NOTICE %s :%s\r\n", channel, text);
	tcp_sendf ((struct conn *)serv, "NOTICE %s :%s\r\n", channel, text);
}

void
irc_topic (ircconn *serv, char *channel, char *topic)
{
	if (!topic)
		tcp_sendf (serv, "TOPIC %s :\r\n", channel);
		tcp_sendf ((struct conn *)serv, "TOPIC %s :\r\n", channel);
	else if (topic[0])
		tcp_sendf (serv, "TOPIC %s :%s\r\n", channel, topic);
		tcp_sendf ((struct conn *)serv, "TOPIC %s :%s\r\n", channel, topic);
	else
		tcp_sendf (serv, "TOPIC %s\r\n", channel);
		tcp_sendf ((struct conn *)serv, "TOPIC %s\r\n", channel);
}

void


@@ 394,35 454,35 @@ irc_list_channels (ircconn *serv, char *arg, int min_users)
{
	if (arg[0])
	{
		tcp_sendf (serv, "LIST %s\r\n", arg);
		tcp_sendf ((struct conn *)serv, "LIST %s\r\n", arg);
		return;
	}

	if (serv->use_listargs)
		tcp_sendf (serv, "LIST >%d,<10000\r\n", min_users - 1);
		tcp_sendf ((struct conn *)serv, "LIST >%d,<10000\r\n", min_users - 1);
	else
		tcp_send_len (serv, "LIST\r\n", 6);
		tcp_send_len ((struct conn *)serv, "LIST\r\n", 6);
}

void
irc_names (ircconn *serv, char *channel)
{
	tcp_sendf (serv, "NAMES %s\r\n", channel);
	tcp_sendf ((struct conn *)serv, "NAMES %s\r\n", channel);
}

void
irc_change_nick (ircconn *serv, char *new_nick)
{
	tcp_sendf (serv, "NICK %s\r\n", new_nick);
	tcp_sendf ((struct conn *)serv, "NICK %s\r\n", new_nick);
}

void
irc_ping (ircconn *serv, char *to, char *timestring)
{
	if (*to)
		tcp_sendf (serv, "PRIVMSG %s :\001PING %s\001\r\n", to, timestring);
		tcp_sendf ((struct conn *)serv, "PRIVMSG %s :\001PING %s\001\r\n", to, timestring);
	else
		tcp_sendf (serv, "PING %s\r\n", timestring);
		tcp_sendf ((struct conn *)serv, "PING %s\r\n", timestring);
}

int


@@ 436,11 496,11 @@ irc_raw (ircconn *serv, char *raw)
		if (len < sizeof (tbuf) - 3)
		{
			len = g_snprintf (tbuf, sizeof (tbuf), "%s\r\n", raw);
			tcp_send_len (serv, tbuf, len);
			tcp_send_len ((struct conn *)serv, tbuf, len);
		} else
		{
			tcp_send_len (serv, raw, len);
			tcp_send_len (serv, "\r\n", 2);
			tcp_send_len ((struct conn *)serv, raw, len);
			tcp_send_len ((struct conn *)serv, "\r\n", 2);
		}
		return TRUE;
	}


@@ 531,7 591,7 @@ process_numeric (session * sess, int n,
			if (eq)
			{
				*eq = 0;
				if (!serv->p_cmp (word[4] + 1, serv->nick))
				if (!serv->cmp_fn (word[4] + 1, serv->nick))
				{
					char *at = strrchr (eq + 1, '@');
					if (at)


@@ 953,7 1013,7 @@ process_numeric (session * sess, int n,
		if (!serv->sent_capend)
		{
			serv->sent_capend = TRUE;
			tcp_send_len (serv, "CAP END\r\n", 9);
			tcp_send_len ((struct conn *)serv, "CAP END\r\n", 9);
		}
		break;
	case 908:	/* Supported SASL Mechs */


@@ 1046,7 1106,7 @@ process_named_msg (session *sess, char *type, char *word[], char *word_eol[],
					realname++;
				if (*chan == ':')
					chan++;
				if (!serv->p_cmp (nick, serv->nick))
				if (!serv->cmp_fn (nick, serv->nick))
					inbound_ujoin (serv, chan, nick, ip, tags_data);
				else
					inbound_join (serv, chan, nick, ip, account, realname,


@@ 1198,7 1258,7 @@ process_named_msg (session *sess, char *type, char *word[], char *word_eol[],
				return;

			text = STRIP_COLON(word, word_eol, 4);
			if (serv->p_cmp (word[3], serv->nick))
			if (serv->cmp_fn (word[3], serv->nick))
				EMIT_SIGNAL_TAGS (XP_TE_INVITEDOTHER, sess, text, nick,
						  word[3], serv->servername, 0, tags_data);
			else


@@ 1217,7 1277,7 @@ process_named_msg (session *sess, char *type, char *word[], char *word_eol[],

#ifdef USE_OPENSSL
				/* QuakeNet CHALLENGE upon our request */
				if (serv->loginmethod == LOGIN_CHALLENGEAUTH && !serv->p_cmp (word[1], CHALLENGEAUTH_FULLHOST)
				if (serv->loginmethod == LOGIN_CHALLENGEAUTH && !serv->cmp_fn (word[1], CHALLENGEAUTH_FULLHOST)
				    && !strncmp (text, "CHALLENGE ", 10) && *serv->password)
				{
					char *response;


@@ 1226,7 1286,7 @@ process_named_msg (session *sess, char *type, char *word[], char *word_eol[],

					response = challengeauth_response (user, serv->password, word[5]);

					tcp_sendf (serv, "PRIVMSG %s :CHALLENGEAUTH %s %s %s\r\n",
					tcp_sendf ((struct conn *)serv, "PRIVMSG %s :CHALLENGEAUTH %s %s %s\r\n",
						CHALLENGEAUTH_NICK,
						user,
						response,


@@ 1563,7 1623,7 @@ handle_message_tags (ircconn *serv, const char *tags_str,
}

/* irc_inline() - 1 single line received from serv */
static void
void
irc_inline (ircconn *serv, char *buf, int len)
{
	session *sess, *tmp;


@@ 1692,7 1752,7 @@ ircconn_set_name (ircconn *serv, char *name)
	session *sess;

	if (name[0] == 0)
		name = serv->hostname;
		name = serv->c.hostname;

	/* strncpy parameters must NOT overlap */
	if (name != serv->servername)


@@ 1725,6 1785,9 @@ ircconn_set_name (ircconn *serv, char *name)
static void
irc_connected (ircconn *serv)
{
	serv->ping_recv = time (0);
	serv->lag_sent = 0;

	{ /* FIXME: UGLY HACK!! should be handled with a hook or something. */
		struct sockaddr_storage addr;
		int addr_len = sizeof (addr);


@@ 1732,7 1795,7 @@ irc_connected (ircconn *serv)
		ircnet *net = serv->network;
		char outbuf[512];

		if (!getsockname (serv->sok, (struct sockaddr *)&addr, &addr_len))
		if (!getsockname (serv->c.sok, (struct sockaddr *)&addr, &addr_len))
		{
			if (addr.ss_family == AF_INET)
				port = ntohs(((struct sockaddr_in *)&addr)->sin_port);


@@ 1785,6 1848,11 @@ irc_disconnected (ircconn *serv, int shutup, int err)
	GSList *list;
	session *sess;

	serv->motd_skipped = FALSE;
	serv->no_login = FALSE;
	serv->servername[0] = 0;
	serv->lag_sent = 0;

	fe_server_event (serv, FE_SE_DISCONNECT, 0);
	
	list = sess_list;


@@ 1811,29 1879,30 @@ irc_ssl_message (ircconn *serv, const char *msg)
}

static void
irc_serverlookup (ircconn *serv, const char *msg)
irc_serverlookup (ircconn *serv, const char *hostname)
{
	fe_progressbar_start (serv->server_session);
	EMIT_SIGNAL (XP_TE_SERVERLOOKUP, serv->server_session, (char *)msg, NULL, NULL, NULL, 0);
	EMIT_SIGNAL (XP_TE_SERVERLOOKUP, serv->server_session, (char *)hostname, NULL, NULL, NULL, 0);
	safe_strcpy (serv->servername, hostname, sizeof (serv->servername));
}

static int
timeout_auto_reconnect (ircconn *serv)
{
	if (is_server (serv))  /* make sure it hasnt been closed during the delay */
	if (is_server (&serv->c))  /* make sure it hasnt been closed during the delay */
	{
		serv->recondelay_tag = 0;
							  // afaict this check will never fail
		if (!serv->connected && !serv->connecting /*&& serv->server_session*/)
		if (!serv->c.connected && !serv->c.connecting /*&& serv->server_session*/)
		{
			server_connect (serv, serv->hostname, serv->port, FALSE);
			server_connect ((struct conn *)serv, serv->c.hostname, serv->c.port, FALSE);
		}
	}
	return 0;			  /* returning 0 should remove the timeout handler */
}

static void
auto_reconnect (ircconn *serv, int send_quit, int err)
void
irc_auto_reconnect (ircconn *serv, int send_quit, int err)
{
	session *s;
	int del;


@@ 1857,8 1926,8 @@ auto_reconnect (ircconn *serv, int send_quit, int err)
		}
	}

	if (serv->connected)
		server_disconnect (serv, send_quit, err);
	if (serv->c.connected)
		server_disconnect ((struct conn *)serv, send_quit, err);

	del = prefs.hex_net_reconnect_delay * 1000;
	if (del < 1000)


@@ 1889,7 1958,7 @@ irc_connfail (ircconn *serv, const char *msg)
	
	if (!servlist_cycle (serv))
		if (prefs.hex_net_auto_reconnectonfail)
			auto_reconnect (serv, FALSE, -1);
			irc_auto_reconnect (serv, FALSE, -1);
}

static void


@@ 1916,7 1985,7 @@ irc_unknhost (ircconn *serv)

	if (!servlist_cycle (serv))
		if (prefs.hex_net_auto_reconnectonfail)
			auto_reconnect (serv, FALSE, -1);
			irc_auto_reconnect (serv, FALSE, -1);
}

static void


@@ 1924,18 1993,18 @@ irc_readerr (ircconn *serv, int error)
{
	if (!serv->end_of_motd)
	{
		server_disconnect (serv, FALSE, error);
		server_disconnect ((struct conn *)serv, FALSE, error);
		if (!servlist_cycle (serv))
		{
			if (prefs.hex_net_auto_reconnect)
				auto_reconnect (serv, FALSE, error);
				irc_auto_reconnect (serv, FALSE, error);
		}
	} else
	{
		if (prefs.hex_net_auto_reconnect)
			auto_reconnect (serv, FALSE, error);
			irc_auto_reconnect (serv, FALSE, error);
		else
			server_disconnect (serv, FALSE, error);
			server_disconnect ((struct conn *)serv, FALSE, error);
	}
}



@@ 1945,7 2014,7 @@ irc_serverlog (ircconn *serv, const char *msg)
	PrintText (serv->server_session, (char *)msg);
}

static void
static gboolean
irc_cleanup (ircconn *serv)
{
	if (serv->joindelay_tag)


@@ 1956,6 2025,15 @@ irc_cleanup (ircconn *serv)
	
	serv->end_of_motd = FALSE;
	fe_set_lag (serv, 0);
	
	if (!serv->c.connecting && !serv->c.connected && serv->recondelay_tag)
	{
		g_source_remove (serv->recondelay_tag);
		serv->recondelay_tag = 0;
		return TRUE; // shutup
	}
	
	return FALSE;
}

static void


@@ 1971,30 2049,110 @@ irc_stopconnect (ircconn *serv)
	fe_server_event (serv, FE_SE_DISCONNECT, 0);
}

static void
irc_reset (ircconn *serv)
{
	g_free (serv->chantypes);
	g_free (serv->chanmodes);
	g_free (serv->nick_prefixes);
	g_free (serv->nick_modes);

	serv->chantypes = g_strdup ("#&!+");
	serv->chanmodes = g_strdup ("beI,k,l");
	serv->nick_prefixes = g_strdup ("@%+");
	serv->nick_modes = g_strdup ("ohv");
	serv->modes_per_line = 3; /* https://datatracker.ietf.org/doc/html/rfc1459#section-4.2.3.1 */
	serv->sasl_mech = MECH_PLAIN;
	
	serv->nickcount = 1;
	serv->end_of_motd = FALSE;
	serv->sent_capend = FALSE;
	serv->use_listargs = FALSE;
	serv->is_away = FALSE;
	serv->supports_watch = FALSE;
	serv->supports_monitor = FALSE;
	serv->bad_prefix = FALSE;
	serv->use_who = TRUE;
	serv->have_namesx = FALSE;
	serv->have_awaynotify = FALSE;
	serv->have_uhnames = FALSE;
	serv->have_whox = FALSE;
	serv->have_idmsg = FALSE;
	serv->have_accnotify = FALSE;
	serv->have_extjoin = FALSE;
	serv->have_account_tag = FALSE;
	serv->have_echo_message = FALSE;
	serv->have_message_tags = FALSE;
	serv->have_server_time = FALSE;
	serv->have_sasl = FALSE;
	serv->have_except = FALSE;
	serv->have_invite = FALSE;
}

static void
irc_fill_her_up (ircconn *serv)
{
	serv->c.p_connected =		(void (*)(struct conn *))irc_connected;
	serv->c.p_disconnected =	(void (*)(struct conn *, int, int))irc_disconnected;
	serv->c.p_ssl_message =		(void (*)(struct conn *, const char *))irc_ssl_message;
	serv->c.p_serverlookup =	(void (*)(struct conn *, const char *))irc_serverlookup;
	serv->c.p_connfail =		(void (*)(struct conn *, const char *))irc_connfail;
	serv->c.p_connstop =		(void (*)(struct conn *, const char *))irc_connstop;
	serv->c.p_connecting =		(void (*)(struct conn *, const char *, const char *, const char *))irc_connecting;
	serv->c.p_unknhost =		(void (*)(struct conn *))irc_unknhost;
	serv->c.p_readerr =		(void (*)(struct conn *, int))irc_readerr;
	serv->c.p_log =			(void (*)(struct conn *, const char *))irc_serverlog;
	serv->c.p_cleanup =		(gboolean (*)(struct conn *))irc_cleanup;
	serv->c.p_stopconnect =		(void (*)(struct conn *))irc_stopconnect;
	serv->c.p_rawlog =		(void (*)(struct conn *, char *, int,  int))fe_add_rawlog;
	serv->c.p_throttle_update =	(void (*)(struct conn *))fe_set_throttle;
	serv->c.p_get_network =		(char * (*)(struct conn *, gboolean))ircconn_get_network;
	serv->c.p_quit =		(void (*)(struct conn *))irc_quit;
	serv->c.p_inline =		(void (*)(struct conn *, char *, int))irc_inline;
	serv->c.p_reset =		(void (*)(struct conn *))irc_reset;

	serv->cmp_fn =			rfc_casecmp;	/* can be changed by 005 in modes.c */
}
ircconn *
ircconn_new (void)
{
	ircconn *serv;
	static int id = 0;


	serv = g_new0 (struct ircconn, 1);
	serv->id = id++;

	conn_fill (&serv->c);	// calls irc_reset
	irc_fill_her_up (serv);

	strcpy (serv->nick, prefs.hex_irc_nick1);

	fe_new_server (serv);

	return serv;
}

void
proto_fill_her_up (ircconn *serv)
ircconn_free (ircconn *conn)
{
	serv->auto_reconnect = auto_reconnect;

	serv->p_connected = irc_connected;
	serv->p_disconnected = irc_disconnected;
	serv->p_ssl_message = irc_ssl_message;
	serv->p_serverlookup = irc_serverlookup;
	serv->p_connfail = irc_connfail;
	serv->p_connstop = irc_connstop;
	serv->p_connecting = irc_connecting;
	serv->p_unknhost = irc_unknhost;
	serv->p_readerr = irc_readerr;
	serv->p_log = irc_serverlog;
	serv->p_cleanup = irc_cleanup;
	serv->p_stopconnect = irc_stopconnect;

	serv->p_rawlog = fe_add_rawlog;
	serv->p_throttle_update = fe_set_throttle;

	serv->p_get_network = ircconn_get_network;

	serv->p_quit = irc_quit;
	serv->p_inline = irc_inline;
	serv->p_cmp = rfc_casecmp;	/* can be changed by 005 in modes.c */
	dcc_notify_kill (conn);

	irc_away_free_messages (conn);

	g_free (conn->nick_modes);
	g_free (conn->nick_prefixes);
	g_free (conn->chanmodes);
	g_free (conn->chantypes);
	g_free (conn->bad_nick_prefixes);
	g_free (conn->last_away_reason);
	
	if (conn->favlist)
		g_slist_free_full (conn->favlist, (GDestroyNotify) servlist_favchan_free);

	fe_server_callback (conn);
	
	conn_free ((struct conn *)conn);

	notify_cleanup ();
}

M src/common/proto-irc.h => src/common/proto-irc.h +10 -1
@@ 49,7 49,10 @@ typedef struct

void message_tags_data_free (message_tags_data *tags_data);

void proto_fill_her_up (ircconn *serv);
ircconn *ircconn_new (void);
void ircconn_free (ircconn *);
char *ircconn_get_network (ircconn *serv, gboolean fallback);
void ircconn_set_name (ircconn *serv, char *name);

void irc_invite (ircconn *serv, char *channel, char *nick);
void irc_cycle (ircconn *serv, char *channel, char *key);


@@ 77,5 80,11 @@ void irc_mode (ircconn *, char *target, char *mode);
void irc_join (ircconn *serv, char *channel, char *key);
void irc_ctcp (ircconn *serv, char *to, char *msg);
void irc_nctcp (ircconn *serv, char *to, char *msg);
void irc_auto_reconnect (ircconn *serv, int send_quit, int err);
void irc_inline (ircconn *serv, char *buf, int len);
void irc_quit (ircconn *serv);

void irc_away_save_message (ircconn *serv, char *nick, char *msg);
struct away_msg *irc_away_find_message (ircconn *serv, char *nick);

#endif

M src/common/server.c => src/common/server.c +48 -186
@@ 41,7 41,6 @@
#endif

#include "hexchat.h"
#include "fe.h"
#include "cfgfiles.h"
#include "network.h"
#include "notify.h"


@@ 62,13 61,13 @@

#ifdef USE_OPENSSL
/* local variables */
static struct ircconn *g_serv = NULL;
static struct conn *g_serv = NULL;
#endif

static GSList *away_list = NULL;
GSList *serv_list = NULL;

static int  server_cleanup (ircconn * serv);
static int server_cleanup (conn *serv);
static void server_set_defaults (conn *serv);

static void
write_error (char *message, GError **error)


@@ 104,13 103,13 @@ tcp_send_real (void *ssl, int sok, GIConv write_converter, char *buf, int len)
}

static int
server_send_real (ircconn *serv, char *buf, int len)
server_send_real (conn *conn, char *buf, int len)
{
	serv->p_rawlog (serv, buf, len, TRUE);
	conn->p_rawlog (conn, buf, len, TRUE);

	url_check_line (buf);

	return tcp_send_real (serv->ssl, serv->sok, serv->write_converter, buf, len);
	return tcp_send_real (conn->ssl, conn->sok, conn->write_converter, buf, len);
}

/* new throttling system, uses the same method as the Undernet


@@ 118,7 117,7 @@ server_send_real (ircconn *serv, char *buf, int len)
   off the client */

static int
tcp_send_queue (ircconn *serv)
tcp_send_queue (conn *serv)
{
	char *buf, *p;
	int len, i, pri;


@@ 177,12 176,13 @@ tcp_send_queue (ircconn *serv)
}

int
tcp_send_len (ircconn *serv, char *buf, int len)
tcp_send_len (conn *serv, char *buf, int len)
{
	char *dbuf;
	int noqueue = !serv->outbound_queue;

	if (!prefs.hex_net_throttle || !serv->end_of_motd || g_ascii_strncasecmp (buf, "PONG", 4) == 0)
	if (!prefs.hex_net_throttle || // should wait on login, need to add a conn.proto_ready
	    g_ascii_strncasecmp (buf, "PONG", 4) == 0 || g_ascii_strncasecmp (buf, "CAP", 3) == 0)
		return server_send_real (serv, buf, len);

	dbuf = g_malloc (len + 2);	/* first byte is the priority */


@@ 239,11 239,11 @@ keep_priority:
}

void
tcp_sendf (ircconn *serv, const char *fmt, ...)
tcp_sendf (conn *serv, const char *fmt, ...)
{
	va_list args;
	/* keep this buffer in BSS. Converting UTF-8 to ISO-8859-x might make the
      string shorter, so allow alot more than 512 for now. */
	   string shorter, so allow alot more than 512 for now. */
	static char send_buf[1540];	/* good code hey (no it's not overflowable) */
	int len;



@@ 275,7 275,7 @@ close_socket (int sok)
/* handle 1 line of text received from the server */

static void
server_inline (ircconn *serv, char *line, gssize len)
server_inline (conn *serv, char *line, gssize len)
{
	gsize len_utf8;
	if (!strcmp (serv->encoding, "UTF-8"))


@@ 294,7 294,7 @@ server_inline (ircconn *serv, char *line, gssize len)
/* read data from socket */

static gboolean
server_read (GIOChannel *source, GIOCondition condition, ircconn *serv)
server_read (GIOChannel *source, GIOCondition condition, conn *serv)
{
	int sok = serv->sok;
	int error, i, len;


@@ 354,11 354,9 @@ server_read (GIOChannel *source, GIOCondition condition, ircconn *serv)
}

static void
server_connected (ircconn *serv)
server_connected (conn *serv)
{
	prefs.wait_on_exit = TRUE;
	serv->ping_recv = time (0);
	serv->lag_sent = 0;
	prefs.wait_on_exit = TRUE; // TODO: ???
	serv->connected = TRUE;
	set_nonblocking (serv->sok);
	serv->iotag = net_input_add (serv->sok, NIA_READ|NIA_EX, server_read, serv);


@@ 380,7 378,7 @@ server_close_pipe (int *pipefd)	/* see comments below */
#endif

static void
server_stopconnecting (ircconn * serv)
server_stopconnecting (conn * serv)
{
	if (serv->iotag)
	{


@@ 461,7 459,7 @@ ssl_cb_verify (int ok, X509_STORE_CTX *ctx)
}

static int
ssl_do_connect (ircconn *serv)
ssl_do_connect (conn *serv)
{
	char buf[256]; // ERR_error_string() MUST have this size



@@ 615,10 613,10 @@ conn_fail:
}
#endif

static void
server_flush_queue (ircconn *serv)
void
server_flush_queue (conn *serv)
{
	list_free (&serv->outbound_queue);
	list_free (&serv->outbound_queue); //FIXME: memory leak?
	serv->sendq_len = 0;
	serv->p_throttle_update (serv);
}


@@ 626,7 624,7 @@ server_flush_queue (ircconn *serv)
/* connect() successed */

static void
server_connect_success (ircconn *serv)
server_connect_success (conn *serv)
{
#ifdef USE_OPENSSL
#define	SSLDOCONNTMOUT	300


@@ 661,7 659,7 @@ server_connect_success (ircconn *serv)
/* receive info from the child-process about connection progress */

static gboolean
server_read_child (GIOChannel *source, GIOCondition condition, ircconn *serv)
server_read_child (GIOChannel *source, GIOCondition condition, conn *serv)
{
	char tbuf[128];
	char outbuf[512];


@@ 750,9 748,11 @@ server_read_child (GIOChannel *source, GIOCondition condition, ircconn *serv)
   disconnect if already connected. */

static int
server_cleanup (ircconn * serv)
server_cleanup (conn *serv)
{
	serv->p_cleanup (serv);
	gboolean shutup;
	
	shutup = serv->p_cleanup (serv);

	if (serv->iotag)
	{


@@ 790,20 790,12 @@ server_cleanup (ircconn * serv)
		serv->connected = FALSE;
		return 2;
	}

	/* is this server in a reconnect delay? remove it! */
	if (serv->recondelay_tag)
	{
		g_source_remove (serv->recondelay_tag);
		serv->recondelay_tag = 0;
		return 3;
	}
	
	return 0;
	return shutup ? 3 : 0;
}

int
server_disconnect (ircconn *serv, int sendquit, int err)
server_disconnect (conn *serv, int sendquit, int err)
{
	char tbuf[64];
	gboolean shutup = FALSE;


@@ 832,10 824,6 @@ server_disconnect (ircconn *serv, int sendquit, int err)
	serv->p_disconnected (serv, shutup, err);

	serv->pos = 0;
	serv->motd_skipped = FALSE;
	serv->no_login = FALSE;
	serv->servername[0] = 0;
	serv->lag_sent = 0;

	notify_cleanup ();
	


@@ 1160,7 1148,7 @@ traverse_proxy (int proxy_type, int print_fd, int sok, char *ip, int port, netst
/* this is the child process making the connection attempt */

static int
server_child (ircconn * serv)
server_child (conn *serv)
{
	netstore *ns_server;
	netstore *ns_proxy = NULL;


@@ 1348,7 1336,7 @@ xit:
}

void
server_connect (ircconn *serv, char *hostname, int port, int no_login)
server_connect (conn *serv, char *hostname, int port, int no_login)
{
	int pid, read_des[2];



@@ 1377,12 1365,11 @@ server_connect (ircconn *serv, char *hostname, int port, int no_login)
		g_debug ("Attempted to connect to invalid port, assuming default port %d", port);
	}

	if (serv->connected || serv->connecting || serv->recondelay_tag)
	if (serv->connected || serv->connecting) // || serv->recondelay_tag) FIXME
		server_disconnect (serv, TRUE, -1);

	serv->p_serverlookup (serv, hostname);

	safe_strcpy (serv->servername, hostname, sizeof (serv->servername));
	/* overlap illegal in strncpy */
	if (hostname != serv->hostname)
		safe_strcpy (serv->hostname, hostname, sizeof (serv->hostname));


@@ 1418,7 1405,7 @@ server_connect (ircconn *serv, char *hostname, int port, int no_login)
	server_set_defaults (serv);
	serv->connecting = TRUE;
	serv->port = port;
	serv->no_login = no_login;
	// serv->no_login = no_login; FIXME

	server_flush_queue (serv);



@@ 1474,16 1461,7 @@ server_connect (ircconn *serv, char *hostname, int port, int no_login)
}

void
server_fill_her_up (ircconn *serv)
{
	serv->cleanup = server_cleanup;
	serv->flush_queue = server_flush_queue;

	proto_fill_her_up (serv);
}

void
server_set_encoding (ircconn *serv, char *new_encoding)
server_set_encoding (conn *serv, char *new_encoding)
{
	char *space;



@@ 1529,162 1507,50 @@ server_set_encoding (ircconn *serv, char *new_encoding)
	serv->write_converter = g_iconv_open (serv->encoding, "UTF-8");
}

ircconn *
ircconn_new (void)
static void
server_set_defaults (conn *serv)
{
	static int id = 0;
	ircconn *serv;

	serv = g_new0 (struct ircconn, 1);
	serv->p_reset (serv);

	/* use server.c and proto-irc.c functions */
	server_fill_her_up (serv);

	serv->id = id++;
	serv->sok = -1;
	strcpy (serv->nick, prefs.hex_irc_nick1);
	server_set_defaults (serv);

	serv_list = g_slist_prepend (serv_list, serv);

	fe_new_server (serv);

	return serv;
}

int
is_server (ircconn *serv)
{
	return g_slist_find (serv_list, serv) ? 1 : 0;
}

void
server_set_defaults (ircconn *serv)
{
	g_free (serv->chantypes);
	g_free (serv->chanmodes);
	g_free (serv->nick_prefixes);
	g_free (serv->nick_modes);
#ifdef USE_OPENSSL
        g_clear_pointer (&serv->scram_session, scram_session_free);
#endif
	serv->chantypes = g_strdup ("#&!+");
	serv->chanmodes = g_strdup ("beI,k,l");
	serv->nick_prefixes = g_strdup ("@%+");
	serv->nick_modes = g_strdup ("ohv");
	serv->modes_per_line = 3; /* https://datatracker.ietf.org/doc/html/rfc1459#section-4.2.3.1 */
	serv->sasl_mech = MECH_PLAIN;

	if (!serv->encoding)
		server_set_encoding (serv, "UTF-8");

	serv->nickcount = 1;
	serv->end_of_motd = FALSE;
	serv->sent_capend = FALSE;
	serv->use_listargs = FALSE;
	serv->is_away = FALSE;
	serv->supports_watch = FALSE;
	serv->supports_monitor = FALSE;
	serv->bad_prefix = FALSE;
	serv->use_who = TRUE;
	serv->have_namesx = FALSE;
	serv->have_awaynotify = FALSE;
	serv->have_uhnames = FALSE;
	serv->have_whox = FALSE;
	serv->have_idmsg = FALSE;
	serv->have_accnotify = FALSE;
	serv->have_extjoin = FALSE;
	serv->have_account_tag = FALSE;
	serv->have_echo_message = FALSE;
	serv->have_message_tags = FALSE;
	serv->have_server_time = FALSE;
	serv->have_sasl = FALSE;
	serv->have_except = FALSE;
	serv->have_invite = FALSE;
}

struct away_msg *
server_away_find_message (ircconn *serv, char *nick)
void
conn_fill (conn * conn)
{
	struct away_msg *away;
	GSList *list = away_list;
	while (list)
	{
		away = (struct away_msg *) list->data;
		if (away->server == serv && !serv->p_cmp (nick, away->nick))
			return away;
		list = list->next;
	}
	return NULL;
}
	conn->sok = -1;
	server_set_defaults (conn);

static void
server_away_free_messages (ircconn *serv)
{
	GSList *list, *next;
	struct away_msg *away;
	serv_list = g_slist_prepend (serv_list, conn);

	list = away_list;
	while (list)
	{
		away = list->data;
		next = list->next;
		if (away->server == serv)
		{
			away_list = g_slist_remove (away_list, away);
			g_free (away->message);
			g_free (away);
			next = away_list;
		}
		list = next;
	}
}

void
server_away_save_message (ircconn *serv, char *nick, char *msg)
int
is_server (conn *serv)
{
	struct away_msg *away = server_away_find_message (serv, nick);

	if (away)						  /* Change message for known user */
	{
		g_free (away->message);
		away->message = g_strdup (msg);
	}
	else
	{
		/* Create brand new entry */
		away = g_new(struct away_msg, 1);
		away->server = serv;
		safe_strcpy (away->nick, nick, sizeof (away->nick));
		away->message = g_strdup (msg);
		away_list = g_slist_prepend (away_list, away);
	}
	return g_slist_find (serv_list, serv) ? 1 : 0;
}

void
server_free (ircconn *serv)
conn_free (conn *serv)
{
	serv->cleanup (serv);
	server_cleanup (serv);

	serv_list = g_slist_remove (serv_list, serv);

	dcc_notify_kill (serv);
	serv->flush_queue (serv);
	server_away_free_messages (serv);
	server_flush_queue (serv);

	g_free (serv->nick_modes);
	g_free (serv->nick_prefixes);
	g_free (serv->chanmodes);
	g_free (serv->chantypes);
	g_free (serv->bad_nick_prefixes);
	g_free (serv->last_away_reason);
	g_free (serv->encoding);

	g_iconv_close (serv->read_converter);
	g_iconv_close (serv->write_converter);

	if (serv->favlist)
		g_slist_free_full (serv->favlist, (GDestroyNotify) servlist_favchan_free);
#ifdef USE_OPENSSL
	if (serv->ctx)
		_SSL_context_free (serv->ctx);


@@ 1692,9 1558,5 @@ server_free (ircconn *serv)
        g_clear_pointer (&serv->scram_session, scram_session_free);
#endif

	fe_server_callback (serv);

	g_free (serv);

	notify_cleanup ();
}

M src/common/server.h => src/common/server.h +9 -16
@@ 23,24 23,17 @@
extern GSList *serv_list;

/* eventually need to keep the tcp_* functions isolated to server.c */
int tcp_send_len (ircconn *serv, char *buf, int len);
void tcp_sendf (ircconn *serv, const char *fmt, ...) G_GNUC_PRINTF (2, 3);
int tcp_send_len (conn *serv, char *buf, int len);
void tcp_sendf (conn *serv, const char *fmt, ...) G_GNUC_PRINTF (2, 3);
int tcp_send_real (void *ssl, int sok, GIConv write_converter, char *buf, int len);

ircconn *ircconn_new (void);
int is_server (ircconn *serv);
void server_fill_her_up (ircconn *serv);
void server_set_encoding (ircconn *serv, char *new_encoding);
void server_set_defaults (ircconn *serv);
void server_free (ircconn *serv);
void server_connect (ircconn *serv, char *hostname, int port, int no_login);
int server_disconnect (ircconn *serv, int sendquit, int err);

char *ircconn_get_network (ircconn *serv, gboolean fallback);
void ircconn_set_name (ircconn *serv, char *name);

void server_away_save_message (ircconn *serv, char *nick, char *msg);
struct away_msg *server_away_find_message (ircconn *serv, char *nick);
void conn_fill (conn *conn);
int is_server (conn *serv);
void server_set_encoding (conn *serv, char *new_encoding);
void conn_free (conn *serv);
void server_connect (conn *serv, char *hostname, int port, int no_login);
int server_disconnect (conn *serv, int sendquit, int err);
void server_flush_queue (conn *serv);

void base64_encode (char *to, char *from, unsigned int len);


M src/common/servlist.c => src/common/servlist.c +8 -11
@@ 369,9 369,6 @@ servlist_connect (session *sess, ircnet *net, gboolean join)
		return;
	ircserv = list->data;

	/* in case a protocol switch is added to the servlist gui */
	server_fill_her_up (sess->server);

	if (join)
	{
		sess->willjoinchannel[0] = 0;


@@ 412,11 409,11 @@ servlist_connect (session *sess, ircnet *net, gboolean join)
			strcpy (serv->nick, net->nick);
	}

	serv->dont_use_proxy = (net->flags & FLAG_USE_PROXY) ? FALSE : TRUE;
	serv->c.dont_use_proxy = (net->flags & FLAG_USE_PROXY) ? FALSE : TRUE;

#ifdef USE_OPENSSL
	serv->use_ssl = (net->flags & FLAG_USE_SSL) ? TRUE : FALSE;
	serv->accept_invalid_cert =
	serv->c.use_ssl = (net->flags & FLAG_USE_SSL) ? TRUE : FALSE;
	serv->c.accept_invalid_cert =
		(net->flags & FLAG_ALLOW_INVALID) ? TRUE : FALSE;
#endif



@@ 431,19 428,19 @@ servlist_connect (session *sess, ircnet *net, gboolean join)
		if (port[1] == '+')
		{
#ifdef USE_OPENSSL
			serv->use_ssl = TRUE;
			serv->c.use_ssl = TRUE;
#endif
			server_connect (serv, ircserv->hostname, atoi (port + 2), FALSE);
			server_connect ((struct conn *)serv, ircserv->hostname, atoi (port + 2), FALSE);
		} else
		{
			server_connect (serv, ircserv->hostname, atoi (port + 1), FALSE);
			server_connect ((struct conn *)serv, ircserv->hostname, atoi (port + 1), FALSE);
		}

		*port = '/';
	} else
		server_connect (serv, ircserv->hostname, -1, FALSE);
		server_connect ((struct conn *)serv, ircserv->hostname, -1, FALSE);

	server_set_encoding (serv, net->encoding);
	server_set_encoding ((struct conn *)serv, net->encoding);
}

int

M src/common/userlist.c => src/common/userlist.c +4 -4
@@ 49,13 49,13 @@ nick_cmp_az_ops (ircconn *serv, struct User *user1, struct User *user2)
		}
	}

	return serv->p_cmp (user1->nick, user2->nick);
	return serv->cmp_fn (user1->nick, user2->nick);
}

int
nick_cmp_alpha (struct User *user1, struct User *user2, ircconn *serv)
{
	return serv->p_cmp (user1->nick, user2->nick);
	return serv->cmp_fn (user1->nick, user2->nick);
}

/*


@@ 195,7 195,7 @@ userlist_clear (session *sess)
static int
find_cmp (const char *name, struct User *user, ircconn *serv)
{
	return serv->p_cmp ((char *)name, user->nick);
	return serv->cmp_fn ((char *)name, user->nick);
}

struct User *


@@ 383,7 383,7 @@ userlist_add (struct session *sess, char *name, char *hostname,
		user->hostname = g_strdup (hostname);
	safe_strcpy (user->nick, name + prefix_chars, NICKLEN);
	/* is it me? */
	if (!sess->server->p_cmp (user->nick, sess->server->nick))
	if (!sess->server->cmp_fn (user->nick, sess->server->nick))
		user->me = TRUE;
	/* extended join info */
	if (sess->server->have_extjoin)

M src/fe-gtk/banlist.c => src/fe-gtk/banlist.c +1 -1
@@ 432,7 432,7 @@ banlist_do_refresh (banlist_info *banl)

	banlist_sensitize (banl);

	if (sess->server->connected)
	if (sess->server->c.connected)
	{
		GtkListStore *store;


M src/fe-gtk/chanlist.c => src/fe-gtk/chanlist.c +2 -2
@@ 287,7 287,7 @@ chanlist_do_refresh (ircconn *serv)
		serv->gui->chanlist_flash_tag = 0;
	}

	if (!serv->connected)
	if (!serv->c.connected)
	{
		fe_message (_("Not connected."), FE_MSG_ERROR);
		return;


@@ 454,7 454,7 @@ chanlist_join (GtkWidget * wid, ircconn *serv)
	char *chan = chanlist_get_selected (serv, FALSE);
	if (chan)
	{
		if (serv->connected && (strcmp (chan, "*") != 0))
		if (serv->c.connected && (strcmp (chan, "*") != 0))
		{
			g_snprintf (tbuf, sizeof (tbuf), "join %s", chan);
			handle_command (serv->server_session, tbuf, FALSE);

M src/fe-gtk/fe-gtk.c => src/fe-gtk/fe-gtk.c +3 -3
@@ 761,7 761,7 @@ fe_set_throttle (ircconn *serv)
	char tbuf[96];
	char tip[160];

	per = (float) serv->sendq_len / 1024.0;
	per = (float) serv->c.sendq_len / 1024.0;
	if (per > 1.0)
		per = 1.0;



@@ 770,8 770,8 @@ fe_set_throttle (ircconn *serv)
		sess = list->data;
		if (sess->server == serv)
		{
			g_snprintf (tbuf, sizeof (tbuf) - 1, _("%d bytes"), serv->sendq_len);
			g_snprintf (tip, sizeof (tip) - 1, _("Network send queue: %d bytes"), serv->sendq_len);
			g_snprintf (tbuf, sizeof (tbuf) - 1, _("%d bytes"), serv->c.sendq_len);
			g_snprintf (tip, sizeof (tip) - 1, _("Network send queue: %d bytes"), serv->c.sendq_len);

			g_free (sess->res->queue_tip);
			sess->res->queue_tip = g_strdup (tip);

M src/fe-gtk/maingui.c => src/fe-gtk/maingui.c +10 -10
@@ 388,7 388,7 @@ fe_set_title (session *sess)

	type = sess->type;

	if (sess->server->connected == FALSE && sess->type != SESS_DIALOG)
	if (sess->server->c.connected == FALSE && sess->type != SESS_DIALOG)
		goto def;

	switch (type)


@@ 952,10 952,10 @@ mg_populate (session *sess)

	/* menu items */
	menu_set_away (gui, sess->server->is_away);
	gtk_widget_set_sensitive (gui->menu_item[MENU_ID_AWAY], sess->server->connected);
	gtk_widget_set_sensitive (gui->menu_item[MENU_ID_AWAY], sess->server->c.connected);
	gtk_widget_set_sensitive (gui->menu_item[MENU_ID_JOIN], sess->server->end_of_motd);
	gtk_widget_set_sensitive (gui->menu_item[MENU_ID_DISCONNECT],
				  sess->server->connected || sess->server->recondelay_tag);
				  sess->server->c.connected || sess->server->recondelay_tag);

	mg_set_topic_tip (sess);



@@ 1125,7 1125,7 @@ mg_count_networks (void)

	for (list = serv_list; list; list = list->next)
	{
		if (((ircconn *)list->data)->connected)
		if (((ircconn *)list->data)->c.connected)
			cons++;
	}
	return cons;


@@ 1765,7 1765,7 @@ mg_topic_cb (GtkWidget *entry, gpointer userdata)
	session *sess = current_sess;
	char *text;

	if (sess->channel[0] && sess->server->connected && sess->type == SESS_CHANNEL)
	if (sess->channel[0] && sess->server->c.connected && sess->type == SESS_CHANNEL)
	{
		text = (char *)gtk_entry_get_text (GTK_ENTRY (entry));
		if (text[0] == 0)


@@ 1907,7 1907,7 @@ mg_change_flag (GtkWidget * wid, session *sess, char flag)

	mode[1] = flag;
	mode[2] = '\0';
	if (serv->connected && sess->channel[0])
	if (serv->c.connected && sess->channel[0])
	{
		if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (wid)))
			mode[0] = '+';


@@ 1929,7 1929,7 @@ flagl_hit (GtkWidget * wid, struct session *sess)

	if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (wid)))
	{
		if (serv->connected && sess->channel[0])
		if (serv->c.connected && sess->channel[0])
		{
			limit_str = gtk_entry_get_text (GTK_ENTRY (sess->gui->limit_entry));
			if (check_is_number ((char *)limit_str) == FALSE)


@@ 1953,7 1953,7 @@ flagk_hit (GtkWidget * wid, struct session *sess)
	char modes[512];
	ircconn *serv = sess->server;

	if (serv->connected && sess->channel[0])
	if (serv->c.connected && sess->channel[0])
	{
		g_snprintf (modes, sizeof (modes), "-k %s", 
			  gtk_entry_get_text (GTK_ENTRY (sess->gui->key_entry)));


@@ 2026,7 2026,7 @@ mg_key_entry_cb (GtkWidget * igad, gpointer userdata)
	session *sess = current_sess;
	ircconn *serv = sess->server;

	if (serv->connected && sess->channel[0])
	if (serv->c.connected && sess->channel[0])
	{
		g_snprintf (modes, sizeof (modes), "+k %s",
				gtk_entry_get_text (GTK_ENTRY (igad)));


@@ 2042,7 2042,7 @@ mg_limit_entry_cb (GtkWidget * igad, gpointer userdata)
	session *sess = current_sess;
	ircconn *serv = sess->server;

	if (serv->connected && sess->channel[0])
	if (serv->c.connected && sess->channel[0])
	{
		if (check_is_number ((char *)gtk_entry_get_text (GTK_ENTRY (igad))) == FALSE)
		{

M src/fe-gtk/menu.c => src/fe-gtk/menu.c +3 -3
@@ 673,7 673,7 @@ menu_create_nickinfo_menu (struct User *user, GtkWidget *submenu)

	if (user->away)
	{
		away = server_away_find_message (current_sess->server, user->nick);
		away = irc_away_find_message (current_sess->server, user->nick);
		if (away)
		{
			char *msg = strip_color (away->message ? away->message : unknown, -1, STRIP_ALL|STRIP_ESCMARKUP);


@@ 700,7 700,7 @@ fe_userlist_update (session *sess, struct User *user)
		return;

	/* not the same nick as the menu? */
	if (sess->server->p_cmp (user->nick, str_copy))
	if (sess->server->cmp_fn (user->nick, str_copy))
		return;

	/* get rid of the "show" signal */


@@ 1316,7 1316,7 @@ menu_disconnect (GtkWidget * wid, gpointer none)
static void
menu_reconnect (GtkWidget * wid, gpointer none)
{
	if (current_sess->server->hostname[0])
	if (current_sess->server->c.hostname[0])
		handle_command (current_sess, "RECONNECT", FALSE);
	else
		fe_serverlist_open (current_sess);

M src/fe-gtk/plugin-tray.c => src/fe-gtk/plugin-tray.c +3 -3
@@ 115,7 115,7 @@ tray_count_channels (void)
	for (list = sess_list; list; list = list->next)
	{
		sess = list->data;
		if (sess->server->connected && sess->channel[0] &&
		if (sess->server->c.connected && sess->channel[0] &&
			 sess->type == SESS_CHANNEL)
			cons++;
	}


@@ 130,7 130,7 @@ tray_count_networks (void)

	for (list = serv_list; list; list = list->next)
	{
		if (((ircconn *)list->data)->connected)
		if (((ircconn *)list->data)->c.connected)
			cons++;
	}
	return cons;


@@ 444,7 444,7 @@ tray_foreach_server (GtkWidget *item, char *cmd)
	for (list = serv_list; list; list = list->next)
	{
		serv = list->data;
		if (serv->connected)
		if (serv->c.connected)
			handle_command (serv->server_session, cmd, FALSE);
	}
}

M src/fe-gtk/servlistgui.c => src/fe-gtk/servlistgui.c +2 -2
@@ 1154,7 1154,7 @@ servlist_connect_cb (GtkWidget *button, gpointer userdata)
			if (sess->server->network == selected_net)
			{
				servlist_sess = sess;
				if (sess->server->connected)
				if (sess->server->c.connected)
					servlist_sess = NULL;	/* open a new one */
				break;
			}


@@ 1163,7 1163,7 @@ servlist_connect_cb (GtkWidget *button, gpointer userdata)
		/* use the chosen one, if it's empty */
		if (!servlist_sess &&
			  chosen &&
			 !chosen->server->connected &&
			 !chosen->server->c.connected &&
			  chosen->server->server_session->channel[0] == 0)
		{
			servlist_sess = chosen;

M src/fe-gtk/userlistgui.c => src/fe-gtk/userlistgui.c +2 -2
@@ 145,7 145,7 @@ userlist_select (session *sess, char *name)
		do
		{
			gtk_tree_model_get (model, &iter, COL_USER, &row_user, -1);
			if (sess->server->p_cmp (row_user->nick, name) == 0)
			if (sess->server->cmp_fn (row_user->nick, name) == 0)
			{
				if (gtk_tree_selection_iter_is_selected (selection, &iter))
					gtk_tree_selection_unselect_iter (selection, &iter);


@@ 737,7 737,7 @@ fe_uselect (session *sess, char *word[], int do_clear, int scroll_to)
				thisname = 0;
				while ( *(name = word[thisname++]) )
				{
					if (sess->server->p_cmp (row_user->nick, name) == 0)
					if (sess->server->cmp_fn (row_user->nick, name) == 0)
					{
						gtk_tree_selection_select_iter (selection, &iter);
						if (scroll_to)