summary refs log tree commit diff stats
path: root/src/common
diff options
context:
space:
mode:
Diffstat (limited to 'src/common')
-rw-r--r--src/common/chanopt.c2
-rw-r--r--src/common/common.vcxproj2
-rw-r--r--src/common/dcc.c8
-rw-r--r--src/common/dcc.h2
-rw-r--r--src/common/hexchat.c1
-rw-r--r--src/common/hexchat.h14
-rw-r--r--src/common/inbound.c36
-rw-r--r--src/common/inbound.h2
-rw-r--r--src/common/meson.build5
-rw-r--r--src/common/modes.c4
-rw-r--r--src/common/outbound.c2
-rw-r--r--src/common/plugin.c119
-rw-r--r--src/common/proto-irc.c65
-rw-r--r--src/common/proto-irc.h6
-rw-r--r--src/common/server.c56
-rw-r--r--src/common/servlist.c61
-rw-r--r--src/common/text.c7
-rw-r--r--src/common/textevents.in6
-rw-r--r--src/common/url.c2
-rw-r--r--src/common/userlist.c17
20 files changed, 294 insertions, 123 deletions
diff --git a/src/common/chanopt.c b/src/common/chanopt.c
index 1870c99c..67cb46cf 100644
--- a/src/common/chanopt.c
+++ b/src/common/chanopt.c
@@ -58,6 +58,7 @@ typedef struct
 
 static const channel_options chanopt[] =
 {
+	{"alert_balloon", NULL, S_F(alert_balloon)},
 	{"alert_beep", "BEEP", S_F(alert_beep)},
 	{"alert_taskbar", NULL, S_F(alert_taskbar)},
 	{"alert_tray", "TRAY", S_F(alert_tray)},
@@ -182,6 +183,7 @@ typedef struct
 {
 	/* Per-Channel Alerts */
 	/* use a byte, because we need a pointer to each element */
+	guint8 alert_balloon;
 	guint8 alert_beep;
 	guint8 alert_taskbar;
 	guint8 alert_tray;
diff --git a/src/common/common.vcxproj b/src/common/common.vcxproj
index 33a883bf..bc191f43 100644
--- a/src/common/common.vcxproj
+++ b/src/common/common.vcxproj
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>

 <Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

   <PropertyGroup Label="Configuration">

-    <PlatformToolset>v140</PlatformToolset>

+    <PlatformToolset>v142</PlatformToolset>

     <ConfigurationType>StaticLibrary</ConfigurationType>

   </PropertyGroup>

   <ItemGroup Label="ProjectConfigurations">

diff --git a/src/common/dcc.c b/src/common/dcc.c
index 51f6d661..df129e68 100644
--- a/src/common/dcc.c
+++ b/src/common/dcc.c
@@ -1618,13 +1618,13 @@ dcc_accept (GIOChannel *source, GIOCondition condition, struct DCC *dcc)
 }
 
 guint32
-dcc_get_my_address (void)	/* the address we'll tell the other person */
+dcc_get_my_address (session *sess)	/* the address we'll tell the other person */
 {
 	struct hostent *dns_query;
 	guint32 addr = 0;
 
-	if (prefs.hex_dcc_ip_from_server && prefs.dcc_ip)
-		addr = prefs.dcc_ip;
+	if (prefs.hex_dcc_ip_from_server && sess->server->dcc_ip)
+		addr = sess->server->dcc_ip;
 	else if (prefs.hex_dcc_ip[0])
 	{
 	   dns_query = gethostbyname ((const char *) prefs.hex_dcc_ip);
@@ -1710,7 +1710,7 @@ dcc_listen_init (struct DCC *dcc, session *sess)
 	/*if we have a dcc_ip, we use that, so the remote client can connect*/
 	/*else we try to take an address from hex_dcc_ip*/
 	/*if something goes wrong we tell the client to connect to our LAN ip*/
-	dcc->addr = dcc_get_my_address ();
+	dcc->addr = dcc_get_my_address (sess);
 
 	/*if nothing else worked we use the address we bound to*/
 	if (dcc->addr == 0)
diff --git a/src/common/dcc.h b/src/common/dcc.h
index 379385e6..74028205 100644
--- a/src/common/dcc.h
+++ b/src/common/dcc.h
@@ -124,7 +124,7 @@ void dcc_chat (session *sess, char *nick, int passive);
 void handle_dcc (session *sess, char *nick, char *word[], char *word_eol[],
 					  const message_tags_data *tags_data);
 void dcc_show_list (session *sess);
-guint32 dcc_get_my_address (void);
+guint32 dcc_get_my_address (session *sess);
 void dcc_get_with_destfile (struct DCC *dcc, char *utf8file);
 
 #endif
diff --git a/src/common/hexchat.c b/src/common/hexchat.c
index 347cf1ce..9be2e56d 100644
--- a/src/common/hexchat.c
+++ b/src/common/hexchat.c
@@ -493,6 +493,7 @@ session_new (server *serv, char *from, int type, int focus)
 	sess->logfd = -1;
 	sess->type = type;
 
+	sess->alert_balloon = SET_DEFAULT;
 	sess->alert_beep = SET_DEFAULT;
 	sess->alert_taskbar = SET_DEFAULT;
 	sess->alert_tray = SET_DEFAULT;
diff --git a/src/common/hexchat.h b/src/common/hexchat.h
index 8c4a6a41..d8effa1f 100644
--- a/src/common/hexchat.h
+++ b/src/common/hexchat.h
@@ -75,7 +75,7 @@
 #define DOMAINLEN	100
 #define NICKLEN		64				/* including the NULL, so 63 really */
 #define CHANLEN		300
-#define PDIWORDS		32
+#define PDIWORDS	32
 #define USERNAMELEN 10
 #define HIDDEN_CHAR	8			/* invisible character for xtext */
 
@@ -301,8 +301,8 @@ struct hexchatprefs
 	char hex_irc_user_name[127];
 	char hex_net_bind_host[127];
 	char hex_net_proxy_host[64];
-	char hex_net_proxy_pass[32];
-	char hex_net_proxy_user[32];
+	char hex_net_proxy_pass[256];
+	char hex_net_proxy_user[256];
 	char hex_stamp_log_format[64];
 	char hex_stamp_text_format[64];
 	char hex_text_background[PATHLEN + 1];
@@ -313,7 +313,6 @@ struct hexchatprefs
 
 	/* these are the private variables */
 	guint32 local_ip;
-	guint32 dcc_ip;
 
 	unsigned int wait_on_exit;	/* wait for logs to be flushed to disk IF we're connected */
 
@@ -364,6 +363,7 @@ typedef struct session
 {
 	/* Per-Channel Alerts */
 	/* use a byte, because we need a pointer to each element */
+	guint8 alert_balloon;
 	guint8 alert_beep;
 	guint8 alert_taskbar;
 	guint8 alert_tray;
@@ -482,6 +482,10 @@ typedef struct server
 	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;
+
 #ifdef USE_OPENSSL
 	SSL_CTX *ctx;
 	SSL *ssl;
@@ -566,6 +570,7 @@ typedef struct server
 	unsigned int have_idmsg:1;		/* freenode's IDENTIFY-MSG */
 	unsigned int have_accnotify:1; /* cap account-notify */
 	unsigned int have_extjoin:1;	/* cap extended-join */
+	unsigned int have_account_tag:1;	/* cap account-tag */
 	unsigned int have_server_time:1;	/* cap server-time */
 	unsigned int have_sasl:1;		/* SASL capability */
 	unsigned int have_except:1;	/* ban exemptions +e */
@@ -575,6 +580,7 @@ typedef struct server
 	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 */
diff --git a/src/common/inbound.c b/src/common/inbound.c
index fb8eb511..7175b2ae 100644
--- a/src/common/inbound.c
+++ b/src/common/inbound.c
@@ -815,7 +815,9 @@ inbound_topictime (server *serv, char *chan, char *nick, time_t stamp,
 	if (!sess)
 		sess = serv->server_session;
 
-	tim[24] = 0;	/* get rid of the \n */
+	if (tim != NULL)
+		tim[24] = 0;	/* get rid of the \n */
+
 	EMIT_SIGNAL_TIMESTAMP (XP_TE_TOPICDATE, sess, chan, nick, tim, NULL, 0,
 								  tags_data->timestamp);
 }
@@ -1420,7 +1422,7 @@ inbound_foundip (session *sess, char *ip, const message_tags_data *tags_data)
 	HostAddr = gethostbyname (ip);
 	if (HostAddr)
 	{
-		prefs.dcc_ip = ((struct in_addr *) HostAddr->h_addr)->s_addr;
+		sess->server->dcc_ip = ((struct in_addr *) HostAddr->h_addr)->s_addr;
 		EMIT_SIGNAL_TIMESTAMP (XP_TE_FOUNDIP, sess->server->server_session,
 									  inet_ntoa (*((struct in_addr *) HostAddr->h_addr)),
 									  NULL, NULL, NULL, 0, tags_data->timestamp);
@@ -1489,7 +1491,7 @@ inbound_banlist (session *sess, time_t stamp, char *chan, char *mask,
 	server *serv = sess->server;
 	char *nl;
 
-	if (stamp <= 0)
+	if (stamp <= 0 || time_str == NULL)
 	{
 		time_str = "";
 	}
@@ -1669,6 +1671,8 @@ inbound_toggle_caps (server *serv, const char *extensions_str, gboolean enable)
 			serv->have_server_time = enable;
 		else if (!strcmp (extension, "away-notify"))
 			serv->have_awaynotify = enable;
+		else if (!strcmp (extension, "account-tag"))
+			serv->have_account_tag = enable;
 		else if (!strcmp (extension, "sasl"))
 		{
 			serv->have_sasl = enable;
@@ -1722,6 +1726,9 @@ static const char * const supported_caps[] = {
 	"userhost-in-names",
 	"cap-notify",
 	"chghost",
+	"setname",
+	"invite-notify",
+	"account-tag",
 
 	/* ZNC */
 	"znc.in/server-time-iso",
@@ -1768,7 +1775,6 @@ inbound_cap_ls (server *serv, char *nick, char *extensions_str,
 {
 	char buffer[500];	/* buffer for requesting capabilities and emitting the signal */
 	gboolean want_cap = FALSE; /* format the CAP REQ string based on previous capabilities being requested or not */
-	gboolean want_sasl = FALSE; /* CAP END shouldn't be sent when SASL is requested, it needs further responses */
 	char **extensions;
 	int i;
 
@@ -1806,7 +1812,7 @@ inbound_cap_ls (server *serv, char *nick, char *extensions_str,
 		/* if the SASL password is set AND auth mode is set to SASL, request SASL auth */
 		if (!g_strcmp0 (extension, "sasl") &&
 			((serv->loginmethod == LOGIN_SASL && strlen (serv->password) != 0)
-				|| (serv->loginmethod == LOGIN_SASLEXTERNAL && serv->have_cert)))
+				|| serv->loginmethod == LOGIN_SASLEXTERNAL))
 		{
 			if (value)
 			{
@@ -1816,7 +1822,7 @@ inbound_cap_ls (server *serv, char *nick, char *extensions_str,
 				serv->sasl_mech = sasl_mech;
 			}
 			want_cap = TRUE;
-			want_sasl = TRUE;
+			serv->waiting_on_sasl = TRUE;
 			g_strlcat (buffer, "sasl ", sizeof(buffer));
 			continue;
 		}
@@ -1842,7 +1848,7 @@ inbound_cap_ls (server *serv, char *nick, char *extensions_str,
 									  tags_data->timestamp);
 		tcp_sendf (serv, "%s\r\n", g_strchomp (buffer));
 	}
-	if (!want_sasl && !serv->waiting_on_cap)
+	if (!serv->waiting_on_sasl && !serv->waiting_on_cap)
 	{
 		/* if we use SASL, CAP END is dealt via raw numerics */
 		serv->sent_capend = TRUE;
@@ -1851,13 +1857,25 @@ inbound_cap_ls (server *serv, char *nick, char *extensions_str,
 }
 
 void
-inbound_cap_nak (server *serv, const message_tags_data *tags_data)
+inbound_cap_nak (server *serv, char *extensions_str, const message_tags_data *tags_data)
 {
-	if (!serv->waiting_on_cap && !serv->sent_capend)
+	char **extensions;
+	int i;
+
+	extensions = g_strsplit (extensions_str, " ", 0);
+	for (i=0; extensions[i]; i++)
+	{
+		if (!g_strcmp0 (extensions[i], "sasl"))
+			serv->waiting_on_sasl = FALSE;
+	}
+
+	if (!serv->waiting_on_cap && !serv->waiting_on_sasl && !serv->sent_capend)
 	{
 		serv->sent_capend = TRUE;
 		tcp_send_len (serv, "CAP END\r\n", 9);
 	}
+
+	g_strfreev (extensions);
 }
 
 void
diff --git a/src/common/inbound.h b/src/common/inbound.h
index 83e78d5d..6e7c171f 100644
--- a/src/common/inbound.h
+++ b/src/common/inbound.h
@@ -92,7 +92,7 @@ void inbound_cap_ack (server *serv, char *nick, char *extensions,
 							 const message_tags_data *tags_data);
 void inbound_cap_ls (server *serv, char *nick, char *extensions,
 							const message_tags_data *tags_data);
-void inbound_cap_nak (server *serv, const message_tags_data *tags_data);
+void inbound_cap_nak (server *serv, char *extensions, const message_tags_data *tags_data);
 void inbound_cap_list (server *serv, char *nick, char *extensions,
 							  const message_tags_data *tags_data);
 void inbound_cap_del (server *serv, char *nick, char *extensions,
diff --git a/src/common/meson.build b/src/common/meson.build
index a0d6ce2b..492227b2 100644
--- a/src/common/meson.build
+++ b/src/common/meson.build
@@ -93,10 +93,6 @@ endif
 
 if get_option('with-plugin')
   common_deps += libgmodule_dep
-  common_cflags += '-DHEXCHATLIBDIR="@0@"'.format(join_paths(get_option('prefix'),
-                                                  get_option('libdir'),
-                                                  'hexchat/plugins'))
-
   install_headers('hexchat-plugin.h')
 endif
 
@@ -109,6 +105,7 @@ hexchat_common = static_library('hexchatcommon',
 )
 
 hexchat_common_dep = declare_dependency(
+  sources: [textevents] + marshal,
   link_with: hexchat_common,
   include_directories: common_includes,
   compile_args: common_cflags,
diff --git a/src/common/modes.c b/src/common/modes.c
index c65bf279..3c0ac8ab 100644
--- a/src/common/modes.c
+++ b/src/common/modes.c
@@ -735,6 +735,8 @@ handle_mode (server * serv, char *word[], char *word_eol[],
 		if (!(*word[i + offset]))
 			break;
 		num_args++;
+		if (word[i + offset][0] == ':')
+			break;
 	}
 
 	/* count the number of modes (without the -/+ chars */
@@ -765,7 +767,7 @@ handle_mode (server * serv, char *word[], char *word_eol[],
 			if ((all_modes_have_args || mode_has_arg (serv, sign, *modes)) && arg < (num_args + 1))
 			{
 				arg++;
-				argstr = word[arg + offset];
+				argstr = STRIP_COLON(word, word_eol, arg+offset);
 			}
 			handle_single_mode (&mr, sign, *modes, nick, chan,
 									  argstr, numeric_324 || prefs.hex_irc_raw_modes,
diff --git a/src/common/outbound.c b/src/common/outbound.c
index 96fb7fe4..614aad38 100644
--- a/src/common/outbound.c
+++ b/src/common/outbound.c
@@ -3287,7 +3287,7 @@ cmd_send (struct session *sess, char *tbuf, char *word[], char *word_eol[])
 	if (!word[2][0])
 		return FALSE;
 
-	addr = dcc_get_my_address ();
+	addr = dcc_get_my_address (sess);
 	if (addr == 0)
 	{
 		/* use the one from our connected server socket */
diff --git a/src/common/plugin.c b/src/common/plugin.c
index 0aa8859d..d3f3b7ca 100644
--- a/src/common/plugin.c
+++ b/src/common/plugin.c
@@ -115,6 +115,34 @@ enum
 	HOOK_DELETED      = 1 << 7  /* marked for deletion */
 };
 
+enum
+{
+	CHANNEL_FLAG_CONNECTED             = 1 << 0,
+	CHANNEL_FLAG_CONNECING             = 1 << 1,
+	CHANNEL_FLAG_AWAY                  = 1 << 2,
+	CHANNEL_FLAG_END_OF_MOTD           = 1 << 3,
+	CHANNEL_FLAG_HAS_WHOX              = 1 << 4,
+	CHANNEL_FLAG_HAS_IDMSG             = 1 << 5,
+	CHANNEL_FLAG_HIDE_JOIN_PARTS       = 1 << 6,
+	CHANNEL_FLAG_HIDE_JOIN_PARTS_UNSET = 1 << 7,
+	CHANNEL_FLAG_BEEP                  = 1 << 8,
+	CHANNEL_FLAG_BEEP_UNSET            = 1 << 9,
+	CHANNEL_FLAG_UNUSED                = 1 << 10,
+	CHANNEL_FLAG_LOGGING               = 1 << 11,
+	CHANNEL_FLAG_LOGGING_UNSET         = 1 << 12,
+	CHANNEL_FLAG_SCROLLBACK            = 1 << 13,
+	CHANNEL_FLAG_SCROLLBACK_UNSET      = 1 << 14,
+	CHANNEL_FLAG_STRIP_COLORS          = 1 << 15,
+	CHANNEL_FLAG_STRIP_COLORS_UNSET    = 1 << 16,
+	CHANNEL_FLAG_TRAY                  = 1 << 17,
+	CHANNEL_FLAG_TRAY_UNSET            = 1 << 18,
+	CHANNEL_FLAG_TASKBAR               = 1 << 19,
+	CHANNEL_FLAG_TASKBAR_UNSET         = 1 << 20,
+	CHANNEL_FLAG_BALLOON               = 1 << 21,
+	CHANNEL_FLAG_BALLOON_UNSET         = 1 << 22,
+	CHANNEL_FLAG_COUNT                 = 23
+};
+
 GSList *plugin_list = NULL;	/* export for plugingui.c */
 static GSList *hook_list = NULL;
 
@@ -355,7 +383,8 @@ plugin_kill_all (void)
 	}
 }
 
-#ifdef USE_PLUGIN
+#if defined(USE_PLUGIN) || defined(WIN32)
+/* used for loading plugins, and in fe-gtk/notifications/notification-windows.c */
 
 GModule *
 module_load (char *filename)
@@ -384,6 +413,10 @@ module_load (char *filename)
 	return handle;
 }
 
+#endif
+
+#ifdef USE_PLUGIN
+
 /* load a plugin from a filename. Returns: NULL-success or an error string */
 
 char *
@@ -676,11 +709,11 @@ check_and_invalidate(void *plug_, void *killsess_)
 int
 plugin_emit_dummy_print (session *sess, char *name)
 {
-	char *word[32];
+	char *word[PDIWORDS];
 	int i;
 
 	word[0] = name;
-	for (i = 1; i < 32; i++)
+	for (i = 1; i < PDIWORDS; i++)
 		word[i] = "\000";
 
 	i = plugin_hook_run (sess, name, word, NULL, NULL, HOOK_PRINT);
@@ -1544,7 +1577,11 @@ hexchat_list_int (hexchat_plugin *ph, hexchat_list *xlist, const char *name)
 {
 	guint32 hash = str_hash (name);
 	gpointer data = ph->context;
-	int tmp = 0;
+
+	int channel_flag;
+	int channel_flags[CHANNEL_FLAG_COUNT];
+	int channel_flags_used = 0;
+
 	int type = LIST_CHANNELS;
 
 	/* a NULL xlist is a shortcut to current "channels" context */
@@ -1605,48 +1642,38 @@ hexchat_list_int (hexchat_plugin *ph, hexchat_list *xlist, const char *name)
 		case 0xd1b:	/* id */
 			return ((struct session *)data)->server->id;
 		case 0x5cfee87:	/* flags */
-			/* used if alert_taskbar is unset */                 /* 20 */
-			tmp <<= 1;
-			tmp |= ((struct session *)data)->alert_taskbar;      /* 19 */
-			tmp <<= 1;
-			/* used if alert_tray is unset */                    /* 18 */
-			tmp <<= 1;
-			tmp |= ((struct session *)data)->alert_tray;         /* 17 */
-			tmp <<= 1;
-			/* used if text_strip is unset */                    /* 16 */
-			tmp <<= 1;
-			tmp |= ((struct session *)data)->text_strip;          /* 15 */
-			tmp <<= 1;
-			/* used if text_scrollback is unset */               /* 14 */
-			tmp <<= 1;
-			tmp |= ((struct session *)data)->text_scrollback;    /* 13 */
-			tmp <<= 1;
-			/* used if text_logging is unset */                  /* 12 */
-			tmp <<= 1;
-			tmp |= ((struct session *)data)->text_logging;       /* 11 */
-			tmp <<= 1;
-			/* unused for historical reasons */                  /* 10 */
-			tmp <<= 1;
-			/* used if alert_beep is unset */                    /* 9 */
-			tmp <<= 1;
-			tmp |= ((struct session *)data)->alert_beep;         /* 8 */
-			tmp <<= 1;
-			/* used if text_hidejoinpart is unset */              /* 7 */
-			tmp <<= 1;
-			tmp |= ((struct session *)data)->text_hidejoinpart;   /* 6 */
-			tmp <<= 1;
-			tmp |= ((struct session *)data)->server->have_idmsg; /* 5 */
-			tmp <<= 1;
-			tmp |= ((struct session *)data)->server->have_whox;  /* 4 */
-			tmp <<= 1;
-			tmp |= ((struct session *)data)->server->end_of_motd;/* 3 */
-			tmp <<= 1;
-			tmp |= ((struct session *)data)->server->is_away;    /* 2 */
-			tmp <<= 1;
-			tmp |= ((struct session *)data)->server->connecting; /* 1 */ 
-			tmp <<= 1;
-			tmp |= ((struct session *)data)->server->connected;  /* 0 */
-			return tmp;
+			channel_flags[0] = ((struct session *)data)->server->connected;
+			channel_flags[1] = ((struct session *)data)->server->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;
+			channel_flags[5] = ((struct session *)data)->server->have_idmsg;
+			channel_flags[6] = ((struct session *)data)->text_hidejoinpart;
+			channel_flags[7] = ((struct session *)data)->text_hidejoinpart == SET_DEFAULT;
+			channel_flags[8] = ((struct session *)data)->alert_beep;
+			channel_flags[9] = ((struct session *)data)->alert_beep == SET_DEFAULT;
+			channel_flags[10] = 0; /* unused for historical reasons */
+			channel_flags[11] = ((struct session *)data)->text_logging;
+			channel_flags[12] = ((struct session *)data)->text_logging == SET_DEFAULT;
+			channel_flags[13] = ((struct session *)data)->text_scrollback;
+			channel_flags[14] = ((struct session *)data)->text_scrollback == SET_DEFAULT;
+			channel_flags[15] = ((struct session *)data)->text_strip;
+			channel_flags[16] = ((struct session *)data)->text_strip == SET_DEFAULT;
+			channel_flags[17] = ((struct session *)data)->alert_tray;
+			channel_flags[18] = ((struct session *)data)->alert_tray == SET_DEFAULT;
+			channel_flags[19] = ((struct session *)data)->alert_taskbar;
+			channel_flags[20] = ((struct session *)data)->alert_taskbar == SET_DEFAULT;
+			channel_flags[21] = ((struct session *)data)->alert_balloon;
+			channel_flags[22] = ((struct session *)data)->alert_balloon == SET_DEFAULT;
+
+			/* Set flags */
+			for (channel_flag = 0; channel_flag < CHANNEL_FLAG_COUNT; ++channel_flag) {
+				if (channel_flags[channel_flag]) {
+					channel_flags_used |= 1 << channel_flag;
+				}
+			}
+
+			return channel_flags_used;
 		case 0x1a192: /* lag */
 			return ((struct session *)data)->server->lag;
 		case 0x1916144c: /* maxmodes */
diff --git a/src/common/proto-irc.c b/src/common/proto-irc.c
index 776d1434..c8e44b62 100644
--- a/src/common/proto-irc.c
+++ b/src/common/proto-irc.c
@@ -60,8 +60,8 @@ irc_login (server *serv, char *user, char *realname)
 
 	tcp_sendf (serv,
 				  "NICK %s\r\n"
-				  "USER %s %s %s :%s\r\n",
-				  serv->nick, user, user, serv->servername, realname);
+				  "USER %s 0 * :%s\r\n",
+				  serv->nick, user, realname);
 }
 
 static void
@@ -454,7 +454,8 @@ channel_date (session *sess, char *chan, char *timestr,
 {
 	time_t timestamp = (time_t) atol (timestr);
 	char *tim = ctime (&timestamp);
-	tim[24] = 0;	/* get rid of the \n */
+	if (tim != NULL)
+		tim[24] = 0;	/* get rid of the \n */
 	EMIT_SIGNAL_TIMESTAMP (XP_TE_CHANDATE, sess, chan, tim, NULL, NULL, 0,
 								  tags_data->timestamp);
 }
@@ -602,7 +603,8 @@ process_numeric (session * sess, int n,
 			else
 			{
 				tim = ctime (&timestamp);
-				tim[19] = 0; 	/* get rid of the \n */
+				if (tim != NULL)
+					tim[19] = 0; 	/* get rid of the \n */
 				EMIT_SIGNAL_TIMESTAMP (XP_TE_WHOIS4T, whois_sess, word[4],
 											  outbuf, tim, NULL, 0, tags_data->timestamp);
 			}
@@ -621,7 +623,7 @@ process_numeric (session * sess, int n,
 	case 319:
 		if (!serv->skip_next_whois)
 			EMIT_SIGNAL_TIMESTAMP (XP_TE_WHOIS2, whois_sess, word[4],
-										  word_eol[5] + 1, NULL, NULL, 0,
+										  word_eol[5][0] == ':' ? word_eol[5] + 1 : word_eol[5], NULL, NULL, 0,
 										  tags_data->timestamp);
 		break;
 
@@ -714,7 +716,7 @@ process_numeric (session * sess, int n,
 		break;
 
 	case 333:
-		inbound_topictime (serv, word[4], word[5], atol (word[6]), tags_data);
+		inbound_topictime (serv, word[4], word[5], atol (STRIP_COLON(word, word_eol, 6)), tags_data);
 		break;
 
 #if 0
@@ -726,7 +728,7 @@ process_numeric (session * sess, int n,
 #endif
 
 	case 341:						  /* INVITE ACK */
-		EMIT_SIGNAL_TIMESTAMP (XP_TE_UINVITE, sess, word[4], word[5],
+		EMIT_SIGNAL_TIMESTAMP (XP_TE_UINVITE, sess, word[4], STRIP_COLON(word, word_eol, 5),
 									  serv->servername, NULL, 0, tags_data->timestamp);
 		break;
 
@@ -957,6 +959,7 @@ process_numeric (session * sess, int n,
 		EMIT_SIGNAL_TIMESTAMP (XP_TE_SASLRESPONSE, serv->server_session, word[1],
 									  word[2], word[3], ++word_eol[4], 0,
 									  tags_data->timestamp);
+		serv->waiting_on_sasl = FALSE;
 		if (!serv->sent_capend)
 		{
 			serv->sent_capend = TRUE;
@@ -1007,6 +1010,7 @@ process_named_msg (session *sess, char *type, char *word[], char *word_eol[],
 						 const message_tags_data *tags_data)
 {
 	server *serv = sess->server;
+	char *account;
 	char ip[128], nick[NICKLEN];
 	char *text, *ex;
 	int len = strlen (type);
@@ -1025,6 +1029,14 @@ process_named_msg (session *sess, char *type, char *word[], char *word_eol[],
 		ex[0] = '!';
 	}
 
+
+	/** Update the account for this message's source. */
+	if (serv->have_account_tag)
+	{
+		account = tags_data->account && *tags_data->account ? tags_data->account : "*";
+		inbound_account (serv, nick, account, tags_data);
+	}
+
 	if (len == 4)
 	{
 		guint32 t;
@@ -1142,7 +1154,7 @@ process_named_msg (session *sess, char *type, char *word[], char *word_eol[],
 		{
 
 		case WORDL('A','C','C','O'):
-			inbound_account (serv, nick, word[3], tags_data);
+			inbound_account (serv, nick, STRIP_COLON(word, word_eol, 3), tags_data);
 			return;
 
 		case WORDL('A', 'U', 'T', 'H'):
@@ -1150,20 +1162,26 @@ process_named_msg (session *sess, char *type, char *word[], char *word_eol[],
 			return;
 
 		case WORDL('C', 'H', 'G', 'H'):
-			inbound_user_info (sess, NULL, word[3], word[4], NULL, nick, NULL,
+			inbound_user_info (sess, NULL, word[3], STRIP_COLON(word, word_eol, 4), NULL, nick, NULL,
+							   NULL, 0xff, tags_data);
+			return;
+
+		case WORDL('S', 'E', 'T', 'N'):
+			inbound_user_info (sess, NULL, NULL, NULL, NULL, nick, STRIP_COLON(word, word_eol, 3),
 							   NULL, 0xff, tags_data);
 			return;
 
 		case WORDL('I','N','V','I'):
 			if (ignore_check (word[1], IG_INVI))
 				return;
-			
-			if (word[4][0] == ':')
-				EMIT_SIGNAL_TIMESTAMP (XP_TE_INVITED, sess, word[4] + 1, nick,
-											  serv->servername, NULL, 0,
+
+			text = STRIP_COLON(word, word_eol, 4);
+			if (serv->p_cmp (word[3], serv->nick))
+				EMIT_SIGNAL_TIMESTAMP (XP_TE_INVITEDOTHER, sess, text, nick,
+											  word[3], serv->servername, 0,
 											  tags_data->timestamp);
 			else
-				EMIT_SIGNAL_TIMESTAMP (XP_TE_INVITED, sess, word[4], nick,
+				EMIT_SIGNAL_TIMESTAMP (XP_TE_INVITED, sess, text, nick,
 											  serv->servername, NULL, 0,
 											  tags_data->timestamp);
 				
@@ -1241,10 +1259,13 @@ process_named_msg (session *sess, char *type, char *word[], char *word_eol[],
 							text++;
 					}
 					len = strlen (text);
-					if (text[0] == 1 && text[len - 1] == 1)	/* ctcp */
+					if (text[0] == 1)	/* ctcp */
 					{
 						char *new_pdibuf = NULL;
-						text[len - 1] = 0;
+						if (text[len - 1] == 1)
+						{
+							text[len - 1] = 0;
+						}
 						text++;
 						if (g_ascii_strncasecmp (text, "ACTION", 6) != 0)
 							flood_check (nick, ip, serv, sess, 0);
@@ -1330,7 +1351,7 @@ process_named_msg (session *sess, char *type, char *word[], char *word_eol[],
 				}
 				else if (strncasecmp (word[4], "NAK", 3) == 0)
 				{
-					inbound_cap_nak (serv, tags_data);
+					inbound_cap_nak (serv, word[5][0] == ':' ? word_eol[5] + 1 : word_eol[5], tags_data);
 				}
 				else if (strncasecmp (word[4], "LIST", 4) == 0)	
 				{
@@ -1513,6 +1534,9 @@ handle_message_tags (server *serv, const char *tags_str,
 		*value = '\0';
 		value++;
 
+		if (serv->have_account_tag && !strcmp (key, "account"))
+			tags_data->account = g_strdup (value);
+
 		if (serv->have_server_time && !strcmp (key, "time"))
 			handle_message_tag_time (value, tags_data);
 	}
@@ -1610,10 +1634,17 @@ irc_inline (server *serv, char *buf, int len)
 	}
 
 xit:
+	message_tags_data_free (&tags_data);
 	g_free (pdibuf);
 }
 
 void
+message_tags_data_free (message_tags_data *tags_data)
+{
+	g_clear_pointer (&tags_data->account, g_free);
+}
+
+void
 proto_fill_her_up (server *serv)
 {
 	serv->p_inline = irc_inline;
diff --git a/src/common/proto-irc.h b/src/common/proto-irc.h
index a7b4029c..0f72c644 100644
--- a/src/common/proto-irc.h
+++ b/src/common/proto-irc.h
@@ -25,18 +25,24 @@
 
 #define MESSAGE_TAGS_DATA_INIT			\
 	{									\
+		NULL, /* account name */		\
 		(time_t)0, /* timestamp */		\
 	}
 
+#define STRIP_COLON(word, word_eol, idx) (word)[(idx)][0] == ':' ? (word_eol)[(idx)]+1 : (word)[(idx)]
+
 /* Message tag information that might be passed along with a server message
  *
  * See http://ircv3.atheme.org/specification/capability-negotiation-3.1
  */
 typedef struct 
 {
+	char *account;
 	time_t timestamp;
 } message_tags_data;
 
+void message_tags_data_free (message_tags_data *tags_data);
+
 void proto_fill_her_up (server *serv);
 
 #endif
diff --git a/src/common/server.c b/src/common/server.c
index 3db0a963..5c645eb5 100644
--- a/src/common/server.c
+++ b/src/common/server.c
@@ -200,13 +200,35 @@ tcp_send_len (server *serv, char *buf, int len)
 	}
 	else
 	{
-		/* WHO/MODE get the lowest priority */
-		if (g_ascii_strncasecmp (dbuf + 1, "WHO ", 4) == 0 ||
-		/* but only MODE queries, not changes */
-			(g_ascii_strncasecmp (dbuf + 1, "MODE", 4) == 0 &&
-			 strchr (dbuf, '-') == NULL &&
-			 strchr (dbuf, '+') == NULL))
+		/* WHO gets the lowest priority */
+		if (g_ascii_strncasecmp (dbuf + 1, "WHO ", 4) == 0)
 			dbuf[0] = 0;
+		/* as do MODE queries (but not changes) */
+		else if (g_ascii_strncasecmp (dbuf + 1, "MODE ", 5) == 0)
+		{
+			char *mode_str, *mode_str_end, *loc;
+			/* skip spaces before channel/nickname */
+			for (mode_str = dbuf + 5; *mode_str == ' '; ++mode_str);
+			/* skip over channel/nickname */
+			mode_str = strchr (mode_str, ' ');
+			if (mode_str)
+			{
+				/* skip spaces before mode string */
+				for (; *mode_str == ' '; ++mode_str);
+				/* find spaces after end of mode string */
+				mode_str_end = strchr (mode_str, ' ');
+				/* look for +/- within the mode string */
+				loc = strchr (mode_str, '-');
+				if (loc && (!mode_str_end || loc < mode_str_end))
+					goto keep_priority;
+				loc = strchr (mode_str, '+');
+				if (loc && (!mode_str_end || loc < mode_str_end))
+					goto keep_priority;
+			}
+			dbuf[0] = 0;
+keep_priority:
+			;
+		}
 	}
 
 	serv->outbound_queue = g_slist_append (serv->outbound_queue, dbuf);
@@ -1094,6 +1116,7 @@ traverse_socks5 (int print_fd, int sok, char *serverAddr, int port)
 	if (auth)
 	{
 		int len_u=0, len_p=0;
+		unsigned char *u_p_buf;
 
 		/* authentication sub-negotiation (RFC1929) */
 		if (buf[1] != 2)  /* UPA not supported by server */
@@ -1102,18 +1125,22 @@ traverse_socks5 (int print_fd, int sok, char *serverAddr, int port)
 			return 1;
 		}
 
-		memset (buf, 0, sizeof(buf));
-
 		/* form the UPA request */
 		len_u = strlen (prefs.hex_net_proxy_user);
 		len_p = strlen (prefs.hex_net_proxy_pass);
-		buf[0] = 1;
-		buf[1] = len_u;
-		memcpy (buf + 2, prefs.hex_net_proxy_user, len_u);
-		buf[2 + len_u] = len_p;
-		memcpy (buf + 3 + len_u, prefs.hex_net_proxy_pass, len_p);
 
-		send (sok, buf, 3 + len_u + len_p, 0);
+        packetlen = 2 + len_u + 1 + len_p;
+		u_p_buf = g_malloc0 (packetlen);
+
+		u_p_buf[0] = 1;
+		u_p_buf[1] = len_u;
+		memcpy (u_p_buf + 2, prefs.hex_net_proxy_user, len_u);
+		u_p_buf[2 + len_u] = len_p;
+		memcpy (u_p_buf + 3 + len_u, prefs.hex_net_proxy_pass, len_p);
+
+		send (sok, u_p_buf, packetlen, 0);
+		g_free(u_p_buf);
+
 		if ( recv (sok, buf, 2, 0) != 2 )
 			goto read_error;
 		if ( buf[1] != 0 )
@@ -1757,6 +1784,7 @@ server_set_defaults (server *serv)
 	serv->have_idmsg = FALSE;
 	serv->have_accnotify = FALSE;
 	serv->have_extjoin = FALSE;
+	serv->have_account_tag = FALSE;
 	serv->have_server_time = FALSE;
 	serv->have_sasl = FALSE;
 	serv->have_except = FALSE;
diff --git a/src/common/servlist.c b/src/common/servlist.c
index 449f982b..79a5694b 100644
--- a/src/common/servlist.c
+++ b/src/common/servlist.c
@@ -54,15 +54,13 @@ static const struct defaultserver def[] =
 	/* Invalid hostname in cert */
 	{0,			"irc.2600.net"},
 
-	{"2ch", 0, 0, "iso-2022-jp", 0, 0},
-	{0,			"irc.2ch.sc"},
-	{0,			"irc.nurs.or.jp"},
-	{0,			"irc.juggler.jp"},
-
 	{"AccessIRC",	0},
 	/* Self signed */
 	{0,			"irc.accessirc.net"},
 
+	{"ACN", 0, 0, 0, LOGIN_SASL, 0, TRUE},
+	{0,			"global.acn.gr"},
+
 	{"AfterNET", 0, 0, 0, LOGIN_SASL, 0, TRUE},
 	{0,			"irc.afternet.org"},
 
@@ -125,6 +123,13 @@ static const struct defaultserver def[] =
 	{"DarkMyst", 0, 0, 0, LOGIN_SASL, 0, TRUE},
 	{0,			"irc.darkmyst.org"},
 
+#ifdef USE_OPENSSL
+	{"darkscience", 0, 0, 0, LOGIN_SASL, 0, TRUE},
+	{0,			"irc.darkscience.net"},
+	{0,			"irc.drk.sc"},
+	{0,			"irc.darkscience.ws"},
+#endif
+
 	{"Dark-Tou-Net",	0},
 	{0,			"irc.d-t-net.de"},
 
@@ -166,9 +171,6 @@ static const struct defaultserver def[] =
 	/* irc. points to chat. but many users and urls still reference it */
 	{0,				"irc.freenode.net"},
 
-	{"Furnet", 0, 0, 0, 0, 0, TRUE},
-	{0,			"irc.furnet.org"},
-
 	{"GalaxyNet",	0},
 	{0,			"irc.galaxynet.org"},
 
@@ -196,6 +198,12 @@ static const struct defaultserver def[] =
 #endif
 	{0,			"irc.globalgamers.net"},
 
+#ifdef USE_OPENSSL
+	{"hackint", 0, 0, 0, LOGIN_SASL, 0, TRUE},
+	{0,			"irc.hackint.org"},
+	{0,			"irc.eu.hackint.org"},
+#endif
+
 	{"Hashmark",	0},
 	{0,			"irc.hashmark.net"},
 
@@ -231,10 +239,18 @@ static const struct defaultserver def[] =
 	{"Krstarica", 0},
 	{0,			"irc.krstarica.com"},
 
+#ifdef USE_OPENSSL
+	{"LibertaCasa", 0, 0, 0, LOGIN_SASL, 0, TRUE},
+	{0,			"irc.liberta.casa"},
+#endif
+
 	{"LibraIRC", 0},
 	/* Self signed */
 	{0,			"irc.librairc.net"},
 
+	{"Libera.Chat", 0, 0, 0, LOGIN_SASL, 0, TRUE},
+	{0,			"irc.libera.chat"},
+
 #ifdef USE_OPENSSL
 	{"LinkNet",	0},
 	{0,			"irc.link-net.org/+7000"},
@@ -246,9 +262,6 @@ static const struct defaultserver def[] =
 	{"MIXXnet",		0},
 	{0,			"irc.mixxnet.net"},
 
-	{"Moznet", 0, 0, 0, 0, 0, TRUE},
-	{0,			"irc.mozilla.org"},
-	
 	{"ObsidianIRC",  0},
 	/* Self signed */
 	{0,      "irc.obsidianirc.net"}, 
@@ -273,9 +286,12 @@ static const struct defaultserver def[] =
 	{0,			"irc.ponychat.net"},
 
 	{"PTNet.org",	0},
-	/* Note that the network suggests ISO-8859-1 but most users ignore this */
+	{0,			"irc.ptnet.org"},
 	{0,			"uevora.ptnet.org"},
-	{0,			"vianetworks.ptnet.org"},
+	{0,			"claranet.ptnet.org"},
+	{0,			"sonaquela.ptnet.org"},
+	{0,			"uc.ptnet.org"},
+	{0,			"ipg.ptnet.org"},
 
 	{"QuakeNet", 0, 0, 0, LOGIN_CHALLENGEAUTH},
 	{0,			"irc.quakenet.org"},
@@ -299,6 +315,9 @@ static const struct defaultserver def[] =
 	{"Serenity-IRC",	0},
 	{0,			"irc.serenity-irc.net"},
 
+	{"SimosNap", 0, 0, 0, LOGIN_SASL, 0, TRUE},
+	{0,            "irc.simosnap.com"},
+
 	{"SlashNET",	0},
 	/* Self signed */
 	{0,			"irc.slashnet.org"},
@@ -340,7 +359,18 @@ static const struct defaultserver def[] =
 
 	{"Techtronix",	0, 0, 0, LOGIN_SASL, 0, TRUE},
 	{0,			"irc.techtronix.net"},
+	
+	{"tilde.chat", 0, 0, 0, LOGIN_SASL, 0, TRUE},
+	{0,			"irc.tilde.chat"},
 
+#ifdef USE_OPENSSL
+	{"TripSit", 0, 0, 0, LOGIN_SASL, 0, TRUE},
+	{0,			"irc.tripsit.me"},
+	{0,			"newirc.tripsit.me"},
+	{0,			"coconut.tripsit.me"},
+	{0,			"innsbruck.tripsit.me"},
+#endif	
+	
 	{"TURLINet", 0, 0, 0, 0, 0, TRUE},
 	/* Other servers use CP1251 and invalid certs */
 	{0,			"irc.servx.ru"},
@@ -358,6 +388,9 @@ static const struct defaultserver def[] =
 
 	{"Xertion", 0, 0, 0, LOGIN_SASL, 0, TRUE},
 	{0,			"irc.xertion.org"},
+	
+	{"DeltaPool", 0, 0, 0, LOGIN_SASL, 0, TRUE},
+	{0,			"irc.deltapool.net"},
 
 	{0,0}
 };
@@ -928,7 +961,7 @@ servlist_load_defaults (void)
 {
 	int i = 0, j = 0;
 	ircnet *net = NULL;
-	guint def_hash = g_str_hash ("freenode");
+	guint def_hash = g_str_hash ("Libera.Chat");
 
 	while (1)
 	{
diff --git a/src/common/text.c b/src/common/text.c
index 4a274f98..b0a90e03 100644
--- a/src/common/text.c
+++ b/src/common/text.c
@@ -1295,6 +1295,13 @@ static char * const pevt_invited_help[] = {
 	N_("Server Name"),
 };
 
+static char * const pevt_invitedother_help[] = {
+	N_("Channel Name"),
+	N_("Nick of person who sent the invite"),
+	N_("Nick of person who was invited"),
+	N_("Server Name"),
+};
+
 static char * const pevt_usersonchan_help[] = {
 	N_("Channel Name"),
 	N_("Users"),
diff --git a/src/common/textevents.in b/src/common/textevents.in
index 9790c18d..14bd4b06 100644
--- a/src/common/textevents.in
+++ b/src/common/textevents.in
@@ -496,6 +496,12 @@ pevt_invited_help
 %C24*%O$tYou have been invited to %C22$1%O by %C18$2%O (%C29$3%O)
 3
 
+Invited Other
+XP_TE_INVITEDOTHER
+pevt_invitedother_help
+%C24*%O$t%C26$3%C has been invited to %C22$1%O by %C18$2%O (%C29$4%O)
+4
+
 Join
 XP_TE_JOIN
 pevt_join_help
diff --git a/src/common/url.c b/src/common/url.c
index a741fb04..6a1d09e8 100644
--- a/src/common/url.c
+++ b/src/common/url.c
@@ -518,6 +518,8 @@ struct
 	{ "pop",       "/", URI_AUTHORITY | URI_OPT_USERINFO | URI_PATH },
 	{ "nfs",       "/", URI_AUTHORITY | URI_OPT_USERINFO | URI_PATH },
 	{ "smb",       "/", URI_AUTHORITY | URI_OPT_USERINFO | URI_PATH },
+	{ "gopher",    "/", URI_AUTHORITY | URI_PATH },
+	{ "gemini",    "/", URI_AUTHORITY | URI_PATH },
 	{ "ssh",       "",  URI_AUTHORITY | URI_OPT_USERINFO },
 	{ "sip",       "",  URI_AUTHORITY | URI_USERINFO },
 	{ "sips",      "",  URI_AUTHORITY | URI_USERINFO },
diff --git a/src/common/userlist.c b/src/common/userlist.c
index 0f28a000..5f2d67f2 100644
--- a/src/common/userlist.c
+++ b/src/common/userlist.c
@@ -101,13 +101,15 @@ userlist_set_account (struct session *sess, char *nick, char *account)
 	user = userlist_find (sess, nick);
 	if (user)
 	{
-		g_free (user->account);
-			
 		if (strcmp (account, "*") == 0)
-			user->account = NULL;
-		else
+		{
+			g_clear_pointer (&user->account, g_free);
+		} else if (g_strcmp0 (user->account, account))
+		{
+			g_free (user->account);
 			user->account = g_strdup (account);
-			
+		}
+
 		/* gui doesnt currently reflect login status, maybe later
 		fe_userlist_rehash (sess, user); */
 	}
@@ -130,8 +132,11 @@ userlist_add_hostname (struct session *sess, char *nick, char *hostname,
 			g_free (user->hostname);
 			user->hostname = g_strdup (hostname);
 		}
-		if (!user->realname && realname && *realname)
+		if (realname && *realname && g_strcmp0 (user->realname, realname) != 0)
+		{
+			g_free (user->realname);
 			user->realname = g_strdup (realname);
+		}
 		if (!user->servername && servername)
 			user->servername = g_strdup (servername);
 		if (!user->account && account && strcmp (account, "0") != 0)