summary refs log tree commit diff stats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/common/Makefile.am20
-rw-r--r--src/common/cfgfiles.c96
-rw-r--r--src/common/cfgfiles.h6
-rw-r--r--src/common/chanopt.c18
-rw-r--r--src/common/common.vcxproj88
-rw-r--r--src/common/common.vcxproj.filters30
-rw-r--r--src/common/ctcp.c8
-rw-r--r--src/common/dbus/dbus-client.c4
-rw-r--r--src/common/dbus/dbus-plugin.c5
-rw-r--r--src/common/dbus/example.c2
-rw-r--r--src/common/dcc.c594
-rw-r--r--src/common/dcc.h27
-rw-r--r--src/common/fe.h4
-rw-r--r--src/common/hexchat.c153
-rw-r--r--src/common/hexchat.h46
-rw-r--r--src/common/history.c18
-rw-r--r--src/common/identd.c201
-rw-r--r--src/common/ignore.c43
-rw-r--r--src/common/inbound.c47
-rw-r--r--src/common/inet.h6
-rw-r--r--src/common/make-te.c4
-rw-r--r--src/common/make-te.vcxproj63
-rw-r--r--src/common/modes.c67
-rw-r--r--src/common/msproxy.c470
-rw-r--r--src/common/msproxy.h262
-rw-r--r--src/common/network.c209
-rw-r--r--src/common/network.h11
-rw-r--r--src/common/notify.c77
-rw-r--r--src/common/outbound.c441
-rw-r--r--src/common/outbound.h2
-rw-r--r--src/common/plugin-identd.c220
-rw-r--r--src/common/plugin-identd.h (renamed from src/common/identd.h)9
-rw-r--r--src/common/plugin-timer.c24
-rw-r--r--src/common/plugin.c117
-rw-r--r--src/common/plugin.h3
-rw-r--r--src/common/proto-irc.c27
-rw-r--r--src/common/server.c540
-rw-r--r--src/common/server.h3
-rw-r--r--src/common/servlist.c331
-rw-r--r--src/common/servlist.h1
-rw-r--r--src/common/ssl.c237
-rw-r--r--src/common/ssl.h4
-rw-r--r--src/common/text.c582
-rw-r--r--src/common/text.h10
-rw-r--r--src/common/tree.c7
-rw-r--r--src/common/tree.h2
-rw-r--r--src/common/url.c34
-rw-r--r--src/common/url.h2
-rw-r--r--src/common/userlist.c107
-rw-r--r--src/common/userlist.h2
-rw-r--r--src/common/util.c184
-rw-r--r--src/common/util.h6
-rw-r--r--src/dirent/dirent-win32.h184
-rw-r--r--src/fe-gtk/Makefile.am26
-rw-r--r--src/fe-gtk/banlist.c25
-rw-r--r--src/fe-gtk/chanlist.c14
-rw-r--r--src/fe-gtk/chanview-tabs.c7
-rw-r--r--src/fe-gtk/chanview.c19
-rw-r--r--src/fe-gtk/custom-list.c11
-rw-r--r--src/fe-gtk/custom-list.h8
-rw-r--r--src/fe-gtk/dccgui.c49
-rw-r--r--src/fe-gtk/editlist.c2
-rw-r--r--src/fe-gtk/fe-gtk.c70
-rw-r--r--src/fe-gtk/fe-gtk.h25
-rw-r--r--src/fe-gtk/fe-gtk.vcxproj89
-rw-r--r--src/fe-gtk/fe-gtk.vcxproj.filters15
-rw-r--r--src/fe-gtk/fkeys.c153
-rw-r--r--src/fe-gtk/gtkutil.c4
-rw-r--r--src/fe-gtk/gtkutil.h8
-rw-r--r--src/fe-gtk/hexchat.exe.manifest22
-rw-r--r--src/fe-gtk/hexchat.rc.tt5
-rw-r--r--src/fe-gtk/joind.c12
-rw-r--r--src/fe-gtk/maingui.c166
-rw-r--r--src/fe-gtk/menu.c92
-rw-r--r--src/fe-gtk/notifications/notification-backend.h27
-rw-r--r--src/fe-gtk/notifications/notification-dummy.c39
-rw-r--r--src/fe-gtk/notifications/notification-libnotify.c72
-rw-r--r--src/fe-gtk/notifications/notification-osx.m54
-rw-r--r--src/fe-gtk/notifications/notification-windows.c87
-rw-r--r--src/fe-gtk/notifications/notification-winrt.cpp100
-rw-r--r--src/fe-gtk/notifications/notifications-winrt.vcxproj62
-rw-r--r--src/fe-gtk/notifications/notifications-winrt.vcxproj.filters22
-rw-r--r--src/fe-gtk/notifygui.c10
-rw-r--r--src/fe-gtk/palette.c56
-rw-r--r--src/fe-gtk/pixmaps.c3
-rw-r--r--src/fe-gtk/plugin-notification.c215
-rw-r--r--src/fe-gtk/plugin-notification.h25
-rw-r--r--src/fe-gtk/plugin-tray.c124
-rw-r--r--src/fe-gtk/plugingui.c33
-rw-r--r--src/fe-gtk/rawlog.c6
-rw-r--r--src/fe-gtk/servlistgui.c39
-rw-r--r--src/fe-gtk/setup.c118
-rw-r--r--src/fe-gtk/sexy-iso-codes.c3
-rw-r--r--src/fe-gtk/sexy-spell-entry.c34
-rw-r--r--src/fe-gtk/textgui.c36
-rw-r--r--src/fe-gtk/userlistgui.c116
-rw-r--r--src/fe-gtk/userlistgui.h2
-rw-r--r--src/fe-gtk/xtext.c249
-rw-r--r--src/fe-gtk/xtext.h2
-rw-r--r--src/fe-text/fe-text.c41
-rw-r--r--src/fe-text/fe-text.vcxproj60
-rw-r--r--src/htm/htm.csproj15
-rw-r--r--src/libenchant_win8/libenchant_win8.def2
-rw-r--r--src/libenchant_win8/libenchant_win8.vcxproj47
-rw-r--r--src/libenchant_win8/libenchant_win8.vcxproj.filters22
-rw-r--r--src/libenchant_win8/win8_provider.cpp293
106 files changed, 3999 insertions, 4495 deletions
diff --git a/src/common/Makefile.am b/src/common/Makefile.am
index e9255d0c..75aa12bc 100644
--- a/src/common/Makefile.am
+++ b/src/common/Makefile.am
@@ -1,5 +1,7 @@
 ## Process this file with automake to produce Makefile.in
 
+include $(top_srcdir)/m4/clang-analyze.am
+
 noinst_LIBRARIES = libhexchatcommon.a
 
 AM_CPPFLAGS = $(COMMON_CFLAGS) -I$(top_srcdir)
@@ -14,17 +16,16 @@ EXTRA_DIST = \
 	hexchatc.h \
 	hexchat-plugin.h \
 	history.h \
-	identd.c \
 	ignore.h \
 	inbound.h \
 	inet.h \
 	make-te.c \
 	modes.h \
-	msproxy.h \
 	network.h \
 	notify.h \
 	outbound.h \
 	plugin.h \
+	plugin-identd.h \
 	plugin-timer.h \
 	proto-irc.h \
 	server.h \
@@ -32,6 +33,7 @@ EXTRA_DIST = \
 	ssl.h \
 	ssl.c	\
 	text.h \
+	typedef.h \
 	textenums.h \
 	textevents.h \
 	textevents.in \
@@ -44,10 +46,6 @@ if USE_OPENSSL
 ssl_c = ssl.c
 endif
 
-if USE_MSPROXY
-msproxy_c = msproxy.c
-endif
-
 if USE_DBUS
 dbusdir = dbus
 libhexchatcommon_a_LIBADD =				\
@@ -62,8 +60,8 @@ endif
 noinst_PROGRAMS = make-te
 
 libhexchatcommon_a_SOURCES = cfgfiles.c chanopt.c ctcp.c dcc.c hexchat.c \
-	history.c ignore.c inbound.c marshal.c modes.c $(msproxy_c) network.c notify.c \
-	outbound.c plugin.c plugin-timer.c proto-irc.c server.c servlist.c \
+	history.c ignore.c inbound.c marshal.c modes.c network.c notify.c \
+	outbound.c plugin.c plugin-identd.c plugin-timer.c proto-irc.c server.c servlist.c \
 	$(ssl_c) text.c tree.c url.c userlist.c util.c
 libhexchatcommon_a_CFLAGS = $(LIBPROXY_CFLAGS)
 
@@ -79,6 +77,12 @@ marshal.c: $(srcdir)/marshalers.list
 	$(AM_V_GEN) $(GLIB_GENMARSHAL) --prefix=_hexchat_marshal --body $< > $@
 
 
+if DO_STATIC_ANALYSIS
+analyze_plists = $(libhexchatcommon_a_SOURCES:%.c=%.plist)
+all-local: $(analyze_plists)
+MOSTLYCLEANFILES = $(analyze_plists)
+endif
+
 BUILT_SOURCES = textenums.h textevents.h marshal.c marshal.h
 
 CLEANFILES = $(BUILT_SOURCES)
diff --git a/src/common/cfgfiles.c b/src/common/cfgfiles.c
index a8bd32f9..a49e17ae 100644
--- a/src/common/cfgfiles.c
+++ b/src/common/cfgfiles.c
@@ -39,7 +39,7 @@
 #endif
 
 #define DEF_FONT "Monospace 9"
-#define DEF_FONT_ALTER "Arial Unicode MS,Lucida Sans Unicode,MS Gothic,Unifont"
+#define DEF_FONT_ALTER "Arial Unicode MS,Segoe UI Emoji,Lucida Sans Unicode,Meiryo,Symbola,Unifont"
 
 const char * const languages[LANGUAGES_LENGTH] = {
 	"af", "sq", "am", "ast", "az", "eu", "be", "bg", "ca", "zh_CN",      /*  0 ..  9 */
@@ -57,15 +57,11 @@ list_addentry (GSList ** list, char *cmd, char *name)
 	size_t name_len;
 	size_t cmd_len = 1;
 
-	/* remove <2.8.0 stuff */
-	if (!strcmp (cmd, "away") && !strcmp (name, "BACK"))
-		return;
-
 	if (cmd)
 		cmd_len = strlen (cmd) + 1;
 	name_len = strlen (name) + 1;
 
-	pop = malloc (sizeof (struct popup) + cmd_len + name_len);
+	pop = g_malloc (sizeof (struct popup) + cmd_len + name_len);
 	pop->name = (char *) pop + sizeof (struct popup);
 	pop->cmd = pop->name + name_len;
 
@@ -137,13 +133,13 @@ list_loadconf (char *file, GSList ** list, char *defaultconf)
 		abort ();
 	}
 
-	ibuf = malloc (st.st_size);
+	ibuf = g_malloc (st.st_size);
 	read (fd, ibuf, st.st_size);
 	close (fd);
 
 	list_load_from_data (list, ibuf, st.st_size);
 
-	free (ibuf);
+	g_free (ibuf);
 }
 
 void
@@ -153,7 +149,7 @@ list_free (GSList ** list)
 	while (*list)
 	{
 		data = (void *) (*list)->data;
-		free (data);
+		g_free (data);
 		*list = g_slist_remove (*list, data);
 	}
 }
@@ -170,7 +166,7 @@ list_delentry (GSList ** list, char *name)
 		if (!g_ascii_strcasecmp (name, pop->name))
 		{
 			*list = g_slist_remove (*list, pop);
-			free (pop);
+			g_free (pop);
 			return 1;
 		}
 		alist = alist->next;
@@ -211,10 +207,10 @@ cfg_get_str (char *cfg, const char *var, char *dest, int dest_len)
 		while (*cfg != 0 && *cfg != '\n')
 			cfg++;
 		if (*cfg == 0)
-			return 0;
+			return NULL;
 		cfg++;
 		if (*cfg == 0)
-			return 0;
+			return NULL;
 	}
 }
 
@@ -224,18 +220,18 @@ cfg_put_str (int fh, char *var, char *value)
 	char buf[512];
 	int len;
 
-	snprintf (buf, sizeof buf, "%s = %s\n", var, value);
+	g_snprintf (buf, sizeof buf, "%s = %s\n", var, value);
 	len = strlen (buf);
 	return (write (fh, buf, len) == len);
 }
 
 int
-cfg_put_color (int fh, int r, int g, int b, char *var)
+cfg_put_color (int fh, guint16 r, guint16 g, guint16 b, char *var)
 {
 	char buf[400];
 	int len;
 
-	snprintf (buf, sizeof buf, "%s = %04x %04x %04x\n", var, r, g, b);
+	g_snprintf (buf, sizeof buf, "%s = %04hx %04hx %04hx\n", var, r, g, b);
 	len = strlen (buf);
 	return (write (fh, buf, len) == len);
 }
@@ -249,20 +245,20 @@ cfg_put_int (int fh, int value, char *var)
 	if (value == -1)
 		value = 1;
 
-	snprintf (buf, sizeof buf, "%s = %d\n", var, value);
+	g_snprintf (buf, sizeof buf, "%s = %d\n", var, value);
 	len = strlen (buf);
 	return (write (fh, buf, len) == len);
 }
 
 int
-cfg_get_color (char *cfg, char *var, int *r, int *g, int *b)
+cfg_get_color (char *cfg, char *var, guint16 *r, guint16 *g, guint16 *b)
 {
 	char str[128];
 
 	if (!cfg_get_str (cfg, var, str, sizeof (str)))
 		return 0;
 
-	sscanf (str, "%04x %04x %04x", r, g, b);
+	sscanf (str, "%04hx %04hx %04hx", r, g, b);
 	return 1;
 }
 
@@ -312,9 +308,7 @@ get_xdir (void)
 
 		if (portable_mode () || SHGetKnownFolderPath (&FOLDERID_RoamingAppData, 0, NULL, &roaming_path_wide) != S_OK)
 		{
-			char *path;
-
-			path = g_win32_get_package_installation_directory_of_module (NULL);
+			char *path = g_win32_get_package_installation_directory_of_module (NULL);
 			if (path)
 			{
 				xdir = g_build_filename (path, "config", NULL);
@@ -440,6 +434,7 @@ const struct prefs vars[] =
 	{"gui_tab_dots", P_OFFINT (hex_gui_tab_dots), TYPE_BOOL},
 	{"gui_tab_icons", P_OFFINT (hex_gui_tab_icons), TYPE_BOOL},
 	{"gui_tab_layout", P_OFFINT (hex_gui_tab_layout), TYPE_INT},
+	{"gui_tab_middleclose", P_OFFINT (hex_gui_tab_middleclose), TYPE_BOOL},
 	{"gui_tab_newtofront", P_OFFINT (hex_gui_tab_newtofront), TYPE_INT},
 	{"gui_tab_pos", P_OFFINT (hex_gui_tab_pos), TYPE_INT},
 	{"gui_tab_scrollchans", P_OFFINT (hex_gui_tab_scrollchans), TYPE_BOOL},
@@ -482,11 +477,11 @@ const struct prefs vars[] =
 	{"gui_win_width", P_OFFINT (hex_gui_win_width), TYPE_INT},
 
 	{"identd", P_OFFINT (hex_identd), TYPE_BOOL},
+	{"identd_port", P_OFFINT (hex_identd_port), TYPE_INT},
 
 	{"input_balloon_chans", P_OFFINT (hex_input_balloon_chans), TYPE_BOOL},
 	{"input_balloon_hilight", P_OFFINT (hex_input_balloon_hilight), TYPE_BOOL},
 	{"input_balloon_priv", P_OFFINT (hex_input_balloon_priv), TYPE_BOOL},
-	{"input_balloon_time", P_OFFINT (hex_input_balloon_time), TYPE_INT},
 	{"input_beep_chans", P_OFFINT (hex_input_beep_chans), TYPE_BOOL},
 	{"input_beep_hilight", P_OFFINT (hex_input_beep_hilight), TYPE_BOOL},
 	{"input_beep_priv", P_OFFINT (hex_input_beep_priv), TYPE_BOOL},
@@ -590,10 +585,10 @@ const struct prefs vars[] =
 	{0, 0, 0},
 };
 
-static char *
+static const char *
 convert_with_fallback (const char *str, const char *fallback)
 {
-	char *utf;
+	const char *utf;
 
 #ifndef WIN32
 	/* On non-Windows, g_get_user_name and g_get_real_name return a string in system locale, so convert it to utf-8. */
@@ -652,7 +647,7 @@ get_default_language (void)
 
 	if (lang_no >= 0)
 	{
-		free (lang);
+		g_free (lang);
 		return lang_no;
 	}
 
@@ -661,7 +656,7 @@ get_default_language (void)
 
 	lang_no = find_language_number (lang);
 
-	free (lang);
+	g_free (lang);
 
 	return lang_no >= 0 ? lang_no : find_language_number ("en");
 }
@@ -703,8 +698,8 @@ get_default_spell_languages (void)
 				}
 			}
 		}
-		if (last != NULL)
-			g_free(last);
+
+		g_free (last);
 
 		if (lang_list[0])
 			return g_strdup (ret);
@@ -765,6 +760,7 @@ load_default_config(void)
 	prefs.hex_gui_tab_chans = 1;
 	prefs.hex_gui_tab_dialogs = 1;
 	prefs.hex_gui_tab_icons = 1;
+	prefs.hex_gui_tab_middleclose = 1;
 	prefs.hex_gui_tab_server = 1;
 	prefs.hex_gui_tab_sort = 1;
 	prefs.hex_gui_topicbar = 1;
@@ -776,7 +772,6 @@ load_default_config(void)
 	prefs.hex_gui_ulist_resizable = 1;
 	prefs.hex_gui_ulist_style = 1;
 	prefs.hex_gui_win_save = 1;
-	prefs.hex_identd = 1;
 	prefs.hex_input_flash_hilight = 1;
 	prefs.hex_input_flash_priv = 1;
 	prefs.hex_input_tray_hilight = 1;
@@ -832,7 +827,6 @@ load_default_config(void)
 	prefs.hex_gui_ulist_pos = 3;
 	prefs.hex_gui_win_height = 400;
 	prefs.hex_gui_win_width = 640;
-	prefs.hex_input_balloon_time = 20;
 	prefs.hex_irc_ban_type = 1;
 	prefs.hex_irc_join_delay = 5;
 	prefs.hex_net_reconnect_delay = 10;
@@ -847,7 +841,7 @@ load_default_config(void)
 #ifdef WIN32
 	if (portable_mode () || SHGetKnownFolderPath (&FOLDERID_Downloads, 0, NULL, &roaming_path_wide) != S_OK)
 	{
-		snprintf (prefs.hex_dcc_dir, sizeof (prefs.hex_dcc_dir), "%s\\downloads", get_xdir ());
+		g_snprintf (prefs.hex_dcc_dir, sizeof (prefs.hex_dcc_dir), "%s\\downloads", get_xdir ());
 	}
 	else
 	{
@@ -861,34 +855,36 @@ load_default_config(void)
 #else
 	if (g_get_user_special_dir (G_USER_DIRECTORY_DOWNLOAD))
 	{
-		strcpy (prefs.hex_dcc_dir, g_get_user_special_dir (G_USER_DIRECTORY_DOWNLOAD));
+		safe_strcpy (prefs.hex_dcc_dir, g_get_user_special_dir (G_USER_DIRECTORY_DOWNLOAD), sizeof(prefs.hex_dcc_dir));
 	}
 	else
 	{
-		strcpy (prefs.hex_dcc_dir, g_build_filename (g_get_home_dir (), "Downloads", NULL));
+		char *download_dir = g_build_filename (g_get_home_dir (), "Downloads", NULL);
+		safe_strcpy (prefs.hex_dcc_dir, download_dir, sizeof(prefs.hex_dcc_dir));
+		g_free (download_dir);
 	}
 #endif
 	strcpy (prefs.hex_gui_ulist_doubleclick, "QUERY %s");
 	strcpy (prefs.hex_input_command_char, "/");
-	strcpy (prefs.hex_irc_logmask, g_build_filename ("%n", "%c.log", NULL));
-	strcpy (prefs.hex_irc_nick1, username);
-	strcpy (prefs.hex_irc_nick2, username);
-	strcat (prefs.hex_irc_nick2, "_");
-	strcpy (prefs.hex_irc_nick3, username);
-	strcat (prefs.hex_irc_nick3, "__");
+	strcpy (prefs.hex_irc_logmask, "%n"G_DIR_SEPARATOR_S"%c.log");
+	safe_strcpy (prefs.hex_irc_nick1, username, sizeof(prefs.hex_irc_nick1));
+	safe_strcpy (prefs.hex_irc_nick2, username, sizeof(prefs.hex_irc_nick2));
+	g_strlcat (prefs.hex_irc_nick2, "_", sizeof(prefs.hex_irc_nick2));
+	safe_strcpy (prefs.hex_irc_nick3, username, sizeof(prefs.hex_irc_nick3));
+	g_strlcat (prefs.hex_irc_nick3, "__", sizeof(prefs.hex_irc_nick3));
 	strcpy (prefs.hex_irc_no_hilight, "NickServ,ChanServ,InfoServ,N,Q");
-	strcpy (prefs.hex_irc_part_reason, _("Leaving"));
-	strcpy (prefs.hex_irc_quit_reason, prefs.hex_irc_part_reason);
-	strcpy (prefs.hex_irc_real_name, realname);
-	strcpy (prefs.hex_irc_user_name, username);
+	safe_strcpy (prefs.hex_irc_part_reason, _("Leaving"), sizeof(prefs.hex_irc_part_reason));
+	safe_strcpy (prefs.hex_irc_quit_reason, prefs.hex_irc_part_reason, sizeof(prefs.hex_irc_quit_reason));
+	safe_strcpy (prefs.hex_irc_real_name, realname, sizeof(prefs.hex_irc_real_name));
+	safe_strcpy (prefs.hex_irc_user_name, username, sizeof(prefs.hex_irc_user_name));
 	strcpy (prefs.hex_stamp_log_format, "%b %d %H:%M:%S ");
 	strcpy (prefs.hex_stamp_text_format, "[%H:%M:%S] ");
 
 	font = fe_get_default_font ();
 	if (font)
 	{
-		strcpy (prefs.hex_text_font, font);
-		strcpy (prefs.hex_text_font_main, font);
+		safe_strcpy (prefs.hex_text_font, font, sizeof(prefs.hex_text_font));
+		safe_strcpy (prefs.hex_text_font_main, font, sizeof(prefs.hex_text_font_main));
 	}
 	else
 	{
@@ -898,7 +894,7 @@ load_default_config(void)
 
 	strcpy (prefs.hex_text_font_alternative, DEF_FONT_ALTER);
 	langs = get_default_spell_languages ();
-	strcpy (prefs.hex_text_spell_langs, langs);
+	safe_strcpy (prefs.hex_text_spell_langs, langs, sizeof(prefs.hex_text_spell_langs));
 
 
 	/* private variables */
@@ -1228,7 +1224,7 @@ cmd_set (struct session *sess, char *tbuf, char *word[], char *word_eol[])
 				if (erase || *val)
 				{
 					/* save the previous value until we print it out */
-					prev_string = (char*) malloc (vars[i].len + 1);
+					prev_string = g_malloc (vars[i].len + 1);
 					strncpy (prev_string, (char *) &prefs + vars[i].offset, vars[i].len);
 
 					/* update the variable */
@@ -1240,7 +1236,7 @@ cmd_set (struct session *sess, char *tbuf, char *word[], char *word_eol[])
 						PrintTextf (sess, "%s set to: %s (was: %s)\n", var, (char *) &prefs + vars[i].offset, prev_string);
 					}
 
-					free (prev_string);
+					g_free (prev_string);
 				}
 				else
 				{
@@ -1315,7 +1311,7 @@ cmd_set (struct session *sess, char *tbuf, char *word[], char *word_eol[])
 }
 
 int
-hexchat_open_file (char *file, int flags, int mode, int xof_flags)
+hexchat_open_file (const char *file, int flags, int mode, int xof_flags)
 {
 	char *buf;
 	int fd;
@@ -1351,7 +1347,7 @@ hexchat_fopen_file (const char *file, const char *mode, int xof_flags)
 	FILE *fh;
 
 	if (xof_flags & XOF_FULLPATH)
-		return fopen (file, mode);
+		return g_fopen (file, mode);
 
 	buf = g_build_filename (get_xdir (), file, NULL);
 	fh = g_fopen (buf, mode);
diff --git a/src/common/cfgfiles.h b/src/common/cfgfiles.h
index 8b996ca0..b421884a 100644
--- a/src/common/cfgfiles.h
+++ b/src/common/cfgfiles.h
@@ -34,8 +34,8 @@ int cfg_get_bool (char *var);
 int cfg_get_int_with_result (char *cfg, char *var, int *result);
 int cfg_get_int (char *cfg, char *var);
 int cfg_put_int (int fh, int value, char *var);
-int cfg_get_color (char *cfg, char *var, int *r, int *g, int *b);
-int cfg_put_color (int fh, int r, int g, int b, char *var);
+int cfg_get_color (char *cfg, char *var, guint16 *r, guint16 *g, guint16 *b);
+int cfg_put_color (int fh, guint16 r, guint16 g, guint16 b, char *var);
 char *get_xdir (void);
 int check_config_dir (void);
 void load_default_config (void);
@@ -48,7 +48,7 @@ void list_loadconf (char *file, GSList ** list, char *defaultconf);
 int list_delentry (GSList ** list, char *name);
 void list_addentry (GSList ** list, char *cmd, char *name);
 int cmd_set (session *sess, char *tbuf, char *word[], char *word_eol[]);
-int hexchat_open_file (char *file, int flags, int mode, int xof_flags);
+int hexchat_open_file (const char *file, int flags, int mode, int xof_flags);
 FILE *hexchat_fopen_file (const char *file, const char *mode, int xof_flags);
 
 #define XOF_DOMODE 1
diff --git a/src/common/chanopt.c b/src/common/chanopt.c
index 820a31fb..7bd66b4a 100644
--- a/src/common/chanopt.c
+++ b/src/common/chanopt.c
@@ -119,7 +119,7 @@ chanopt_command (session *sess, char *tbuf, char *word[], char *word_eol[])
 	if (!quiet)
 		PrintTextf (sess, "\002Network\002: %s \002Channel\002: %s\n",
 						sess->server->network ? server_get_network (sess->server, TRUE) : _("<none>"),
-						sess->channel[0] ? sess->channel : _("<none>"));
+						sess->session_name[0] ? sess->session_name : _("<none>"));
 
 	while (i < sizeof (chanopt) / sizeof (channel_options))
 	{
@@ -208,7 +208,7 @@ chanopt_find (char *network, char *channel, gboolean add_new)
 		return NULL;
 
 	/* allocate a new one */
-	co = g_malloc0 (sizeof (chanopt_in_memory));
+	co = g_new0 (chanopt_in_memory, 1);
 	co->channel = g_strdup (channel);
 	co->network = g_strdup (network);
 
@@ -298,7 +298,7 @@ chanopt_load (session *sess)
 	chanopt_in_memory *co;
 	char *network;
 
-	if (sess->channel[0] == 0)
+	if (sess->session_name[0] == 0)
 		return;
 
 	network = server_get_network (sess->server, FALSE);
@@ -311,7 +311,7 @@ chanopt_load (session *sess)
 		chanopt_load_all ();
 	}
 
-	co = chanopt_find (network, sess->channel, FALSE);
+	co = chanopt_find (network, sess->session_name, FALSE);
 	if (!co)
 		return;
 
@@ -334,7 +334,7 @@ chanopt_save (session *sess)
 	chanopt_in_memory *co;
 	char *network;
 
-	if (sess->channel[0] == 0)
+	if (sess->session_name[0] == 0)
 		return;
 
 	network = server_get_network (sess->server, FALSE);
@@ -343,7 +343,7 @@ chanopt_save (session *sess)
 
 	/* 2. reconcile sess with what we loaded from disk */
 
-	co = chanopt_find (network, sess->channel, TRUE);
+	co = chanopt_find (network, sess->session_name, TRUE);
 
 	i = 0;
 	while (i < sizeof (chanopt) / sizeof (channel_options))
@@ -368,10 +368,10 @@ chanopt_save_one_channel (chanopt_in_memory *co, int fh)
 	char buf[256];
 	guint8 val;
 
-	snprintf (buf, sizeof (buf), "%s = %s\n", "network", co->network);
+	g_snprintf (buf, sizeof (buf), "%s = %s\n", "network", co->network);
 	write (fh, buf, strlen (buf));
 
-	snprintf (buf, sizeof (buf), "%s = %s\n", "channel", co->channel);
+	g_snprintf (buf, sizeof (buf), "%s = %s\n", "channel", co->channel);
 	write (fh, buf, strlen (buf));
 
 	i = 0;
@@ -380,7 +380,7 @@ chanopt_save_one_channel (chanopt_in_memory *co, int fh)
 		val = G_STRUCT_MEMBER (guint8, co, chanopt[i].offset);
 		if (val != SET_DEFAULT)
 		{
-			snprintf (buf, sizeof (buf), "%s = %d\n", chanopt[i].name, val);
+			g_snprintf (buf, sizeof (buf), "%s = %d\n", chanopt[i].name, val);
 			write (fh, buf, strlen (buf));
 		}
 		i++;
diff --git a/src/common/common.vcxproj b/src/common/common.vcxproj
index 02449340..28a4da11 100644
--- a/src/common/common.vcxproj
+++ b/src/common/common.vcxproj
@@ -2,6 +2,7 @@
 <Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

   <PropertyGroup Label="Configuration">

     <PlatformToolset>v120</PlatformToolset>

+    <ConfigurationType>StaticLibrary</ConfigurationType>

   </PropertyGroup>

   <ItemGroup Label="ProjectConfigurations">

     <ProjectConfiguration Include="Release|Win32">

@@ -20,16 +21,15 @@
     <ClInclude Include="dcc.h" />

     <ClInclude Include="fe.h" />

     <ClInclude Include="history.h" />

-    <ClInclude Include="identd.h" />

     <ClInclude Include="ignore.h" />

     <ClInclude Include="inbound.h" />

     <ClInclude Include="inet.h" />

-    <ClInclude Include="marshal.h" />

+    <ClInclude Include="$(HexChatLib)marshal.h" />

     <ClInclude Include="modes.h" />

-    <ClInclude Include="msproxy.h" />

     <ClInclude Include="network.h" />

     <ClInclude Include="notify.h" />

     <ClInclude Include="outbound.h" />

+    <ClInclude Include="plugin-identd.h" />

     <ClInclude Include="plugin-timer.h" />

     <ClInclude Include="plugin.h" />

     <ClInclude Include="proto-irc.h" />

@@ -37,8 +37,8 @@
     <ClInclude Include="servlist.h" />

     <ClInclude Include="ssl.h" />

     <ClInclude Include="text.h" />

-    <ClInclude Include="textenums.h" />

-    <ClInclude Include="textevents.h" />

+    <ClInclude Include="$(HexChatLib)textenums.h" />

+    <ClInclude Include="$(HexChatLib)textevents.h" />

     <ClInclude Include="tree.h" />

     <ClInclude Include="typedef.h" />

     <ClInclude Include="url.h" />

@@ -54,12 +54,11 @@
     <ClCompile Include="ctcp.c" />

     <ClCompile Include="dcc.c" />

     <ClCompile Include="history.c" />

-    <ClCompile Include="identd.c" />

+    <ClCompile Include="plugin-identd.c" />

     <ClCompile Include="ignore.c" />

     <ClCompile Include="inbound.c" />

-    <ClCompile Include="marshal.c" />

+    <ClCompile Include="$(HexChatLib)marshal.c" />

     <ClCompile Include="modes.c" />

-    <ClCompile Include="msproxy.c" />

     <ClCompile Include="network.c" />

     <ClCompile Include="notify.c" />

     <ClCompile Include="outbound.c" />

@@ -78,7 +77,7 @@
   </ItemGroup>

   <ItemGroup>

     <None Include="..\..\win32\config.h.tt" />

-    <ClInclude Include="..\..\config.h" />

+    <ClInclude Include="$(HexChatLib)config.h" />

   </ItemGroup>

   <PropertyGroup Label="Globals">

     <ProjectGuid>{87554B59-006C-4D94-9714-897B27067BA3}</ProjectGuid>

@@ -86,85 +85,36 @@
     <RootNamespace>common</RootNamespace>

   </PropertyGroup>

   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />

-  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">

-    <ConfigurationType>StaticLibrary</ConfigurationType>

-    <UseDebugLibraries>false</UseDebugLibraries>

-    <WholeProgramOptimization>true</WholeProgramOptimization>

-    <CharacterSet>MultiByte</CharacterSet>

-  </PropertyGroup>

-  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">

-    <ConfigurationType>StaticLibrary</ConfigurationType>

-    <UseDebugLibraries>false</UseDebugLibraries>

-    <WholeProgramOptimization>true</WholeProgramOptimization>

-    <CharacterSet>MultiByte</CharacterSet>

-  </PropertyGroup>

   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />

-  <ImportGroup Label="ExtensionSettings">

-  </ImportGroup>

-  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">

-    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />

-    <Import Project="..\..\win32\hexchat.props" />

-  </ImportGroup>

-  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">

-    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />

-    <Import Project="..\..\win32\hexchat.props" />

-  </ImportGroup>

-  <PropertyGroup Label="UserMacros" />

-  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">

-    <OutDir>$(HexChatBin)</OutDir>

-    <IntDir>$(HexChatObj)$(ProjectName)\</IntDir>

-  </PropertyGroup>

-  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">

-    <OutDir>$(HexChatBin)</OutDir>

-    <IntDir>$(HexChatObj)$(ProjectName)\</IntDir>

+  <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />

+  <Import Project="..\..\win32\hexchat.props" />

+  <PropertyGroup>

+    <OutDir>$(HexChatLib)</OutDir>

   </PropertyGroup>

   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">

     <ClCompile>

-      <PrecompiledHeader>

-      </PrecompiledHeader>

-      <FunctionLevelLinking>true</FunctionLevelLinking>

-      <IntrinsicFunctions>true</IntrinsicFunctions>

       <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;$(OwnFlags);%(PreprocessorDefinitions)</PreprocessorDefinitions>

-      <AdditionalIncludeDirectories>$(SolutionDir)..;$(DepsRoot)\include;$(Glib);$(Gtk);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>

-      <MultiProcessorCompilation>true</MultiProcessorCompilation>

+      <AdditionalIncludeDirectories>$(HexChatLib);$(DepsRoot)\include;$(Glib);$(Gtk);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>

     </ClCompile>

-    <Link>

-      <SubSystem>Windows</SubSystem>

-      <GenerateDebugInformation>true</GenerateDebugInformation>

-      <EnableCOMDATFolding>true</EnableCOMDATFolding>

-      <OptimizeReferences>true</OptimizeReferences>

-    </Link>

   </ItemDefinitionGroup>

   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">

     <ClCompile>

-      <PrecompiledHeader>

-      </PrecompiledHeader>

-      <FunctionLevelLinking>true</FunctionLevelLinking>

-      <IntrinsicFunctions>true</IntrinsicFunctions>

       <PreprocessorDefinitions>WIN32;_WIN64;_AMD64_;NDEBUG;_LIB;$(OwnFlags);%(PreprocessorDefinitions)</PreprocessorDefinitions>

-      <AdditionalIncludeDirectories>$(SolutionDir)..;$(DepsRoot)\include;$(Glib);$(Gtk);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>

-      <MultiProcessorCompilation>true</MultiProcessorCompilation>

+      <AdditionalIncludeDirectories>$(HexChatLib);$(DepsRoot)\include;$(Glib);$(Gtk);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>

       <DisableSpecificWarnings>4267;%(DisableSpecificWarnings)</DisableSpecificWarnings>

     </ClCompile>

-    <Link>

-      <SubSystem>Windows</SubSystem>

-      <GenerateDebugInformation>true</GenerateDebugInformation>

-      <EnableCOMDATFolding>true</EnableCOMDATFolding>

-      <OptimizeReferences>true</OptimizeReferences>

-    </Link>

   </ItemDefinitionGroup>

   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />

-  <ImportGroup Label="ExtensionTargets">

-  </ImportGroup>

   <ItemDefinitionGroup>

     <PreBuildEvent>

       <Command><![CDATA[

 SET SOLUTIONDIR=$(SolutionDir)..\

-powershell -File "$(SolutionDir)..\win32\version-template.ps1" "$(SolutionDir)..\win32\config.h.tt" "$(SolutionDir)..\config.h"

-"$(DepsRoot)\bin\glib-genmarshal.exe" --prefix=_hexchat_marshal --header "$(ProjectDir)marshalers.list" > "$(ProjectDir)marshal.h"

-"$(DepsRoot)\bin\glib-genmarshal.exe" --prefix=_hexchat_marshal --body "$(ProjectDir)marshalers.list" > "$(ProjectDir)marshal.c"

+"$(HexChatLib)make-te.exe" < "$(ProjectDir)textevents.in" > "$(HexChatLib)textevents.h" 2> "$(HexChatLib)textenums.h"

+powershell -File "$(SolutionDir)..\win32\version-template.ps1" "$(SolutionDir)..\win32\config.h.tt" "$(HexChatLib)config.h"

+"$(DepsRoot)\bin\glib-genmarshal.exe" --prefix=_hexchat_marshal --header "$(ProjectDir)marshalers.list" > "$(HexChatLib)marshal.h"

+"$(DepsRoot)\bin\glib-genmarshal.exe" --prefix=_hexchat_marshal --body "$(ProjectDir)marshalers.list" > "$(HexChatLib)marshal.c"

 

       ]]></Command>

     </PreBuildEvent>

   </ItemDefinitionGroup>

-</Project>
\ No newline at end of file
+</Project>

diff --git a/src/common/common.vcxproj.filters b/src/common/common.vcxproj.filters
index c2d0ce5d..79e64cb4 100644
--- a/src/common/common.vcxproj.filters
+++ b/src/common/common.vcxproj.filters
@@ -29,9 +29,6 @@
     <ClInclude Include="history.h">

       <Filter>Header Files</Filter>

     </ClInclude>

-    <ClInclude Include="identd.h">

-      <Filter>Header Files</Filter>

-    </ClInclude>

     <ClInclude Include="ignore.h">

       <Filter>Header Files</Filter>

     </ClInclude>

@@ -44,9 +41,6 @@
     <ClInclude Include="modes.h">

       <Filter>Header Files</Filter>

     </ClInclude>

-    <ClInclude Include="msproxy.h">

-      <Filter>Header Files</Filter>

-    </ClInclude>

     <ClInclude Include="network.h">

       <Filter>Header Files</Filter>

     </ClInclude>

@@ -77,10 +71,10 @@
     <ClInclude Include="text.h">

       <Filter>Header Files</Filter>

     </ClInclude>

-    <ClInclude Include="textenums.h">

+    <ClInclude Include="$(HexChatLib)textenums.h">

       <Filter>Header Files</Filter>

     </ClInclude>

-    <ClInclude Include="textevents.h">

+    <ClInclude Include="$(HexChatLib)textevents.h">

       <Filter>Header Files</Filter>

     </ClInclude>

     <ClInclude Include="tree.h">

@@ -104,13 +98,16 @@
     <ClInclude Include="hexchat-plugin.h">

       <Filter>Header Files</Filter>

     </ClInclude>

-    <ClInclude Include="..\..\config.h">

+    <ClInclude Include="$(HexChatLib)config.h">

       <Filter>Header Files</Filter>

     </ClInclude>

     <ClInclude Include="typedef.h">

       <Filter>Header Files</Filter>

     </ClInclude>

-    <ClInclude Include="marshal.h">

+    <ClInclude Include="$(HexChatLib)marshal.h">

+      <Filter>Header Files</Filter>

+    </ClInclude>

+    <ClInclude Include="plugin-identd.h">

       <Filter>Header Files</Filter>

     </ClInclude>

   </ItemGroup>

@@ -130,9 +127,6 @@
     <ClCompile Include="history.c">

       <Filter>Source Files</Filter>

     </ClCompile>

-    <ClCompile Include="identd.c">

-      <Filter>Source Files</Filter>

-    </ClCompile>

     <ClCompile Include="ignore.c">

       <Filter>Source Files</Filter>

     </ClCompile>

@@ -142,9 +136,6 @@
     <ClCompile Include="modes.c">

       <Filter>Source Files</Filter>

     </ClCompile>

-    <ClCompile Include="msproxy.c">

-      <Filter>Source Files</Filter>

-    </ClCompile>

     <ClCompile Include="network.c">

       <Filter>Source Files</Filter>

     </ClCompile>

@@ -190,11 +181,14 @@
     <ClCompile Include="hexchat.c">

       <Filter>Source Files</Filter>

     </ClCompile>

-    <ClCompile Include="marshal.c">

+    <ClCompile Include="$(HexChatLib)marshal.c">

+      <Filter>Source Files</Filter>

+    </ClCompile>

+    <ClCompile Include="plugin-identd.c">

       <Filter>Source Files</Filter>

     </ClCompile>

   </ItemGroup>

   <ItemGroup>

     <None Include="..\..\win32\config.h.tt" />

   </ItemGroup>

-</Project>
\ No newline at end of file
+</Project>

diff --git a/src/common/ctcp.c b/src/common/ctcp.c
index b4fb55b7..bf0a8e7f 100644
--- a/src/common/ctcp.c
+++ b/src/common/ctcp.c
@@ -44,12 +44,12 @@ ctcp_reply (session *sess, char *nick, char *word[], char *word_eol[],
 {
 	char tbuf[4096];	/* can receive 2048 from IRC, so this is enough */
 
-	conf = strdup (conf);
+	conf = g_strdup (conf);
 	/* process %C %B etc */
 	check_special_chars (conf, TRUE);
 	auto_insert (tbuf, sizeof (tbuf), conf, word, word_eol, "", "", word_eol[5],
 					 server_get_network (sess->server, TRUE), "", "", nick, "");
-	free (conf);
+	g_free (conf);
 	handle_command (sess, tbuf, FALSE);
 }
 
@@ -139,10 +139,10 @@ ctcp_handle (session *sess, char *to, char *nick, char *ip,
 	if (!g_ascii_strcasecmp (msg, "VERSION") && !prefs.hex_irc_hide_version)
 	{
 #ifdef WIN32
-		snprintf (outbuf, sizeof (outbuf), "VERSION HexChat "PACKAGE_VERSION" [x%d] / %s",
+		g_snprintf (outbuf, sizeof (outbuf), "VERSION HexChat "PACKAGE_VERSION" [x%d] / %s",
 					 get_cpu_arch (), get_sys_str (1));
 #else
-		snprintf (outbuf, sizeof (outbuf), "VERSION HexChat "PACKAGE_VERSION" / %s",
+		g_snprintf (outbuf, sizeof (outbuf), "VERSION HexChat "PACKAGE_VERSION" / %s",
 					 get_sys_str (1));
 #endif
 		serv->p_nctcp (serv, nick, outbuf);
diff --git a/src/common/dbus/dbus-client.c b/src/common/dbus/dbus-client.c
index e507883d..bbbe10e8 100644
--- a/src/common/dbus/dbus-client.c
+++ b/src/common/dbus/dbus-client.c
@@ -19,6 +19,8 @@
  * xclaesse@gmail.com
  */
 
+#include "config.h"
+
 #define GLIB_DISABLE_DEPRECATION_WARNINGS
 #include <dbus/dbus-glib.h>
 #include "dbus-client.h"
@@ -91,7 +93,7 @@ hexchat_remote (void)
 	g_object_unref (dbus);
 
 	if (!hexchat_running) {
-		//dbus_g_connection_unref (connection);
+		/* dbus_g_connection_unref (connection); */
 		return;
 	}
 
diff --git a/src/common/dbus/dbus-plugin.c b/src/common/dbus/dbus-plugin.c
index ee8accfe..1afd9ef0 100644
--- a/src/common/dbus/dbus-plugin.c
+++ b/src/common/dbus/dbus-plugin.c
@@ -26,6 +26,7 @@
 #include <dbus/dbus-glib-lowlevel.h>
 #include <glib/gi18n.h>
 #include "hexchat-plugin.h"
+#include "dbus-plugin.h"
 
 #define PNAME _("remote access")
 #define PDESC _("plugin for remote access using DBUS")
@@ -365,6 +366,7 @@ remote_object_connect (RemoteObject *obj,
 	static guint count = 0;
 	char *sender, *path;
 	RemoteObject *remote_object;
+	gchar count_buffer[15];
 	
 	sender = dbus_g_method_get_sender (context);
 	remote_object = g_hash_table_lookup (clients, sender);
@@ -373,7 +375,8 @@ remote_object_connect (RemoteObject *obj,
 		g_free (sender);
 		return TRUE;
 	}
-	path = g_build_filename (DBUS_OBJECT_PATH, count++, NULL);
+	g_snprintf(count_buffer, sizeof(count_buffer), "%u", count++);
+	path = g_build_filename (DBUS_OBJECT_PATH, count_buffer, NULL);
 	remote_object = g_object_new (REMOTE_TYPE_OBJECT, NULL);
 	remote_object->dbus_path = path;
 	remote_object->filename = g_path_get_basename (filename);
diff --git a/src/common/dbus/example.c b/src/common/dbus/example.c
index c3ad4ff3..0228b884 100644
--- a/src/common/dbus/example.c
+++ b/src/common/dbus/example.c
@@ -33,7 +33,7 @@ guint command_id;
 guint server_id;
 
 static void
-write_error (char *message,
+write_error (const char *message,
 	     GError **error)
 {
 	if (error == NULL || *error == NULL) {
diff --git a/src/common/dcc.c b/src/common/dcc.c
index 169a0f76..881bcf78 100644
--- a/src/common/dcc.c
+++ b/src/common/dcc.c
@@ -23,8 +23,9 @@
  * Jim Seymour (jseymour@LinxNet.com)
  */
 
-/* we only use 32 bits, but without this define, you get only 31! */
+/* Required to make lseek use off64_t, but doesn't work on Windows */
 #define _FILE_OFFSET_BITS 64
+
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -57,13 +58,9 @@
 #include "url.h"
 #include "hexchatc.h"
 
-#ifdef USE_DCC64
-#define BIG_STR_TO_INT(x) strtoull(x,NULL,10)
+/* Setting _FILE_OFFSET_BITS to 64 doesn't change lseek to use off64_t on Windows, so override lseek to the version that does */
 #ifdef WIN32
-#define stat _stat64
-#endif
-#else
-#define BIG_STR_TO_INT(x) strtoul(x,NULL,10)
+#define lseek _lseeki64
 #endif
 
 static char *dcctypes[] = { "SEND", "RECV", "CHAT", "CHAT" };
@@ -78,7 +75,7 @@ struct dccstat_info dccstat[] = {
 };
 
 static int dcc_global_throttle;	/* 0x1 = sends, 0x2 = gets */
-/*static*/ int dcc_sendcpssum, dcc_getcpssum;
+static gint64 dcc_sendcpssum, dcc_getcpssum;
 
 static struct DCC *new_dcc (void);
 static void dcc_close (struct DCC *dcc, int dccstat, int destroy);
@@ -127,11 +124,12 @@ static void
 dcc_calc_cps (struct DCC *dcc)
 {
 	GTimeVal now;
-	int oldcps;
+	gint64 oldcps;
 	double timediff, startdiff;
 	int glob_throttle_bit, wasthrottled;
-	int *cpssum, glob_limit;
-	DCC_SIZE pos, posdiff;
+	gint64 *cpssum;
+	int glob_limit;
+	goffset pos, posdiff;
 
 	g_get_current_time (&now);
 
@@ -169,8 +167,7 @@ dcc_calc_cps (struct DCC *dcc)
 
 		posdiff = pos - dcc->lastcpspos;
 		oldcps = dcc->cps;
-		dcc->cps = ((double) posdiff / timediff) * (timediff / startdiff) +
-			(double) dcc->cps * (1.0 - (timediff / startdiff));
+		dcc->cps = (gint64) ((posdiff / timediff) * (timediff / startdiff) + dcc->cps * (1.0 - (timediff / startdiff)));
 
 		*cpssum += dcc->cps - oldcps;
 	}
@@ -312,7 +309,7 @@ dcc_lookup_proxy (char *host, struct sockaddr_in *addr)
 			memcpy (&addr->sin_addr, &cache_addr, 4);
 			return TRUE;
 		}
-		free (cache_host);
+		g_free (cache_host);
 		cache_host = NULL;
 	}
 
@@ -321,7 +318,7 @@ dcc_lookup_proxy (char *host, struct sockaddr_in *addr)
 	{
 		memcpy (&addr->sin_addr, h->h_addr, 4);
 		memcpy (&cache_addr, h->h_addr, 4);
-		cache_host = strdup (host);
+		cache_host = g_strdup (host);
 		/* cppcheck-suppress memleak */
 		return TRUE;
 	}
@@ -409,7 +406,7 @@ dcc_close (struct DCC *dcc, int dccstat, int destroy)
 	dcc->dccstat = dccstat;
 	if (dcc->dccchat)
 	{
-		free (dcc->dccchat);
+		g_free (dcc->dccchat);
 		dcc->dccchat = NULL;
 	}
 
@@ -417,14 +414,11 @@ dcc_close (struct DCC *dcc, int dccstat, int destroy)
 	{
 		dcc_list = g_slist_remove (dcc_list, dcc);
 		fe_dcc_remove (dcc);
-		if (dcc->proxy)
-			free (dcc->proxy);
-		if (dcc->file)
-			free (dcc->file);
-		if (dcc->destfile)
-			g_free (dcc->destfile);
-		free (dcc->nick);
-		free (dcc);
+		g_free (dcc->proxy);
+		g_free (dcc->file);
+		g_free (dcc->destfile);
+		g_free (dcc->nick);
+		g_free (dcc);
 		return;
 	}
 
@@ -493,14 +487,13 @@ dcc_write_chat (char *nick, char *text)
 	if (dcc && dcc->dccstat == STAT_ACTIVE)
 	{
 		len = strlen (text);
-		tcp_send_real (NULL, dcc->sok, dcc->serv->encoding, dcc->serv->using_irc,
-							text, len);
+		tcp_send_real (NULL, dcc->sok, dcc->serv->write_converter, text, len);
 		send (dcc->sok, "\n", 1, 0);
 		dcc->size += len;
 		fe_dcc_update (dcc);
 		return dcc;
 	}
-	return 0;
+	return NULL;
 }
 
 /* returns: 0 - ok
@@ -512,36 +505,11 @@ dcc_chat_line (struct DCC *dcc, char *line)
 	session *sess;
 	char *word[PDIWORDS];
 	char *po;
-	char *utf;
-	char *conv;
 	int ret, i;
-	int len;
-	gsize utf_len;
 	char portbuf[32];
 	message_tags_data no_tags = MESSAGE_TAGS_DATA_INIT;
 
-	len = strlen (line);
-	if (dcc->serv->using_cp1255)
-		len++;	/* include the NUL terminator */
-
-	if (dcc->serv->using_irc) /* using "IRC" encoding (CP1252/UTF-8 hybrid) */
-		utf = NULL;
-	else if (dcc->serv->encoding == NULL)     /* system */
-		utf = g_locale_to_utf8 (line, len, NULL, &utf_len, NULL);
-	else
-		utf = g_convert (line, len, "UTF-8", dcc->serv->encoding, 0, &utf_len, 0);
-
-	if (utf)
-	{
-		line = utf;
-		len = utf_len;
-	}
-
-	if (dcc->serv->using_cp1255 && len > 0)
-		len--;
-
-	/* we really need valid UTF-8 now */
-	conv = text_validate (&line, &len);
+	line = text_convert_invalid (line, -1, dcc->serv->read_converter, unicode_fallback_string, NULL);
 
 	sess = find_dialog (dcc->serv, dcc->nick);
 	if (!sess)
@@ -562,24 +530,18 @@ dcc_chat_line (struct DCC *dcc, char *line)
 	/* did the plugin close it? */
 	if (!g_slist_find (dcc_list, dcc))
 	{
-		if (utf)
-			g_free (utf);
-		if (conv)
-			g_free (conv);
+		g_free (line);
 		return 1;
 	}
 
 	/* did the plugin eat the event? */
 	if (ret)
 	{
-		if (utf)
-			g_free (utf);
-		if (conv)
-			g_free (conv);
+		g_free (line);
 		return 0;
 	}
 
-	url_check_line (line, len);
+	url_check_line (line);
 
 	if (line[0] == 1 && !g_ascii_strncasecmp (line + 1, "ACTION", 6))
 	{
@@ -592,10 +554,7 @@ dcc_chat_line (struct DCC *dcc, char *line)
 	{
 		inbound_privmsg (dcc->serv, dcc->nick, "", line, FALSE, &no_tags);
 	}
-	if (utf)
-		g_free (utf);
-	if (conv)
-		g_free (conv);
+	g_free (line);
 	return 0;
 }
 
@@ -700,20 +659,26 @@ dcc_read (GIOChannel *source, GIOCondition condition, struct DCC *dcc)
 
 		if (dcc->resumable)
 		{
-			dcc->fp = g_open (dcc->destfile, O_WRONLY | O_APPEND | OFLAGS, 0);
+			gchar *filename_fs = g_filename_from_utf8(dcc->destfile, -1, NULL, NULL, NULL);
+			dcc->fp = g_open(dcc->destfile, O_WRONLY | O_APPEND | OFLAGS, 0);
+			g_free (filename_fs);
+
 			dcc->pos = dcc->resumable;
 			dcc->ack = dcc->resumable;
-		} else
+		}
+		else
 		{
+			gchar *filename_fs;
+
 			if (g_access (dcc->destfile, F_OK) == 0)
 			{
 				n = 0;
 				do
 				{
 					n++;
-					snprintf (buf, sizeof (buf), "%s.%d", dcc->destfile, n);
+					g_snprintf (buf, sizeof (buf), "%s.%d", dcc->destfile, n);
 				}
-				while (access (buf, F_OK) == 0);
+				while (g_access (buf, F_OK) == 0);
 
 				old = dcc->destfile;
 				dcc->destfile = g_strdup (buf);
@@ -722,9 +687,10 @@ dcc_read (GIOChannel *source, GIOCondition condition, struct DCC *dcc)
 								 old, dcc->destfile, NULL, NULL, 0);
 				g_free (old);
 			}
-			dcc->fp =
-				g_open (dcc->destfile, OFLAGS | O_TRUNC | O_WRONLY | O_CREAT,
-						prefs.hex_dcc_permissions);
+
+			filename_fs = g_filename_from_utf8 (dcc->destfile, -1, NULL, NULL, NULL);
+			dcc->fp = g_open (filename_fs, OFLAGS | O_TRUNC | O_WRONLY | O_CREAT, prefs.hex_dcc_permissions);
+			g_free (filename_fs);
 		}
 	}
 	if (dcc->fp == -1)
@@ -792,7 +758,7 @@ dcc_read (GIOChannel *source, GIOCondition condition, struct DCC *dcc)
 			dcc_close (dcc, STAT_DONE, FALSE);
 			dcc_calc_average_cps (dcc);	/* this must be done _after_ dcc_close, or dcc_remove_from_sum will see the wrong value in dcc->cps */
 			/* cppcheck-suppress deallocuse */
-			sprintf (buf, "%d", dcc->cps);
+			sprintf (buf, "%" G_GINT64_FORMAT, dcc->cps);
 			EMIT_SIGNAL (XP_TE_DCCRECVCOMP, dcc->serv->front_session,
 							 dcc->file, dcc->destfile, dcc->nick, buf, 0);
 			return TRUE;
@@ -870,7 +836,7 @@ dcc_connect_finished (GIOChannel *source, GIOCondition condition, struct DCC *dc
 		return TRUE;
 
 	dcc->dccstat = STAT_ACTIVE;
-	snprintf (host, sizeof host, "%s:%d", net_ip (dcc->addr), dcc->port);
+	g_snprintf (host, sizeof host, "%s:%d", net_ip (dcc->addr), dcc->port);
 
 	switch (dcc->type)
 	{
@@ -893,8 +859,7 @@ dcc_connect_finished (GIOChannel *source, GIOCondition condition, struct DCC *dc
 		dcc_open_query (dcc->serv, dcc->nick);
 	case TYPE_CHATRECV:	/* normal chat */
 		dcc->iotag = fe_input_add (dcc->sok, FIA_READ|FIA_EX, dcc_read_chat, dcc);
-		dcc->dccchat = malloc (sizeof (struct dcc_chat));
-		dcc->dccchat->pos = 0;
+		dcc->dccchat = g_new0 (struct dcc_chat, 1);
 		EMIT_SIGNAL (XP_TE_DCCCONCHAT, dcc->serv->front_session,
 						 dcc->nick, host, NULL, NULL, 0);
 		break;
@@ -990,7 +955,7 @@ dcc_wingate_proxy_traverse (GIOChannel *source, GIOCondition condition, struct D
 	struct proxy_state *proxy = dcc->proxy;
 	if (proxy->phase == 0)
 	{
-		proxy->buffersize = snprintf ((char*) proxy->buffer, MAX_PROXY_BUFFER,
+		proxy->buffersize = g_snprintf ((char*) proxy->buffer, MAX_PROXY_BUFFER,
 										"%s %d\r\n", net_ip(dcc->addr),
 										dcc->port);
 		proxy->bufferused = 0;
@@ -1288,16 +1253,16 @@ dcc_http_proxy_traverse (GIOChannel *source, GIOCondition condition, struct DCC
 		char auth_data2[68];
 		int n, n2;
 
-		n = snprintf (buf, sizeof (buf), "CONNECT %s:%d HTTP/1.0\r\n",
+		n = g_snprintf (buf, sizeof (buf), "CONNECT %s:%d HTTP/1.0\r\n",
                                           net_ip(dcc->addr), dcc->port);
 		if (prefs.hex_net_proxy_auth)
 		{
-			n2 = snprintf (auth_data2, sizeof (auth_data2), "%s:%s",
+			n2 = g_snprintf (auth_data2, sizeof (auth_data2), "%s:%s",
 							prefs.hex_net_proxy_user, prefs.hex_net_proxy_pass);
 			base64_encode (auth_data, auth_data2, n2);
-			n += snprintf (buf+n, sizeof (buf)-n, "Proxy-Authorization: Basic %s\r\n", auth_data);
+			n += g_snprintf (buf+n, sizeof (buf)-n, "Proxy-Authorization: Basic %s\r\n", auth_data);
 		}
-		n += snprintf (buf+n, sizeof (buf)-n, "\r\n");
+		n += g_snprintf (buf+n, sizeof (buf)-n, "\r\n");
 		proxy->buffersize = n;
 		proxy->bufferused = 0;
 		memcpy (proxy->buffer, buf, proxy->buffersize);
@@ -1373,14 +1338,7 @@ dcc_proxy_connect (GIOChannel *source, GIOCondition condition, struct DCC *dcc)
 	if (!dcc_did_connect (source, condition, dcc))
 		return TRUE;
 
-	dcc->proxy = malloc (sizeof (struct proxy_state));
-	if (!dcc->proxy)
-	{
-		dcc->dccstat = STAT_FAILED;
-		fe_dcc_update (dcc);
-		return TRUE;
-	}
-	memset (dcc->proxy, 0, sizeof (struct proxy_state));
+	dcc->proxy = g_new0 (struct proxy_state, 1);
 
 	switch (prefs.hex_net_proxy_type)
 	{
@@ -1415,12 +1373,12 @@ dcc_connect (struct DCC *dcc)
 		}
 		/* possible problems with filenames containing spaces? */
 		if (dcc->type == TYPE_RECV)
-			snprintf (tbuf, sizeof (tbuf), strchr (dcc->file, ' ') ?
-					"DCC SEND \"%s\" %u %d %"DCC_SFMT" %d" :
-					"DCC SEND %s %u %d %"DCC_SFMT" %d", dcc->file,
+			g_snprintf (tbuf, sizeof (tbuf), strchr (dcc->file, ' ') ?
+					"DCC SEND \"%s\" %u %d %" G_GUINT64_FORMAT " %d" :
+					"DCC SEND %s %u %d %" G_GUINT64_FORMAT " %d", dcc->file,
 					dcc->addr, dcc->port, dcc->size, dcc->pasvid);
 		else
-			snprintf (tbuf, sizeof (tbuf), "DCC CHAT chat %u %d %d",
+			g_snprintf (tbuf, sizeof (tbuf), "DCC CHAT chat %u %d %d",
 				dcc->addr, dcc->port, dcc->pasvid);
 		dcc->serv->p_ctcp (dcc->serv, dcc->nick, tbuf);
 	}
@@ -1463,15 +1421,13 @@ dcc_send_data (GIOChannel *source, GIOCondition condition, struct DCC *dcc)
 
 	if (!dcc->fastsend)
 	{
-		if (dcc->ack < dcc->pos)
+		if (dcc->ack < (dcc->pos & 0xFFFFFFFF))
 			return TRUE;
 	}
 	else if (!dcc->wiotag)
 		dcc->wiotag = fe_input_add (sok, FIA_WRITE, dcc_send_data, dcc);
 
-	buf = malloc (prefs.hex_dcc_blocksize);
-	if (!buf)
-		return TRUE;
+	buf = g_malloc (prefs.hex_dcc_blocksize);
 
 	lseek (dcc->fp, dcc->pos, SEEK_SET);
 	len = read (dcc->fp, buf, prefs.hex_dcc_blocksize);
@@ -1482,7 +1438,7 @@ dcc_send_data (GIOChannel *source, GIOCondition condition, struct DCC *dcc)
 	if (sent < 0 && !(would_block ()))
 	{
 abortit:
-		free (buf);
+		g_free (buf);
 		EMIT_SIGNAL (XP_TE_DCCSENDFAIL, dcc->serv->front_session,
 						 file_part (dcc->file), dcc->nick,
 						 errorstring (sock_error ()), NULL, 0);
@@ -1506,7 +1462,7 @@ abortit:
 		}
 	}
 
-	free (buf);
+	g_free (buf);
 
 	return TRUE;
 }
@@ -1538,7 +1494,7 @@ dcc_handle_new_ack (struct DCC *dcc)
 		dcc_close (dcc, STAT_DONE, FALSE);
 		dcc_calc_average_cps (dcc);	/* this must be done _after_ dcc_close, or dcc_remove_from_sum will see the wrong value in dcc->cps */
 		/* cppcheck-suppress deallocuse */
-		sprintf (buf, "%d", dcc->cps);
+		sprintf (buf, "%" G_GINT64_FORMAT, dcc->cps);
 		EMIT_SIGNAL (XP_TE_DCCSENDCOMP, dcc->serv->front_session,
 						 file_part (dcc->file), dcc->nick, buf, NULL, 0);
 		done = TRUE;
@@ -1548,12 +1504,10 @@ dcc_handle_new_ack (struct DCC *dcc)
 		dcc_send_data (NULL, 0, (gpointer)dcc);
 	}
 
-#ifdef USE_DCC64
 	/* take the top 32 of "bytes send" and bottom 32 of "ack" */
 	dcc->ack = (dcc->pos & G_GINT64_CONSTANT (0xffffffff00000000)) |
 					(dcc->ack & 0xffffffff);
 	/* dcc->ack is only used for CPS and PERCENTAGE calcs from now on... */
-#endif
 
 	return done;
 }
@@ -1622,7 +1576,7 @@ dcc_accept (GIOChannel *source, GIOCondition condition, struct DCC *dcc)
 	dcc->lasttime = dcc->starttime = time (0);
 	dcc->fastsend = prefs.hex_dcc_fast_send;
 
-	snprintf (host, sizeof (host), "%s:%d", net_ip (dcc->addr), dcc->port);
+	g_snprintf (host, sizeof (host), "%s:%d", net_ip (dcc->addr), dcc->port);
 
 	switch (dcc->type)
 	{
@@ -1638,8 +1592,7 @@ dcc_accept (GIOChannel *source, GIOCondition condition, struct DCC *dcc)
 	case TYPE_CHATSEND:
 		dcc_open_query (dcc->serv, dcc->nick);
 		dcc->iotag = fe_input_add (dcc->sok, FIA_READ|FIA_EX, dcc_read_chat, dcc);
-		dcc->dccchat = malloc (sizeof (struct dcc_chat));
-		dcc->dccchat->pos = 0;
+		dcc->dccchat = g_new0 (struct dcc_chat, 1);
 		EMIT_SIGNAL (XP_TE_DCCCONCHAT, dcc->serv->front_session,
 						 dcc->nick, host, NULL, NULL, 0);
 		break;
@@ -1762,7 +1715,7 @@ dcc_listen_init (struct DCC *dcc, session *sess)
 
 static struct session *dccsess;
 static char *dccto;				  /* lame!! */
-static int dccmaxcps;
+static gint64 dccmaxcps;
 static int recursive = FALSE;
 
 static void
@@ -1772,21 +1725,25 @@ dcc_send_wild (char *file)
 }
 
 void
-dcc_send (struct session *sess, char *to, char *file, int maxcps, int passive)
+dcc_send (struct session *sess, char *to, char *filename, gint64 maxcps, int passive)
 {
 	char outbuf[512];
-	GStatBuf st;
+	GFileInfo *file_info;
+	GFile *file;
 	struct DCC *dcc;
+	gchar *filename_fs;
+	GFileType file_type;
+	goffset file_size;
 
-	file = expand_homedir (file);
+	filename = expand_homedir (filename);
 
-	if (!recursive && (strchr (file, '*') || strchr (file, '?')))
+	if (!recursive && (strchr (filename, '*') || strchr (filename, '?')))
 	{
 		char path[256];
 		char wild[256];
 
-		safe_strcpy (wild, file_part (file), sizeof (wild));
-		path_part (file, path, sizeof (path));
+		safe_strcpy (wild, file_part (filename), sizeof (wild));
+		path_part (filename, path, sizeof (path));
 		if (path[0] != '/' || path[1] != '\0')
 			path[strlen (path) - 1] = 0;	/* remove trailing slash */
 
@@ -1794,7 +1751,7 @@ dcc_send (struct session *sess, char *to, char *file, int maxcps, int passive)
 		dccto = to;
 		dccmaxcps = maxcps;
 
-		free (file);
+		g_free (filename);
 
 		recursive = TRUE;
 		for_files (path, wild, dcc_send_wild);
@@ -1806,91 +1763,135 @@ dcc_send (struct session *sess, char *to, char *file, int maxcps, int passive)
 	dcc = new_dcc ();
 	if (!dcc)
 	{
-		free (file);
+		g_free (filename);
 		return;
 	}
-	dcc->file = file;
+
+	dcc->file = filename;
 	dcc->maxcps = maxcps;
 
-	if (g_stat (file, &st) != -1)
+	filename_fs = g_filename_from_utf8 (filename, -1, NULL, NULL, NULL);
+	if (filename_fs == NULL)
 	{
+		PrintTextf (sess, _("Cannot access %s\n"), dcc->file);
+		PrintTextf (sess, "%s %d: %s\n", _("Error"), errno, errorstring (errno));
 
-#ifndef USE_DCC64
-		if (sizeof (st.st_size) > 4 && st.st_size > 4294967295U)
-		{
-			PrintText (sess, "Cannot send files larger than 4 GB.\n");
-			goto xit;
-		}
-#endif
+		dcc_close (dcc, 0, TRUE); /* dcc_close will free dcc->file */
 
-		if (!(*file_part (file)) || S_ISDIR (st.st_mode) || st.st_size < 1)
-		{
-			PrintText (sess, "Cannot send directories or empty files.\n");
-			goto xit;
-		}
+		return;
+	}
 
-		dcc->starttime = dcc->offertime = time (0);
-		dcc->serv = sess->server;
-		dcc->dccstat = STAT_QUEUED;
-		dcc->size = st.st_size;
-		dcc->type = TYPE_SEND;
-		dcc->fp = g_open (file, OFLAGS | O_RDONLY, 0);
-		if (dcc->fp != -1)
-		{
-			if (passive || dcc_listen_init (dcc, sess))
-			{
-				char havespaces = 0;
-				while (*file)
-				{
-					if (*file == ' ')
-					{
-						if (prefs.hex_dcc_send_fillspaces)
-				    		*file = '_';
-					  	else
-					   	havespaces = 1;
-					}
-					file++;
-				}
-				dcc->nick = strdup (to);
-				if (prefs.hex_gui_autoopen_send)
-				{
-					if (fe_dcc_open_send_win (TRUE))	/* already open? add */
-						fe_dcc_add (dcc);
-				} else
-					fe_dcc_add (dcc);
+	file = g_file_new_for_path (filename_fs);
+	if (file == NULL)
+	{
+		PrintTextf (sess, _("Cannot access %s\n"), dcc->file);
+		PrintTextf (sess, "%s %d: %s\n", _("Error"), errno, errorstring (errno));
 
-				if (passive)
-				{
-					dcc->pasvid = new_id();
-					snprintf (outbuf, sizeof (outbuf), (havespaces) ?
-							"DCC SEND \"%s\" 199 0 %" DCC_SFMT " %d" :
-							"DCC SEND %s 199 0 %" DCC_SFMT " %d",
-							file_part (dcc->file),
-							dcc->size, dcc->pasvid);
-				}
-				else
-				{
-					snprintf (outbuf, sizeof (outbuf), (havespaces) ?
-							"DCC SEND \"%s\" %u %d %"DCC_SFMT :
-							"DCC SEND %s %u %d %"DCC_SFMT,
-							file_part (dcc->file), dcc->addr,
-							dcc->port, dcc->size);
-				}
-				sess->server->p_ctcp (sess->server, to, outbuf);
+		dcc_close (dcc, 0, TRUE); /* dcc_close will free dcc->file */
 
-				EMIT_SIGNAL (XP_TE_DCCOFFER, sess, file_part (dcc->file),
-								 to, dcc->file, NULL, 0);
-			} else
+		g_free (filename_fs);
+
+		return;
+	}
+
+	file_info = g_file_query_info (file, G_FILE_ATTRIBUTE_STANDARD_SIZE "," G_FILE_ATTRIBUTE_STANDARD_TYPE, G_FILE_QUERY_INFO_NONE, NULL, NULL);
+
+	g_object_unref (file);
+
+	if (file_info == NULL)
+	{
+		PrintTextf (sess, _("Cannot access %s\n"), dcc->file);
+		PrintTextf (sess, "%s %d: %s\n", _("Error"), errno, errorstring (errno));
+
+		dcc_close (dcc, 0, TRUE); /* dcc_close will free dcc->file */
+
+		g_free (filename_fs);
+
+		return;
+	}
+
+	file_type = g_file_info_get_file_type (file_info);
+	file_size = g_file_info_get_size (file_info);
+
+	g_object_unref (file_info);
+
+	if (*file_part (filename) == '\0' || file_type == G_FILE_TYPE_DIRECTORY || file_size <= 0)
+	{
+		PrintText (sess, "Cannot send directories or empty files.\n");
+
+		dcc_close (dcc, 0, TRUE); /* dcc_close will free dcc->file */
+
+		g_free (filename_fs);
+
+		return;
+	}
+
+	dcc->starttime = dcc->offertime = time (0);
+	dcc->serv = sess->server;
+	dcc->dccstat = STAT_QUEUED;
+	dcc->size = file_size;
+	dcc->type = TYPE_SEND;
+	dcc->fp = g_open (filename_fs, OFLAGS | O_RDONLY, 0);
+
+	g_free (filename_fs);
+
+	if (dcc->fp == -1)
+	{
+		PrintText (sess, "Cannot send directories or empty files.\n");
+
+		dcc_close (dcc, 0, TRUE); /* dcc_close will free dcc->file */
+
+		return;
+	}
+
+	if (passive || dcc_listen_init (dcc, sess))
+	{
+		char havespaces = 0;
+		while (*filename)
+		{
+			if (*filename == ' ')
 			{
-				dcc_close (dcc, 0, TRUE);
+				if (prefs.hex_dcc_send_fillspaces)
+				    *filename = '_';
+				else
+				havespaces = 1;
 			}
-			return;
+			filename++;
 		}
+		dcc->nick = g_strdup (to);
+		if (prefs.hex_gui_autoopen_send)
+		{
+			if (fe_dcc_open_send_win (TRUE))	/* already open? add */
+				fe_dcc_add (dcc);
+		} else
+			fe_dcc_add (dcc);
+
+		if (passive)
+		{
+			dcc->pasvid = new_id();
+			g_snprintf (outbuf, sizeof (outbuf), (havespaces) ?
+					"DCC SEND \"%s\" 199 0 %" G_GUINT64_FORMAT " %d" :
+					"DCC SEND %s 199 0 %" G_GUINT64_FORMAT " %d",
+					file_part (dcc->file),
+					dcc->size, dcc->pasvid);
+		}
+		else
+		{
+			g_snprintf (outbuf, sizeof (outbuf), (havespaces) ?
+					"DCC SEND \"%s\" %u %d %" G_GUINT64_FORMAT :
+					"DCC SEND %s %u %d %" G_GUINT64_FORMAT,
+					file_part (dcc->file), dcc->addr,
+					dcc->port, dcc->size);
+		}
+		sess->server->p_ctcp (sess->server, to, outbuf);
+
+		EMIT_SIGNAL (XP_TE_DCCOFFER, sess, file_part (dcc->file),
+							to, dcc->file, NULL, 0);
+	}
+	else
+	{
+		dcc_close (dcc, 0, TRUE);
 	}
-	PrintTextf (sess, _("Cannot access %s\n"), dcc->file);
-	PrintTextf (sess, "%s %d: %s\n", _("Error"), errno, errorstring (errno));
-xit:
-	dcc_close (dcc, 0, TRUE);		/* dcc_close will free dcc->file */
 }
 
 static struct DCC *
@@ -1906,7 +1907,7 @@ find_dcc_from_id (int id, int type)
 		return dcc;
 		list = list->next;
 	}
-	return 0;
+	return NULL;
 }
 
 static struct DCC *
@@ -1922,7 +1923,7 @@ find_dcc_from_port (int port, int type)
 			return dcc;
 		list = list->next;
 	}
-	return 0;
+	return NULL;
 }
 
 struct DCC *
@@ -1947,7 +1948,7 @@ find_dcc (char *nick, char *file, int type)
 		}
 		list = list->next;
 	}
-	return 0;
+	return NULL;
 }
 
 /* called when we receive a NICK change from server */
@@ -1965,9 +1966,8 @@ dcc_change_nick (struct server *serv, char *oldnick, char *newnick)
 		{
 			if (!serv->p_cmp (dcc->nick, oldnick))
 			{
-				if (dcc->nick)
-					free (dcc->nick);
-				dcc->nick = strdup (newnick);
+				g_free (dcc->nick);
+				dcc->nick = g_strdup (newnick);
 			}
 		}
 		list = list->next;
@@ -1976,68 +1976,153 @@ dcc_change_nick (struct server *serv, char *oldnick, char *newnick)
 
 /* is the destination file the same? new_dcc is not opened yet */
 
-static int
+static gboolean
 is_same_file (struct DCC *dcc, struct DCC *new_dcc)
 {
-#ifndef WIN32
-	GStatBuf st_a, st_b;
-#endif
+	gboolean result = FALSE;
+	gchar *filename_fs = NULL, *new_filename_fs = NULL;
+	GFile *file = NULL, *new_file = NULL;
+	GFileInfo *file_info = NULL, *new_file_info = NULL;
+	char *file_id = NULL, *new_file_id = NULL;
+	char *filesystem_id = NULL, *new_filesystem_id = NULL;
 
 	/* if it's the same filename, must be same */
 	if (strcmp (dcc->destfile, new_dcc->destfile) == 0)
+	{
 		return TRUE;
+	}
 
-	/* now handle case-insensitive Filesystems: HFS+, FAT */
-#ifdef WIN32
-	/* warning no win32 implementation - behaviour may be unreliable */
-#else
-	/* this fstat() shouldn't really fail */
-	if ((dcc->fp == -1 ? g_stat (dcc->destfile, &st_a) : fstat (dcc->fp, &st_a)) == -1)
-		return FALSE;
-	if (g_stat (new_dcc->destfile, &st_b) == -1)
-		return FALSE;
+	filename_fs = g_filename_from_utf8 (dcc->file, -1, NULL, NULL, NULL);
+	if (filename_fs == NULL)
+	{
+		goto exit;
+	}
 
-	/* same inode, same device, same file! */
-	if (st_a.st_ino == st_b.st_ino &&
-		 st_a.st_dev == st_b.st_dev)
-		return TRUE;
-#endif
+	new_filename_fs = g_filename_from_utf8 (new_dcc->file, -1, NULL, NULL, NULL);
+	if (new_filename_fs == NULL)
+	{
+		goto exit;
+	}
 
-	return FALSE;
+	file = g_file_new_for_path (filename_fs);
+	if (file == NULL)
+	{
+		goto exit;
+	}
+
+	new_file = g_file_new_for_path (new_filename_fs);
+	if (new_file == NULL)
+	{
+		goto exit;
+	}
+
+	file_info = g_file_query_info (file, G_FILE_ATTRIBUTE_ID_FILE "," G_FILE_ATTRIBUTE_ID_FILESYSTEM, G_FILE_QUERY_INFO_NONE, NULL, NULL);
+	if (file_info == NULL)
+	{
+		goto exit;
+	}
+
+	new_file_info = g_file_query_info (new_file, G_FILE_ATTRIBUTE_ID_FILE "," G_FILE_ATTRIBUTE_ID_FILESYSTEM, G_FILE_QUERY_INFO_NONE, NULL, NULL);
+	if (new_file_info == NULL)
+	{
+		goto exit;
+	}
+
+	file_id = g_file_info_get_attribute_as_string (file_info, G_FILE_ATTRIBUTE_ID_FILE);
+	new_file_id = g_file_info_get_attribute_as_string (new_file_info, G_FILE_ATTRIBUTE_ID_FILE);
+
+	filesystem_id = g_file_info_get_attribute_as_string (file_info, G_FILE_ATTRIBUTE_ID_FILE);
+	new_filesystem_id = g_file_info_get_attribute_as_string (new_file_info, G_FILE_ATTRIBUTE_ID_FILE);
+
+	if (file_id != NULL && new_file_id != NULL && filesystem_id != NULL && new_filesystem_id != NULL && strcmp (file_id, new_file_id) == 0 && strcmp (filesystem_id, new_filesystem_id) == 0)
+	{
+		result = TRUE;
+	}
+
+exit:
+	g_free (filename_fs);
+	g_free (new_filename_fs);
+
+	if (file != NULL)
+	{
+		g_object_unref (file);
+	}
+
+	if (new_file != NULL)
+	{
+		g_object_unref (new_file);
+	}
+
+	if (file_info != NULL)
+	{
+		g_object_unref (file_info);
+	}
+
+	if (new_file_info != NULL)
+	{
+		g_object_unref (new_file_info);
+	}
+
+	g_free (file_id);
+	g_free (new_file_id);
+	g_free(filesystem_id);
+	g_free(new_filesystem_id);
+
+	return result;
 }
 
-static int
-is_resumable (struct DCC *dcc)
+static void
+update_is_resumable (struct DCC *dcc)
 {
+	gchar *filename_fs = g_filename_from_utf8 (dcc->destfile, -1, NULL, NULL, NULL);
+
 	dcc->resumable = 0;
 
 	/* Check the file size */
-	if (g_access (dcc->destfile, W_OK) == 0)
+	if (filename_fs != NULL && g_access(filename_fs, W_OK) == 0)
 	{
-		GStatBuf st;
-
-		if (g_stat (dcc->destfile, &st) != -1)
+		GFile *file = g_file_new_for_path (filename_fs);
+		if (file != NULL)
 		{
-			if (st.st_size < dcc->size)
+			GFileInfo *file_info = g_file_query_info (file, G_FILE_ATTRIBUTE_STANDARD_SIZE "," G_FILE_ATTRIBUTE_STANDARD_TYPE, G_FILE_QUERY_INFO_NONE, NULL, NULL);
+
+			if (file_info != NULL)
 			{
-				dcc->resumable = st.st_size;
-				dcc->pos = st.st_size;
+				goffset file_size_offset = g_file_info_get_size (file_info);
+				guint64 file_size = (file_size_offset >= 0) ? (guint64) file_size_offset : 0;
+				if (file_size < dcc->size)
+				{
+					dcc->resumable = file_size;
+					dcc->pos = file_size;
+				}
+				else
+				{
+					dcc->resume_error = 2;
+				}
+
+				g_object_unref (file_info);
 			}
 			else
-				dcc->resume_error = 2;
-		} else
+			{
+				dcc->resume_errno = errno;
+				dcc->resume_error = 1;
+			}
+
+			g_object_unref(file);
+		}
+		else
 		{
 			dcc->resume_errno = errno;
 			dcc->resume_error = 1;
 		}
-	} else
+	}
+	else
 	{
 		dcc->resume_errno = errno;
 		dcc->resume_error = 1;
 	}
 
 	/* Now verify that this DCC is not already in progress from someone else */
-
 	if (dcc->resumable)
 	{
 		GSList *list = dcc_list;
@@ -2059,8 +2144,6 @@ is_resumable (struct DCC *dcc)
 			list = list->next;
 		}
 	}
-
-	return dcc->resumable;
 }
 
 void
@@ -2100,7 +2183,7 @@ dcc_get_with_destfile (struct DCC *dcc, char *file)
 	dcc->destfile = g_strdup (file);	/* utf-8 */
 
 	/* since destfile changed, must check resumability again */
-	is_resumable (dcc);
+	update_is_resumable (dcc);
 
 	dcc_get (dcc);
 }
@@ -2133,14 +2216,11 @@ dcc_get_nick (struct session *sess, char *nick)
 static struct DCC *
 new_dcc (void)
 {
-	struct DCC *dcc = malloc (sizeof (struct DCC));
-	if (!dcc)
-		return 0;
-	memset (dcc, 0, sizeof (struct DCC));
+	struct DCC *dcc = g_new0 (struct DCC, 1);
 	dcc->sok = -1;
 	dcc->fp = -1;
 	dcc_list = g_slist_prepend (dcc_list, dcc);
-	return (dcc);
+	return dcc;
 }
 
 void
@@ -2187,7 +2267,7 @@ dcc_chat (struct session *sess, char *nick, int passive)
 	dcc->serv = sess->server;
 	dcc->dccstat = STAT_QUEUED;
 	dcc->type = TYPE_CHATSEND;
-	dcc->nick = strdup (nick);
+	dcc->nick = g_strdup (nick);
 	if (passive || dcc_listen_init (dcc, sess))
 	{
 		if (prefs.hex_gui_autoopen_chat)
@@ -2200,11 +2280,11 @@ dcc_chat (struct session *sess, char *nick, int passive)
 		if (passive)
 		{
 			dcc->pasvid = new_id ();
-			snprintf (outbuf, sizeof (outbuf), "DCC CHAT chat 199 %d %d",
+			g_snprintf (outbuf, sizeof (outbuf), "DCC CHAT chat 199 %d %d",
 						 dcc->port, dcc->pasvid);
 		} else
 		{
-			snprintf (outbuf, sizeof (outbuf), "DCC CHAT chat %u %d",
+			g_snprintf (outbuf, sizeof (outbuf), "DCC CHAT chat %u %d",
 						 dcc->addr, dcc->port);
 		}
 		dcc->serv->p_ctcp (dcc->serv, nick, outbuf);
@@ -2230,9 +2310,9 @@ dcc_resume (struct DCC *dcc)
 	{
 		dcc->resume_sent = 1;
 		/* filename contains spaces? Quote them! */
-		snprintf (tbuf, sizeof (tbuf) - 10, strchr (dcc->file, ' ') ?
-					  "DCC RESUME \"%s\" %d %"DCC_SFMT :
-					  "DCC RESUME %s %d %"DCC_SFMT,
+		g_snprintf (tbuf, sizeof (tbuf) - 10, strchr (dcc->file, ' ') ?
+					  "DCC RESUME \"%s\" %d %" G_GUINT64_FORMAT :
+					  "DCC RESUME %s %d %" G_GUINT64_FORMAT,
 					  dcc->file, dcc->port, dcc->resumable);
 
 		if (dcc->pasvid)
@@ -2287,7 +2367,7 @@ dcc_add_chat (session *sess, char *nick, int port, guint32 addr, int pasvid)
 		dcc->addr = addr;
 		dcc->port = port;
 		dcc->pasvid = pasvid;
-		dcc->nick = strdup (nick);
+		dcc->nick = g_strdup (nick);
 		dcc->starttime = time (0);
 
 		EMIT_SIGNAL (XP_TE_DCCCHATOFFER, sess->server->front_session, nick,
@@ -2307,7 +2387,7 @@ dcc_add_chat (session *sess, char *nick, int port, guint32 addr, int pasvid)
 		else
 		{
 			char buff[128];
-			snprintf (buff, sizeof (buff), "%s is offering DCC Chat. Do you want to accept?", nick);
+			g_snprintf (buff, sizeof (buff), "%s is offering DCC Chat. Do you want to accept?", nick);
 			fe_confirm (buff, dcc_confirm_chat, dcc_deny_chat, dcc);
 		}
 	}
@@ -2316,7 +2396,7 @@ dcc_add_chat (session *sess, char *nick, int port, guint32 addr, int pasvid)
 }
 
 static struct DCC *
-dcc_add_file (session *sess, char *file, DCC_SIZE size, int port, char *nick, guint32 addr, int pasvid)
+dcc_add_file (session *sess, char *file, guint64 size, int port, char *nick, guint32 addr, int pasvid)
 {
 	struct DCC *dcc;
 	char tbuf[512];
@@ -2324,7 +2404,7 @@ dcc_add_file (session *sess, char *file, DCC_SIZE size, int port, char *nick, gu
 	dcc = new_dcc ();
 	if (dcc)
 	{
-		dcc->file = strdup (file);
+		dcc->file = g_strdup (file);
 
 		dcc->destfile = g_malloc (strlen (prefs.hex_dcc_dir) + strlen (nick) +
 										  strlen (file) + 4);
@@ -2359,14 +2439,14 @@ dcc_add_file (session *sess, char *file, DCC_SIZE size, int port, char *nick, gu
 		dcc->port = port;
 		dcc->pasvid = pasvid;
 		dcc->size = size;
-		dcc->nick = strdup (nick);
+		dcc->nick = g_strdup (nick);
 		dcc->maxcps = prefs.hex_dcc_max_get_cps;
 
-		is_resumable (dcc);
+		update_is_resumable (dcc);
 
 		if (prefs.hex_dcc_auto_recv == 1)
 		{
-			snprintf (tbuf, sizeof (tbuf), _("%s is offering \"%s\". Do you want to accept?"), nick, file);
+			g_snprintf (tbuf, sizeof (tbuf), _("%s is offering \"%s\". Do you want to accept?"), nick, file);
 			fe_confirm (tbuf, dcc_confirm_send, dcc_deny_send, dcc);
 		}
 		else if (prefs.hex_dcc_auto_recv == 2)
@@ -2380,8 +2460,8 @@ dcc_add_file (session *sess, char *file, DCC_SIZE size, int port, char *nick, gu
 		} else
 			fe_dcc_add (dcc);
 	}
-	sprintf (tbuf, "%"DCC_SFMT, size);
-	snprintf (tbuf + 24, 300, "%s:%d", net_ip (addr), port);
+	sprintf (tbuf, "%" G_GUINT64_FORMAT, size);
+	g_snprintf (tbuf + 24, 300, "%s:%d", net_ip (addr), port);
 	EMIT_SIGNAL (XP_TE_DCCSENDOFFER, sess->server->front_session, nick,
 					 file, tbuf, tbuf + 24, 0);
 
@@ -2397,7 +2477,7 @@ handle_dcc (struct session *sess, char *nick, char *word[], char *word_eol[],
 	char *type = word[5];
 	int port, pasvid = 0;
 	guint32 addr;
-	DCC_SIZE size;
+	guint64 size;
 	int psend = 0;
 
 	if (!g_ascii_strcasecmp (type, "CHAT"))
@@ -2463,7 +2543,7 @@ handle_dcc (struct session *sess, char *nick, char *word[], char *word_eol[],
 			dcc = find_dcc (nick, word[6], TYPE_SEND);
 		if (dcc)
 		{
-			size = BIG_STR_TO_INT (word[8]);
+			size = g_ascii_strtoull (word[8], NULL, 10);
 			dcc->resumable = size;
 			if (dcc->resumable < dcc->size)
 			{
@@ -2473,19 +2553,19 @@ handle_dcc (struct session *sess, char *nick, char *word[], char *word_eol[],
 
 				/* Checking if dcc is passive and if filename contains spaces */
 				if (dcc->pasvid)
-					snprintf (tbuf, sizeof (tbuf), strchr (file_part (dcc->file), ' ') ?
-							"DCC ACCEPT \"%s\" %d %"DCC_SFMT" %d" :
-							"DCC ACCEPT %s %d %"DCC_SFMT" %d",
+					g_snprintf (tbuf, sizeof (tbuf), strchr (file_part (dcc->file), ' ') ?
+							"DCC ACCEPT \"%s\" %d %" G_GUINT64_FORMAT " %d" :
+							"DCC ACCEPT %s %d %" G_GUINT64_FORMAT " %d",
 							file_part (dcc->file), port, dcc->resumable, dcc->pasvid);
 				else
-					snprintf (tbuf, sizeof (tbuf), strchr (file_part (dcc->file), ' ') ?
-							"DCC ACCEPT \"%s\" %d %"DCC_SFMT :
-							"DCC ACCEPT %s %d %"DCC_SFMT,
+					g_snprintf (tbuf, sizeof (tbuf), strchr (file_part (dcc->file), ' ') ?
+							"DCC ACCEPT \"%s\" %d %" G_GUINT64_FORMAT :
+							"DCC ACCEPT %s %d %" G_GUINT64_FORMAT,
 							file_part (dcc->file), port, dcc->resumable);
 
 				dcc->serv->p_ctcp (dcc->serv, dcc->nick, tbuf);
 			}
-			sprintf (tbuf, "%"DCC_SFMT, dcc->pos);
+			sprintf (tbuf, "%" G_GUINT64_FORMAT, dcc->pos);
 			EMIT_SIGNAL_TIMESTAMP (XP_TE_DCCRESUMEREQUEST, sess, nick,
 										  file_part (dcc->file), tbuf, NULL, 0,
 										  tags_data->timestamp);
@@ -2508,7 +2588,7 @@ handle_dcc (struct session *sess, char *nick, char *word[], char *word_eol[],
 
 		port = atoi (word[8]);
 		addr = strtoul (word[7], NULL, 10);
-		size = BIG_STR_TO_INT (word[9]);
+		size = g_ascii_strtoull (word[9], NULL, 10);
 
 		if (port == 0) /* Passive dcc requested */
 			pasvid = atoi (word[10]);
@@ -2576,7 +2656,7 @@ dcc_show_list (struct session *sess)
 	{
 		dcc = (struct DCC *) list->data;
 		i++;
-		PrintTextf (sess, " %s  %-10.10s %-7.7s %-7"DCC_SFMT" %-7"DCC_SFMT" %s\n",
+		PrintTextf (sess, " %s  %-10.10s %-7.7s %-7" G_GUINT64_FORMAT " %-7" G_GUINT64_FORMAT " %s\n",
 					 dcctypes[dcc->type], dcc->nick,
 					 _(dccstat[dcc->dccstat].name), dcc->size, dcc->pos,
 					 file_part (dcc->file));
diff --git a/src/common/dcc.h b/src/common/dcc.h
index ade1dae7..e7115b32 100644
--- a/src/common/dcc.h
+++ b/src/common/dcc.h
@@ -39,17 +39,6 @@
 
 #define CPS_AVG_WINDOW 10
 
-/* can we do 64-bit dcc? */
-#if defined(G_GINT64_FORMAT) && defined(HAVE_STRTOULL)
-#define USE_DCC64
-/* we really get only 63 bits, since st_size is signed */
-#define DCC_SIZE gint64
-#define DCC_SFMT G_GINT64_FORMAT
-#else
-#define DCC_SIZE unsigned int
-#define DCC_SFMT "u"
-#endif
-
 struct DCC
 {
 	struct server *serv;
@@ -62,21 +51,21 @@ struct DCC
 	int wiotag;						/* writing/sending io tag */
 	int port;
 	int pasvid;						/* mIRC's passive DCC id */
-	int cps;
+	gint64 cps;
 	int resume_error;
 	int resume_errno;
 
 	GTimeVal lastcpstv, firstcpstv;
-	DCC_SIZE lastcpspos;
-	int maxcps;
+	goffset lastcpspos;
+	gint64 maxcps;
 
 	unsigned char ack_buf[4];	/* buffer for reading 4-byte ack */
 	int ack_pos;
 
-	DCC_SIZE size;
-	DCC_SIZE resumable;
-	DCC_SIZE ack;
-	DCC_SIZE pos;
+	guint64 size;
+	guint64 resumable;
+	guint64 ack;
+	guint64 pos;
 	time_t starttime;
 	time_t offertime;
 	time_t lasttime;
@@ -125,7 +114,7 @@ void dcc_check_timeouts (void);
 void dcc_change_nick (server *serv, char *oldnick, char *newnick);
 void dcc_notify_kill (struct server *serv);
 struct DCC *dcc_write_chat (char *nick, char *text);
-void dcc_send (struct session *sess, char *to, char *file, int maxcps, int passive);
+void dcc_send (struct session *sess, char *to, char *file, gint64 maxcps, int passive);
 struct DCC *find_dcc (char *nick, char *file, int type);
 void dcc_get_nick (struct session *sess, char *nick);
 void dcc_chat (session *sess, char *nick, int passive);
diff --git a/src/common/fe.h b/src/common/fe.h
index 2ca15c60..a3bd2afa 100644
--- a/src/common/fe.h
+++ b/src/common/fe.h
@@ -88,11 +88,10 @@ void fe_progressbar_start (struct session *sess);
 void fe_progressbar_end (struct server *serv);
 void fe_print_text (struct session *sess, char *text, time_t stamp,
 					gboolean no_activity);
-void fe_userlist_insert (struct session *sess, struct User *newuser, int row, int sel);
+void fe_userlist_insert (struct session *sess, struct User *newuser, gboolean sel);
 int fe_userlist_remove (struct session *sess, struct User *user);
 void fe_userlist_rehash (struct session *sess, struct User *user);
 void fe_userlist_update (struct session *sess, struct User *user);
-void fe_userlist_move (struct session *sess, struct User *user, int new_row);
 void fe_userlist_numbers (struct session *sess);
 void fe_userlist_clear (struct session *sess);
 void fe_userlist_set_selected (struct session *sess);
@@ -179,7 +178,6 @@ typedef enum
 } feicon;
 void fe_tray_set_icon (feicon icon);
 void fe_tray_set_tooltip (const char *text);
-void fe_tray_set_balloon (const char *title, const char *text);
 void fe_open_chan_list (server *serv, char *filter, int do_refresh);
 const char *fe_get_default_font ();
 
diff --git a/src/common/hexchat.c b/src/common/hexchat.c
index 09afa445..a76db332 100644
--- a/src/common/hexchat.c
+++ b/src/common/hexchat.c
@@ -41,7 +41,9 @@
 #include "chanopt.h"
 #include "ignore.h"
 #include "hexchat-plugin.h"
+#include "inbound.h"
 #include "plugin.h"
+#include "plugin-identd.h"
 #include "plugin-timer.h"
 #include "notify.h"
 #include "server.h"
@@ -55,15 +57,6 @@
 #include <glib-object.h>			/* for g_type_init() */
 #endif
 
-#ifdef USE_OPENSSL
-#include <openssl/ssl.h>			/* SSL_() */
-#include "ssl.h"
-#endif
-
-#ifdef USE_MSPROXY
-#include "msproxy.h"
-#endif
-
 #ifdef USE_LIBPROXY
 #include <proxy.h>
 #endif
@@ -118,10 +111,6 @@ struct session *current_tab;
 struct session *current_sess = 0;
 struct hexchatprefs prefs;
 
-#ifdef USE_OPENSSL
-SSL_CTX *ctx = NULL;
-#endif
-
 #ifdef USE_LIBPROXY
 pxProxyFactory *libproxy_factory;
 #endif
@@ -221,7 +210,7 @@ find_dialog (server *serv, char *nick)
 		}
 		list = list->next;
 	}
-	return 0;
+	return NULL;
 }
 
 session *
@@ -232,14 +221,14 @@ find_channel (server *serv, char *chan)
 	while (list)
 	{
 		sess = list->data;
-		if ((!serv || serv == sess->server) && sess->type == SESS_CHANNEL)
+		if ((serv == sess->server) && sess->type == SESS_CHANNEL)
 		{
 			if (!serv->p_cmp (chan, sess->channel))
 				return sess;
 		}
 		list = list->next;
 	}
-	return 0;
+	return NULL;
 }
 
 static void
@@ -269,7 +258,7 @@ lag_check (void)
 	unsigned long tim;
 	char tbuf[128];
 	time_t now = time (0);
-	int lag;
+	time_t lag;
 
 	tim = make_ping_time ();
 
@@ -279,16 +268,17 @@ lag_check (void)
 		if (serv->connected && serv->end_of_motd)
 		{
 			lag = now - serv->ping_recv;
-			if (prefs.hex_net_ping_timeout && lag > prefs.hex_net_ping_timeout && lag > 0)
+			if (prefs.hex_net_ping_timeout != 0 && lag > prefs.hex_net_ping_timeout && lag > 0)
 			{
-				sprintf (tbuf, "%d", lag);
+				sprintf (tbuf, "%" G_GINT64_FORMAT, (gint64) lag);
 				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);
-			} else
+			}
+			else
 			{
-				snprintf (tbuf, sizeof (tbuf), "LAG%lu", tim);
+				g_snprintf (tbuf, sizeof (tbuf), "LAG%lu", tim);
 				serv->p_ping (serv, "", tbuf);
 				
 				if (!serv->lag_sent)
@@ -368,9 +358,6 @@ static int
 hexchat_misc_checks (void)		/* this gets called every 1/2 second */
 {
 	static int count = 0;
-#ifdef USE_MSPROXY
-	static int count2 = 0;
-#endif
 
 	count++;
 
@@ -386,15 +373,6 @@ hexchat_misc_checks (void)		/* this gets called every 1/2 second */
 		count = 0;
 	}
 
-#ifdef USE_MSPROXY	
-	count2++;
-	if (count2 >= 720)			/* 720 every 6 minutes */
-	{
-		msproxy_keepalive ();
-		count2 = 0;
-	}
-#endif
-
 	return 1;
 }
 
@@ -405,7 +383,6 @@ irc_init (session *sess)
 {
 	static int done_init = FALSE;
 	char *buf;
-	int i;
 
 	if (done_init)
 		return;
@@ -413,6 +390,7 @@ irc_init (session *sess)
 	done_init = TRUE;
 
 	plugin_add (sess, NULL, NULL, timer_plugin_init, NULL, NULL, FALSE);
+	plugin_add (sess, NULL, NULL, identd_plugin_init, identd_plugin_deinit, NULL, FALSE);
 
 #ifdef USE_PLUGIN
 	if (!arg_skip_plugins)
@@ -440,7 +418,8 @@ irc_init (session *sess)
 	
 	if (arg_urls != NULL)
 	{
-		for (i = 0; i < g_strv_length(arg_urls); i++)
+		guint i;
+		for (i = 0; i < g_strv_length (arg_urls); i++)
 		{
 			buf = g_strdup_printf ("%s %s", i==0? "server" : "newserver", arg_urls[i]);
 			handle_command (sess, buf, FALSE);
@@ -464,12 +443,7 @@ session_new (server *serv, char *from, int type, int focus)
 {
 	session *sess;
 
-	sess = malloc (sizeof (struct session));
-	if (sess == NULL)
-	{
-		return NULL;
-	}
-	memset (sess, 0, sizeof (struct session));
+	sess = g_new0 (struct session, 1);
 
 	sess->server = serv;
 	sess->logfd = -1;
@@ -488,7 +462,10 @@ session_new (server *serv, char *from, int type, int focus)
 	sess->lastact_idx = LACT_NONE;
 
 	if (from != NULL)
-		safe_strcpy (sess->channel, from, CHANLEN);
+	{
+		safe_strcpy(sess->channel, from, CHANLEN);
+		safe_strcpy(sess->session_name, from, CHANLEN);
+	}
 
 	sess_list = g_slist_prepend (sess_list, sess);
 
@@ -515,7 +492,6 @@ new_ircwindow (server *serv, char *name, int type, int focus)
 		break;
 	case SESS_DIALOG:
 		sess = session_new (serv, name, type, focus);
-		log_open_or_close (sess);
 		break;
 	default:
 /*	case SESS_CHANNEL:
@@ -530,6 +506,16 @@ new_ircwindow (server *serv, char *name, int type, int focus)
 	scrollback_load (sess);
 	if (sess->scrollwritten && sess->scrollback_replay_marklast)
 		sess->scrollback_replay_marklast (sess);
+	if (type == SESS_DIALOG)
+	{
+		struct User *user;
+
+		log_open_or_close (sess);
+
+		user = userlist_find_global (serv, name);
+		if (user && user->hostname)
+			set_topic (sess, user->hostname, user->hostname);
+	}
 	plugin_emit_dummy_print (sess, "Open Context");
 
 	return sess;
@@ -548,9 +534,8 @@ exec_notify_kill (session * sess)
 		waitpid (re->childpid, NULL, WNOHANG);
 		fe_input_remove (re->iotag);
 		close (re->myfd);
-		if (re->linebuf)
-			free(re->linebuf);
-		free (re);
+		g_free(re->linebuf);
+		g_free (re);
 	}
 #endif
 }
@@ -656,10 +641,8 @@ session_free (session *killsess)
 	send_quit_or_part (killsess);
 
 	history_free (&killsess->history);
-	if (killsess->topic)
-		free (killsess->topic);
-	if (killsess->current_modes)
-		free (killsess->current_modes);
+	g_free (killsess->topic);
+	g_free (killsess->current_modes);
 
 	fe_session_callback (killsess);
 
@@ -670,7 +653,7 @@ session_free (session *killsess)
 			current_sess = sess_list->data;
 	}
 
-	free (killsess);
+	g_free (killsess);
 
 	if (!sess_list && !in_hexchat_exit)
 		hexchat_exit ();						/* sess_list is empty, quit! */
@@ -784,20 +767,15 @@ static void
 xchat_init (void)
 {
 	char buf[3068];
-	const char *cs = NULL;
 
 #ifdef WIN32
 	WSADATA wsadata;
 
-#ifdef USE_IPV6
 	if (WSAStartup(0x0202, &wsadata) != 0)
 	{
 		MessageBox (NULL, "Cannot find winsock 2.2+", "Error", MB_OK);
 		exit (0);
 	}
-#else
-	WSAStartup(0x0101, &wsadata);
-#endif	/* !USE_IPV6 */
 #endif	/* !WIN32 */
 
 #ifdef USE_SIGACTION
@@ -826,15 +804,12 @@ xchat_init (void)
 #endif
 #endif
 
-	if (g_get_charset (&cs))
-		prefs.utf8_locale = TRUE;
-
 	load_text_events ();
 	sound_load ();
 	notify_load ();
 	ignore_load ();
 
-	snprintf (buf, sizeof (buf),
+	g_snprintf (buf, sizeof (buf),
 		"NAME %s~%s~\n"				"CMD query %%s\n\n"\
 		"NAME %s~%s~\n"				"CMD send %%s\n\n"\
 		"NAME %s~%s~\n"				"CMD whois %%s %%s\n\n"\
@@ -890,7 +865,7 @@ xchat_init (void)
 
 	list_loadconf ("popup.conf", &popup_list, buf);
 
-	snprintf (buf, sizeof (buf),
+	g_snprintf (buf, sizeof (buf),
 		"NAME %s\n"				"CMD part\n\n"
 		"NAME %s\n"				"CMD getstr # join \"%s\"\n\n"
 		"NAME %s\n"				"CMD quote LINKS\n\n"
@@ -904,7 +879,7 @@ xchat_init (void)
 				_("Hide Version"));
 	list_loadconf ("usermenu.conf", &usermenu_list, buf);
 
-	snprintf (buf, sizeof (buf),
+	g_snprintf (buf, sizeof (buf),
 		"NAME %s\n"		"CMD op %%a\n\n"
 		"NAME %s\n"		"CMD deop %%a\n\n"
 		"NAME %s\n"		"CMD ban %%s\n\n"
@@ -921,7 +896,7 @@ xchat_init (void)
 				_("Dialog"));
 	list_loadconf ("buttons.conf", &button_list, buf);
 
-	snprintf (buf, sizeof (buf),
+	g_snprintf (buf, sizeof (buf),
 		"NAME %s\n"				"CMD whois %%s %%s\n\n"
 		"NAME %s\n"				"CMD send %%s\n\n"
 		"NAME %s\n"				"CMD dcc chat %%s\n\n"
@@ -1021,29 +996,37 @@ main (int argc, char *argv[])
 	int i;
 	int ret;
 
-	srand (time (0));	/* CL: do this only once! */
+#ifdef WIN32
+	HRESULT coinit_result;
+#endif
+
+	srand ((unsigned int) time (NULL)); /* CL: do this only once! */
 
 	/* We must check for the config dir parameter, otherwise load_config() will behave incorrectly.
 	 * load_config() must come before fe_args() because fe_args() calls gtk_init() which needs to
 	 * know the language which is set in the config. The code below is copy-pasted from fe_args()
 	 * for the most part. */
-	if (argc >= 3)
+	if (argc >= 2)
 	{
-		for (i = 1; i < argc - 1; i++)
+		for (i = 1; i < argc; i++)
 		{
-			if (strcmp (argv[i], "-d") == 0)
+			if ((strcmp (argv[i], "-d") == 0 || strcmp (argv[i], "--cfgdir") == 0)
+				&& i + 1 < argc)
 			{
-				if (xdir)
-				{
-					g_free (xdir);
-				}
-
-				xdir = strdup (argv[i + 1]);
+				xdir = g_strdup (argv[i + 1]);
+			}
+			else if (strncmp (argv[i], "--cfgdir=", 9) == 0)
+			{
+				xdir = g_strdup (argv[i] + 9);
+			}
 
+			if (xdir != NULL)
+			{
 				if (xdir[strlen (xdir) - 1] == G_DIR_SEPARATOR)
 				{
 					xdir[strlen (xdir) - 1] = 0;
 				}
+				break;
 			}
 		}
 	}
@@ -1067,10 +1050,6 @@ main (int argc, char *argv[])
 	/* we MUST do this after load_config () AND before fe_init (thus gtk_init) otherwise it will fail */
 	set_locale ();
 
-#ifdef SOCKS
-	SOCKSinit (argv[0]);
-#endif
-
 	ret = fe_args (argc, argv);
 	if (ret != -1)
 		return ret;
@@ -1083,6 +1062,14 @@ main (int argc, char *argv[])
 	libproxy_factory = px_proxy_factory_new();
 #endif
 
+#ifdef WIN32
+	coinit_result = CoInitializeEx (NULL, COINIT_APARTMENTTHREADED);
+	if (SUCCEEDED (coinit_result))
+	{
+		CoInitializeSecurity (NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_DEFAULT, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, NULL);
+	}
+#endif
+
 	fe_init ();
 
 	/* This is done here because cfgfiles.c is too early in
@@ -1110,13 +1097,15 @@ main (int argc, char *argv[])
 
 	fe_main ();
 
-#ifdef USE_LIBPROXY
-	px_proxy_factory_free(libproxy_factory);
+#ifdef WIN32
+	if (SUCCEEDED (coinit_result))
+	{
+		CoUninitialize ();
+	}
 #endif
 
-#ifdef USE_OPENSSL
-	if (ctx)
-		_SSL_context_free (ctx);
+#ifdef USE_LIBPROXY
+	px_proxy_factory_free(libproxy_factory);
 #endif
 
 #ifdef WIN32
diff --git a/src/common/hexchat.h b/src/common/hexchat.h
index bbf32da5..652dcf1d 100644
--- a/src/common/hexchat.h
+++ b/src/common/hexchat.h
@@ -22,6 +22,7 @@
 #include <glib.h>
 #include <glib/gstdio.h>
 #include <glib/gi18n.h>
+#include <gio/gio.h>
 
 #include <time.h>			/* need time_t */
 
@@ -36,22 +37,7 @@
 #endif
 
 #include "history.h"
-
-#ifndef HAVE_SNPRINTF
-#define snprintf g_snprintf
-#endif
-
-#ifndef HAVE_VSNPRINTF
-#define vsnprintf _vsnprintf
-#endif
-
-#ifdef SOCKS
-#ifdef __sgi
-#include <sys/time.h>
-#define INCLUDE_PROTOTYPES 1
-#endif
-#include <socks.h>
-#endif
+#include "tree.h"
 
 #ifdef USE_OPENSSL
 #include <openssl/ssl.h>		  /* SSL_() */
@@ -262,6 +248,7 @@ struct hexchatprefs
 	int hex_gui_search_pos;
 	int hex_gui_slist_select;
 	int hex_gui_tab_layout;
+	int hex_gui_tab_middleclose;
 	int hex_gui_tab_newtofront;
 	int hex_gui_tab_pos;
 	int hex_gui_tab_small;
@@ -277,7 +264,7 @@ struct hexchatprefs
 	int hex_gui_win_state;
 	int hex_gui_win_top;
 	int hex_gui_win_width;
-	int hex_input_balloon_time;
+	int hex_identd_port;
 	int hex_irc_ban_type;
 	int hex_irc_join_delay;
 	int hex_irc_notice_pos;
@@ -329,7 +316,6 @@ struct hexchatprefs
 	guint32 dcc_ip;
 
 	unsigned int wait_on_exit;	/* wait for logs to be flushed to disk IF we're connected */
-	unsigned int utf8_locale;
 
 	/* Tells us if we need to save, only when they've been edited.
 		This is so that we continue using internal defaults (which can
@@ -382,12 +368,12 @@ typedef struct session
 	guint8 text_strip;
 
 	struct server *server;
-	void *usertree_alpha;			/* pure alphabetical tree */
-	void *usertree;					/* ordered with Ops first */
+	tree *usertree;					/* alphabetical tree */
 	struct User *me;					/* points to myself in the usertree */
 	char channel[CHANLEN];
 	char waitchannel[CHANLEN];		  /* waiting to join channel (/join sent) */
 	char willjoinchannel[CHANLEN];	  /* will issue /join for this channel */
+	char session_name[CHANLEN];		 /* the name of the session, should not modified */
 	char channelkey[64];			  /* XXX correct max length? */
 	int limit;						  /* channel user limit */
 	int logfd;
@@ -434,14 +420,6 @@ typedef struct session
 	void (*scrollback_replay_marklast) (struct session *sess);
 } session;
 
-struct msproxy_state_t
-{
-	gint32				clientid;
-	gint32				serverid;
-	unsigned char		seq_recv;		/* seq number of last packet recv.	*/
-	unsigned char		seq_sent;		/* seq number of last packet sent.	*/
-};
-
 /* SASL Mechanisms */
 #define MECH_PLAIN 0
 #define MECH_BLOWFISH 1
@@ -499,9 +477,9 @@ typedef struct server
 	int proxy_sok;				/* Additional information for MS Proxy beast */
 	int proxy_sok4;
 	int proxy_sok6;
-	struct msproxy_state_t msp_state;
 	int id;					/* unique ID number (for plugin API) */
 #ifdef USE_OPENSSL
+	SSL_CTX *ctx;
 	SSL *ssl;
 	int ssl_do_connect_tag;
 #else
@@ -554,7 +532,10 @@ typedef struct server
 	time_t ping_recv;					/* when we last got a ping reply */
 	time_t away_time;					/* when we were marked away */
 
-	char *encoding;					/* NULL for system */
+	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 */
 
 	unsigned int motd_skipped:1;
@@ -587,8 +568,6 @@ typedef struct server
 	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 using_cp1255:1;	/* encoding is CP1255/WINDOWS-1255? */
-	unsigned int using_irc:1;		/* encoding is "IRC" (CP1252/UTF-8 hybrid)? */
 	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_saslauth:1;	/* have sent AUTHENICATE yet */
@@ -631,7 +610,4 @@ struct popup
 /* CL: get a random int in the range [0..n-1]. DON'T use rand() % n, it gives terrible results. */
 #define RAND_INT(n) ((int)(rand() / (RAND_MAX + 1.0) * (n)))
 
-#define hexchat_filename_from_utf8 g_filename_from_utf8
-#define hexchat_filename_to_utf8 g_filename_to_utf8
-
 #endif
diff --git a/src/common/history.c b/src/common/history.c
index 1acd3327..23a8463e 100644
--- a/src/common/history.c
+++ b/src/common/history.c
@@ -18,14 +18,14 @@
 
 #include <string.h>
 #include <stdlib.h>
+#include <glib.h>
 #include "history.h"
 
 void
 history_add (struct history *his, char *text)
 {
-	if (his->lines[his->realpos])
-		free (his->lines[his->realpos]);
-	his->lines[his->realpos] = strdup (text);
+	g_free (his->lines[his->realpos]);
+	his->lines[his->realpos] = g_strdup (text);
 	his->realpos++;
 	if (his->realpos == HISTORY_SIZE)
 		his->realpos = 0;
@@ -40,7 +40,7 @@ history_free (struct history *his)
 	{
 		if (his->lines[i])
 		{
-			free (his->lines[i]);
+			g_free (his->lines[i]);
 			his->lines[i] = 0;
 		}
 	}
@@ -52,7 +52,7 @@ history_down (struct history *his)
 	int next;
 
 	if (his->pos == his->realpos)	/* allow down only after up */
-		return 0;
+		return NULL;
 	if (his->realpos == 0)
 	{
 		if (his->pos == HISTORY_SIZE - 1)
@@ -79,7 +79,7 @@ history_down (struct history *his)
 		return his->lines[his->pos];
 	}
 
-	return 0;
+	return NULL;
 }
 
 char *
@@ -90,11 +90,11 @@ history_up (struct history *his, char *current_text)
 	if (his->realpos == HISTORY_SIZE - 1)
 	{
 		if (his->pos == 0)
-			return 0;
+			return NULL;
 	} else
 	{
 		if (his->pos == his->realpos + 1)
-			return 0;
+			return NULL;
 	}
 
 	next = HISTORY_SIZE - 1;
@@ -117,5 +117,5 @@ history_up (struct history *his, char *current_text)
 		return his->lines[his->pos];
 	}
 
-	return 0;
+	return NULL;
 }
diff --git a/src/common/identd.c b/src/common/identd.c
deleted file mode 100644
index c4050929..00000000
--- a/src/common/identd.c
+++ /dev/null
@@ -1,201 +0,0 @@
-/* HexChat
- * Copyright (C) 1998-2010 Peter Zelezny.
- * Copyright (C) 2009-2013 Berke Viktor.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
- */
-
-/* simple identd server for HexChat under Win32 */
-
-#include "inet.h"
-#include "hexchat.h"
-#include "hexchatc.h"
-#include "text.h"
-
-static int identd_is_running = FALSE;
-#ifdef USE_IPV6
-static int identd_ipv6_is_running = FALSE;
-#endif
-
-static int
-identd (char *username)
-{
-	int sok, read_sok, len;
-	char *p;
-	char buf[256];
-	char outbuf[256];
-	char ipbuf[INET_ADDRSTRLEN];
-	struct sockaddr_in addr;
-
-	sok = socket (AF_INET, SOCK_STREAM, 0);
-	if (sok == INVALID_SOCKET)
-	{
-		free (username);
-		return 0;
-	}
-
-	len = 1;
-	setsockopt (sok, SOL_SOCKET, SO_REUSEADDR, (char *) &len, sizeof (len));
-
-	memset (&addr, 0, sizeof (addr));
-	addr.sin_family = AF_INET;
-	addr.sin_port = htons (113);
-
-	if (bind (sok, (struct sockaddr *) &addr, sizeof (addr)) == SOCKET_ERROR)
-	{
-		closesocket (sok);
-		free (username);
-		return 0;
-	}
-
-	if (listen (sok, 1) == SOCKET_ERROR)
-	{
-		closesocket (sok);
-		free (username);
-		return 0;
-	}
-
-	len = sizeof (addr);
-	read_sok = accept (sok, (struct sockaddr *) &addr, &len);
-	closesocket (sok);
-	if (read_sok == INVALID_SOCKET)
-	{
-		free (username);
-		return 0;
-	}
-
-	identd_is_running = FALSE;
-
-#if 0	/* causes random crashes, probably due to CreateThread */
-	EMIT_SIGNAL (XP_TE_IDENTD, current_sess, inet_ntoa (addr.sin_addr), username, NULL, NULL, 0);
-#endif
-	inet_ntop (AF_INET, &addr.sin_addr, ipbuf, sizeof (ipbuf));
-	snprintf (outbuf, sizeof (outbuf), "*\tServicing ident request from %s as %s\n", ipbuf, username);
-	PrintText (current_sess, outbuf);
-
-	recv (read_sok, buf, sizeof (buf) - 1, 0);
-	buf[sizeof (buf) - 1] = 0;	  /* ensure null termination */
-
-	p = strchr (buf, ',');
-	if (p)
-	{
-		snprintf (outbuf, sizeof (outbuf) - 1, "%d, %d : USERID : UNIX : %s\r\n",
-					 atoi (buf), atoi (p + 1), username);
-		outbuf[sizeof (outbuf) - 1] = 0;	/* ensure null termination */
-		send (read_sok, outbuf, strlen (outbuf), 0);
-	}
-
-	sleep (1);
-	closesocket (read_sok);
-	free (username);
-
-	return 0;
-}
-
-#ifdef USE_IPV6
-static int
-identd_ipv6 (char *username)
-{
-	int sok, read_sok, len;
-	char *p;
-	char buf[256];
-	char outbuf[256];
-	char ipbuf[INET6_ADDRSTRLEN];
-	struct sockaddr_in6 addr;
-
-	sok = socket (AF_INET6, SOCK_STREAM, 0);
-	if (sok == INVALID_SOCKET)
-	{
-		free (username);
-		return 0;
-	}
-
-	len = 1;
-	setsockopt (sok, SOL_SOCKET, SO_REUSEADDR, (char *) &len, sizeof (len));
-
-	memset (&addr, 0, sizeof (addr));
-	addr.sin6_family = AF_INET6;
-	addr.sin6_port = htons (113);
-
-	if (bind (sok, (struct sockaddr *) &addr, sizeof (addr)) == SOCKET_ERROR)
-	{
-		closesocket (sok);
-		free (username);
-		return 0;
-	}
-
-	if (listen (sok, 1) == SOCKET_ERROR)
-	{
-		closesocket (sok);
-		free (username);
-		return 0;
-	}
-
-	len = sizeof (addr);
-	read_sok = accept (sok, (struct sockaddr *) &addr, &len);
-	closesocket (sok);
-	if (read_sok == INVALID_SOCKET)
-	{
-		free (username);
-		return 0;
-	}
-
-	identd_ipv6_is_running = FALSE;
-
-	inet_ntop (AF_INET6, &addr.sin6_addr, ipbuf, sizeof (ipbuf));
-	snprintf (outbuf, sizeof (outbuf), "*\tServicing ident request from %s as %s\n", ipbuf, username);
-	PrintText (current_sess, outbuf);
-
-	recv (read_sok, buf, sizeof (buf) - 1, 0);
-	buf[sizeof (buf) - 1] = 0;	  /* ensure null termination */
-
-	p = strchr (buf, ',');
-	if (p)
-	{
-		snprintf (outbuf, sizeof (outbuf) - 1, "%d, %d : USERID : UNIX : %s\r\n", atoi (buf), atoi (p + 1), username);
-		outbuf[sizeof (outbuf) - 1] = 0;	/* ensure null termination */
-		send (read_sok, outbuf, strlen (outbuf), 0);
-	}
-
-	sleep (1);
-	closesocket (read_sok);
-	free (username);
-
-	return 0;
-}
-#endif
-
-void
-identd_start (char *username)
-{
-	DWORD tid;
-
-#ifdef USE_IPV6
-	DWORD tidv6;
-	if (identd_ipv6_is_running == FALSE)
-	{
-		identd_ipv6_is_running = TRUE;
-		CloseHandle (CreateThread (NULL, 0, (LPTHREAD_START_ROUTINE) identd_ipv6,
-						 strdup (username), 0, &tidv6));
-	}
-#endif
-
-	if (identd_is_running == FALSE)
-	{
-		identd_is_running = TRUE;
-		CloseHandle (CreateThread (NULL, 0, (LPTHREAD_START_ROUTINE) identd,
-						 strdup (username), 0, &tid));
-	}
-}
diff --git a/src/common/ignore.c b/src/common/ignore.c
index 045224ba..6085b657 100644
--- a/src/common/ignore.c
+++ b/src/common/ignore.c
@@ -53,7 +53,7 @@ static int ignored_total = 0;
 struct ignore *
 ignore_exists (char *mask)
 {
-	struct ignore *ig = 0;
+	struct ignore *ig = NULL;
 	GSList *list;
 
 	list = ignore_list;
@@ -79,7 +79,7 @@ ignore_exists (char *mask)
 int
 ignore_add (char *mask, int type, gboolean overwrite)
 {
-	struct ignore *ig = 0;
+	struct ignore *ig = NULL;
 	int change_only = FALSE;
 
 	/* first check if it's already ignored */
@@ -88,12 +88,9 @@ ignore_add (char *mask, int type, gboolean overwrite)
 		change_only = TRUE;
 
 	if (!change_only)
-		ig = malloc (sizeof (struct ignore));
+		ig = g_new (struct ignore, 1);
 
-	if (!ig)
-		return 0;
-
-	ig->mask = strdup (mask);
+	ig->mask = g_strdup (mask);
 
 	if (!overwrite && change_only)
 		ig->type |= type;
@@ -125,7 +122,7 @@ ignore_showlist (session *sess)
 		ig = list->data;
 		i++;
 
-		snprintf (tbuf, sizeof (tbuf), " %-25s ", ig->mask);
+		g_snprintf (tbuf, sizeof (tbuf), " %-25s ", ig->mask);
 		if (ig->type & IG_PRIV)
 			strcat (tbuf, _("YES  "));
 		else
@@ -192,8 +189,8 @@ ignore_del (char *mask, struct ignore *ig)
 	if (ig)
 	{
 		ignore_list = g_slist_remove (ignore_list, ig);
-		free (ig->mask);
-		free (ig);
+		g_free (ig->mask);
+		g_free (ig);
 		fe_ignore_update (1);
 		return TRUE;
 	}
@@ -265,7 +262,7 @@ ignore_read_next_entry (char *my_cfg, struct ignore *ignore)
 		my_cfg = cfg_get_str (my_cfg, "mask", tbuf, sizeof (tbuf));
 		if (!my_cfg)
 			return NULL;
-		ignore->mask = strdup (tbuf);
+		ignore->mask = g_strdup (tbuf);
 	}
 	if (my_cfg)
 	{
@@ -281,7 +278,7 @@ ignore_load ()
 	struct ignore *ignore;
 	struct stat st;
 	char *cfg, *my_cfg;
-	int fh, i;
+	int fh;
 
 	fh = hexchat_open_file ("ignore.conf", O_RDONLY, 0, 0);
 	if (fh != -1)
@@ -289,22 +286,18 @@ ignore_load ()
 		fstat (fh, &st);
 		if (st.st_size)
 		{
-			cfg = malloc (st.st_size + 1);
-			cfg[0] = '\0';
-			i = read (fh, cfg, st.st_size);
-			if (i >= 0)
-				cfg[i] = '\0';
+			cfg = g_malloc0 (st.st_size + 1);
+			read (fh, cfg, st.st_size);
 			my_cfg = cfg;
 			while (my_cfg)
 			{
-				ignore = malloc (sizeof (struct ignore));
-				memset (ignore, 0, sizeof (struct ignore));
+				ignore = g_new0 (struct ignore, 1);
 				if ((my_cfg = ignore_read_next_entry (my_cfg, ignore)))
 					ignore_list = g_slist_prepend (ignore_list, ignore);
 				else
-					free (ignore);
+					g_free (ignore);
 			}
-			free (cfg);
+			g_free (cfg);
 		}
 		close (fh);
 	}
@@ -326,7 +319,7 @@ ignore_save ()
 			ig = (struct ignore *) temp->data;
 			if (!(ig->type & IG_NOSAVE))
 			{
-				snprintf (buf, sizeof (buf), "mask = %s\ntype = %u\n\n",
+				g_snprintf (buf, sizeof (buf), "mask = %s\ntype = %u\n\n",
 							 ig->mask, ig->type);
 				write (fh, buf, strlen (buf));
 			}
@@ -379,9 +372,9 @@ flood_check (char *nick, char *ip, server *serv, session *sess, int what)	/*0=ct
 					for (i = 0; i < 128; i++)
 						if (ip[i] == '@')
 							break;
-					snprintf (real_ip, sizeof (real_ip), "*!*%s", &ip[i]);
+					g_snprintf (real_ip, sizeof (real_ip), "*!*%s", &ip[i]);
 
-					snprintf (buf, sizeof (buf),
+					g_snprintf (buf, sizeof (buf),
 								 _("You are being CTCP flooded from %s, ignoring %s\n"),
 								 nick, real_ip);
 					PrintText (sess, buf);
@@ -406,7 +399,7 @@ flood_check (char *nick, char *ip, server *serv, session *sess, int what)	/*0=ct
 				serv->msg_counter++;
 				if (serv->msg_counter == prefs.hex_flood_msg_num)	/*if we reached the maximun numbers of ctcp in the seconds limits */
 				{
-					snprintf (buf, sizeof (buf),
+					g_snprintf (buf, sizeof (buf),
 					 _("You are being MSG flooded from %s, setting gui_autoopen_dialog OFF.\n"),
 								 ip);
 					PrintText (sess, buf);
diff --git a/src/common/inbound.c b/src/common/inbound.c
index b80553b3..ef26890b 100644
--- a/src/common/inbound.c
+++ b/src/common/inbound.c
@@ -33,8 +33,6 @@
 #define WANTDNS
 #include "inet.h"
 
-#include <gio/gio.h>
-
 #include "hexchat.h"
 #include "util.h"
 #include "ignore.h"
@@ -64,7 +62,7 @@ clear_channel (session *sess)
 
 	if (sess->current_modes)
 	{
-		free (sess->current_modes);
+		g_free (sess->current_modes);
 		sess->current_modes = NULL;
 	}
 
@@ -83,9 +81,17 @@ clear_channel (session *sess)
 void
 set_topic (session *sess, char *topic, char *stripped_topic)
 {
-	if (sess->topic)
-		free (sess->topic);
-	sess->topic = strdup (stripped_topic);
+	/* The topic of dialogs are the users hostname which is logged is new */
+	if (sess->type == SESS_DIALOG && (!sess->topic || strcmp(sess->topic, stripped_topic))
+		&& sess->logfd != -1)
+	{
+		char tbuf[1024];
+		g_snprintf (tbuf, sizeof (tbuf), "[%s has address %s]\n", sess->channel, stripped_topic);
+		write (sess->logfd, tbuf, strlen (tbuf));
+	}
+
+	g_free (sess->topic);
+	sess->topic = g_strdup (stripped_topic);
 	fe_set_topic (sess, topic, stripped_topic);
 }
 
@@ -121,7 +127,7 @@ find_session_from_nick (char *nick, server *serv)
 		}
 		list = list->next;
 	}
-	return 0;
+	return NULL;
 }
 
 static session *
@@ -182,16 +188,7 @@ inbound_privmsg (server *serv, char *from, char *ip, char *text, int id,
 		}
 
 		if (ip && ip[0])
-		{
-			if (prefs.hex_irc_logging && sess->logfd != -1 &&
-				(!sess->topic || strcmp(sess->topic, ip)))
-			{
-				char tbuf[1024];
-				snprintf (tbuf, sizeof (tbuf), "[%s has address %s]\n", from, ip);
-				write (sess->logfd, tbuf, strlen (tbuf));
-			}
 			set_topic (sess, ip, ip);
-		}
 		inbound_chanmsg (serv, NULL, NULL, from, text, FALSE, id, tags_data);
 		return;
 	}
@@ -558,7 +555,7 @@ find_unused_session (server *serv)
 		}
 		list = list->next;
 	}
-	return 0;
+	return NULL;
 }
 
 static session *
@@ -576,7 +573,7 @@ find_session_from_waitchannel (char *chan, struct server *serv)
 		}
 		list = list->next;
 	}
-	return 0;
+	return NULL;
 }
 
 void
@@ -682,7 +679,8 @@ inbound_nameslist (server *serv, char *chan, char *names,
 	char **name_list;
 	char *host, *nopre_name;
 	char name[NICKLEN];
-	int i, offset;
+	int i;
+	size_t offset;
 
 	sess = find_channel (serv, chan);
 	if (!sess)
@@ -916,7 +914,7 @@ inbound_ping_reply (session *sess, char *timestring, char *from,
 										  tags_data->timestamp);
 	} else
 	{
-		snprintf (outbuf, sizeof (outbuf), "%ld.%03ld", dif / 1000, dif % 1000);
+		g_snprintf (outbuf, sizeof (outbuf), "%ld.%03ld", dif / 1000, dif % 1000);
 		EMIT_SIGNAL_TIMESTAMP (XP_TE_PINGREP, sess, from, outbuf, NULL, NULL, 0,
 									  tags_data->timestamp);
 	}
@@ -934,7 +932,7 @@ find_session_from_type (int type, server *serv)
 			return sess;
 		list = list->next;
 	}
-	return 0;
+	return NULL;
 }
 
 void
@@ -969,14 +967,14 @@ inbound_notice (server *serv, char *to, char *nick, char *msg, char *ip, int id,
 				/* guess where chanserv meant to post this -sigh- */
 				if (!g_ascii_strcasecmp (nick, "ChanServ") && !find_dialog (serv, nick))
 				{
-					char *dest = strdup (msg + 1);
+					char *dest = g_strdup (msg + 1);
 					char *end = strchr (dest, ']');
 					if (end)
 					{
 						*end = 0;
 						sess = find_channel (serv, dest);
 					}
-					free (dest);
+					g_free (dest);
 				}
 			}
 			if (!sess)
@@ -1455,8 +1453,7 @@ inbound_user_info (session *sess, char *chan, char *user, char *host,
 
 	if (user && host)
 	{
-		uhost = g_malloc (strlen (user) + strlen (host) + 2);
-		sprintf (uhost, "%s@%s", user, host);
+		uhost = g_strdup_printf ("%s@%s", user, host);
 	}
 
 	if (chan)
diff --git a/src/common/inet.h b/src/common/inet.h
index 990415be..7056d473 100644
--- a/src/common/inet.h
+++ b/src/common/inet.h
@@ -47,13 +47,9 @@
 
 #else
 
-#include "../../config.h"
-#ifdef USE_IPV6
+#include "config.h"
 #include <winsock2.h>
 #include <ws2tcpip.h>
-#else
-#include <winsock2.h>
-#endif
 
 #define set_blocking(sok)	{ \
 									unsigned long zero = 0; \
diff --git a/src/common/make-te.c b/src/common/make-te.c
index 309eec2f..834646ef 100644
--- a/src/common/make-te.c
+++ b/src/common/make-te.c
@@ -42,7 +42,7 @@
 #include <string.h>
 #include <stdlib.h>
 
-int main()
+int main(void)
 {
 	char name[512];
 	char num[512];
@@ -87,9 +87,11 @@ int main()
 		if (i + 1 < max)
 		{
 			fprintf(stderr, "\t%s,\t\t%s,\n", defines[i], defines[i+1]);
+			free (defines[i]);
 			i++;
 		} else
 			fprintf(stderr, "\t%s,\n", defines[i]);
+		free (defines[i]);
 		i++;
 	}
 	fprintf(stderr, "\tNUM_XP\n};\n");
diff --git a/src/common/make-te.vcxproj b/src/common/make-te.vcxproj
index e9b4c533..24d8f9b6 100644
--- a/src/common/make-te.vcxproj
+++ b/src/common/make-te.vcxproj
@@ -2,6 +2,7 @@
 <Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

   <PropertyGroup Label="Configuration">

     <PlatformToolset>v120</PlatformToolset>

+    <ConfigurationType>Application</ConfigurationType>

   </PropertyGroup>

   <ItemGroup Label="ProjectConfigurations">

     <ProjectConfiguration Include="Release|Win32">

@@ -19,82 +20,30 @@
     <RootNamespace>makete</RootNamespace>

   </PropertyGroup>

   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />

-  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">

-    <ConfigurationType>Application</ConfigurationType>

-    <UseDebugLibraries>false</UseDebugLibraries>

-    <WholeProgramOptimization>true</WholeProgramOptimization>

-    <CharacterSet>MultiByte</CharacterSet>

-  </PropertyGroup>

-  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">

-    <ConfigurationType>Application</ConfigurationType>

-    <UseDebugLibraries>false</UseDebugLibraries>

-    <WholeProgramOptimization>true</WholeProgramOptimization>

-    <CharacterSet>MultiByte</CharacterSet>

-  </PropertyGroup>

   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />

-  <ImportGroup Label="ExtensionSettings">

-  </ImportGroup>

-  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">

-    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />

-    <Import Project="..\..\win32\hexchat.props" />

-  </ImportGroup>

-  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">

-    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />

-    <Import Project="..\..\win32\hexchat.props" />

-  </ImportGroup>

-  <PropertyGroup Label="UserMacros" />

-  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">

-    <LinkIncremental>false</LinkIncremental>

-    <OutDir>$(HexChatBin)</OutDir>

-    <IntDir>$(HexChatObj)$(ProjectName)\</IntDir>

-  </PropertyGroup>

-  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">

-    <LinkIncremental>false</LinkIncremental>

-    <OutDir>$(HexChatBin)</OutDir>

-    <IntDir>$(HexChatObj)$(ProjectName)\</IntDir>

+  <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />

+  <Import Project="..\..\win32\hexchat.props" />

+  <PropertyGroup>

+    <OutDir>$(HexChatLib)</OutDir>

   </PropertyGroup>

   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">

     <ClCompile>

-      <PrecompiledHeader>

-      </PrecompiledHeader>

-      <FunctionLevelLinking>true</FunctionLevelLinking>

-      <IntrinsicFunctions>true</IntrinsicFunctions>

       <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>

-      <MultiProcessorCompilation>true</MultiProcessorCompilation>

     </ClCompile>

     <Link>

       <SubSystem>Console</SubSystem>

-      <GenerateDebugInformation>true</GenerateDebugInformation>

-      <EnableCOMDATFolding>true</EnableCOMDATFolding>

-      <OptimizeReferences>true</OptimizeReferences>

     </Link>

-    <PostBuildEvent>

-      <Command>"$(HexChatBin)make-te.exe" &lt; "$(ProjectDir)textevents.in" &gt; "$(ProjectDir)textevents.h" 2&gt; "$(ProjectDir)textenums.h"</Command>

-    </PostBuildEvent>

   </ItemDefinitionGroup>

   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">

     <ClCompile>

-      <PrecompiledHeader>

-      </PrecompiledHeader>

-      <FunctionLevelLinking>true</FunctionLevelLinking>

-      <IntrinsicFunctions>true</IntrinsicFunctions>

       <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>

-      <MultiProcessorCompilation>true</MultiProcessorCompilation>

     </ClCompile>

     <Link>

       <SubSystem>Console</SubSystem>

-      <GenerateDebugInformation>true</GenerateDebugInformation>

-      <EnableCOMDATFolding>true</EnableCOMDATFolding>

-      <OptimizeReferences>true</OptimizeReferences>

     </Link>

-    <PostBuildEvent>

-      <Command>"$(HexChatBin)make-te.exe" &lt; "$(ProjectDir)textevents.in" &gt; "$(ProjectDir)textevents.h" 2&gt; "$(ProjectDir)textenums.h"</Command>

-    </PostBuildEvent>

   </ItemDefinitionGroup>

   <ItemGroup>

     <ClCompile Include="make-te.c" />

   </ItemGroup>

   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />

-  <ImportGroup Label="ExtensionTargets">

-  </ImportGroup>

-</Project>
\ No newline at end of file
+</Project>

diff --git a/src/common/modes.c b/src/common/modes.c
index b7cd471f..c65bf279 100644
--- a/src/common/modes.c
+++ b/src/common/modes.c
@@ -331,7 +331,7 @@ record_chan_mode (session *sess, char sign, char mode, char *arg)
 				current = g_string_erase(current, argument_offset+1, argument_length-1);
 				current = g_string_insert(current, argument_offset+1, arg);
 
-				free(sess->current_modes);
+				g_free(sess->current_modes);
 				sess->current_modes = g_string_free(current, FALSE);
 			}
 		}
@@ -348,7 +348,7 @@ record_chan_mode (session *sess, char sign, char mode, char *arg)
 				current = g_string_append(current, arg);
 			}
 
-			free(sess->current_modes);
+			g_free(sess->current_modes);
 			sess->current_modes = g_string_free(current, FALSE);
 		}
 	}
@@ -361,7 +361,7 @@ record_chan_mode (session *sess, char sign, char mode, char *arg)
 		/* remove the mode character */
 		current = g_string_erase(current, mode_pos, 1);
 
-		free(sess->current_modes);
+		g_free(sess->current_modes);
 		sess->current_modes = g_string_free(current, FALSE);
 	}
 }
@@ -374,12 +374,13 @@ mode_cat (char *str, char *addition)
 	if (str)
 	{
 		len = strlen (str) + strlen (addition) + 2;
-		str = realloc (str, len);
+		str = g_realloc (str, len);
 		strcat (str, " ");
 		strcat (str, addition);
-	} else
+	}
+	else
 	{
-		str = strdup (addition);
+		str = g_strdup (addition);
 	}
 
 	return str;
@@ -560,12 +561,12 @@ handle_single_mode (mode_run *mr, char sign, char mode, char *nick,
 	{
 		if (*arg)
 		{
-			char *buf = malloc (strlen (chan) + strlen (arg) + 2);
-			sprintf (buf, "%s %s", chan, arg);
+			char *buf = g_strdup_printf ("%s %s", chan, arg);
 			EMIT_SIGNAL_TIMESTAMP (XP_TE_CHANMODEGEN, sess, nick, outbuf,
 										  outbuf + 2, buf, 0, tags_data->timestamp);
-			free (buf);
-		} else
+			g_free (buf);
+		}
+		else
 			EMIT_SIGNAL_TIMESTAMP (XP_TE_CHANMODEGEN, sess, nick, outbuf,
 										  outbuf + 2, chan, 0, tags_data->timestamp);
 	}
@@ -635,7 +636,7 @@ mode_print_grouped (session *sess, char *nick, mode_run *mr,
 	{
 		EMIT_SIGNAL_TIMESTAMP (XP_TE_CHANOP, sess, nick, mr->op, NULL, NULL, 0,
 									  tags_data->timestamp);
-		free (mr->op);
+		g_free(mr->op);
 		mr->op = NULL;
 	}
 
@@ -643,7 +644,7 @@ mode_print_grouped (session *sess, char *nick, mode_run *mr,
 	{
 		EMIT_SIGNAL_TIMESTAMP (XP_TE_CHANDEOP, sess, nick, mr->deop, NULL, NULL,
 									  0, tags_data->timestamp);
-		free (mr->deop);
+		g_free(mr->deop);
 		mr->deop = NULL;
 	}
 
@@ -651,7 +652,7 @@ mode_print_grouped (session *sess, char *nick, mode_run *mr,
 	{
 		EMIT_SIGNAL_TIMESTAMP (XP_TE_CHANVOICE, sess, nick, mr->voice, NULL, NULL,
 									  0, tags_data->timestamp);
-		free (mr->voice);
+		g_free(mr->voice);
 		mr->voice = NULL;
 	}
 
@@ -659,7 +660,7 @@ mode_print_grouped (session *sess, char *nick, mode_run *mr,
 	{
 		EMIT_SIGNAL_TIMESTAMP (XP_TE_CHANDEVOICE, sess, nick, mr->devoice, NULL,
 									  NULL, 0, tags_data->timestamp);
-		free (mr->devoice);
+		g_free(mr->devoice);
 		mr->devoice = NULL;
 	}
 }
@@ -677,10 +678,10 @@ handle_mode (server * serv, char *word[], char *word_eol[],
 	char *argstr;
 	char sign;
 	int len;
-	int arg;
-	int i, num_args;
+	size_t arg;
+	size_t i, num_args;
 	int num_modes;
-	int offset = 3;
+	size_t offset = 3;
 	int all_modes_have_args = FALSE;
 	int using_front_tab = FALSE;
 	mode_run mr;
@@ -717,9 +718,8 @@ handle_mode (server * serv, char *word[], char *word_eol[],
 
 	if (numeric_324 && !using_front_tab)
 	{
-		if (sess->current_modes)
-			free (sess->current_modes);
-		sess->current_modes = strdup (word_eol[offset+1]);
+		g_free (sess->current_modes);
+		sess->current_modes = g_strdup (word_eol[offset+1]);
 	}
 
 	sign = *modes;
@@ -762,7 +762,7 @@ handle_mode (server * serv, char *word[], char *word_eol[],
 			break;
 		default:
 			argstr = "";
-			if ((all_modes_have_args || mode_has_arg (serv, sign, *modes)) && arg < (num_args+1))
+			if ((all_modes_have_args || mode_has_arg (serv, sign, *modes)) && arg < (num_args + 1))
 			{
 				arg++;
 				argstr = word[arg + offset];
@@ -799,30 +799,29 @@ inbound_005 (server * serv, char *word[], const message_tags_data *tags_data)
 			serv->modes_per_line = atoi (word[w] + 6);
 		} else if (strncmp (word[w], "CHANTYPES=", 10) == 0)
 		{
-			free (serv->chantypes);
-			serv->chantypes = strdup (word[w] + 10);
+			g_free (serv->chantypes);
+			serv->chantypes = g_strdup (word[w] + 10);
 		} else if (strncmp (word[w], "CHANMODES=", 10) == 0)
 		{
-			free (serv->chanmodes);
-			serv->chanmodes = strdup (word[w] + 10);
+			g_free (serv->chanmodes);
+			serv->chanmodes = g_strdup (word[w] + 10);
 		} else if (strncmp (word[w], "PREFIX=", 7) == 0)
 		{
 			pre = strchr (word[w] + 7, ')');
 			if (pre)
 			{
 				pre[0] = 0;			  /* NULL out the ')' */
-				free (serv->nick_prefixes);
-				free (serv->nick_modes);
-				serv->nick_prefixes = strdup (pre + 1);
-				serv->nick_modes = strdup (word[w] + 8);
+				g_free (serv->nick_prefixes);
+				g_free (serv->nick_modes);
+				serv->nick_prefixes = g_strdup (pre + 1);
+				serv->nick_modes = g_strdup (word[w] + 8);
 			} else
 			{
 				/* bad! some ircds don't give us the modes. */
 				/* in this case, we use it only to strip /NAMES */
 				serv->bad_prefix = TRUE;
-				if (serv->bad_nick_prefixes)
-					free (serv->bad_nick_prefixes);
-				serv->bad_nick_prefixes = strdup (word[w] + 7);
+				g_free (serv->bad_nick_prefixes);
+				serv->bad_nick_prefixes = g_strdup (word[w] + 7);
 			}
 		} else if (strncmp (word[w], "WATCH=", 6) == 0)
 		{
@@ -832,10 +831,6 @@ inbound_005 (server * serv, char *word[], const message_tags_data *tags_data)
 			serv->supports_monitor = TRUE;
 		} else if (strncmp (word[w], "NETWORK=", 8) == 0)
 		{
-/*			if (serv->networkname)
-				free (serv->networkname);
-			serv->networkname = strdup (word[w] + 8);*/
-
 			if (serv->server_session->type == SESS_SERVER)
 			{
 				safe_strcpy (serv->server_session->channel, word[w] + 8, CHANLEN);
diff --git a/src/common/msproxy.c b/src/common/msproxy.c
deleted file mode 100644
index 5f631c7f..00000000
--- a/src/common/msproxy.c
+++ /dev/null
@@ -1,470 +0,0 @@
-/* X-Chat
- * Copyright (C) 1998 Peter Zelezny.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
- *
- * MS Proxy (ISA server) support is (c) 2006 Pavel Fedin <sonic_amiga@rambler.ru>
- * based on Dante source code
- * Copyright (c) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006
- *      Inferno Nettverk A/S, Norway.  All rights reserved.
- */
-
-/*#define DEBUG_MSPROXY*/
-
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-#include <fcntl.h>
-
-#ifndef WIN32
-#include <unistd.h>
-#endif
-
-#define WANTSOCKET
-#define WANTARPA
-#include "inet.h"
-
-#include "hexchat.h"
-#include "network.h"
-#include "hexchatc.h"
-#include "server.h"
-#include "msproxy.h"
-
-
-#ifdef USE_MSPROXY
-#include <ntlm.h>
-
-static int
-send_msprequest(s, state, request, end)
-	int s;
-	struct msproxy_state_t *state;
-	struct msproxy_request_t *request;
-	char *end;
-{
-	ssize_t w;
-	size_t l;
-
-	request->magic25 = htonl(MSPROXY_VERSION);
-	request->serverack = state->seq_recv;
-	/* don't start incrementing sequence until we are acking packet #2. */
-	request->sequence	= (unsigned char)(request->serverack >= 2 ? state->seq_sent + 1 : 0);
-
-	memcpy(request->RWSP, "RWSP", sizeof(request->RWSP));
-
-	l = end - (char *)request;
-	/* all requests must be atleast MSPROXY_MINLENGTH it seems. */
-	if (l < MSPROXY_MINLENGTH) {
-		bzero(end, (size_t)(MSPROXY_MINLENGTH - l));
-		l = MSPROXY_MINLENGTH;
-	}
-
-	if ((w = send(s, request, l, 0)) != l) {
-#ifdef DEBUG_MSPROXY
-		printf ("send_msprequest(): send() failed (%ld bytes sent instead of %Iu\n", w, l);
-		perror ("Error is");
-#endif
-		return -1;
-	}
-	state->seq_sent = request->sequence;
-
-	return w;
-}
-
-static int
-recv_mspresponse(s, state, response)
-	int s;
-	struct msproxy_state_t *state;
-	struct msproxy_response_t *response;
-{
-	ssize_t r;
-
-	do {
-		if ((r = recv (s, response, sizeof (*response), 0)) < MSPROXY_MINLENGTH) {
-#ifdef DEBUG_MSPROXY
-			printf ("recv_mspresponse(): expected to read atleast %d, read %ld\n", MSPROXY_MINLENGTH, r);
-#endif
-			return -1;
-		}
-		if (state->seq_recv == 0)
-			break; /* not started incrementing yet. */
-#ifdef DEBUG_MSPROXY
-		if (response->sequence == state->seq_recv)
-			printf ("seq_recv: %d, dup response, seqnumber: 0x%x\n", state->seq_recv, response->sequence);
-#endif
-	} while (response->sequence == state->seq_recv);
-
-	state->seq_recv = response->sequence;
-
-	return r;
-}
-
-int
-traverse_msproxy (int sok, char *serverAddr, int port, struct msproxy_state_t *state, netstore *ns_proxy, int csok4, int csok6, int *csok, char bound)
-{
-	struct msproxy_request_t req;
-	struct msproxy_response_t res;
-	char *data, *p;
-	char hostname[NT_MAXNAMELEN];
-	char ntdomain[NT_MAXNAMELEN];
-	char challenge[8];
-	netstore *ns_client;
-	int clientport;
-	guint32 destaddr;
-	guint32 flags;
-
-	if (!prefs.hex_net_proxy_auth || !prefs.hex_net_proxy_user[0] || !prefs.hex_net_proxy_pass[0] )
-		return 1;
-
-	/* MS proxy protocol implementation currently doesn't support IPv6 */
-	destaddr = net_getsockaddr_v4 (ns_proxy);
-	if (!destaddr)
-		return 1;
-
-	state->seq_recv = 0;
-	state->seq_sent = 0;
-
-#ifdef DEBUG_MSPROXY
-	printf ("Connecting to %s:%d via MS proxy\n", serverAddr, port);
-#endif
-
-	gethostname (hostname, NT_MAXNAMELEN);
-	p = strchr (hostname, '.');
-        if (p)
-        	*p = '\0';
-
-	bzero (&req, sizeof(req));
-	req.clientid		= htonl(0x0a000000);	/* Initial client ID is always 0x0a		*/
-	req.command		= htons(MSPROXY_HELLO);	/* HELLO command					*/
-	req.packet.hello.magic5	= htons(0x4b00);	/* Fill in magic values				*/
-	req.packet.hello.magic10	= htons(0x1400);
-	req.packet.hello.magic15	= htons(0x0400);
-	req.packet.hello.magic20	= htons(0x5704);
-	req.packet.hello.magic25	= htons(0x0004);
-	req.packet.hello.magic30	= htons(0x0100);
-	req.packet.hello.magic35	= htons(0x4a02);
-	req.packet.hello.magic40	= htons(0x3000);
-	req.packet.hello.magic45	= htons(0x4400);
-	req.packet.hello.magic50	= htons(0x3900);
-	data = req.packet.hello.data;
-	strcpy (data, prefs.hex_net_proxy_user);		/* Append a username				*/
-	data += strlen (prefs.hex_net_proxy_user)+2;		/* +2 automatically creates second empty string	*/
-	strcpy (data, MSPROXY_EXECUTABLE);		/* Append an application name			*/
-	data += strlen (MSPROXY_EXECUTABLE)+1;
-	strcpy (data, hostname);				/* Append a hostname				*/
-	data += strlen (hostname)+1;
-
-	if (send_msprequest(sok, state, &req, data) == -1)
-		return 1;
-
-	if (recv_mspresponse(sok, state, &res) == -1)
-		return 1;
-
-	if (strcmp(res.RWSP, "RWSP") != 0) {
-#ifdef DEBUG_MSPROXY
-		printf ("Received mailformed packet (no RWSP signature)\n");
-#endif
-		return 1;
-	}
-
-	if (ntohs(res.command) >> 8 != 0x10) {
-#ifdef DEBUG_MSPROXY
-		printf ("expected res.command = 10??, is %x", ntohs(res.command));
-#endif
-		return 1;
-	}
-
-	state->clientid	= htonl(rand());
-	state->serverid	= res.serverid;
-
-#ifdef DEBUG_MSPROXY
-	printf ("clientid: 0x%x, serverid: 0x%0x\n", state->clientid, state->serverid);
-	printf ("packet #2\n");
-#endif
-
-	/* almost identical. */
-	req.clientid	= state->clientid;
-	req.serverid	= state->serverid;
-
-	if (send_msprequest(sok, state, &req, data) == -1)
-		return 1;
-
-	if (recv_mspresponse(sok, state, &res) == -1)
-		return 1;
-
-	if (res.serverid != state->serverid) {
-#ifdef DEBUG_MSPROXY
-		printf ("expected serverid = 0x%x, is 0x%x\n",state->serverid, res.serverid);
-#endif
-		return 1;
-	}
-
-	if (res.sequence != 0x01) {
-#ifdef DEBUG_MSPROXY
-		printf ("expected res.sequence = 0x01, is 0x%x\n", res.sequence);
-#endif
-		return 1;
-	}
-
-	if (ntohs(res.command) != MSPROXY_USERINFO_ACK) {
-#ifdef DEBUG_MSPROXY
-		printf ("expected res.command = 0x%x, is 0x%x\n", MSPROXY_USERINFO_ACK, ntohs(res.command));
-#endif
-		return 1;
-	}
-
-#ifdef DEBUG_MSPROXY
-	printf ("packet #3\n");
-#endif
-
-	bzero(&req, sizeof(req));
-	req.clientid			= state->clientid;
-	req.serverid			= state->serverid;
-	req.command			= htons(MSPROXY_AUTHENTICATE);
-	memcpy(req.packet.auth.NTLMSSP, "NTLMSSP", sizeof("NTLMSSP"));
-	req.packet.auth.bindaddr	= htonl(0x02000000);
-	req.packet.auth.msgtype		= htonl(0x01000000);
-	/* NTLM flags:	0x80000000 Negotiate LAN Manager key
-			0x10000000 Negotiate sign
-			0x04000000 Request target
-			0x02000000 Negotiate OEM
-			0x00800000 Always sign
-			0x00020000 Negotiate NTLM
-	*/
-	req.packet.auth.flags		= htonl(0x06020000);
-
-	if (send_msprequest(sok, state, &req, &req.packet.auth.data) == -1)
-		return 1;
-
-	if (recv_mspresponse(sok, state, &res) == -1)
-		return 1;
-
-	if (res.serverid != state->serverid) {
-#ifdef DEBUG_MSPROXY
-		printf ("expected serverid = 0x%x, is 0x%x\n", state->serverid, res.serverid);
-#endif
-		return 1;
-	}
-
-	if (ntohs(res.command) != MSPROXY_AUTHENTICATE_ACK) {
-#ifdef DEBUG_MSPROXY
-		printf ("expected res.command = 0x%x, is 0x%x\n", MSPROXY_AUTHENTICATE_ACK, ntohs(res.command));
-#endif
-		return 1;
-	}
-
-	flags = res.packet.auth.flags & htonl(0x00020000);			/* Remember if the server supports NTLM */
-	memcpy(challenge, &res.packet.auth.challenge, sizeof(challenge));
-	memcpy(ntdomain, &res.packet.auth.NTLMSSP[res.packet.auth.target.offset], res.packet.auth.target.len);
-	ntdomain[res.packet.auth.target.len] = 0;
-
-#ifdef DEBUG_MSPROXY
-	printf ("ntdomain: \"%s\"\n", ntdomain);
-	printf ("packet #4\n");
-#endif
-
-	bzero(&req, sizeof(req));
-	req.clientid			= state->clientid;
-	req.serverid			= state->serverid;
-	req.command			= htons(MSPROXY_AUTHENTICATE_2);	/* Authentication response			*/
-	req.packet.auth2.magic3		= htons(0x0200);			/* Something					*/
-	memcpy(req.packet.auth2.NTLMSSP, "NTLMSSP", sizeof("NTLMSSP"));		/* Start of NTLM message				*/
-	req.packet.auth2.msgtype		= htonl(0x03000000);			/* Message type 2				*/
-	req.packet.auth2.flags		= flags | htonl(0x02000000);		/* Choose authentication method			*/
-	data = req.packet.auth2.data;
-	if (flags) {
-		req.packet.auth2.lm_resp.len	= 0;				/* We are here if NTLM is supported,		*/
-		req.packet.auth2.lm_resp.alloc	= 0;				/* Do not fill in insecure LM response		*/
-		req.packet.auth2.lm_resp.offset	= data - req.packet.auth2.NTLMSSP;
-		req.packet.auth2.ntlm_resp.len = 24;				/* Fill in NTLM response security buffer	*/
-		req.packet.auth2.ntlm_resp.alloc = 24;
-		req.packet.auth2.ntlm_resp.offset = data - req.packet.auth2.NTLMSSP;
-		ntlm_smb_nt_encrypt(prefs.hex_net_proxy_pass, challenge, data);		/* Append an NTLM response			*/
-		data += 24;	
-	} else {
-		req.packet.auth2.lm_resp.len	= 24;				/* Fill in LM response security buffer		*/
-		req.packet.auth2.lm_resp.alloc	= 24;
-		req.packet.auth2.lm_resp.offset	= data - req.packet.auth2.NTLMSSP;
-		ntlm_smb_encrypt(prefs.hex_net_proxy_pass, challenge, data);		/* Append an LM response			*/
-		data += 24;
-		req.packet.auth2.ntlm_resp.len = 0;				/* NTLM response is empty			*/
-		req.packet.auth2.ntlm_resp.alloc = 0;
-		req.packet.auth2.ntlm_resp.offset = data - req.packet.auth2.NTLMSSP;
-	}
-	req.packet.auth2.ntdomain_buf.len = strlen(ntdomain);			/* Domain name					*/
-	req.packet.auth2.ntdomain_buf.alloc = req.packet.auth2.ntdomain_buf.len;
-	req.packet.auth2.ntdomain_buf.offset = data - req.packet.auth2.NTLMSSP;
-	strcpy(data, ntdomain);
-	data += req.packet.auth2.ntdomain_buf.len;
-	req.packet.auth2.username_buf.len = strlen(prefs.hex_net_proxy_user);		/* Username					*/
-	req.packet.auth2.username_buf.alloc = req.packet.auth2.username_buf.len;
-	req.packet.auth2.username_buf.offset = data - req.packet.auth2.NTLMSSP;
-	strcpy(data, prefs.hex_net_proxy_user);
-	data += req.packet.auth2.username_buf.len;
-	req.packet.auth2.clienthost_buf.len = strlen(hostname);			/* Hostname					*/
-	req.packet.auth2.clienthost_buf.alloc = req.packet.auth2.clienthost_buf.len;
-	req.packet.auth2.clienthost_buf.offset = data - req.packet.auth2.NTLMSSP;
-	strcpy(data, hostname);
-	data += req.packet.auth2.clienthost_buf.len;
-	req.packet.auth2.sessionkey_buf.len = 0;				/* Session key (we don't use it)		*/
-	req.packet.auth2.sessionkey_buf.alloc = 0;
-	req.packet.auth2.sessionkey_buf.offset = data - req.packet.auth2.NTLMSSP;
-
-	if (send_msprequest(sok, state, &req, data) == -1)
-		return 1;
-
-	if (recv_mspresponse(sok, state, &res) == -1)
-		return 1;
-
-	if (res.serverid != state->serverid) {
-#ifdef DEBUG_MSPROXY
-		printf ("expected res.serverid = 0x%x, is 0x%x\n", state->serverid, res.serverid);
-#endif
-		return 1;
-	}
-
-	if (res.clientack != 0x01) {
-#ifdef DEBUG_MSPROXY
-		printf ("expected res.clientack = 0x01, is 0x%x\n", res.clientack);
-#endif
-		return 1;
-	}
-
-	if (ntohs(res.command) >> 8 != 0x47) {
-#ifdef DEBUG_MSPROXY
-		printf ("expected res.command = 47??, is 0x%x\n", ntohs(res.command));
-#endif
-		return 1;
-	}
-
-	if (ntohs(res.command) ==  MSPROXY_AUTHENTICATE_2_NAK) {
-#ifdef DEBUG_MSPROXY
-		printf ("Authentication failed\n");
-#endif
-		return -1;
-	}
-
-#ifdef DEBUG_MSPROXY
-	printf ("packet #5\n");
-#endif
-
-	bzero(&req, sizeof(req));
-	req.clientid			= state->clientid;
-	req.serverid			= state->serverid;
-	req.command			= htons(MSPROXY_CONNECT);
-	req.packet.connect.magic2	= htons(0x0200);
-	req.packet.connect.magic6	= htons(0x0200);
-	req.packet.connect.destport	= htons(port);
-	req.packet.connect.destaddr	= destaddr;
-	data = req.packet.connect.executable;
-	strcpy(data, MSPROXY_EXECUTABLE);
-	data += strlen(MSPROXY_EXECUTABLE) + 1;
-
-	/*
-	 * need to tell server what port we will connect from, so we bind our sockets.
-	 */
-	ns_client = net_store_new ();
-	if (!bound) {
-		net_store_fill_any (ns_client);
-		net_bind(ns_client, csok4, csok6);
-#ifdef DEBUG_MSPROXY
-		perror ("bind() result");
-#endif
-	}
- 	clientport = net_getsockport(csok4, csok6);
-	if (clientport == -1) {
-#ifdef DEBUG_MSPROXY
-		printf ("Unable to obtain source port\n");
-#endif
-		return 1;
-	}
-	req.packet.connect.srcport = clientport;
-
-	if (send_msprequest(sok, state, &req, data) == -1)
-		return 1;
-
-	if (recv_mspresponse(sok, state, &res) == -1)
-		return 1;
-
-	if (ntohs(res.command) != MSPROXY_CONNECT_ACK) {
-#ifdef DEBUG_MSPROXY
-		printf ("expected res.command = 0x%x, is 0x%x\n",MSPROXY_CONNECT_ACK, ntohs(res.command));
-#endif
-		return 1;
-	}
-
-	net_store_fill_v4 (ns_client, res.packet.connect.clientaddr, res.packet.connect.clientport);
-
-#ifdef DEBUG_MSPROXY
-	printf ("Connecting...\n");
-#endif
-	if (net_connect (ns_client, csok4, csok6, csok) != 0) {
-#ifdef DEBUG_MSPROXY
-		printf ("Failed to connect to port %d\n", htons(res.packet.connect.clientport));
-#endif
-		net_store_destroy (ns_client);
-		return 1;
-	}
-	net_store_destroy (ns_client);
-#ifdef DEBUG_MSPROXY
-	printf ("packet #6\n");
-#endif
-
-	req.clientid	= state->clientid;
-	req.serverid	= state->serverid;
-	req.command	= htons(MSPROXY_USERINFO_ACK);		
-
-	if (send_msprequest(sok, state, &req, req.packet.connack.data) == -1)
-		return 1;
-
-	return 0;
-}
-
-void
-msproxy_keepalive (void)
-{
-	server *serv;
-	GSList *list = serv_list;
-	struct msproxy_request_t req;
-	struct msproxy_response_t res;
-
-	while (list)
-	{
-		serv = list->data;
-		if (serv->connected && (serv->proxy_sok != -1))
-		{
-#ifdef DEBUG_MSPROXY
-			printf ("sending MS proxy keepalive packet\n");
-#endif
-
-			bzero(&req, sizeof(req));
-			req.clientid	= serv->msp_state.clientid;
-			req.serverid	= serv->msp_state.serverid;
-			req.command	= htons(MSPROXY_HELLO);
-			
-			if (send_msprequest(serv->proxy_sok, &serv->msp_state, &req, req.packet.hello.data) == -1)
-				continue;
-
-			recv_mspresponse(serv->proxy_sok, &serv->msp_state, &res);
-
-#ifdef DEBUG_MSPROXY
-			if (ntohs(res.command) != MSPROXY_USERINFO_ACK)
-				printf ("expected res.command = 0x%x, is 0x%x\n", MSPROXY_USERINFO_ACK, ntohs(res.command));
-#endif
-		}
-		list = list->next;
-	}
-}
-
-#endif
diff --git a/src/common/msproxy.h b/src/common/msproxy.h
deleted file mode 100644
index 4371d704..00000000
--- a/src/common/msproxy.h
+++ /dev/null
@@ -1,262 +0,0 @@
-/* X-Chat
- * Copyright (C) 1998 Peter Zelezny.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
- *
- * MS Proxy (ISA server) support is (c) 2006 Pavel Fedin <sonic_amiga@rambler.ru>
- * based on Dante source code
- * Copyright (c) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006
- *      Inferno Nettverk A/S, Norway.  All rights reserved.
- */
-
-#ifndef HEXCHAT_MSPROXY_H
-#define HEXCHAT_MSPROXY_H
-
-#include "network.h"
-
-#define MSPROXY_EXECUTABLE 		"hexchat.exe"	/* This probably can be used for access control on the server side */
-
-#define MSPROXY_MINLENGTH		172		/* minimum length of packet.				*/
-#define NT_MAXNAMELEN			17		/* maximum name length (domain etc), comes from NetBIOS */
-#define MSPROXY_VERSION			0x00010200	/* MS Proxy v2 ?					*/
-
-/* Commands / responses */
-#define MSPROXY_HELLO			0x0500	/* packet 1 from client.			*/
-#define MSPROXY_HELLO_ACK		0x1000	/* packet 1 from server.			*/
-
-#define MSPROXY_USERINFO_ACK		0x0400	/* packet 2 from server.			*/
-
-#define MSPROXY_AUTHENTICATE		0x4700	/* authentication request		*/
-#define MSPROXY_AUTHENTICATE_ACK	0x4714	/* authentication challenge		*/
-
-#define MSPROXY_AUTHENTICATE_2		0x4701	/* authentication response		*/
-#define MSPROXY_AUTHENTICATE_2_ACK	0x4715	/* authentication passed		*/
-#define MSPROXY_AUTHENTICATE_2_NAK	0x4716	/* authentication failure		*/
-
-#define MSPROXY_CONNECT			0x071e	/* connect request.			*/
-#define MSPROXY_CONNECT_ACK		0x0703	/* connect request accepted.		*/
-
-#pragma pack(1)
-
-struct ntlm_buffer {
-	guint16	len;
-	guint16	alloc;
-	guint32	offset;
-};
-
-struct msproxy_request_t {
-	guint32					clientid;			/* 1-4							*/
-	guint32					magic25;				/* 5-8							*/
-	guint32					serverid;			/* 9-12							*/
-	unsigned char				serverack;			/* 13: ack of last server packet			*/
-	char					pad10[3];			/* 14-16						*/
-	unsigned char				sequence;			/* 17: sequence # of this packet.			*/
-	char					pad11[7];			/* 18-24						*/
-	char					RWSP[4];			/* 25-28: 0x52,0x57,0x53,0x50				*/
-	char					pad15[8];			/* 29-36						*/
-	guint16					command;				/* 37-38						*/
-
-	/* packet specifics start at 39. */
-	union {
-		struct {
-			char			pad1[18];			/* 39-56						*/
-			guint16			magic3;				/* 57-58						*/
-			char           		pad3[114];			/* 59-172						*/
-			guint16			magic5;				/* 173-174: 0x4b, 0x00					*/
-			char			pad5[2];			/* 175-176						*/
-			guint16			magic10;				/* 177-178: 0x14, 0x00					*/
-			char			pad6[2];			/* 179-180						*/
-			guint16			magic15;				/* 181-182: 0x04, 0x00					*/
-			char			pad10[2];			/* 183-184						*/
-			guint16			magic16;				/* 185-186						*/
-			char			pad11[2];			/* 187-188						*/
-			guint16			magic20;				/* 189-190: 0x57, 0x04					*/
-			guint16			magic25;				/* 191-192: 0x00, 0x04					*/
-			guint16			magic30;				/* 193-194: 0x01, 0x00					*/
-			char			pad20[2];			/* 195-196: 0x4a, 0x02					*/
-			guint16			magic35;				/* 197-198: 0x4a, 0x02					*/
-			char			pad30[10];			/* 199-208						*/
-			guint16			magic40;				/* 209-210: 0x30, 0x00					*/
-			char			pad40[2];			/* 211-212						*/
-			guint16			magic45;				/* 213-214: 0x44, 0x00					*/
-			char			pad45[2];			/* 215-216						*/
-			guint16			magic50;				/* 217-218: 0x39, 0x00					*/
-			char			pad50[2];			/* 219-220						*/
-			char			data[256];			/* 221-EOP: a sequence of NULL-terminated strings:
-											- username;
-											- empty string (just a NULL);
-											- application name;
-											- hostname					*/
-		} hello;
-
-		struct {
-			char			pad1[4];			/* 39-42						*/
-			guint16			magic2;				/* 43-44						*/
-			char			pad10[12];			/* 45-56						*/
-			guint32			bindaddr;			/* 57-60: address to bind.				*/
-			guint16			bindport;			/* 61-62: port to bind.					*/
-			char           		pad15[2];			/* 63-64						*/
-			guint16			magic3;				/* 65-66						*/
-			guint16			boundport;			/* 67-68						*/
-			char           		pad20[104];			/* 69-172						*/
-			char			NTLMSSP[sizeof("NTLMSSP")];	/* 173-180: "NTLMSSP"					*/
-			guint32			msgtype;				/* 181-184: NTLM message type = 1			*/
-			guint32			flags;				/* 185-188: NTLM message flags				*/
-			guint16			magic20;				/* 189-190: 0x28, 0x00					*/
-			char			pad30[2];			/* 191-192						*/
-			guint16			magic25;				/* 193-194: 0x96, 0x82					*/
-			guint16			magic30;				/* 195-196: 0x01, 0x00					*/
-			char			pad40[12];			/* 197-208						*/
-			guint16			magic50;				/* 209-210: 0x30, 0x00					*/
-			char			pad50[6];			/* 211-216						*/
-			guint16			magic55;				/* 217-218: 0x30, 0x00					*/
-			char			pad55[2];			/* 219-220						*/
-			char			data[0];			/* Dummy end marker, no real data required		*/
-		} auth;
-
-		struct {
-			char			pad1[4];			/* 39-42						*/
-			guint16			magic1;				/* 43-44						*/
-			guint32			magic2;				/* 45-48						*/
-			char			pad2[8];			/* 49-56						*/
-			guint16			magic3;				/* 57-58						*/
-			char			pad3[6];			/* 59-64						*/
-			guint16			magic4;				/* 65-66						*/
-			guint16			boundport;			/* 67-68						*/
-			char           		pad4[104];			/* 69-172						*/
-			char			NTLMSSP[sizeof("NTLMSSP")];	/* 173-180: "NTLMSSP"					*/
-			guint32			msgtype;				/* 181-184: NTLM message type = 3			*/
-			struct ntlm_buffer	lm_resp;				/* 185-192: LM response security buffer			*/
-			struct ntlm_buffer	ntlm_resp;			/* 193-200: NTLM response security buffer		*/
-			struct ntlm_buffer	ntdomain_buf;			/* 201-208: domain name security buffer			*/
-			struct ntlm_buffer	username_buf;			/* 209-216: username security buffer			*/
-			struct ntlm_buffer	clienthost_buf;			/* 217-224: hostname security buffer			*/
-			struct ntlm_buffer	sessionkey_buf;			/* 225-232: session key security buffer			*/
-			guint32			flags;				/* 233-236: message flags				*/
-			char			data[1024];			/* 237-EOP: data area					*/
-		} auth2;
-
-		struct {
-			guint16			magic1;				/* 39-40						*/
-			char			pad1[2];			/* 41-42						*/
-			guint16			magic2;				/* 43-44						*/
-			guint32			magic3;				/* 45-48						*/
-			char			pad5[8];			/* 48-56						*/
-			guint16			magic6;				/* 57-58: 0x0200					*/
-			guint16			destport;			/* 59-60						*/
-			guint32			destaddr;			/* 61-64						*/
-			char			pad10[4];			/* 65-68						*/
-			guint16			magic10;				/* 69-70						*/
-			char			pad15[2];			/* 71-72						*/
-			guint16			srcport;			/* 73-74: port client connects from			*/
-			char			pad20[82];			/* 75-156						*/
-			char			executable[256];		/* 76-EOP: application name				*/
-		} connect;
-
-		struct {
-			guint16			magic1;				/* 39-40						*/
-			char			pad5[2];			/* 41-42						*/
-			guint16			magic5;				/* 43-44						*/
-			guint32			magic10;				/* 45-48						*/
-			char			pad10[2];			/* 49-50						*/
-			guint16			magic15;				/* 51-52						*/
-			guint32			magic16;				/* 53-56						*/
-			guint16			magic20;				/* 57-58						*/
-			guint16			clientport;			/* 59-60: forwarded port.				*/
-			guint32			clientaddr;			/* 61-64: forwarded address.				*/
-			guint32			magic30;				/* 65-68						*/
-			guint32			magic35;				/* 69-72						*/
-			guint16			serverport;			/* 73-74: port server will connect to us from.		*/
-			guint16			srcport;			/* 75-76: connect request; port used on client behalf.	*/
-			guint16			boundport;			/* 77-78: bind request; port used on client behalf.	*/
-			guint32			boundaddr;			/* 79-82: addr used on client behalf			*/
-			char			pad30[90];			/* 83-172						*/
-			char			data[0];			/* End marker						*/
-		} connack;
-
-	} packet;
-};
-
-struct msproxy_response_t {
-	guint32					packetid;			/* 1-4							*/
-	guint32					magic5;				/* 5-8							*/
-	guint32             			serverid;			/* 9-12							*/
-	char					clientack;			/* 13: ack of last client packet.			*/
-	char					pad5[3];			/* 14-16						*/
-	unsigned char				sequence;			/* 17: sequence # of this packet.			*/
-	char					pad10[7];			/* 18-24						*/
-	char					RWSP[4];			/* 25-28: 0x52,0x57,0x53,0x50				*/
-	char					pad15[8];			/* 29-36						*/
-	guint16					command;				/* 37-38						*/
-
-	union {
-		struct {
-			char			pad5[18];			/* 39-56						*/
-			guint16			magic20;				/* 57-58: 0x02, 0x00					*/
-			char			pad10[6];			/* 59-64						*/
-			guint16			magic30;				/* 65-66: 0x74, 0x01					*/
-			char			pad15[2];			/* 67-68						*/
-			guint16			magic35;				/* 69-70: 0x0c, 0x00					*/
-			char			pad20[6];			/* 71-76						*/
-			guint16			magic50;				/* 77-78: 0x04, 0x00					*/
-			char			pad30[6];			/* 79-84						*/
-			guint16			magic60;				/* 85-86: 0x65, 0x05					*/
-			char			pad35[2];			/* 87-88						*/
-			guint16			magic65;				/* 89-90: 0x02, 0x00					*/
-			char			pad40[8];			/* 91-98						*/
-			guint16			udpport;			/* 99-100						*/
-			guint32			udpaddr;			/* 101-104						*/
-		} hello;
-
-		struct {
-			char			pad1[6];			/* 39-44						*/
-			guint32			magic10;				/* 45-48						*/
-			char			pad3[10];			/* 49-58						*/
-			guint16			boundport;			/* 59-60: port server bound for us.			*/
-			guint32			boundaddr;			/* 61-64: addr server bound for us.			*/
-			char			pad10[4];			/* 65-68						*/
-			guint16			magic15;				/* 69-70						*/
-			char			pad15[102];			/* 70-172						*/
-			char			NTLMSSP[sizeof("NTLMSSP")];	/* 173-180: "NTLMSSP"					*/
-			guint32			msgtype;				/* 181-184: NTLM message type = 2			*/
-			struct ntlm_buffer	target;				/* 185-192: target security buffer			*/
-			guint32			flags;				/* 193-196: NTLM message flags				*/
-			char			challenge[8];			/* 197-204: NTLM challenge request			*/
-			char			context[8];			/* 205-212: NTLM context				*/
-			char			data[1024];			/* 213-EOP: target information data			*/
-		} auth;
-
-		struct {
-			guint16			magic1;				/* 39-40						*/
-			char			pad5[18];			/* 41-58						*/
-			guint16			clientport;			/* 59-60: forwarded port.				*/
-			guint32			clientaddr;			/* 61-64: forwarded address.				*/
-			guint32			magic10;				/* 65-68						*/
-			guint32			magic15;				/* 69-72						*/
-			guint16			serverport;			/* 73-74: port server will connect to us from.		*/
-			guint16			srcport;			/* 75-76: connect request; port used on client behalf.	*/
-			guint16			boundport;			/* 77-78: bind request; port used on client behalf.	*/
-			guint32			boundaddr;			/* 79-82: addr used on client behalf			*/
-			char			pad10[90];			/* 83-172						*/
-		} connect;
-	} packet;
-};
-
-#pragma pack()
-
-int traverse_msproxy (int sok, char *serverAddr, int port, struct msproxy_state_t *state, netstore *ns_proxy, int csok4, int csok6, int *csok, char bound);
-void msproxy_keepalive (void);
-
-#endif
diff --git a/src/common/network.c b/src/common/network.c
index 8790f673..fcdaf547 100644
--- a/src/common/network.c
+++ b/src/common/network.c
@@ -18,6 +18,8 @@
 
 /* ipv4 and ipv6 networking functions with a common interface */
 
+#include "config.h"
+
 #include <stdlib.h>
 #include <string.h>
 #include <stdio.h>
@@ -26,7 +28,6 @@
 #ifndef WIN32
 #include <unistd.h>
 #endif
-#include "../../config.h"
 
 #define WANTSOCKET
 #define WANTARPA
@@ -64,128 +65,17 @@ net_ip (guint32 addr)
 void
 net_store_destroy (netstore * ns)
 {
-#ifdef USE_IPV6
 	if (ns->ip6_hostent)
 		freeaddrinfo (ns->ip6_hostent);
-#endif
-	free (ns);
+	g_free (ns);
 }
 
 netstore *
 net_store_new (void)
 {
-	netstore *ns;
-
-	ns = malloc (sizeof (netstore));
-	memset (ns, 0, sizeof (netstore));
-
-	return ns;
-}
-
-#ifndef USE_IPV6
-
-/* =================== IPV4 ================== */
-
-/*
-	A note about net_resolve and lookupd:
-
-	Many IRC networks rely on round-robin DNS for load balancing, rotating the list
-	of IP address on each query. However, this method breaks when DNS queries are
-	cached. Mac OS X and Darwin handle DNS lookups through the lookupd daemon, which
-	caches queries in its default configuration: thus, if we always pick the first
-	address, we will be stuck with the same host (which might be down!) until the
-	TTL reaches 0 or lookupd is reset (typically, at reboot). Therefore, we need to
-	pick a random address from the result list, instead of always using the first.
-*/
-
-char *
-net_resolve (netstore * ns, char *hostname, int port, char **real_host)
-{
-	ns->ip4_hostent = gethostbyname (hostname);
-	if (!ns->ip4_hostent)
-		return NULL;
-
-	memset (&ns->addr, 0, sizeof (ns->addr));
-#ifdef LOOKUPD
-	int count = 0;
-	while (ns->ip4_hostent->h_addr_list[count]) count++;
-	memcpy (&ns->addr.sin_addr,
-			ns->ip4_hostent->h_addr_list[RAND_INT(count)],
-			ns->ip4_hostent->h_length);
-#else
-	memcpy (&ns->addr.sin_addr, ns->ip4_hostent->h_addr,
-			  ns->ip4_hostent->h_length);
-#endif
-	ns->addr.sin_port = htons (port);
-	ns->addr.sin_family = AF_INET;
-
-	*real_host = strdup (ns->ip4_hostent->h_name);
-	return strdup (inet_ntoa (ns->addr.sin_addr));
-}
-
-int
-net_connect (netstore * ns, int sok4, int sok6, int *sok_return)
-{
-	*sok_return = sok4;
-	return connect (sok4, (struct sockaddr *) &ns->addr, sizeof (ns->addr));
-}
-
-void
-net_bind (netstore * tobindto, int sok4, int sok6)
-{
-	bind (sok4, (struct sockaddr *) &tobindto->addr, sizeof (tobindto->addr));
-}
-
-void
-net_sockets (int *sok4, int *sok6)
-{
-	*sok4 = socket (AF_INET, SOCK_STREAM, 0);
-	*sok6 = -1;
-	net_set_socket_options (*sok4);
-}
-
-void
-udp_sockets (int *sok4, int *sok6)
-{
-	*sok4 = socket (AF_INET, SOCK_DGRAM, 0);
-	*sok6 = -1;
-}
-
-void
-net_store_fill_any (netstore *ns)
-{
-	ns->addr.sin_family = AF_INET;
-	ns->addr.sin_addr.s_addr = INADDR_ANY;
-	ns->addr.sin_port = 0;
-}
-
-void
-net_store_fill_v4 (netstore *ns, guint32 addr, int port)
-{
-	ns->addr.sin_family = AF_INET;
-	ns->addr.sin_addr.s_addr = addr;
-	ns->addr.sin_port = port;
+	return g_new0 (netstore, 1);
 }
 
-guint32
-net_getsockaddr_v4 (netstore *ns)
-{
-	return ns->addr.sin_addr.s_addr;
-}
-
-int
-net_getsockport (int sok4, int sok6)
-{
-	struct sockaddr_in addr;
-	int len = sizeof (addr);
-
-	if (getsockname (sok4, (struct sockaddr *)&addr, &len) == -1)
-		return -1;
-	return addr.sin_port;
-}
-
-#else
-
 /* =================== IPV6 ================== */
 
 char *
@@ -231,11 +121,11 @@ net_resolve (netstore * ns, char *hostname, int port, char **real_host)
 					 ipstring, sizeof (ipstring), NULL, 0, NI_NUMERICHOST);
 
 	if (ns->ip6_hostent->ai_canonname)
-		*real_host = strdup (ns->ip6_hostent->ai_canonname);
+		*real_host = g_strdup (ns->ip6_hostent->ai_canonname);
 	else
-		*real_host = strdup (hostname);
+		*real_host = g_strdup (hostname);
 
-	return strdup (ipstring);
+	return g_strdup (ipstring);
 }
 
 /* the only thing making this interface unclean, this shitty sok4, sok6 business */
@@ -298,88 +188,3 @@ udp_sockets (int *sok4, int *sok6)
 	*sok4 = socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP);
 	*sok6 = socket (AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
 }
-
-/* the following functions are used only by MSPROXY and are not
-   proper ipv6 implementations - do not use in new code! */
-
-void
-net_store_fill_any (netstore *ns)
-{
-	struct addrinfo *ai;
-	struct sockaddr_in *sin;
-
-	ai = ns->ip6_hostent;
-	if (!ai) {
-		ai = malloc (sizeof (struct addrinfo));
-		memset (ai, 0, sizeof (struct addrinfo));
-		ns->ip6_hostent = ai;
-	}
-	sin = (struct sockaddr_in *)ai->ai_addr;
-	if (!sin) {
-		sin = malloc (sizeof (struct sockaddr_in));
-		memset (sin, 0, sizeof (struct sockaddr_in));
-		ai->ai_addr = (struct sockaddr *)sin;
-	}
-	ai->ai_family = AF_INET;
-	ai->ai_addrlen = sizeof(struct sockaddr_in);
-	sin->sin_family = AF_INET;
-	sin->sin_addr.s_addr = INADDR_ANY;
-	sin->sin_port = 0;
-	ai->ai_next = NULL;
-}
-
-void
-net_store_fill_v4 (netstore *ns, guint32 addr, int port)
-{
-	struct addrinfo *ai;
-	struct sockaddr_in *sin;
-
-	ai = ns->ip6_hostent;
-	if (!ai) {
-		ai = malloc (sizeof (struct addrinfo));
-		memset (ai, 0, sizeof (struct addrinfo));
-		ns->ip6_hostent = ai;
-	}
-	sin = (struct sockaddr_in *)ai->ai_addr;
-	if (!sin) {
-		sin = malloc (sizeof (struct sockaddr_in));
-		memset (sin, 0, sizeof (struct sockaddr_in));
-		ai->ai_addr = (struct sockaddr *)sin;
-	}
-	ai->ai_family = AF_INET;
-	ai->ai_addrlen = sizeof(struct sockaddr_in);
-	sin->sin_family = AF_INET;
-	sin->sin_addr.s_addr = addr;
-	sin->sin_port = port;
-	ai->ai_next = NULL;
-}
-
-guint32
-net_getsockaddr_v4 (netstore *ns)
-{
-	struct addrinfo *ai;
-	struct sockaddr_in *sin;
-
-	ai = ns->ip6_hostent;
-
-	while (ai->ai_family != AF_INET) {
-		ai = ai->ai_next;
-		if (!ai)
-			return 0;
-	}
-	sin = (struct sockaddr_in *)ai->ai_addr;
-	return sin->sin_addr.s_addr;
-}
-
-int
-net_getsockport (int sok4, int sok6)
-{
-	struct sockaddr_in addr;
-	int len = sizeof (addr);
-
-	if (getsockname (sok4, (struct sockaddr *)&addr, &len) == -1)
-		return -1;
-	return addr.sin_port;
-}
-
-#endif
diff --git a/src/common/network.h b/src/common/network.h
index 6a4dce39..8c1c0c79 100644
--- a/src/common/network.h
+++ b/src/common/network.h
@@ -23,13 +23,8 @@
 typedef struct netstore_
 {
 #ifdef NETWORK_PRIVATE
-#ifdef USE_IPV6
 	struct addrinfo *ip6_hostent;
 #else
-	struct hostent *ip4_hostent;
-	struct sockaddr_in addr;
-#endif
-#else
 	int _dummy;	/* some compilers don't like empty structs */
 #endif
 } netstore;
@@ -43,11 +38,5 @@ char *net_resolve (netstore *ns, char *hostname, int port, char **real_host);
 void net_bind (netstore *tobindto, int sok4, int sok6);
 char *net_ip (guint32 addr);
 void net_sockets (int *sok4, int *sok6);
-/* functions for MSPROXY only! */
-void udp_sockets (int *sok4, int *sok6);
-void net_store_fill_any (netstore *ns);
-void net_store_fill_v4 (netstore *ns, guint32 addr, int port);
-guint32 net_getsockaddr_v4 (netstore *ns);
-int net_getsockport(int sok4, int sok6);
 
 #endif
diff --git a/src/common/notify.c b/src/common/notify.c
index bf80a1b5..b5316c36 100644
--- a/src/common/notify.c
+++ b/src/common/notify.c
@@ -47,7 +47,7 @@ int notify_tag = 0;
 static char *
 despacify_dup (char *str)
 {
-	char *p, *res = malloc (strlen (str) + 1);
+	char *p, *res = g_malloc (strlen (str) + 1);
 
 	p = res;
 	while (1)
@@ -70,11 +70,11 @@ notify_netcmp (char *str, void *serv)
 
 	if (rfc_casecmp (str, net) == 0)
 	{
-		free (net);
+		g_free (net);
 		return 0;	/* finish & return FALSE from token_foreach() */
 	}
 
-	free (net);
+	g_free (net);
 	return 1;	/* keep going... */
 }
 
@@ -111,14 +111,10 @@ notify_find_server_entry (struct notify *notify, struct server *serv)
 	if (!notify_do_network (notify, serv))
 		return NULL;
 
-	servnot = malloc (sizeof (struct notify_per_server));
-	if (servnot)
-	{
-		memset (servnot, 0, sizeof (struct notify_per_server));
-		servnot->server = serv;
-		servnot->notify = notify;
-		notify->server_list = g_slist_prepend (notify->server_list, servnot);
-	}
+	servnot = g_new0 (struct notify_per_server, 1);
+	servnot->server = serv;
+	servnot->notify = notify;
+	notify->server_list = g_slist_prepend (notify->server_list, servnot);
 	return servnot;
 }
 
@@ -200,7 +196,7 @@ notify_find (server *serv, char *nick)
 		list = list->next;
 	}
 
-	return 0;
+	return NULL;
 }
 
 static void
@@ -247,10 +243,9 @@ notify_announce_online (server * serv, struct notify_per_server *servnot,
 
 	    /* Let's do whois with idle time (like in /quote WHOIS %s %s) */
 
-	    char *wii_str = malloc (strlen (nick) * 2 + 2);
-	    sprintf (wii_str, "%s %s", nick, nick);
+	    char *wii_str = g_strdup_printf ("%s %s", nick, nick);
 	    serv->p_whois (serv, wii_str);
-	    free (wii_str);
+	    g_free (wii_str);
 	}
 }
 
@@ -346,9 +341,9 @@ notify_watch (server * serv, char *nick, int add)
 		addchar = '-';
 
 	if (serv->supports_monitor)
-		snprintf (tbuf, sizeof (tbuf), "MONITOR %c %s", addchar, nick);
+		g_snprintf (tbuf, sizeof (tbuf), "MONITOR %c %s", addchar, nick);
 	else if (serv->supports_watch)
-		snprintf (tbuf, sizeof (tbuf), "WATCH %c%s", addchar, nick);
+		g_snprintf (tbuf, sizeof (tbuf), "WATCH %c%s", addchar, nick);
 	else
 		return;
 
@@ -561,9 +556,9 @@ notify_showlist (struct session *sess, const message_tags_data *tags_data)
 		notify = (struct notify *) list->data;
 		servnot = notify_find_server_entry (notify, sess->server);
 		if (servnot && servnot->ison)
-			snprintf (outbuf, sizeof (outbuf), _("  %-20s online\n"), notify->name);
+			g_snprintf (outbuf, sizeof (outbuf), _("  %-20s online\n"), notify->name);
 		else
-			snprintf (outbuf, sizeof (outbuf), _("  %-20s offline\n"), notify->name);
+			g_snprintf (outbuf, sizeof (outbuf), _("  %-20s offline\n"), notify->name);
 		PrintTextTimeStamp (sess, outbuf, tags_data->timestamp);
 		list = list->next;
 	}
@@ -596,14 +591,13 @@ notify_deluser (char *name)
 				servnot = (struct notify_per_server *) notify->server_list->data;
 				notify->server_list =
 					g_slist_remove (notify->server_list, servnot);
-				free (servnot);
+				g_free (servnot);
 			}
 			notify_list = g_slist_remove (notify_list, notify);
 			notify_watch_all (notify, FALSE);
-			if (notify->networks)
-				free (notify->networks);
-			free (notify->name);
-			free (notify);
+			g_free (notify->networks);
+			g_free (notify->name);
+			g_free (notify);
 			fe_notify_update (0);
 			return 1;
 		}
@@ -615,27 +609,18 @@ notify_deluser (char *name)
 void
 notify_adduser (char *name, char *networks)
 {
-	struct notify *notify = malloc (sizeof (struct notify));
-	if (notify)
-	{
-		memset (notify, 0, sizeof (struct notify));
-		if (strlen (name) >= NICKLEN)
-		{
-			notify->name = malloc (NICKLEN);
-			safe_strcpy (notify->name, name, NICKLEN);
-		} else
-		{
-			notify->name = strdup (name);
-		}
-		if (networks)
-			notify->networks = despacify_dup (networks);
-		notify->server_list = 0;
-		notify_list = g_slist_prepend (notify_list, notify);
-		notify_checklist ();
-		fe_notify_update (notify->name);
-		fe_notify_update (0);
-		notify_watch_all (notify, TRUE);
-	}
+	struct notify *notify = g_new0 (struct notify, 1);
+
+	notify->name = g_strndup (name, NICKLEN - 1);
+
+	if (networks != NULL)
+		notify->networks = despacify_dup (networks);
+	notify->server_list = 0;
+	notify_list = g_slist_prepend (notify_list, notify);
+	notify_checklist ();
+	fe_notify_update (notify->name);
+	fe_notify_update (0);
+	notify_watch_all (notify, TRUE);
 }
 
 gboolean
@@ -714,7 +699,7 @@ notify_cleanup ()
 			{
 				notify->server_list =
 					g_slist_remove (notify->server_list, servnot);
-				free (servnot);
+				g_free (servnot);
 				nslist = notify->server_list;
 			} else
 			{
diff --git a/src/common/outbound.c b/src/common/outbound.c
index 651558ce..a4611927 100644
--- a/src/common/outbound.c
+++ b/src/common/outbound.c
@@ -90,7 +90,7 @@ random_line (char *file_name)
 	{
 	 nofile:
 		/* reason is not a file, an actual reason! */
-		return strdup (file_name);
+		return g_strdup (file_name);
 	}
 
 	/* count number of lines in file */
@@ -111,7 +111,7 @@ random_line (char *file_name)
 	}
 	while (lines > ran);
 	fclose (fh);
-	return strdup (buf);
+	return g_strdup (buf);
 }
 
 void
@@ -121,7 +121,7 @@ server_sendpart (server * serv, char *channel, char *reason)
 	{
 		reason = random_line (prefs.hex_irc_part_reason);
 		serv->p_part (serv, channel, reason);
-		free (reason);
+		g_free (reason);
 	} else
 	{
 		/* reason set by /quit, /close argument */
@@ -136,12 +136,12 @@ server_sendquit (session * sess)
 
 	if (!sess->quitreason)
 	{
-		colrea = strdup (prefs.hex_irc_quit_reason);
+		colrea = g_strdup (prefs.hex_irc_quit_reason);
 		check_special_chars (colrea, FALSE);
 		rea = random_line (colrea);
-		free (colrea);
+		g_free (colrea);
 		sess->server->p_quit (sess->server, rea);
-		free (rea);
+		g_free (rea);
 	} else
 	{
 		/* reason set by /quit, /close argument */
@@ -269,7 +269,7 @@ cmd_addserver (struct session *sess, char *tbuf, char *word[], char *word_eol[])
 		if (!network)
 		{
 			network = servlist_net_add (word[2], "", TRUE);
-			network->encoding = strdup (IRC_DEFAULT_CHARSET);
+			network->encoding = g_strdup (IRC_DEFAULT_CHARSET);
 		}
 		/* if we had the network already, check if the given server already exists */
 		else if (servlist_server_find (network, word_eol[3], NULL))
@@ -379,11 +379,10 @@ cmd_away (struct session *sess, char *tbuf, char *word[], char *word_eol[])
 
 	if (sess->server->last_away_reason != reason)
 	{
-		if (sess->server->last_away_reason)
-			free (sess->server->last_away_reason);
+		g_free (sess->server->last_away_reason);
 
 		if (reason == word_eol[2])
-			sess->server->last_away_reason = strdup (reason);
+			sess->server->last_away_reason = g_strdup (reason);
 		else
 			sess->server->last_away_reason = reason;
 	}
@@ -406,8 +405,7 @@ cmd_back (struct session *sess, char *tbuf, char *word[], char *word_eol[])
 		PrintText (sess, _("Already marked back.\n"));
 	}
 
-	if (sess->server->last_away_reason)
-		free (sess->server->last_away_reason);
+	g_free (sess->server->last_away_reason);
 	sess->server->last_away_reason = NULL;
 
 	return TRUE;
@@ -483,19 +481,19 @@ create_mask (session * sess, char *mask, char *mode, char *typestr, int deop)
 			switch (type)
 			{
 			case 0:
-				snprintf (buf, sizeof (buf), "%s %s *!*@%s.*", mode, p2, domain);
+				g_snprintf (buf, sizeof (buf), "%s %s *!*@%s.*", mode, p2, domain);
 				break;
 
 			case 1:
-				snprintf (buf, sizeof (buf), "%s %s *!*@%s", mode, p2, fullhost);
+				g_snprintf (buf, sizeof (buf), "%s %s *!*@%s", mode, p2, fullhost);
 				break;
 
 			case 2:
-				snprintf (buf, sizeof (buf), "%s %s *!%s@%s.*", mode, p2, username, domain);
+				g_snprintf (buf, sizeof (buf), "%s %s *!%s@%s.*", mode, p2, username, domain);
 				break;
 
 			case 3:
-				snprintf (buf, sizeof (buf), "%s %s *!%s@%s", mode, p2, username, fullhost);
+				g_snprintf (buf, sizeof (buf), "%s %s *!%s@%s", mode, p2, username, fullhost);
 				break;
 			}
 		} else
@@ -503,26 +501,26 @@ create_mask (session * sess, char *mask, char *mode, char *typestr, int deop)
 			switch (type)
 			{
 			case 0:
-				snprintf (buf, sizeof (buf), "%s %s *!*@*%s", mode, p2, domain);
+				g_snprintf (buf, sizeof (buf), "%s %s *!*@*%s", mode, p2, domain);
 				break;
 
 			case 1:
-				snprintf (buf, sizeof (buf), "%s %s *!*@%s", mode, p2, fullhost);
+				g_snprintf (buf, sizeof (buf), "%s %s *!*@%s", mode, p2, fullhost);
 				break;
 
 			case 2:
-				snprintf (buf, sizeof (buf), "%s %s *!%s@*%s", mode, p2, username, domain);
+				g_snprintf (buf, sizeof (buf), "%s %s *!%s@*%s", mode, p2, username, domain);
 				break;
 
 			case 3:
-				snprintf (buf, sizeof (buf), "%s %s *!%s@%s", mode, p2, username, fullhost);
+				g_snprintf (buf, sizeof (buf), "%s %s *!%s@%s", mode, p2, username, fullhost);
 				break;
 			}
 		}
 
 	} else
 	{
-		snprintf (buf, sizeof (buf), "%s %s", mode, mask);
+		g_snprintf (buf, sizeof (buf), "%s %s", mode, mask);
 	}
 	
 	return g_strdup (buf);
@@ -592,7 +590,6 @@ static int
 cmd_charset (struct session *sess, char *tbuf, char *word[], char *word_eol[])
 {
 	server *serv = sess->server;
-	const char *locale = NULL;
 	int offset = 0;
 
 	if (strcmp (word[2], "-quiet") == 0)
@@ -600,9 +597,7 @@ cmd_charset (struct session *sess, char *tbuf, char *word[], char *word_eol[])
 
 	if (!word[2 + offset][0])
 	{
-		g_get_charset (&locale);
-		PrintTextf (sess, "Current charset: %s\n",
-						serv->encoding ? serv->encoding : locale);
+		PrintTextf (sess, "Current charset: %s\n", serv->encoding);
 		return TRUE;
 	}
 
@@ -1002,14 +997,14 @@ mdehop_cb (struct User *user, multidata *data)
 static int
 cmd_mdehop (struct session *sess, char *tbuf, char *word[], char *word_eol[])
 {
-	char **nicks = malloc (sizeof (char *) * sess->hops);
+	char **nicks = g_new0 (char *, sess->hops);
 	multidata data;
 
 	data.nicks = nicks;
 	data.i = 0;
 	tree_foreach (sess->usertree, (tree_traverse_func *)mdehop_cb, &data);
 	send_channel_modes (sess, tbuf, nicks, 0, data.i, '-', 'h', 0);
-	free (nicks);
+	g_free (nicks);
 
 	return TRUE;
 }
@@ -1028,14 +1023,14 @@ mdeop_cb (struct User *user, multidata *data)
 static int
 cmd_mdeop (struct session *sess, char *tbuf, char *word[], char *word_eol[])
 {
-	char **nicks = malloc (sizeof (char *) * sess->ops);
+	char **nicks = g_new0(char *, sess->ops);
 	multidata data;
 
 	data.nicks = nicks;
 	data.i = 0;
 	tree_foreach (sess->usertree, (tree_traverse_func *)mdeop_cb, &data);
 	send_channel_modes (sess, tbuf, nicks, 0, data.i, '-', 'o', 0);
-	free (nicks);
+	g_free (nicks);
 
 	return TRUE;
 }
@@ -1045,18 +1040,13 @@ GSList *menu_list = NULL;
 static void
 menu_free (menu_entry *me)
 {
-	free (me->path);
-	if (me->label)
-		free (me->label);
-	if (me->cmd)
-		free (me->cmd);
-	if (me->ucmd)
-		free (me->ucmd);
-	if (me->group)
-		free (me->group);
-	if (me->icon)
-		free (me->icon);
-	free (me);
+	g_free (me->path);
+	g_free (me->label);
+	g_free (me->cmd);
+	g_free (me->ucmd);
+	g_free (me->group);
+	g_free (me->icon);
+	g_free (me);
 }
 
 /* strings equal? but ignore underscores */
@@ -1115,9 +1105,9 @@ menu_del_children (char *path, char *label)
 	if (!label)
 		label = "";
 	if (path[0])
-		snprintf (buf, sizeof (buf), "%s/%s", path, label);
+		g_snprintf (buf, sizeof (buf), "%s/%s", path, label);
 	else
-		snprintf (buf, sizeof (buf), "%s", label);
+		g_snprintf (buf, sizeof (buf), "%s", label);
 
 	list = menu_list;
 	while (list)
@@ -1168,7 +1158,9 @@ menu_is_mainmenu_root (char *path, gint16 *offset)
 	{
 		if (!strncmp (path, menus[i] + 1, menus[i][0]))
 		{
-			*offset = menus[i][0] + 1;	/* number of bytes to offset the root */
+			*offset = menus[i][0];	/* number of bytes to offset the root */
+			if (path[*offset] != '\0')
+				*offset += 1;
 			return 0;	/* is not main menu */
 		}
 	}
@@ -1193,7 +1185,7 @@ menu_add (char *path, char *label, char *cmd, char *ucmd, int pos, int state, in
 		return;
 	}
 
-	me = malloc (sizeof (menu_entry));
+	me = g_new (menu_entry, 1);
 	me->pos = pos;
 	me->modifier = mod;
 	me->is_main = menu_is_mainmenu_root (path, &me->root_offset);
@@ -1201,31 +1193,26 @@ menu_add (char *path, char *label, char *cmd, char *ucmd, int pos, int state, in
 	me->markup = markup;
 	me->enable = enable;
 	me->key = key;
-	me->path = strdup (path);
+	me->path = g_strdup (path);
 	me->label = NULL;
 	me->cmd = NULL;
 	me->ucmd = NULL;
 	me->group = NULL;
 	me->icon = NULL;
 
-	if (label)
-		me->label = strdup (label);
-	if (cmd)
-		me->cmd = strdup (cmd);
-	if (ucmd)
-		me->ucmd = strdup (ucmd);
-	if (group)
-		me->group = strdup (group);
-	if (icon)
-		me->icon = strdup (icon);
+	me->label = g_strdup (label);
+	me->cmd = g_strdup (cmd);
+	me->ucmd = g_strdup (ucmd);
+	me->group = g_strdup (group);
+	me->icon = g_strdup (icon);
 
 	menu_list = g_slist_append (menu_list, me);
 	label = fe_menu_add (me);
 	if (label)
 	{
 		/* FE has given us a stripped label */
-		free (me->label);
-		me->label = strdup (label);
+		g_free (me->label);
+		me->label = g_strdup (label);
 		g_free (label); /* this is from pango */
 	}
 }
@@ -1321,7 +1308,7 @@ cmd_menu (struct session *sess, char *tbuf, char *word[], char *word_eol[])
 	if (markup)
 	{
 		char *p;	/* to force pango closing tags through */
-		for (p = label; *p; p++)
+		for (p = label; p && *p; p++)
 			if (*p == 3)
 				*p = '/';
 	}
@@ -1454,7 +1441,7 @@ exec_check_process (struct session *sess)
 	{
 		close (sess->running_exec->myfd);
 		fe_input_remove (sess->running_exec->iotag);
-		free (sess->running_exec);
+		g_free (sess->running_exec);
 		sess->running_exec = NULL;
 	}
 }
@@ -1531,11 +1518,10 @@ cmd_execw (struct session *sess, char *tbuf, char *word[], char *word_eol[])
 		return FALSE;
 	}
 	len = strlen(word_eol[2]);
-	temp = malloc(len + 2);
-	sprintf(temp, "%s\n", word_eol[2]);
+	temp = g_strconcat (word_eol[2], "\n", NULL);
 	PrintText(sess, temp);
 	write(sess->running_exec->myfd, temp, len + 1);
-	free(temp);
+	g_free(temp);
 
 	return TRUE;
 }
@@ -1559,7 +1545,7 @@ exec_handle_colors (char *buf, int len)
 	if (strchr (buf, 27) == 0)
 		return;
 
-	nbuf = malloc (len + 1);
+	nbuf = g_malloc (len + 1);
 
 	while (i < len)
 	{
@@ -1653,7 +1639,7 @@ norm:			nbuf[j] = buf[i];
 
 	nbuf[j] = 0;
 	memcpy (buf, nbuf, j + 1);
-	free (nbuf);
+	g_free (nbuf);
 }
 
 #ifndef HAVE_MEMRCHR
@@ -1665,7 +1651,7 @@ memrchr (const void *block, int c, size_t size)
 	for (p = (unsigned char *)block + size; p != block; p--)
 		if (*p == c)
 			return p;
-	return 0;
+	return NULL;
 }
 #endif
 
@@ -1679,14 +1665,14 @@ exec_data (GIOChannel *source, GIOCondition condition, struct nbexec *s)
 	len = s->buffill;
 	if (len) {
 		/* append new data to buffered incomplete line */
-		buf = malloc(len + 2050);
+		buf = g_malloc (len + 2050);
 		memcpy(buf, s->linebuf, len);
 		readpos = buf + len;
-		free(s->linebuf);
+		g_free (s->linebuf);
 		s->linebuf = NULL;
 	}
 	else
-		readpos = buf = malloc(2050);
+		readpos = buf = g_malloc (2050);
 
 	rd = read (sok, readpos, 2048);
 	if (rd < 1)
@@ -1707,12 +1693,12 @@ exec_data (GIOChannel *source, GIOCondition condition, struct nbexec *s)
 			else
 				PrintText (s->sess, buf);
 		}
-		free(buf);
+		g_free(buf);
 		waitpid (s->childpid, NULL, 0);
 		s->sess->running_exec = NULL;
 		fe_input_remove (s->iotag);
 		close (sok);
-		free (s);
+		g_free (s);
 		return TRUE;
 	}
 	len += rd;
@@ -1725,7 +1711,7 @@ exec_data (GIOChannel *source, GIOCondition condition, struct nbexec *s)
 		rest = buf;
 	if (*rest) {
 		s->buffill = len - (rest - buf); /* = strlen(rest) */
-		s->linebuf = malloc(s->buffill + 1);
+		s->linebuf = g_malloc (s->buffill + 1);
 		memcpy(s->linebuf, rest, s->buffill);
 		*rest = '\0';
 		len -= s->buffill; /* possibly 0 */
@@ -1741,7 +1727,7 @@ exec_data (GIOChannel *source, GIOCondition condition, struct nbexec *s)
 			PrintText (s->sess, buf);
 	}
 
-	free(buf);
+	g_free (buf);
 	return TRUE;
 }
 
@@ -1803,8 +1789,7 @@ cmd_exec (struct session *sess, char *tbuf, char *word[], char *word_eol[])
 			return FALSE;
 		}
 #endif
-		s = (struct nbexec *) malloc (sizeof (struct nbexec));
-		memset(s, 0, sizeof(*s));
+		s = g_new0 (struct nbexec, 1);
 		s->myfd = fds[0];
 		s->tochannel = tochannel;
 		s->sess = sess;
@@ -1851,8 +1836,9 @@ cmd_exec (struct session *sess, char *tbuf, char *word[], char *word_eol[])
 			PrintText (sess, "Error in fork(2)\n");
 			close(fds[0]);
 			close(fds[1]);
-			free (s);
-		} else
+			g_free (s);
+		}
+		else
 		{
 			/* Parent path */
 			close(fds[1]);
@@ -1940,12 +1926,12 @@ get_bool_cb (int val, getvalinfo *info)
 {
 	char buf[512];
 
-	snprintf (buf, sizeof (buf), "%s %d", info->cmd, val);
+	g_snprintf (buf, sizeof (buf), "%s %d", info->cmd, val);
 	if (is_session (info->sess))
 		handle_command (info->sess, buf, FALSE);
 
-	free (info->cmd);
-	free (info);
+	g_free (info->cmd);
+	g_free (info);
 }
 
 static int
@@ -1956,8 +1942,8 @@ cmd_getbool (struct session *sess, char *tbuf, char *word[], char *word_eol[])
 	if (!word[4][0])
 		return FALSE;
 
-	info = malloc (sizeof (*info));
-	info->cmd = strdup (word[2]);
+	info = g_new (getvalinfo, 1);
+	info->cmd = g_strdup (word[2]);
 	info->sess = sess;
 
 	fe_get_bool (word[3], word_eol[4], get_bool_cb, info);
@@ -1972,13 +1958,13 @@ get_int_cb (int cancel, int val, getvalinfo *info)
 
 	if (!cancel)
 	{
-		snprintf (buf, sizeof (buf), "%s %d", info->cmd, val);
+		g_snprintf (buf, sizeof (buf), "%s %d", info->cmd, val);
 		if (is_session (info->sess))
 			handle_command (info->sess, buf, FALSE);
 	}
 
-	free (info->cmd);
-	free (info);
+	g_free (info->cmd);
+	g_free (info);
 }
 
 static int
@@ -1989,8 +1975,8 @@ cmd_getint (struct session *sess, char *tbuf, char *word[], char *word_eol[])
 	if (!word[4][0])
 		return FALSE;
 
-	info = malloc (sizeof (*info));
-	info->cmd = strdup (word[3]);
+	info = g_new (getvalinfo, 1);
+	info->cmd = g_strdup (word[3]);
 	info->sess = sess;
 
 	fe_get_int (word[4], atoi (word[2]), get_int_cb, info);
@@ -2007,13 +1993,13 @@ get_file_cb (char *cmd, char *file)
       no args */
 	if (file)
 	{
-		snprintf (buf, sizeof (buf), "%s %s", cmd, file);
+		g_snprintf (buf, sizeof (buf), "%s %s", cmd, file);
 		handle_command (current_sess, buf, FALSE);
 	}
 	else
 	{
 		handle_command (current_sess, cmd, FALSE);
-		free (cmd);
+		g_free (cmd);
 	}
 }
 
@@ -2044,7 +2030,7 @@ cmd_getfile (struct session *sess, char *tbuf, char *word[], char *word_eol[])
 		idx++;
 	}
 
-	fe_get_file (word[idx+1], word[idx+2], (void *)get_file_cb, strdup (word[idx]), flags);
+	fe_get_file (word[idx+1], word[idx+2], (void *)get_file_cb, g_strdup (word[idx]), flags);
 
 	return TRUE;
 }
@@ -2056,13 +2042,13 @@ get_str_cb (int cancel, char *val, getvalinfo *info)
 
 	if (!cancel)
 	{
-		snprintf (buf, sizeof (buf), "%s %s", info->cmd, val);
+		g_snprintf (buf, sizeof (buf), "%s %s", info->cmd, val);
 		if (is_session (info->sess))
 			handle_command (info->sess, buf, FALSE);
 	}
 
-	free (info->cmd);
-	free (info);
+	g_free (info->cmd);
+	g_free (info);
 }
 
 static int
@@ -2073,8 +2059,8 @@ cmd_getstr (struct session *sess, char *tbuf, char *word[], char *word_eol[])
 	if (!word[4][0])
 		return FALSE;
 
-	info = malloc (sizeof (*info));
-	info->cmd = strdup (word[3]);
+	info = g_new (getvalinfo, 1);
+	info->cmd = g_strdup (word[3]);
 	info->sess = sess;
 
 	fe_get_str (word[4], word[2], get_str_cb, info);
@@ -2200,7 +2186,7 @@ cmd_help (struct session *sess, char *tbuf, char *word[], char *word_eol[])
 	} else
 	{
 		struct popup *pop;
-		char *buf = malloc (4096);
+		char *buf = g_malloc (4096);
 		help_list hl;
 
 		hl.longfmt = longfmt;
@@ -2245,7 +2231,7 @@ cmd_help (struct session *sess, char *tbuf, char *word[], char *word_eol[])
 		plugin_command_foreach (sess, &hl, (void *)show_help_line);
 		strcat (buf, "\n");
 		PrintText (sess, buf);
-		free (buf);
+		g_free (buf);
 
 		PrintTextf (sess, "\n%s\n\n", _("Type /HELP <command> for more information, or /HELP -l"));
 	}
@@ -2293,7 +2279,7 @@ cmd_ignore (struct session *sess, char *tbuf, char *word[], char *word_eol[])
 			    strchr (mask, '*') == NULL)
 			{
 				mask = tbuf;
-				snprintf (tbuf, TBUFSIZE, "%s!*@*", word[2]);
+				g_snprintf (tbuf, TBUFSIZE, "%s!*@*", word[2]);
 			}
 
 			i = ignore_add (mask, type, TRUE);
@@ -2549,7 +2535,7 @@ cmd_load (struct session *sess, char *tbuf, char *word[], char *word_eol[])
 			PrintText (sess, errorstring (errno));
 			g_free (buf);
 		}
-		free (file);
+		g_free (file);
 		return TRUE;
 	}
 
@@ -2562,7 +2548,7 @@ cmd_load (struct session *sess, char *tbuf, char *word[], char *word_eol[])
 
 		file = expand_homedir (word[2]);
 		error = plugin_load (sess, file, arg);
-		free (file);
+		g_free (file);
 
 		if (error)
 			PrintText (sess, error);
@@ -2651,7 +2637,7 @@ cmd_me (struct session *sess, char *tbuf, char *word[], char *word_eol[])
 		return TRUE;
 	}
 
-	snprintf (tbuf, TBUFSIZE, "\001ACTION %s\001\r", act);
+	g_snprintf (tbuf, TBUFSIZE, "\001ACTION %s\001\r", act);
 	/* first try through DCC CHAT */
 	if (dcc_write_chat (sess->channel, tbuf))
 	{
@@ -2674,7 +2660,7 @@ cmd_me (struct session *sess, char *tbuf, char *word[], char *word_eol[])
 				if (*split_text)
 					offset += strlen(split_text);
 
-				g_free(split_text);
+				g_free (split_text);
 			}
 
 			sess->server->p_action (sess->server, sess->channel, act + offset);
@@ -2693,17 +2679,26 @@ cmd_me (struct session *sess, char *tbuf, char *word[], char *word_eol[])
 static int
 cmd_mode (struct session *sess, char *tbuf, char *word[], char *word_eol[])
 {
-	/* +channel channels are dying, let those servers whine about modes.
-	 * return info about current channel if available and no info is given */
-	if ((*word[2] == '+') || (*word[2] == 0) || (!is_channel(sess->server, word[2]) &&
-				!(rfc_casecmp(sess->server->nick, word[2]) == 0)))
+	/* We allow omitting the target, so we have to figure it out:
+	 * - Can only use info from channels or dialogs
+	 * - Empty arg is always sess info
+	 * - Assume + is mode not channel
+	 * - We know valid channels and our nick
+	 * - We cannot easily know if other nick or valid mode (Need to store 004)
+	 */
+	if ((sess->type != SESS_CHANNEL && sess->type != SESS_DIALOG)
+	    || (!(*word[2] == '-' || *word[2] == '+' || *word[2] == '\0')
+	        && (is_channel (sess->server, word[2]) || !rfc_casecmp (sess->server->nick, word[2])))
+	   )
+	{
+		sess->server->p_mode (sess->server, word[2], word_eol[3]);
+	}
+	else
 	{
 		if(sess->channel[0] == 0)
 			return FALSE;
 		sess->server->p_mode (sess->server, sess->channel, word_eol[2]);
 	}
-	else
-		sess->server->p_mode (sess->server, word[2], word_eol[3]);
 	return TRUE;
 }
 
@@ -2721,7 +2716,7 @@ mop_cb (struct User *user, multidata *data)
 static int
 cmd_mop (struct session *sess, char *tbuf, char *word[], char *word_eol[])
 {
-	char **nicks = malloc (sizeof (char *) * (sess->total - sess->ops));
+	char **nicks = g_new0 (char *, sess->total - sess->ops);
 	multidata data;
 
 	data.nicks = nicks;
@@ -2729,7 +2724,7 @@ cmd_mop (struct session *sess, char *tbuf, char *word[], char *word_eol[])
 	tree_foreach (sess->usertree, (tree_traverse_func *)mop_cb, &data);
 	send_channel_modes (sess, tbuf, nicks, 0, data.i, '+', 'o', 0);
 
-	free (nicks);
+	g_free (nicks);
 
 	return TRUE;
 }
@@ -2780,7 +2775,7 @@ cmd_msg (struct session *sess, char *tbuf, char *word[], char *word_eol[])
 					if (*split_text)
 						offset += strlen(split_text);
 
-					g_free(split_text);
+					g_free (split_text);
 				}
 				sess->server->p_message (sess->server, nick, msg + offset);
 				offset = 0;
@@ -2801,7 +2796,7 @@ cmd_msg (struct session *sess, char *tbuf, char *word[], char *word_eol[])
 					if (*split_text)
 						offset += strlen(split_text);
 
-					g_free(split_text);
+					g_free (split_text);
 				}
 				inbound_chanmsg (newsess->server, NULL, newsess->channel,
 									  newsess->server->nick, msg + offset, TRUE, FALSE,
@@ -2895,7 +2890,7 @@ cmd_notice (struct session *sess, char *tbuf, char *word[], char *word_eol[])
 			if (*split_text)
 				offset += strlen(split_text);
 			
-			g_free(split_text);
+			g_free (split_text);
 		}
 
 		sess->server->p_notice (sess->server, word[2], text + offset);
@@ -2991,7 +2986,7 @@ cmd_ping (struct session *sess, char *tbuf, char *word[], char *word_eol[])
 
 	tim = make_ping_time ();
 
-	snprintf (timestring, sizeof (timestring), "%lu", tim);
+	g_snprintf (timestring, sizeof (timestring), "%lu", tim);
 	sess->server->p_ping (sess->server, to, timestring);
 
 	return TRUE;
@@ -3054,7 +3049,7 @@ cmd_query (struct session *sess, char *tbuf, char *word[], char *word_eol[])
 				if (*split_text)
 					offset += strlen(split_text);
 
-				g_free(split_text);
+				g_free (split_text);
 			}
 			sess->server->p_message (sess->server, nick, msg + offset);
 			inbound_chanmsg (nick_sess->server, nick_sess, nick_sess->channel,
@@ -3231,9 +3226,9 @@ cmd_send (struct session *sess, char *tbuf, char *word[], char *word_eol[])
 	if ((addr & 0xffff0000) == 0xc0a80000 ||	/* 192.168.x.x */
 		 (addr & 0xff000000) == 0x0a000000)		/* 10.x.x.x */
 		/* we got a private net address, let's PSEND or it'll fail */
-		snprintf (tbuf, 512, "DCC PSEND %s", word_eol[2]);
+		g_snprintf (tbuf, 512, "DCC PSEND %s", word_eol[2]);
 	else
-		snprintf (tbuf, 512, "DCC SEND %s", word_eol[2]);
+		g_snprintf (tbuf, 512, "DCC SEND %s", word_eol[2]);
 
 	handle_command (sess, tbuf, FALSE);
 
@@ -3418,8 +3413,9 @@ cmd_server (struct session *sess, char *tbuf, char *word[], char *word_eol[])
 			safe_strcpy (serv->password, net->pass, sizeof (serv->password));
 			serv->loginmethod = net->logintype;
 		}
-		else /* Otherwise ensure no password is sent */
+		else /* Otherwise ensure no password is sent or SASL started */
 		{
+			serv->loginmethod = LOGIN_DEFAULT;
 			serv->password[0] = 0;
 		}
 	}
@@ -3484,12 +3480,6 @@ cmd_topic (struct session *sess, char *tbuf, char *word[], char *word_eol[])
 static int
 cmd_tray (struct session *sess, char *tbuf, char *word[], char *word_eol[])
 {
-	if (strcmp (word[2], "-b") == 0)
-	{
-		fe_tray_set_balloon (word[3], word[4][0] ? word[4] : NULL);
-		return TRUE;
-	}
-
 	if (strcmp (word[2], "-t") == 0)
 	{
 		fe_tray_set_tooltip (word[3][0] ? word[3] : NULL);
@@ -3533,7 +3523,7 @@ cmd_unignore (struct session *sess, char *tbuf, char *word[],
 		if (strchr (mask, '?') == NULL && strchr (mask, '*') == NULL)
 		{
 			mask = tbuf;
-			snprintf (tbuf, TBUFSIZE, "%s!*@*", word[2]);
+			g_snprintf (tbuf, TBUFSIZE, "%s!*@*", word[2]);
 		}
 		
 		if (ignore_del (mask, NULL))
@@ -3873,7 +3863,7 @@ const struct commands xc_cmds[] = {
 	 N_("ALLCHANL <cmd>, sends a command to all channels on the current server")},
 	{"ALLSERV", cmd_allservers, 0, 0, 1,
 	 N_("ALLSERV <cmd>, sends a command to all servers you're in")},
-	{"AWAY", cmd_away, 1, 0, 1, N_("AWAY [<reason>], sets you away")},
+	{"AWAY", cmd_away, 1, 0, 1, N_("AWAY [<reason>], sets you away (use /BACK to unset)")},
 	{"BACK", cmd_back, 1, 0, 1, N_("BACK, sets you back (not away)")},
 	{"BAN", cmd_ban, 1, 1, 1,
 	 N_("BAN <mask> [<bantype>], bans everyone matching the mask from the current channel. If they are already on the channel this doesn't kick them (needs chanop)")},
@@ -4096,7 +4086,7 @@ usercommand_show_help (session *sess, char *name)
 		pop = (struct popup *) list->data;
 		if (!g_ascii_strcasecmp (pop->name, name))
 		{
-			snprintf (buf, sizeof(buf), _("User Command for: %s\n"), pop->cmd);
+			g_snprintf (buf, sizeof(buf), _("User Command for: %s\n"), pop->cmd);
 			PrintText (sess, buf);
 
 			found = TRUE;
@@ -4123,7 +4113,7 @@ help (session *sess, char *tbuf, char *helpcmd, int quiet)
 	{
 		if (cmd->help)
 		{
-			snprintf (tbuf, TBUFSIZE, _("Usage: %s\n"), _(cmd->help));
+			g_snprintf (tbuf, TBUFSIZE, _("Usage: %s\n"), _(cmd->help));
 			PrintText (sess, tbuf);
 		} else
 		{
@@ -4145,7 +4135,7 @@ help (session *sess, char *tbuf, char *helpcmd, int quiet)
  * - this beast is used for UserCommands, UserlistButtons and CTCP replies   */
 
 int
-auto_insert (char *dest, int destlen, unsigned char *src, char *word[],
+auto_insert (char *dest, gsize destlen, unsigned char *src, char *word[],
 				 char *word_eol[], char *a, char *c, char *d, char *e, char *h,
 				 char *n, char *s, char *u)
 {
@@ -4217,7 +4207,7 @@ auto_insert (char *dest, int destlen, unsigned char *src, char *word[],
 				switch (src[0])
 				{
 				case '%':
-					if ((dest - orig) + 2 >= destlen)
+					if ((dest - orig) + 2u >= destlen)
 						return 2;
 					dest[0] = '%';
 					dest[1] = 0;
@@ -4252,7 +4242,7 @@ auto_insert (char *dest, int destlen, unsigned char *src, char *word[],
 				case 'y':
 					now = time (0);
 					tm_ptr = localtime (&now);
-					snprintf (buf, sizeof (buf), "%4d%02d%02d", 1900 +
+					g_snprintf (buf, sizeof (buf), "%4d%02d%02d", 1900 +
 								 tm_ptr->tm_year, 1 + tm_ptr->tm_mon, tm_ptr->tm_mday);
 					utf = buf;
 					break;
@@ -4311,81 +4301,78 @@ check_special_chars (char *cmd, int do_ascii) /* check for %X */
 	if (!len)
 		return;
 
-	buf = malloc (len + 1);
+	buf = g_malloc (len + 1);
 
-	if (buf)
+	while (cmd[j])
 	{
-		while (cmd[j])
+		switch (cmd[j])
 		{
-			switch (cmd[j])
+		case '%':
+			occur++;
+			if (	do_ascii &&
+					j + 3 < len &&
+					(isdigit ((unsigned char) cmd[j + 1]) && isdigit ((unsigned char) cmd[j + 2]) &&
+					isdigit ((unsigned char) cmd[j + 3])))
 			{
-			case '%':
-				occur++;
-				if (	do_ascii &&
-						j + 3 < len &&
-						(isdigit ((unsigned char) cmd[j + 1]) && isdigit ((unsigned char) cmd[j + 2]) &&
-						isdigit ((unsigned char) cmd[j + 3])))
+				tbuf[0] = cmd[j + 1];
+				tbuf[1] = cmd[j + 2];
+				tbuf[2] = cmd[j + 3];
+				tbuf[3] = 0;
+				buf[i] = atoi (tbuf);
+				utf = g_locale_to_utf8 (buf + i, 1, 0, &utf_len, 0);
+				if (utf)
 				{
-					tbuf[0] = cmd[j + 1];
-					tbuf[1] = cmd[j + 2];
-					tbuf[2] = cmd[j + 3];
-					tbuf[3] = 0;
-					buf[i] = atoi (tbuf);
-					utf = g_locale_to_utf8 (buf + i, 1, 0, &utf_len, 0);
-					if (utf)
-					{
-						memcpy (buf + i, utf, utf_len);
-						g_free (utf);
-						i += (utf_len - 1);
-					}
-					j += 3;
-				} else
+					memcpy (buf + i, utf, utf_len);
+					g_free (utf);
+					i += (utf_len - 1);
+				}
+				j += 3;
+			} else
+			{
+				switch (cmd[j + 1])
 				{
-					switch (cmd[j + 1])
-					{
-					case 'R':
-						buf[i] = '\026';
-						break;
-					case 'U':
-						buf[i] = '\037';
-						break;
-					case 'B':
-						buf[i] = '\002';
-						break;
-					case 'I':
-						buf[i] = '\035';
-						break;
-					case 'C':
-						buf[i] = '\003';
-						break;
-					case 'O':
-						buf[i] = '\017';
-						break;
-					case 'H':	/* CL: invisible text code */
-						buf[i] = HIDDEN_CHAR;
-						break;
-					case '%':
-						buf[i] = '%';
-						break;
-					default:
-						buf[i] = '%';
-						j--;
-						break;
-					}
-					j++;
+				case 'R':
+					buf[i] = '\026';
+					break;
+				case 'U':
+					buf[i] = '\037';
+					break;
+				case 'B':
+					buf[i] = '\002';
+					break;
+				case 'I':
+					buf[i] = '\035';
+					break;
+				case 'C':
+					buf[i] = '\003';
+					break;
+				case 'O':
+					buf[i] = '\017';
+					break;
+				case 'H':	/* CL: invisible text code */
+					buf[i] = HIDDEN_CHAR;
+					break;
+				case '%':
+					buf[i] = '%';
+					break;
+				default:
+					buf[i] = '%';
+					j--;
 					break;
-			default:
-					buf[i] = cmd[j];
 				}
+				j++;
+				break;
+		default:
+				buf[i] = cmd[j];
 			}
-			j++;
-			i++;
 		}
-		buf[i] = 0;
-		if (occur)
-			strcpy (cmd, buf);
-		free (buf);
+		j++;
+		i++;
 	}
+	buf[i] = 0;
+	if (occur)
+		strcpy (cmd, buf);
+	g_free (buf);
 }
 
 typedef struct
@@ -4408,7 +4395,7 @@ nick_comp_cb (struct User *user, nickdata *data)
 		lenu = strlen (user->nick);
 		if (lenu == data->len)
 		{
-			snprintf (data->tbuf, TBUFSIZE, "%s%s", user->nick, data->space);
+			g_snprintf (data->tbuf, TBUFSIZE, "%s%s", user->nick, data->space);
 			data->len = -1;
 			return FALSE;
 		} else if (lenu < data->bestlen)
@@ -4452,7 +4439,7 @@ perform_nick_completion (struct session *sess, char *cmd, char *tbuf)
 
 				if (data.best)
 				{
-					snprintf (tbuf, TBUFSIZE, "%s%s", data.best->nick, space - 1);
+					g_snprintf (tbuf, TBUFSIZE, "%s%s", data.best->nick, space - 1);
 					return;
 				}
 			}
@@ -4485,12 +4472,10 @@ handle_say (session *sess, char *text, int check_spch)
 	struct DCC *dcc;
 	char *word[PDIWORDS+1];
 	char *word_eol[PDIWORDS+1];
-	char pdibuf_static[1024];
-	char newcmd_static[1024];
-	char *pdibuf = pdibuf_static;
-	char *newcmd = newcmd_static;
+	char *pdibuf;
+	char *newcmd;
 	int len;
-	int newcmdlen = sizeof newcmd_static;
+	int newcmdlen;
 	message_tags_data no_tags = MESSAGE_TAGS_DATA_INIT;
 
 	if (strcmp (sess->channel, "(lastlog)") == 0)
@@ -4500,11 +4485,9 @@ handle_say (session *sess, char *text, int check_spch)
 	}
 
 	len = strlen (text);
-	if (len >= sizeof pdibuf_static)
-		pdibuf = malloc (len + 1);
-
-	if (len + NICKLEN >= newcmdlen)
-		newcmd = malloc (newcmdlen = len + NICKLEN + 1);
+	pdibuf = g_malloc (len + 1);
+	newcmdlen = MAX(len + NICKLEN + 1, TBUFSIZE);
+	newcmd = g_malloc (newcmdlen);
 
 	if (check_spch && prefs.hex_input_perc_color)
 		check_special_chars (text, prefs.hex_input_perc_ascii);
@@ -4565,7 +4548,7 @@ handle_say (session *sess, char *text, int check_spch)
 			if (*split_text)
 				offset += strlen(split_text);
 			
-			g_free(split_text);
+			g_free (split_text);
 		}
 
 		inbound_chanmsg (sess->server, sess, sess->channel, sess->server->nick,
@@ -4577,11 +4560,9 @@ handle_say (session *sess, char *text, int check_spch)
 	}
 
 xit:
-	if (pdibuf != pdibuf_static)
-		free (pdibuf);
+	g_free (pdibuf);
 
-	if (newcmd != newcmd_static)
-		free (newcmd);
+	g_free (newcmd);
 }
 
 char *
@@ -4675,8 +4656,6 @@ handle_command (session *sess, char *cmd, int check_spch)
 	char *word_eol[PDIWORDS+1];
 	static int command_level = 0;
 	struct commands *int_cmd;
-	char pdibuf_static[1024];
-	char tbuf_static[TBUFSIZE];
 	char *pdibuf;
 	char *tbuf;
 	int len;
@@ -4691,23 +4670,8 @@ handle_command (session *sess, char *cmd, int check_spch)
 	/* anything below MUST DEC command_level before returning */
 
 	len = strlen (cmd);
-	if (len >= sizeof (pdibuf_static))
-	{
-		pdibuf = malloc (len + 1);
-	}
-	else
-	{
-		pdibuf = pdibuf_static;
-	}
-
-	if ((len * 2) >= sizeof (tbuf_static))
-	{
-		tbuf = malloc ((len * 2) + 1);
-	}
-	else
-	{
-		tbuf = tbuf_static;
-	}
+	pdibuf = g_malloc (len + 1);
+	tbuf = g_malloc (MAX(TBUFSIZE, (len * 2) + 1));
 
 	/* split the text into words and word_eol */
 	process_data_init (pdibuf, cmd, word, word_eol, TRUE, TRUE);
@@ -4786,13 +4750,13 @@ handle_command (session *sess, char *cmd, int check_spch)
 	}
 	else
 	{
-		/* unknown command, just send it to the server and hope */
 		if (!sess->server->connected)
 		{
-			PrintText (sess, _("Unknown Command. Try /help\n"));
+			PrintTextf (sess, _("Unknown Command %s. Try /help\n"), word[1]);
 		}
 		else
 		{
+			/* unknown command, just send it to the server and hope */
 			sess->server->p_raw (sess->server, cmd);
 		}
 	}
@@ -4800,15 +4764,8 @@ handle_command (session *sess, char *cmd, int check_spch)
 xit:
 	command_level--;
 
-	if (pdibuf != pdibuf_static)
-	{
-		free (pdibuf);
-	}
-
-	if (tbuf != tbuf_static)
-	{
-		free (tbuf);
-	}
+	g_free (pdibuf);
+	g_free (tbuf);
 
 	return ret;
 }
diff --git a/src/common/outbound.h b/src/common/outbound.h
index b9fe6331..490a58ca 100644
--- a/src/common/outbound.h
+++ b/src/common/outbound.h
@@ -25,7 +25,7 @@
 extern const struct commands xc_cmds[];
 extern GSList *menu_list;
 
-int auto_insert (char *dest, int destlen, unsigned char *src, char *word[], char *word_eol[],
+int auto_insert (char *dest, gsize destlen, unsigned char *src, char *word[], char *word_eol[],
 				 char *a, char *c, char *d, char *e, char *h, char *n, char *s, char *u);
 char *command_insert_vars (session *sess, char *cmd);
 int handle_command (session *sess, char *cmd, int check_spch);
diff --git a/src/common/plugin-identd.c b/src/common/plugin-identd.c
new file mode 100644
index 00000000..ce1bd1e6
--- /dev/null
+++ b/src/common/plugin-identd.c
@@ -0,0 +1,220 @@
+/* HexChat
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+*/
+
+#include "config.h"
+
+#include <string.h>
+#include <gio/gio.h>
+#include "hexchat-plugin.h"
+
+#define _(x) hexchat_gettext(ph,x)
+
+static hexchat_plugin *ph;
+static GSocketService *service;
+static GHashTable *responses;
+
+typedef struct ident_info
+{
+	GSocketConnection *conn;
+	gchar *username;
+	gchar read_buf[16];
+} ident_info;
+
+static int
+identd_cleanup_response_cb (gpointer userdata)
+{
+	g_return_val_if_fail (responses != NULL, 0);
+
+	g_hash_table_remove (responses, userdata);
+
+	return 0;
+}
+
+static int
+identd_command_cb (char *word[], char *word_eol[], void *userdata)
+{
+	g_return_val_if_fail (responses != NULL, HEXCHAT_EAT_ALL);
+
+	if (service == NULL) /* If we are not running plugins can handle it */
+		return HEXCHAT_EAT_HEXCHAT;
+
+	if (word[2] && *word[2] && word[3] && *word[3])
+	{
+		guint64 port = g_ascii_strtoull (word[2], NULL, 0);
+
+		if (port && port <= G_MAXUINT16)
+		{
+			g_hash_table_insert (responses, GINT_TO_POINTER (port), g_strdup (word[3]));
+			/* Automatically remove entry after 30 seconds */
+			hexchat_hook_timer (ph, 30000, identd_cleanup_response_cb, GINT_TO_POINTER (port));
+		}
+	}
+	else
+	{
+		hexchat_command (ph, "HELP IDENTD");
+	}
+
+	return HEXCHAT_EAT_ALL;
+}
+
+static void
+identd_write_ready (GOutputStream *stream, GAsyncResult *res, ident_info *info)
+{
+	g_output_stream_write_finish (stream, res, NULL);
+
+	g_free (info->username);
+	g_object_unref (info->conn);
+	g_free (info);
+}
+
+static void
+identd_read_ready (GInputStream *in_stream, GAsyncResult *res, ident_info *info)
+{
+	GSocketAddress *sok_addr;
+	GOutputStream *out_stream;
+	guint64 local, remote;
+	gchar buf[512], *p;
+
+	if (g_input_stream_read_finish (in_stream, res, NULL))
+	{
+		local = g_ascii_strtoull (info->read_buf, NULL, 0);
+		p = strchr (info->read_buf, ',');
+		if (!p)
+			goto cleanup;
+
+		remote = g_ascii_strtoull (p + 1, NULL, 0);
+
+		if (!local || !remote || local > G_MAXUINT16 || remote > G_MAXUINT16)
+			goto cleanup;
+
+		info->username = g_strdup (g_hash_table_lookup (responses, GINT_TO_POINTER (local)));
+		if (!info->username)
+			goto cleanup;
+		g_hash_table_remove (responses, GINT_TO_POINTER (local));
+
+		if ((sok_addr = g_socket_connection_get_remote_address (info->conn, NULL)))
+		{
+			GInetAddress *inet_addr;
+			gchar *addr;
+
+			inet_addr = g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (sok_addr));
+			addr = g_inet_address_to_string (inet_addr);
+
+			hexchat_printf (ph, _("*\tServicing ident request from %s as %s"), addr, info->username);
+
+			g_object_unref (sok_addr);
+			g_object_unref (inet_addr);
+			g_free (addr);
+		}
+
+		g_snprintf (buf, sizeof (buf), "%"G_GUINT16_FORMAT", %"G_GUINT16_FORMAT" : USERID : UNIX : %s\r\n", (guint16)local, (guint16)remote, info->username);
+		out_stream = g_io_stream_get_output_stream (G_IO_STREAM (info->conn));
+		g_output_stream_write_async (out_stream, buf, strlen (buf), G_PRIORITY_DEFAULT,
+									NULL, (GAsyncReadyCallback)identd_write_ready, info);
+	}
+
+	return;
+
+cleanup:
+	g_object_unref (info->conn);
+	g_free (info);
+}
+
+static gboolean
+identd_incoming_cb (GSocketService *service, GSocketConnection *conn,
+					GObject *source, gpointer userdata)
+{
+	GInputStream *stream;
+	ident_info *info;
+
+	info = g_new0 (ident_info, 1);
+
+	info->conn = conn;
+	g_object_ref (conn);
+
+	stream = g_io_stream_get_input_stream (G_IO_STREAM (conn));
+	g_input_stream_read_async (stream, info->read_buf, sizeof (info->read_buf), G_PRIORITY_DEFAULT,
+							NULL, (GAsyncReadyCallback)identd_read_ready, info);
+
+	return TRUE;
+}
+
+static void
+identd_start_server (void)
+{
+	GError *error = NULL;
+	int enabled, port = 113;
+
+	if (hexchat_get_prefs (ph, "identd", NULL, &enabled) == 3)
+	{
+		if (!enabled)
+			return;
+	}
+	if (hexchat_get_prefs (ph, "identd_port", NULL, &port) == 2 && (port <= 0 || port > G_MAXUINT16))
+	{
+		port = 113;
+	}
+
+	service = g_socket_service_new ();
+
+	g_socket_listener_add_inet_port (G_SOCKET_LISTENER (service), port, NULL, &error);
+	if (error)
+	{
+		hexchat_printf (ph, _("*\tError starting identd server: %s"), error->message);
+
+		g_object_unref (service);
+		service = NULL;
+		return;
+	}
+	/*hexchat_printf (ph, "*\tIdentd listening on port: %d", port); */
+
+	g_signal_connect (G_OBJECT (service), "incoming", G_CALLBACK(identd_incoming_cb), NULL);
+	g_socket_service_start (service);
+}
+
+int
+identd_plugin_init (hexchat_plugin *plugin_handle, char **plugin_name,
+					char **plugin_desc, char **plugin_version, char *arg)
+{
+	ph = plugin_handle;
+	*plugin_name = "";
+	*plugin_desc = "";
+	*plugin_version = "";
+
+
+	responses = g_hash_table_new_full (NULL, NULL, NULL, g_free);
+	hexchat_hook_command (ph, "IDENTD", HEXCHAT_PRI_NORM, identd_command_cb,
+						_("IDENTD <port> <username>"), NULL);
+
+	identd_start_server ();
+
+	return 1; /* This must always succeed for /identd to work */
+}
+
+int
+identd_plugin_deinit (void)
+{
+	if (service)
+	{
+		g_socket_service_stop (service);
+		g_object_unref (service);
+	}
+
+	g_hash_table_destroy (responses);
+
+	return 1;
+}
diff --git a/src/common/identd.h b/src/common/plugin-identd.h
index 3b29135f..5efc2600 100644
--- a/src/common/identd.h
+++ b/src/common/plugin-identd.h
@@ -17,9 +17,12 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
-#ifndef HEXCHAT_IDENTD_H
-#define HEXCHAT_IDENTD_H
+#ifndef HEXCHAT_PLUGIN_IDENTD_H
+#define HEXCHAT_PLUGIN_IDENTD_H
 
-void identd_start (char *username);
+int identd_plugin_init (hexchat_plugin *plugin_handle, char **plugin_name,
+				char **plugin_desc, char **plugin_version, char *arg);
+
+int identd_plugin_deinit ();
 
 #endif
diff --git a/src/common/plugin-timer.c b/src/common/plugin-timer.c
index e6944330..d0c82c28 100644
--- a/src/common/plugin-timer.c
+++ b/src/common/plugin-timer.c
@@ -17,6 +17,8 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
+#include "config.h"
+
 #include <stdlib.h>
 #include <string.h>
 #include <glib.h>
@@ -43,7 +45,7 @@ typedef struct
 	char *command;
 	int ref;
 	int repeat;
-	float timeout;
+	int timeout;
 	unsigned int forever:1;
 } timer;
 
@@ -51,9 +53,9 @@ static void
 timer_del (timer *tim)
 {
 	timer_list = g_slist_remove (timer_list, tim);
-	free (tim->command);
+	g_free (tim->command);
 	hexchat_unhook (ph, tim->hook);
-	free (tim);
+	g_free (tim);
 }
 
 static void
@@ -99,7 +101,7 @@ timeout_cb (timer *tim)
 }
 
 static void
-timer_add (int ref, float timeout, int repeat, char *command)
+timer_add (int ref, int timeout, int repeat, char *command)
 {
 	timer *tim;
 	GSList *list;
@@ -117,18 +119,18 @@ timer_add (int ref, float timeout, int repeat, char *command)
 		}
 	}
 
-	tim = malloc (sizeof (timer));
+	tim = g_new (timer, 1);
 	tim->ref = ref;
 	tim->repeat = repeat;
 	tim->timeout = timeout;
-	tim->command = strdup (command);
+	tim->command = g_strdup (command);
 	tim->context = hexchat_get_context (ph);
 	tim->forever = FALSE;
 
 	if (repeat == 0)
 		tim->forever = TRUE;
 
-	tim->hook = hexchat_hook_timer (ph, timeout * 1000.0, (void *)timeout_cb, tim);
+	tim->hook = hexchat_hook_timer (ph, timeout, (void *)timeout_cb, tim);
 	timer_list = g_slist_append (timer_list, tim);
 }
 
@@ -150,7 +152,7 @@ timer_showlist (void)
 	while (list)
 	{
 		tim = list->data;
-		hexchat_printf (ph, _("%5d %8.1f %7d  %s\n"), tim->ref, tim->timeout,
+		hexchat_printf (ph, _("%5d %8.1f %7d  %s\n"), tim->ref, tim->timeout / 1000.0f,
 						  tim->repeat, tim->command);
 		list = list->next;
 	}
@@ -160,7 +162,7 @@ static int
 timer_cb (char *word[], char *word_eol[], void *userdata)
 {
 	int repeat = 1;
-	float timeout;
+	double timeout;
 	int offset = 0;
 	int ref = 0;
 	int quiet = FALSE;
@@ -199,10 +201,10 @@ timer_cb (char *word[], char *word_eol[], void *userdata)
 	timeout = atof (word[2 + offset]);
 	command = word_eol[3 + offset];
 
-	if (timeout < 0.1 || !command[0])
+	if (timeout < 0.1 || timeout * 1000 > INT_MAX || !command[0])
 		hexchat_print (ph, HELP);
 	else
-		timer_add (ref, timeout, repeat, command);
+		timer_add (ref, (int) timeout * 1000, repeat, command);
 
 	return HEXCHAT_EAT_HEXCHAT;
 }
diff --git a/src/common/plugin.c b/src/common/plugin.c
index d83b69ff..a397c878 100644
--- a/src/common/plugin.c
+++ b/src/common/plugin.c
@@ -161,16 +161,12 @@ plugin_free (hexchat_plugin *pl, int do_deinit, int allow_refuse)
 xit:
 	if (pl->free_strings)
 	{
-		if (pl->name)
-			free (pl->name);
-		if (pl->desc)
-			free (pl->desc);
-		if (pl->version)
-			free (pl->version);
+		g_free (pl->name);
+		g_free (pl->desc);
+		g_free (pl->version);
 	}
-	if (pl->filename)
-		free ((char *)pl->filename);
-	free (pl);
+	g_free ((char *)pl->filename);
+	g_free (pl);
 
 	plugin_list = g_slist_remove (plugin_list, pl);
 
@@ -188,7 +184,7 @@ plugin_list_add (hexchat_context *ctx, char *filename, const char *name,
 {
 	hexchat_plugin *pl;
 
-	pl = malloc (sizeof (hexchat_plugin));
+	pl = g_new (hexchat_plugin, 1);
 	pl->handle = handle;
 	pl->filename = filename;
 	pl->context = ctx;
@@ -239,9 +235,7 @@ plugin_add (session *sess, char *filename, void *handle, void *init_func,
 	hexchat_plugin *pl;
 	char *file;
 
-	file = NULL;
-	if (filename)
-		file = strdup (filename);
+	file = g_strdup (filename);
 
 	pl = plugin_list_add (sess, file, file, NULL, NULL, handle, deinit_func,
 								 fake, FALSE);
@@ -361,15 +355,11 @@ plugin_kill_all (void)
 
 #ifdef USE_PLUGIN
 
-/* load a plugin from a filename. Returns: NULL-success or an error string */
-
-char *
-plugin_load (session *sess, char *filename, char *arg)
+GModule *
+module_load (char *filename)
 {
 	void *handle;
 	char *filepart;
-	hexchat_init_func *init_func;
-	hexchat_deinit_func *deinit_func;
 	char *pluginpath;
 
 	/* get the filename without path */
@@ -389,6 +379,18 @@ plugin_load (session *sess, char *filename, char *arg)
 		handle = g_module_open (filename, 0);
 	}
 
+	return handle;
+}
+
+/* load a plugin from a filename. Returns: NULL-success or an error string */
+
+char *
+plugin_load (session *sess, char *filename, char *arg)
+{
+	GModule *handle = module_load (filename);
+	hexchat_init_func *init_func;
+	hexchat_deinit_func *deinit_func;
+
 	if (handle == NULL)
 		return (char *)g_module_error ();
 
@@ -596,7 +598,7 @@ xit:
 		if (!hook || hook->type == HOOK_DELETED)
 		{
 			hook_list = g_slist_remove (hook_list, hook);
-			free (hook);
+			g_free (hook);
 		}
 		list = next;
 	}
@@ -615,13 +617,7 @@ plugin_emit_command (session *sess, char *name, char *word[], char *word_eol[])
 hexchat_event_attrs *
 hexchat_event_attrs_create (hexchat_plugin *ph)
 {
-	hexchat_event_attrs *attrs;
-
-	attrs = g_malloc (sizeof (*attrs));
-
-	attrs->server_time_utc = (time_t) 0;
-
-	return attrs;
+	return g_new0 (hexchat_event_attrs, 1);
 }
 
 void
@@ -671,26 +667,31 @@ plugin_emit_dummy_print (session *sess, char *name)
 }
 
 int
-plugin_emit_keypress (session *sess, unsigned int state, unsigned int keyval,
-							 int len, char *string)
+plugin_emit_keypress (session *sess, unsigned int state, unsigned int keyval, gunichar key)
 {
 	char *word[PDIWORDS];
 	char keyval_str[16];
 	char state_str[16];
 	char len_str[16];
-	int i;
+	char key_str[7];
+	int i, len;
 
 	if (!hook_list)
 		return 0;
 
 	sprintf (keyval_str, "%u", keyval);
 	sprintf (state_str, "%u", state);
+	if (!key)
+		len = 0;
+	else
+		len = g_unichar_to_utf8 (key, key_str);
+	key_str[len] = '\0';
 	sprintf (len_str, "%d", len);
 
 	word[0] = "Key Press";
 	word[1] = keyval_str;
 	word[2] = state_str;
-	word[3] = string;
+	word[3] = key_str;
 	word[4] = len_str;
 	for (i = 5; i < PDIWORDS; i++)
 		word[i] = "\000";
@@ -796,15 +797,11 @@ plugin_add_hook (hexchat_plugin *pl, int type, int pri, const char *name,
 {
 	hexchat_hook *hook;
 
-	hook = malloc (sizeof (hexchat_hook));
-	memset (hook, 0, sizeof (hexchat_hook));
-
+	hook = g_new0 (hexchat_hook, 1);
 	hook->type = type;
 	hook->pri = pri;
-	if (name)
-		hook->name = strdup (name);
-	if (help_text)
-		hook->help_text = strdup (help_text);
+	hook->name = g_strdup (name);
+	hook->help_text = g_strdup (help_text);
 	hook->callback = callb;
 	hook->pl = pl;
 	hook->userdata = userdata;
@@ -892,10 +889,8 @@ hexchat_unhook (hexchat_plugin *ph, hexchat_hook *hook)
 
 	hook->type = HOOK_DELETED;	/* expunge later */
 
-	if (hook->name)
-		free (hook->name);	/* NULL for timers & fds */
-	if (hook->help_text)
-		free (hook->help_text);	/* NULL for non-commands */
+	g_free (hook->name);	/* NULL for timers & fds */
+	g_free (hook->help_text);	/* NULL for non-commands */
 
 	return hook->userdata;
 }
@@ -988,8 +983,7 @@ hexchat_printf (hexchat_plugin *ph, const char *format, ...)
 void
 hexchat_command (hexchat_plugin *ph, const char *command)
 {
-	char *conv;
-	int len = -1;
+	char *command_utf8;
 
 	if (!is_session (ph->context))
 	{
@@ -998,9 +992,9 @@ hexchat_command (hexchat_plugin *ph, const char *command)
 	}
 
 	/* scripts/plugins continue to send non-UTF8... *sigh* */
-	conv = text_validate ((char **)&command, &len);
-	handle_command (ph->context, (char *)command, FALSE);
-	g_free (conv);
+	command_utf8 = text_fixup_invalid_utf8 (command, -1, NULL);
+	handle_command (ph->context, command_utf8, FALSE);
+	g_free (command_utf8);
 }
 
 void
@@ -1263,8 +1257,7 @@ hexchat_list_get (hexchat_plugin *ph, const char *name)
 {
 	hexchat_list *list;
 
-	list = malloc (sizeof (hexchat_list));
-	list->pos = NULL;
+	list = g_new0 (hexchat_list, 1);
 
 	switch (str_hash (name))
 	{
@@ -1299,7 +1292,7 @@ hexchat_list_get (hexchat_plugin *ph, const char *name)
 		}	/* fall through */
 
 	default:
-		free (list);
+		g_free (list);
 		return NULL;
 	}
 
@@ -1311,7 +1304,7 @@ hexchat_list_free (hexchat_plugin *ph, hexchat_list *xlist)
 {
 	if (xlist->type == LIST_USERS)
 		g_slist_free (xlist->head);
-	free (xlist);
+	g_free (xlist);
 }
 
 int
@@ -1531,7 +1524,14 @@ hexchat_list_int (hexchat_plugin *ph, hexchat_list *xlist, const char *name)
 		case 0x34207553: /* address32 */
 			return ((struct DCC *)data)->addr;
 		case 0x181a6: /* cps */
-			return ((struct DCC *)data)->cps;
+		{
+			gint64 cps = ((struct DCC *)data)->cps;
+			if (cps <= INT_MAX)
+			{
+				return (int) cps;
+			}
+			return INT_MAX;
+		}
 		case 0x349881: /* port */
 			return ((struct DCC *)data)->port;
 		case 0x1b254: /* pos */
@@ -1569,7 +1569,7 @@ hexchat_list_int (hexchat_plugin *ph, hexchat_list *xlist, const char *name)
 		case 0x5cfee87:	/* flags */
 			/* used if text_strip is unset */                    /* 16 */
 			tmp <<= 1;
-			tmp = ((struct session *)data)->text_strip;          /* 15 */
+			tmp |= ((struct session *)data)->text_strip;          /* 15 */
 			tmp <<= 1;
 			/* used if text_scrollback is unset */               /* 14 */
 			tmp <<= 1;
@@ -1644,8 +1644,8 @@ hexchat_plugingui_add (hexchat_plugin *ph, const char *filename,
 							const char *version, char *reserved)
 {
 #ifdef USE_PLUGIN
-	ph = plugin_list_add (NULL, strdup (filename), strdup (name), strdup (desc),
-								 strdup (version), NULL, NULL, TRUE, TRUE);
+	ph = plugin_list_add (NULL, g_strdup (filename), g_strdup (name), g_strdup (desc),
+								 g_strdup (version), NULL, NULL, TRUE, TRUE);
 	fe_pluginlist_update ();
 #endif
 
@@ -1771,6 +1771,8 @@ hexchat_pluginpref_set_str_real (hexchat_plugin *pl, const char *var, const char
 	{
 		g_free (confname);
 		g_free (confname_tmp);
+		if (fpIn)
+			fclose (fpIn);
 		return 0;
 	}
 	else if (fpIn == NULL)	/* no previous config file, no parsing */
@@ -1819,7 +1821,7 @@ hexchat_pluginpref_set_str_real (hexchat_plugin *pl, const char *var, const char
 	{
 		prevSetting = 0;
 
-		while (fscanf (fpIn, " %[^\n]", line_bufp) != EOF)	/* read whole lines including whitespaces */
+		while (fscanf (fpIn, " %511[^\n]", line_bufp) != EOF)	/* read whole lines including whitespaces */
 		{
 			buffer_tmp = g_strdup_printf ("%s ", var);	/* add one space, this way it works against var - var2 checks too */
 
@@ -1908,7 +1910,6 @@ hexchat_pluginpref_get_str_real (hexchat_plugin *pl, const char *var, char *dest
 		g_free (confname);
 		return 0;
 	}
-
 	g_free (confname);
 
 	if (!cfg_get_str (cfg, var, buf, sizeof(buf)))
@@ -1937,7 +1938,7 @@ hexchat_pluginpref_set_int (hexchat_plugin *pl, const char *var, int value)
 {
 	char buffer[12];
 
-	snprintf (buffer, sizeof (buffer), "%d", value);
+	g_snprintf (buffer, sizeof (buffer), "%d", value);
 	return hexchat_pluginpref_set_str_real (pl, var, buffer, 1);
 }
 
diff --git a/src/common/plugin.h b/src/common/plugin.h
index cd3f70a8..5743f39a 100644
--- a/src/common/plugin.h
+++ b/src/common/plugin.h
@@ -163,6 +163,7 @@ struct _hexchat_plugin
 };
 #endif
 
+GModule *module_load (char *filename);
 char *plugin_load (session *sess, char *filename, char *arg);
 int plugin_reload (session *sess, char *name, int by_filename);
 void plugin_add (session *sess, char *filename, void *handle, void *init_func, void *deinit_func, char *arg, int fake);
@@ -174,7 +175,7 @@ int plugin_emit_server (session *sess, char *name, char *word[], char *word_eol[
 						time_t server_time);
 int plugin_emit_print (session *sess, char *word[], time_t server_time);
 int plugin_emit_dummy_print (session *sess, char *name);
-int plugin_emit_keypress (session *sess, unsigned int state, unsigned int keyval, int len, char *string);
+int plugin_emit_keypress (session *sess, unsigned int state, unsigned int keyval, gunichar key);
 GList* plugin_command_list(GList *tmp_list);
 int plugin_show_help (session *sess, char *cmd);
 void plugin_command_foreach (session *sess, void *userdata, void (*cb) (session *sess, void *userdata, char *name, char *usage));
diff --git a/src/common/proto-irc.c b/src/common/proto-irc.c
index 34b4ece1..d8f15cb5 100644
--- a/src/common/proto-irc.c
+++ b/src/common/proto-irc.c
@@ -429,7 +429,7 @@ irc_raw (server *serv, char *raw)
 		len = strlen (raw);
 		if (len < sizeof (tbuf) - 3)
 		{
-			len = snprintf (tbuf, sizeof (tbuf), "%s\r\n", raw);
+			len = g_snprintf (tbuf, sizeof (tbuf), "%s\r\n", raw);
 			tcp_send_len (serv, tbuf, len);
 		} else
 		{
@@ -590,7 +590,7 @@ process_numeric (session * sess, int n,
 			char *tim;
 			char outbuf[64];
 
-			snprintf (outbuf, sizeof (outbuf),
+			g_snprintf (outbuf, sizeof (outbuf),
 						"%02ld:%02ld:%02ld", idle / 3600, (idle / 60) % 60,
 						idle % 60);
 			if (timestamp == 0)
@@ -666,7 +666,6 @@ process_numeric (session * sess, int n,
 			EMIT_SIGNAL_TIMESTAMP (XP_TE_CHANMODES, sess, word[4], word_eol[5],
 										  NULL, NULL, 0, tags_data->timestamp);
 		fe_update_mode_buttons (sess, 'c', '-');
-		fe_update_mode_buttons (sess, 'r', '-');
 		fe_update_mode_buttons (sess, 't', '-');
 		fe_update_mode_buttons (sess, 'n', '-');
 		fe_update_mode_buttons (sess, 'i', '-');
@@ -816,10 +815,7 @@ process_numeric (session * sess, int n,
 	case 349:	/* end of exemption list */
 		sess = find_channel (serv, word[4]);
 		if (!sess)
-		{
-			sess = serv->front_session;
 			goto def;
-		}
 		if (!fe_ban_list_end (sess, 349))
 			goto def;
 		break;
@@ -844,10 +840,7 @@ process_numeric (session * sess, int n,
 	case 368:
 		sess = find_channel (serv, word[4]);
 		if (!sess)
-		{
-			sess = serv->front_session;
 			goto def;
-		}
 		if (!fe_ban_list_end (sess, 368))
 			goto def;
 		break;
@@ -1355,8 +1348,8 @@ process_named_servermsg (session *sess, char *buf, char *rawname, char *word_eol
 /* Returns the timezone offset. This should be the same as the variable
  * "timezone" in time.h, but *BSD doesn't have it.
  */
-static int
-get_timezone(void)
+static time_t
+get_timezone (void)
 {
 	struct tm tm_utc, tm_local;
 	time_t t, time_utc, time_local;
@@ -1481,13 +1474,10 @@ irc_inline (server *serv, char *buf, int len)
 	char *type, *text;
 	char *word[PDIWORDS+1];
 	char *word_eol[PDIWORDS+1];
-	char pdibuf_static[522]; /* 1 line can potentially be 512*6 in utf8 */
-	char *pdibuf = pdibuf_static;
+	char *pdibuf;
 	message_tags_data tags_data = MESSAGE_TAGS_DATA_INIT;
 
-	/* need more than 522? fall back to malloc */
-	if (len >= sizeof (pdibuf_static))
-		pdibuf = malloc (len + 1);
+	pdibuf = g_malloc (len + 1);
 
 	sess = serv->front_session;
 
@@ -1509,7 +1499,7 @@ irc_inline (server *serv, char *buf, int len)
 		handle_message_tags(serv, tags, &tags_data);
 	}
 
-	url_check_line (buf, len);
+	url_check_line (buf);
 
 	/* split line into words and words_to_end_of_line */
 	process_data_init (pdibuf, buf, word, word_eol, FALSE, FALSE);
@@ -1566,8 +1556,7 @@ irc_inline (server *serv, char *buf, int len)
 	}
 
 xit:
-	if (pdibuf != pdibuf_static)
-		free (pdibuf);
+	g_free (pdibuf);
 }
 
 void
diff --git a/src/common/server.c b/src/common/server.c
index 98785937..8ff81553 100644
--- a/src/common/server.c
+++ b/src/common/server.c
@@ -21,8 +21,6 @@
  *      Inferno Nettverk A/S, Norway.  All rights reserved.
  */
 
-/*#define DEBUG_MSPROXY*/
-
 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
@@ -63,20 +61,11 @@
 #include "ssl.h"
 #endif
 
-#ifdef USE_MSPROXY
-#include "msproxy.h"
-#endif
-
-#ifdef WIN32
-#include "identd.h"
-#endif
-
 #ifdef USE_LIBPROXY
 #include <proxy.h>
 #endif
 
 #ifdef USE_OPENSSL
-extern SSL_CTX *ctx;				  /* hexchat.c */
 /* local variables */
 static struct session *g_sess = NULL;
 #endif
@@ -97,57 +86,21 @@ extern pxProxyFactory *libproxy_factory;
    send via SSL. server/dcc both use this function. */
 
 int
-tcp_send_real (void *ssl, int sok, char *encoding, int using_irc, char *buf, int len)
+tcp_send_real (void *ssl, int sok, GIConv write_converter, char *buf, int len)
 {
 	int ret;
-	char *locale;
-	gsize loc_len;
-
-	if (encoding == NULL)	/* system */
-	{
-		locale = NULL;
-		if (!prefs.utf8_locale)
-		{
-			const gchar *charset;
-
-			g_get_charset (&charset);
-			locale = g_convert_with_fallback (buf, len, charset, "UTF-8",
-														 "?", 0, &loc_len, 0);
-		}
-	} else
-	{
-		if (using_irc)	/* using "IRC" encoding (CP1252/UTF-8 hybrid) */
-			/* if all chars fit inside CP1252, use that. Otherwise this
-			   returns NULL and we send UTF-8. */
-			locale = g_convert (buf, len, "CP1252", "UTF-8", 0, &loc_len, 0);
-		else
-			locale = g_convert_with_fallback (buf, len, encoding, "UTF-8",
-														 "?", 0, &loc_len, 0);
-	}
 
-	if (locale)
-	{
-		len = loc_len;
+	gsize buf_encoded_len;
+	gchar *buf_encoded = text_convert_invalid (buf, len, write_converter, "?", &buf_encoded_len);
 #ifdef USE_OPENSSL
-		if (!ssl)
-			ret = send (sok, locale, len, 0);
-		else
-			ret = _SSL_send (ssl, locale, len);
-#else
-		ret = send (sok, locale, len, 0);
-#endif
-		g_free (locale);
-	} else
-	{
-#ifdef USE_OPENSSL
-		if (!ssl)
-			ret = send (sok, buf, len, 0);
-		else
-			ret = _SSL_send (ssl, buf, len);
+	if (!ssl)
+		ret = send (sok, buf_encoded, buf_encoded_len, 0);
+	else
+		ret = _SSL_send (ssl, buf_encoded, buf_encoded_len);
 #else
-		ret = send (sok, buf, len, 0);
+	ret = send (sok, buf_encoded, buf_encoded_len, 0);
 #endif
-	}
+	g_free (buf_encoded);
 
 	return ret;
 }
@@ -157,10 +110,9 @@ server_send_real (server *serv, char *buf, int len)
 {
 	fe_add_rawlog (serv, buf, len, TRUE);
 
-	url_check_line (buf, len);
+	url_check_line (buf);
 
-	return tcp_send_real (serv->ssl, serv->sok, serv->encoding, serv->using_irc,
-								 buf, len);
+	return tcp_send_real (serv->ssl, serv->sok, serv->write_converter, buf, len);
 }
 
 /* new throttling system, uses the same method as the Undernet
@@ -213,7 +165,7 @@ tcp_send_queue (server *serv)
 
 				buf--;
 				serv->outbound_queue = g_slist_remove (serv->outbound_queue, buf);
-				free (buf);
+				g_free (buf);
 				list = serv->outbound_queue;
 			} else
 			{
@@ -235,7 +187,7 @@ tcp_send_len (server *serv, char *buf, int len)
 	if (!prefs.hex_net_throttle)
 		return server_send_real (serv, buf, len);
 
-	dbuf = malloc (len + 2);	/* first byte is the priority */
+	dbuf = g_malloc (len + 2);	/* first byte is the priority */
 	dbuf[0] = 2;	/* pri 2 for most things */
 	memcpy (dbuf + 1, buf, len);
 	dbuf[len + 1] = 0;
@@ -266,12 +218,6 @@ tcp_send_len (server *serv, char *buf, int len)
 	return 1;
 }
 
-/*int
-tcp_send (server *serv, char *buf)
-{
-	return tcp_send_len (serv, buf, strlen (buf));
-}*/
-
 void
 tcp_sendf (server *serv, const char *fmt, ...)
 {
@@ -282,7 +228,7 @@ tcp_sendf (server *serv, const char *fmt, ...)
 	int len;
 
 	va_start (args, fmt);
-	len = vsnprintf (send_buf, sizeof (send_buf) - 1, fmt, args);
+	len = g_vsnprintf (send_buf, sizeof (send_buf) - 1, fmt, args);
 	va_end (args);
 
 	send_buf[sizeof (send_buf) - 1] = '\0';
@@ -309,103 +255,17 @@ close_socket (int sok)
 /* handle 1 line of text received from the server */
 
 static void
-server_inline (server *serv, char *line, int len)
+server_inline (server *serv, char *line, gssize len)
 {
-	char *utf_line_allocated = NULL;
-
-	/* Checks whether we're set to use UTF-8 charset */
-	if (serv->using_irc ||				/* 1. using CP1252/UTF-8 Hybrid */
-		(serv->encoding == NULL && prefs.utf8_locale) || /* OR 2. using system default->UTF-8 */
-	    (serv->encoding != NULL &&				/* OR 3. explicitly set to UTF-8 */
-		 (g_ascii_strcasecmp (serv->encoding, "UTF8") == 0 ||
-		  g_ascii_strcasecmp (serv->encoding, "UTF-8") == 0)))
-	{
-		/* The user has the UTF-8 charset set, either via /charset
-		command or from his UTF-8 locale. Thus, we first try the
-		UTF-8 charset, and if we fail to convert, we assume
-		it to be ISO-8859-1 (see text_validate). */
-
-		utf_line_allocated = text_validate (&line, &len);
-
-	} else
-	{
-		/* Since the user has an explicit charset set, either
-		via /charset command or from his non-UTF8 locale,
-		we don't fallback to ISO-8859-1 and instead try to remove
-		errnoeous octets till the string is convertable in the
-		said charset. */
+	gsize len_utf8;
+	line = text_convert_invalid (line, len, serv->read_converter, unicode_fallback_string, &len_utf8);
 
-		const char *encoding = NULL;
-
-		if (serv->encoding != NULL)
-			encoding = serv->encoding;
-		else
-			g_get_charset (&encoding);
-
-		if (encoding != NULL)
-		{
-			char *conv_line; /* holds a copy of the original string */
-			int conv_len; /* tells g_convert how much of line to convert */
-			gsize utf_len;
-			gsize read_len;
-			GError *err;
-			gboolean retry;
-
-			conv_line = g_malloc (len + 1);
-			memcpy (conv_line, line, len);
-			conv_line[len] = 0;
-			conv_len = len;
-
-			/* if CP1255, convert it with the NUL terminator.
-				Works around SF bug #1122089 */
-			if (serv->using_cp1255)
-				conv_len++;
-
-			do
-			{
-				err = NULL;
-				retry = FALSE;
-				utf_line_allocated = g_convert_with_fallback (conv_line, conv_len, "UTF-8", encoding, "?", &read_len, &utf_len, &err);
-				if (err != NULL)
-				{
-					if (err->code == G_CONVERT_ERROR_ILLEGAL_SEQUENCE && conv_len > (read_len + 1))
-					{
-						/* Make our best bet by removing the erroneous char.
-						   This will work for casual 8-bit strings with non-standard chars. */
-						memmove (conv_line + read_len, conv_line + read_len + 1, conv_len - read_len -1);
-						conv_len--;
-						retry = TRUE;
-					}
-					g_error_free (err);
-				}
-			} while (retry);
-
-			g_free (conv_line);
-
-			/* If any conversion has occured at all. Conversion might fail
-			due to errors other than invalid sequences, e.g. unknown charset. */
-			if (utf_line_allocated != NULL)
-			{
-				line = utf_line_allocated;
-				len = utf_len;
-				if (serv->using_cp1255 && len > 0)
-					len--;
-			}
-			else
-			{
-				/* If all fails, treat as UTF-8 with fallback to ISO-8859-1. */
-				utf_line_allocated = text_validate (&line, &len);
-			}
-		}
-	}
-
-	fe_add_rawlog (serv, line, len, FALSE);
+	fe_add_rawlog (serv, line, len_utf8, FALSE);
 
 	/* let proto-irc.c handle it */
-	serv->p_inline (serv, line, len);
+	serv->p_inline (serv, line, len_utf8);
 
-	if (utf_line_allocated != NULL) /* only if a special copy was allocated */
-		g_free (utf_line_allocated);
+	g_free (line);
 }
 
 /* read data from socket */
@@ -529,7 +389,7 @@ server_close_pipe (int *pipefd)	/* see comments below */
 {
 	close (pipefd[0]);	/* close WRITE end first to cause an EOF on READ */
 	close (pipefd[1]);	/* in giowin32, and end that thread. */
-	free (pipefd);
+	g_free (pipefd);
 	return FALSE;
 }
 
@@ -562,7 +422,7 @@ server_stopconnecting (server * serv)
 
 	{
 		/* if we close the pipe now, giowin32 will crash. */
-		int *pipefd = malloc (sizeof (int) * 2);
+		int *pipefd = g_new (int, 2);
 		pipefd[0] = serv->childwrite;
 		pipefd[1] = serv->childread;
 		g_idle_add ((GSourceFunc)server_close_pipe, pipefd);
@@ -593,7 +453,7 @@ ssl_cb_info (SSL * s, int where, int ret)
 
 	return;							  /* FIXME: make debug level adjustable in serverlist or settings */
 
-/*	snprintf (buf, sizeof (buf), "%s (%d)", SSL_state_string_long (s), where);
+/*	g_snprintf (buf, sizeof (buf), "%s (%d)", SSL_state_string_long (s), where);
 	if (g_sess)
 		EMIT_SIGNAL (XP_TE_SSLMESSAGE, g_sess, buf, NULL, NULL, NULL, 0);
 	else
@@ -613,9 +473,9 @@ ssl_cb_verify (int ok, X509_STORE_CTX * ctx)
 	X509_NAME_oneline (X509_get_issuer_name (ctx->current_cert), issuer,
 							 sizeof (issuer));
 
-	snprintf (buf, sizeof (buf), "* Subject: %s", subject);
+	g_snprintf (buf, sizeof (buf), "* Subject: %s", subject);
 	EMIT_SIGNAL (XP_TE_SSLMESSAGE, g_sess, buf, NULL, NULL, NULL, 0);
-	snprintf (buf, sizeof (buf), "* Issuer: %s", issuer);
+	g_snprintf (buf, sizeof (buf), "* Issuer: %s", issuer);
 	EMIT_SIGNAL (XP_TE_SSLMESSAGE, g_sess, buf, NULL, NULL, NULL, 0);
 
 	return (TRUE);					  /* always ok */
@@ -627,6 +487,10 @@ ssl_do_connect (server * serv)
 	char buf[128];
 
 	g_sess = serv->server_session;
+
+	/* Set SNI hostname before connect */
+	SSL_set_tlsext_host_name(serv->ssl, serv->hostname);
+
 	if (SSL_connect (serv->ssl) <= 0)
 	{
 		char err_buf[128];
@@ -636,7 +500,7 @@ ssl_do_connect (server * serv)
 		if ((err = ERR_get_error ()) > 0)
 		{
 			ERR_error_string (err, err_buf);
-			snprintf (buf, sizeof (buf), "(%d) %s", err, err_buf);
+			g_snprintf (buf, sizeof (buf), "(%d) %s", err, err_buf);
 			EMIT_SIGNAL (XP_TE_CONNFAIL, serv->server_session, buf, NULL,
 							 NULL, NULL, 0);
 
@@ -662,59 +526,59 @@ ssl_do_connect (server * serv)
 
 		if (!_SSL_get_cert_info (&cert_info, serv->ssl))
 		{
-			snprintf (buf, sizeof (buf), "* Certification info:");
+			g_snprintf (buf, sizeof (buf), "* Certification info:");
 			EMIT_SIGNAL (XP_TE_SSLMESSAGE, serv->server_session, buf, NULL, NULL,
 							 NULL, 0);
-			snprintf (buf, sizeof (buf), "  Subject:");
+			g_snprintf (buf, sizeof (buf), "  Subject:");
 			EMIT_SIGNAL (XP_TE_SSLMESSAGE, serv->server_session, buf, NULL, NULL,
 							 NULL, 0);
 			for (i = 0; cert_info.subject_word[i]; i++)
 			{
-				snprintf (buf, sizeof (buf), "    %s", cert_info.subject_word[i]);
+				g_snprintf (buf, sizeof (buf), "    %s", cert_info.subject_word[i]);
 				EMIT_SIGNAL (XP_TE_SSLMESSAGE, serv->server_session, buf, NULL, NULL,
 								 NULL, 0);
 			}
-			snprintf (buf, sizeof (buf), "  Issuer:");
+			g_snprintf (buf, sizeof (buf), "  Issuer:");
 			EMIT_SIGNAL (XP_TE_SSLMESSAGE, serv->server_session, buf, NULL, NULL,
 							 NULL, 0);
 			for (i = 0; cert_info.issuer_word[i]; i++)
 			{
-				snprintf (buf, sizeof (buf), "    %s", cert_info.issuer_word[i]);
+				g_snprintf (buf, sizeof (buf), "    %s", cert_info.issuer_word[i]);
 				EMIT_SIGNAL (XP_TE_SSLMESSAGE, serv->server_session, buf, NULL, NULL,
 								 NULL, 0);
 			}
-			snprintf (buf, sizeof (buf), "  Public key algorithm: %s (%d bits)",
+			g_snprintf (buf, sizeof (buf), "  Public key algorithm: %s (%d bits)",
 						 cert_info.algorithm, cert_info.algorithm_bits);
 			EMIT_SIGNAL (XP_TE_SSLMESSAGE, serv->server_session, buf, NULL, NULL,
 							 NULL, 0);
 			/*if (cert_info.rsa_tmp_bits)
 			{
-				snprintf (buf, sizeof (buf),
+				g_snprintf (buf, sizeof (buf),
 							 "  Public key algorithm uses ephemeral key with %d bits",
 							 cert_info.rsa_tmp_bits);
 				EMIT_SIGNAL (XP_TE_SSLMESSAGE, serv->server_session, buf, NULL, NULL,
 								 NULL, 0);
 			}*/
-			snprintf (buf, sizeof (buf), "  Sign algorithm %s",
+			g_snprintf (buf, sizeof (buf), "  Sign algorithm %s",
 						 cert_info.sign_algorithm/*, cert_info.sign_algorithm_bits*/);
 			EMIT_SIGNAL (XP_TE_SSLMESSAGE, serv->server_session, buf, NULL, NULL,
 							 NULL, 0);
-			snprintf (buf, sizeof (buf), "  Valid since %s to %s",
+			g_snprintf (buf, sizeof (buf), "  Valid since %s to %s",
 						 cert_info.notbefore, cert_info.notafter);
 			EMIT_SIGNAL (XP_TE_SSLMESSAGE, serv->server_session, buf, NULL, NULL,
 							 NULL, 0);
 		} else
 		{
-			snprintf (buf, sizeof (buf), " * No Certificate");
+			g_snprintf (buf, sizeof (buf), " * No Certificate");
 			EMIT_SIGNAL (XP_TE_SSLMESSAGE, serv->server_session, buf, NULL, NULL,
 							 NULL, 0);
 		}
 
 		chiper_info = _SSL_get_cipher_info (serv->ssl);	/* static buffer */
-		snprintf (buf, sizeof (buf), "* Cipher info:");
+		g_snprintf (buf, sizeof (buf), "* Cipher info:");
 		EMIT_SIGNAL (XP_TE_SSLMESSAGE, serv->server_session, buf, NULL, NULL, NULL,
 						 0);
-		snprintf (buf, sizeof (buf), "  Version: %s, cipher %s (%u bits)",
+		g_snprintf (buf, sizeof (buf), "  Version: %s, cipher %s (%u bits)",
 					 chiper_info->version, chiper_info->chiper,
 					 chiper_info->chiper_bits);
 		EMIT_SIGNAL (XP_TE_SSLMESSAGE, serv->server_session, buf, NULL, NULL, NULL,
@@ -724,9 +588,22 @@ ssl_do_connect (server * serv)
 		switch (verify_error)
 		{
 		case X509_V_OK:
-			/* snprintf (buf, sizeof (buf), "* Verify OK (?)"); */
+			{
+				X509 *cert = SSL_get_peer_certificate (serv->ssl);
+				int hostname_err;
+				if ((hostname_err = _SSL_check_hostname(cert, serv->hostname)) != 0)
+				{
+					g_snprintf (buf, sizeof (buf), "* Verify E: Failed to validate hostname? (%d)%s",
+							 hostname_err, serv->accept_invalid_cert ? " -- Ignored" : "");
+					if (serv->accept_invalid_cert)
+						EMIT_SIGNAL (XP_TE_SSLMESSAGE, serv->server_session, buf, NULL, NULL, NULL, 0);
+					else
+						goto conn_fail;
+				}
+				break;
+			}
+			/* g_snprintf (buf, sizeof (buf), "* Verify OK (?)"); */
 			/* EMIT_SIGNAL (XP_TE_SSLMESSAGE, serv->server_session, buf, NULL, NULL, NULL, 0); */
-			break;
 		case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
 		case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE:
 		case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
@@ -734,7 +611,7 @@ ssl_do_connect (server * serv)
 		case X509_V_ERR_CERT_HAS_EXPIRED:
 			if (serv->accept_invalid_cert)
 			{
-				snprintf (buf, sizeof (buf), "* Verify E: %s.? (%d) -- Ignored",
+				g_snprintf (buf, sizeof (buf), "* Verify E: %s.? (%d) -- Ignored",
 							 X509_verify_cert_error_string (verify_error),
 							 verify_error);
 				EMIT_SIGNAL (XP_TE_SSLMESSAGE, serv->server_session, buf, NULL, NULL,
@@ -742,9 +619,10 @@ ssl_do_connect (server * serv)
 				break;
 			}
 		default:
-			snprintf (buf, sizeof (buf), "%s.? (%d)",
+			g_snprintf (buf, sizeof (buf), "%s.? (%d)",
 						 X509_verify_cert_error_string (verify_error),
 						 verify_error);
+conn_fail:
 			EMIT_SIGNAL (XP_TE_CONNFAIL, serv->server_session, buf, NULL, NULL,
 							 NULL, 0);
 
@@ -763,7 +641,7 @@ ssl_do_connect (server * serv)
 	{
 		if (serv->ssl->session && serv->ssl->session->time + SSLTMOUT < time (NULL))
 		{
-			snprintf (buf, sizeof (buf), "SSL handshake timed out");
+			g_snprintf (buf, sizeof (buf), "SSL handshake timed out");
 			EMIT_SIGNAL (XP_TE_CONNFAIL, serv->server_session, buf, NULL,
 							 NULL, NULL, 0);
 			server_cleanup (serv); /* ->connecting = FALSE */
@@ -861,8 +739,8 @@ server_connect_success (server *serv)
 
 		/* it'll be a memory leak, if connection isn't terminated by
 		   server_cleanup() */
-		serv->ssl = _SSL_socket (ctx, serv->sok);
-		if ((err = _SSL_set_verify (ctx, ssl_cb_verify, NULL)))
+		serv->ssl = _SSL_socket (serv->ctx, serv->sok);
+		if ((err = _SSL_set_verify (serv->ctx, ssl_cb_verify, NULL)))
 		{
 			EMIT_SIGNAL (XP_TE_CONNFAIL, serv->server_session, err, NULL,
 							 NULL, NULL, 0);
@@ -894,9 +772,6 @@ server_read_child (GIOChannel *source, GIOCondition condition, server *serv)
 	char outbuf[512];
 	char host[100];
 	char ip[100];
-#ifdef USE_MSPROXY
-	char *p;
-#endif
 
 	waitline2 (source, tbuf, sizeof tbuf);
 
@@ -911,12 +786,10 @@ server_read_child (GIOChannel *source, GIOCondition condition, server *serv)
 		closesocket (serv->sok4);
 		if (serv->proxy_sok4 != -1)
 			closesocket (serv->proxy_sok4);
-#ifdef USE_IPV6
 		if (serv->sok6 != -1)
 			closesocket (serv->sok6);
 		if (serv->proxy_sok6 != -1)
 			closesocket (serv->proxy_sok6);
-#endif
 		EMIT_SIGNAL (XP_TE_UKNHOST, sess, NULL, NULL, NULL, NULL, 0);
 		if (!servlist_cycle (serv))
 			if (prefs.hex_net_auto_reconnectonfail)
@@ -928,12 +801,10 @@ server_read_child (GIOChannel *source, GIOCondition condition, server *serv)
 		closesocket (serv->sok4);
 		if (serv->proxy_sok4 != -1)
 			closesocket (serv->proxy_sok4);
-#ifdef USE_IPV6
 		if (serv->sok6 != -1)
 			closesocket (serv->sok6);
 		if (serv->proxy_sok6 != -1)
 			closesocket (serv->proxy_sok6);
-#endif
 		EMIT_SIGNAL (XP_TE_CONNFAIL, sess, errorstring (atoi (tbuf)), NULL,
 						 NULL, NULL, 0);
 		if (!servlist_cycle (serv))
@@ -945,49 +816,10 @@ server_read_child (GIOChannel *source, GIOCondition condition, server *serv)
 		waitline2 (source, ip, sizeof ip);
 		waitline2 (source, outbuf, sizeof outbuf);
 		EMIT_SIGNAL (XP_TE_CONNECT, sess, host, ip, outbuf, NULL, 0);
-#ifdef WIN32
-		if (prefs.hex_identd)
-		{
-			if (serv->network && ((ircnet *)serv->network)->user)
-			{
-				identd_start (((ircnet *)serv->network)->user);
-			}
-			else
-			{
-				identd_start (prefs.hex_irc_user_name);
-			}
-		}
-#else
-		snprintf (outbuf, sizeof (outbuf), "%s/auth/xchat_auth",
-					 g_get_home_dir ());
-		if (access (outbuf, X_OK) == 0)
-		{
-			snprintf (outbuf, sizeof (outbuf), "exec -d %s/auth/xchat_auth %s",
-						 g_get_home_dir (), prefs.hex_irc_user_name);
-			handle_command (serv->server_session, outbuf, FALSE);
-		}
-#endif
 		break;
 	case '4':						  /* success */
 		waitline2 (source, tbuf, sizeof (tbuf));
-#ifdef USE_MSPROXY
-		serv->sok = strtol (tbuf, &p, 10);
-		if (*p++ == ' ')
-		{
-			serv->proxy_sok = strtol (p, &p, 10);
-			serv->msp_state.clientid = strtol (++p, &p, 10);
-			serv->msp_state.serverid = strtol (++p, &p, 10);
-			serv->msp_state.seq_sent = atoi (++p);
-		} else
-			serv->proxy_sok = -1;
-#ifdef DEBUG_MSPROXY
-		printf ("Parent got main socket: %d, proxy socket: %d\n", serv->sok, serv->proxy_sok);
-		printf ("Client ID 0x%08x server ID 0x%08x seq_sent %d\n", serv->msp_state.clientid, serv->msp_state.serverid, serv->msp_state.seq_sent);
-#endif
-#else
 		serv->sok = atoi (tbuf);
-#endif
-#ifdef USE_IPV6
 		/* close the one we didn't end up using */
 		if (serv->sok == serv->sok4)
 			closesocket (serv->sok6);
@@ -1000,7 +832,29 @@ server_read_child (GIOChannel *source, GIOCondition condition, server *serv)
 			else
 				closesocket (serv->proxy_sok4);
 		}
-#endif
+
+		{
+			struct sockaddr addr;
+			int addr_len = sizeof (addr);
+			guint16 port;
+
+			if (!getsockname (serv->sok, &addr, &addr_len))
+			{
+				if (addr.sa_family == AF_INET)
+					port = ntohs(((struct sockaddr_in *)&addr)->sin_port);
+				else
+					port = ntohs(((struct sockaddr_in6 *)&addr)->sin6_port);
+
+				g_snprintf (outbuf, sizeof (outbuf), "IDENTD %"G_GUINT16_FORMAT" ", port);
+				if (serv->network && ((ircnet *)serv->network)->user)
+					g_strlcat (outbuf, ((ircnet *)serv->network)->user, sizeof (outbuf));
+				else
+					g_strlcat (outbuf, prefs.hex_irc_user_name, sizeof (outbuf));
+
+				handle_command (serv->server_session, outbuf, FALSE);
+			}
+		}
+
 		server_connect_success (serv);
 		break;
 	case '5':						  /* prefs ip discovered */
@@ -1186,7 +1040,7 @@ traverse_socks (int print_fd, int sok, char *serverAddr, int port)
 	if (buf[1] == 90)
 		return 0;
 
-	snprintf (buf, sizeof (buf), "SOCKS\tServer reported error %d,%d.\n", buf[0], buf[1]);
+	g_snprintf (buf, sizeof (buf), "SOCKS\tServer reported error %d,%d.\n", buf[0], buf[1]);
 	proxy_error (print_fd, buf);
 	return 1;
 }
@@ -1270,7 +1124,7 @@ traverse_socks5 (int print_fd, int sok, char *serverAddr, int port)
 
 	addrlen = strlen (serverAddr);
 	packetlen = 4 + 1 + addrlen + 2;
-	sc2 = malloc (packetlen);
+	sc2 = g_malloc (packetlen);
 	sc2[0] = 5;						  /* version */
 	sc2[1] = 1;						  /* command */
 	sc2[2] = 0;						  /* reserved */
@@ -1279,7 +1133,7 @@ traverse_socks5 (int print_fd, int sok, char *serverAddr, int port)
 	memcpy (sc2 + 5, serverAddr, addrlen);
 	*((unsigned short *) (sc2 + 5 + addrlen)) = htons (port);
 	send (sok, sc2, packetlen, 0);
-	free (sc2);
+	g_free (sc2);
 
 	/* consume all of the reply */
 	if (recv (sok, buf, 4, 0) != 4)
@@ -1287,9 +1141,9 @@ traverse_socks5 (int print_fd, int sok, char *serverAddr, int port)
 	if (buf[0] != 5 || buf[1] != 0)
 	{
 		if (buf[1] == 2)
-			snprintf (buf, sizeof (buf), "SOCKS\tProxy refused to connect to host (not allowed).\n");
+			g_snprintf (buf, sizeof (buf), "SOCKS\tProxy refused to connect to host (not allowed).\n");
 		else
-			snprintf (buf, sizeof (buf), "SOCKS\tProxy failed to connect to host (error %d).\n", buf[1]);
+			g_snprintf (buf, sizeof (buf), "SOCKS\tProxy failed to connect to host (error %d).\n", buf[1]);
 		proxy_error (print_fd, buf);
 		return 1;
 	}
@@ -1322,7 +1176,7 @@ traverse_wingate (int print_fd, int sok, char *serverAddr, int port)
 {
 	char buf[128];
 
-	snprintf (buf, sizeof (buf), "%s %d\r\n", serverAddr, port);
+	g_snprintf (buf, sizeof (buf), "%s %d\r\n", serverAddr, port);
 	send (sok, buf, strlen (buf), 0);
 
 	return 0;
@@ -1410,16 +1264,16 @@ traverse_http (int print_fd, int sok, char *serverAddr, int port)
 	char auth_data2[252];
 	int n, n2;
 
-	n = snprintf (buf, sizeof (buf), "CONNECT %s:%d HTTP/1.0\r\n",
+	n = g_snprintf (buf, sizeof (buf), "CONNECT %s:%d HTTP/1.0\r\n",
 					  serverAddr, port);
 	if (prefs.hex_net_proxy_auth)
 	{
-		n2 = snprintf (auth_data2, sizeof (auth_data2), "%s:%s",
+		n2 = g_snprintf (auth_data2, sizeof (auth_data2), "%s:%s",
 							prefs.hex_net_proxy_user, prefs.hex_net_proxy_pass);
 		base64_encode (auth_data, auth_data2, n2);
-		n += snprintf (buf+n, sizeof (buf)-n, "Proxy-Authorization: Basic %s\r\n", auth_data);
+		n += g_snprintf (buf+n, sizeof (buf)-n, "Proxy-Authorization: Basic %s\r\n", auth_data);
 	}
-	n += snprintf (buf+n, sizeof (buf)-n, "\r\n");
+	n += g_snprintf (buf+n, sizeof (buf)-n, "\r\n");
 	send (sok, buf, n, 0);
 
 	n = http_read_line (print_fd, sok, buf, sizeof (buf));
@@ -1439,7 +1293,7 @@ traverse_http (int print_fd, int sok, char *serverAddr, int port)
 }
 
 static int
-traverse_proxy (int proxy_type, int print_fd, int sok, char *ip, int port, struct msproxy_state_t *state, netstore *ns_proxy, int csok4, int csok6, int *csok, char bound)
+traverse_proxy (int proxy_type, int print_fd, int sok, char *ip, int port, netstore *ns_proxy, int csok4, int csok6, int *csok, char bound)
 {
 	switch (proxy_type)
 	{
@@ -1451,10 +1305,6 @@ traverse_proxy (int proxy_type, int print_fd, int sok, char *ip, int port, struc
 		return traverse_socks5 (print_fd, sok, ip, port);
 	case 4:
 		return traverse_http (print_fd, sok, ip, port);
-#ifdef USE_MSPROXY
-	case 5:
-		return traverse_msproxy (sok, ip, port, state, ns_proxy, csok4, csok6, csok, bound);
-#endif
 	}
 
 	return 1;
@@ -1492,7 +1342,7 @@ server_child (server * serv)
 		local_ip = net_resolve (ns_local, prefs.hex_net_bind_host, 0, &real_hostname);
 		if (local_ip != NULL)
 		{
-			snprintf (buf, sizeof (buf), "5\n%s\n", local_ip);
+			g_snprintf (buf, sizeof (buf), "5\n%s\n", local_ip);
 			write (serv->childwrite, buf, strlen (buf));
 			net_bind (ns_local, serv->sok4, serv->sok6);
 			bound = 1;
@@ -1505,10 +1355,8 @@ server_child (server * serv)
 
 	if (!serv->dont_use_proxy) /* blocked in serverlist? */
 	{
-		if (FALSE)
-			;
 #ifdef USE_LIBPROXY
-		else if (prefs.hex_net_proxy_type == 5)
+		if (prefs.hex_net_proxy_type == 5)
 		{
 			char **proxy_list;
 			char *url, *proxy;
@@ -1532,7 +1380,7 @@ server_child (server * serv)
 			if (proxy_type) {
 				char *c;
 				c = strchr (proxy, ':') + 3;
-				proxy_host = strdup (c);
+				proxy_host = g_strdup (c);
 				c = strchr (proxy_host, ':');
 				*c = '\0';
 				proxy_port = atoi (c + 1);
@@ -1542,12 +1390,12 @@ server_child (server * serv)
 			g_free (url);
 		}
 #endif
-		else if (prefs.hex_net_proxy_host[0] &&
+		if (prefs.hex_net_proxy_host[0] &&
 			   prefs.hex_net_proxy_type > 0 &&
 			   prefs.hex_net_proxy_use != 2) /* proxy is NOT dcc-only */
 		{
 			proxy_type = prefs.hex_net_proxy_type;
-			proxy_host = strdup (prefs.hex_net_proxy_host);
+			proxy_host = g_strdup (prefs.hex_net_proxy_host);
 			proxy_port = prefs.hex_net_proxy_port;
 		}
 	}
@@ -1557,10 +1405,10 @@ server_child (server * serv)
 	/* first resolve where we want to connect to */
 	if (proxy_type > 0)
 	{
-		snprintf (buf, sizeof (buf), "9\n%s\n", proxy_host);
+		g_snprintf (buf, sizeof (buf), "9\n%s\n", proxy_host);
 		write (serv->childwrite, buf, strlen (buf));
 		ip = net_resolve (ns_server, proxy_host, proxy_port, &real_hostname);
-		free (proxy_host);
+		g_free (proxy_host);
 		if (!ip)
 		{
 			write (serv->childwrite, "1\n", 2);
@@ -1579,7 +1427,7 @@ server_child (server * serv)
 				goto xit;
 			}
 		} else						  /* otherwise we can just use the hostname */
-			proxy_ip = strdup (hostname);
+			proxy_ip = g_strdup (hostname);
 	} else
 	{
 		ip = net_resolve (ns_server, hostname, port, &real_hostname);
@@ -1591,7 +1439,7 @@ server_child (server * serv)
 		connect_port = port;
 	}
 
-	snprintf (buf, sizeof (buf), "3\n%s\n%s\n%d\n",
+	g_snprintf (buf, sizeof (buf), "3\n%s\n%s\n%d\n",
 				 real_hostname, ip, connect_port);
 	write (serv->childwrite, buf, strlen (buf));
 
@@ -1605,23 +1453,17 @@ server_child (server * serv)
 
 	if (error != 0)
 	{
-		snprintf (buf, sizeof (buf), "2\n%d\n", sock_error ());
+		g_snprintf (buf, sizeof (buf), "2\n%d\n", sock_error ());
 		write (serv->childwrite, buf, strlen (buf));
 	} else
 	{
 		/* connect succeeded */
 		if (proxy_ip)
 		{
-			switch (traverse_proxy (proxy_type, serv->childwrite, psok, proxy_ip, port, &serv->msp_state, ns_proxy, serv->sok4, serv->sok6, &sok, bound))
+			switch (traverse_proxy (proxy_type, serv->childwrite, psok, proxy_ip, port, ns_proxy, serv->sok4, serv->sok6, &sok, bound))
 			{
 			case 0:	/* success */
-#ifdef USE_MSPROXY
-				if (!serv->dont_use_proxy && (proxy_type == 5))
-					snprintf (buf, sizeof (buf), "4\n%d %d %d %d %d\n", sok, psok, serv->msp_state.clientid, serv->msp_state.serverid,
-						serv->msp_state.seq_sent);
-				else
-#endif
-					snprintf (buf, sizeof (buf), "4\n%d\n", sok);	/* success */
+				g_snprintf (buf, sizeof (buf), "4\n%d\n", sok);	/* success */
 				write (serv->childwrite, buf, strlen (buf));
 				break;
 			case 1:	/* socks traversal failed */
@@ -1630,29 +1472,24 @@ server_child (server * serv)
 			}
 		} else
 		{
-			snprintf (buf, sizeof (buf), "4\n%d\n", sok);	/* success */
+			g_snprintf (buf, sizeof (buf), "4\n%d\n", sok);	/* success */
 			write (serv->childwrite, buf, strlen (buf));
 		}
 	}
 
 xit:
 
-#if defined (USE_IPV6) || defined (WIN32)
 	/* this is probably not needed */
 	net_store_destroy (ns_server);
 	if (ns_proxy)
 		net_store_destroy (ns_proxy);
-#endif
 
 	/* no need to free ip/real_hostname, this process is exiting */
 #ifdef WIN32
 	/* under win32 we use a thread -> shared memory, must free! */
-	if (proxy_ip)
-		free (proxy_ip);
-	if (ip)
-		free (ip);
-	if (real_hostname)
-		free (real_hostname);
+	g_free (proxy_ip);
+	g_free (ip);
+	g_free (real_hostname);
 #endif
 
 	return 0;
@@ -1666,9 +1503,9 @@ server_connect (server *serv, char *hostname, int port, int no_login)
 	session *sess = serv->server_session;
 
 #ifdef USE_OPENSSL
-	if (!ctx && serv->use_ssl)
+	if (!serv->ctx && serv->use_ssl)
 	{
-		if (!(ctx = _SSL_context_init (ssl_cb_info, FALSE)))
+		if (!(serv->ctx = _SSL_context_init (ssl_cb_info)))
 		{
 			fprintf (stderr, "_SSL_context_init failed\n");
 			exit (1);
@@ -1711,18 +1548,18 @@ server_connect (server *serv, char *hostname, int port, int no_login)
 		/* first try network specific cert/key */
 		cert_file = g_strdup_printf ("%s" G_DIR_SEPARATOR_S "certs" G_DIR_SEPARATOR_S "%s.pem",
 					 get_xdir (), server_get_network (serv, TRUE));
-		if (SSL_CTX_use_certificate_file (ctx, cert_file, SSL_FILETYPE_PEM) == 1)
+		if (SSL_CTX_use_certificate_file (serv->ctx, cert_file, SSL_FILETYPE_PEM) == 1)
 		{
-			if (SSL_CTX_use_PrivateKey_file (ctx, cert_file, SSL_FILETYPE_PEM) == 1)
+			if (SSL_CTX_use_PrivateKey_file (serv->ctx, cert_file, SSL_FILETYPE_PEM) == 1)
 				serv->have_cert = TRUE;
 		}
 		else
 		{
 			/* if that doesn't exist, try <config>/certs/client.pem */
 			cert_file = g_build_filename (get_xdir (), "certs", "client.pem", NULL);
-			if (SSL_CTX_use_certificate_file (ctx, cert_file, SSL_FILETYPE_PEM) == 1)
+			if (SSL_CTX_use_certificate_file (serv->ctx, cert_file, SSL_FILETYPE_PEM) == 1)
 			{
-				if (SSL_CTX_use_PrivateKey_file (ctx, cert_file, SSL_FILETYPE_PEM) == 1)
+				if (SSL_CTX_use_PrivateKey_file (serv->ctx, cert_file, SSL_FILETYPE_PEM) == 1)
 					serv->have_cert = TRUE;
 			}
 		}
@@ -1754,16 +1591,8 @@ server_connect (server *serv, char *hostname, int port, int no_login)
 
 	/* create both sockets now, drop one later */
 	net_sockets (&serv->sok4, &serv->sok6);
-#ifdef USE_MSPROXY
-	/* In case of MS Proxy we have a separate UDP control connection */
-	if (!serv->dont_use_proxy && (serv->proxy_type == 5))
-		udp_sockets (&serv->proxy_sok4, &serv->proxy_sok6);
-	else
-#endif
-	{
-		serv->proxy_sok4 = -1;
-		serv->proxy_sok6 = -1;
-	}
+	serv->proxy_sok4 = -1;
+	serv->proxy_sok6 = -1;
 
 #ifdef WIN32
 	CloseHandle (CreateThread (NULL, 0,
@@ -1815,31 +1644,46 @@ server_set_encoding (server *serv, char *new_encoding)
 {
 	char *space;
 
-	if (serv->encoding)
-	{
-		free (serv->encoding);
-		/* can be left as NULL to indicate system encoding */
-		serv->encoding = NULL;
-		serv->using_cp1255 = FALSE;
-		serv->using_irc = FALSE;
-	}
+	g_free (serv->encoding);
 
 	if (new_encoding)
 	{
-		serv->encoding = strdup (new_encoding);
+		serv->encoding = g_strdup (new_encoding);
 		/* the serverlist GUI might have added a space 
 			and short description - remove it. */
 		space = strchr (serv->encoding, ' ');
 		if (space)
 			space[0] = 0;
 
-		/* server_inline() uses these flags */
-		if (!g_ascii_strcasecmp (serv->encoding, "CP1255") ||
-			 !g_ascii_strcasecmp (serv->encoding, "WINDOWS-1255"))
-			serv->using_cp1255 = TRUE;
-		else if (!g_ascii_strcasecmp (serv->encoding, "IRC"))
-			serv->using_irc = TRUE;
+		/* Default legacy "IRC" encoding to utf-8. */
+		if (g_ascii_strcasecmp (serv->encoding, "IRC") == 0)
+		{
+			g_free (serv->encoding);
+			serv->encoding = g_strdup ("UTF-8");
+		}
+
+		else if (!servlist_check_encoding (serv->encoding))
+		{
+			g_free (serv->encoding);
+			serv->encoding = g_strdup ("UTF-8");
+		}
+	}
+	else
+	{
+		serv->encoding = g_strdup ("UTF-8");
+	}
+
+	if (serv->read_converter != NULL)
+	{
+		g_iconv_close (serv->read_converter);
 	}
+	serv->read_converter = g_iconv_open ("UTF-8", serv->encoding);
+
+	if (serv->write_converter != NULL)
+	{
+		g_iconv_close (serv->write_converter);
+	}
+	serv->write_converter = g_iconv_open (serv->encoding, "UTF-8");
 }
 
 server *
@@ -1848,8 +1692,7 @@ server_new (void)
 	static int id = 0;
 	server *serv;
 
-	serv = malloc (sizeof (struct server));
-	memset (serv, 0, sizeof (struct server));
+	serv = g_new0 (struct server, 1);
 
 	/* use server.c and proto-irc.c functions */
 	server_fill_her_up (serv);
@@ -1875,19 +1718,17 @@ is_server (server *serv)
 void
 server_set_defaults (server *serv)
 {
-	if (serv->chantypes)
-		free (serv->chantypes);
-	if (serv->chanmodes)
-		free (serv->chanmodes);
-	if (serv->nick_prefixes)
-		free (serv->nick_prefixes);
-	if (serv->nick_modes)
-		free (serv->nick_modes);
-
-	serv->chantypes = strdup ("#&!+");
-	serv->chanmodes = strdup ("beI,k,l");
-	serv->nick_prefixes = strdup ("@%+");
-	serv->nick_modes = strdup ("ohv");
+	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");
+
+	server_set_encoding (serv, "UTF-8");
 
 	serv->nickcount = 1;
 	serv->end_of_motd = FALSE;
@@ -1991,9 +1832,8 @@ server_away_free_messages (server *serv)
 		if (away->server == serv)
 		{
 			away_list = g_slist_remove (away_list, away);
-			if (away->message)
-				free (away->message);
-			free (away);
+			g_free (away->message);
+			g_free (away);
 			next = away_list;
 		}
 		list = next;
@@ -2007,20 +1847,17 @@ server_away_save_message (server *serv, char *nick, char *msg)
 
 	if (away)						  /* Change message for known user */
 	{
-		if (away->message)
-			free (away->message);
-		away->message = strdup (msg);
-	} else
-		/* Create brand new entry */
+		g_free (away->message);
+		away->message = g_strdup (msg);
+	}
+	else
 	{
-		away = malloc (sizeof (struct away_msg));
-		if (away)
-		{
-			away->server = serv;
-			safe_strcpy (away->nick, nick, sizeof (away->nick));
-			away->message = strdup (msg);
-			away_list = g_slist_prepend (away_list, away);
-		}
+		/* 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);
 	}
 }
 
@@ -2035,22 +1872,27 @@ server_free (server *serv)
 	serv->flush_queue (serv);
 	server_away_free_messages (serv);
 
-	free (serv->nick_modes);
-	free (serv->nick_prefixes);
-	free (serv->chanmodes);
-	free (serv->chantypes);
-	if (serv->bad_nick_prefixes)
-		free (serv->bad_nick_prefixes);
-	if (serv->last_away_reason)
-		free (serv->last_away_reason);
-	if (serv->encoding)
-		free (serv->encoding);
+	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);
+#endif
 
 	fe_server_callback (serv);
 
-	free (serv);
+	g_free (serv);
 
 	notify_cleanup ();
 }
diff --git a/src/common/server.h b/src/common/server.h
index 90e9a9c1..ff8ef404 100644
--- a/src/common/server.h
+++ b/src/common/server.h
@@ -24,9 +24,8 @@ extern GSList *serv_list;
 
 /* eventually need to keep the tcp_* functions isolated to server.c */
 int tcp_send_len (server *serv, char *buf, int len);
-int tcp_send (server *serv, char *buf);
 void tcp_sendf (server *serv, const char *fmt, ...) G_GNUC_PRINTF (2, 3);
-int tcp_send_real (void *ssl, int sok, char *encoding, int using_irc, char *buf, int len);
+int tcp_send_real (void *ssl, int sok, GIConv write_converter, char *buf, int len);
 
 server *server_new (void);
 int is_server (server *serv);
diff --git a/src/common/servlist.c b/src/common/servlist.c
index 7de77596..6f9f9ed2 100644
--- a/src/common/servlist.c
+++ b/src/common/servlist.c
@@ -45,6 +45,7 @@ struct defaultserver
 	char *charset;
 	int loginmode;		/* default authentication type */
 	char *connectcmd;	/* default connect command - should only be used for rare login types, paired with LOGIN_CUSTOM */
+	gboolean ssl;
 };
 
 static const struct defaultserver def[] =
@@ -59,20 +60,11 @@ static const struct defaultserver def[] =
 
 	{"AccessIRC",	0},
 	{0,			"irc.accessirc.net"},
-	{0,			"eu.accessirc.net"},
 
 	{"AfterNET",	0},
 	{0,			"irc.afternet.org"},
-	{0,			"us.afternet.org"},
-	{0,			"eu.afternet.org"},
 
 	{"Aitvaras",	0},
-#ifdef USE_IPV6
-#ifdef USE_OPENSSL
-	{0,			"irc6.ktu.lt/+7668"},
-#endif
-	{0,			"irc6.ktu.lt/7666"},
-#endif
 #ifdef USE_OPENSSL
 	{0,			"irc.data.lt/+6668"},
 	{0,			"irc.omnitel.net/+6668"},
@@ -86,52 +78,25 @@ static const struct defaultserver def[] =
 	{0,			"irc.kis.lt"},
 	{0,			"irc.vub.lt"},
 
-	{"AlphaChat",	0, 0, 0, LOGIN_SASL},
-	{0,			"irc.alphachat.net"},
-	{0,			"na.alphachat.net"},
-	{0,			"eu.alphachat.net"},
-	{0,			"au.alphachat.net"},
-	{0,			"za.alphachat.net"},
-
-	{"Anthrochat", 0},
-#ifdef USE_OPENSSL
-	{0,			"irc.anthrochat.net/+6697"},
-#endif
+	{"Anthrochat", 0, 0, 0, 0, 0, TRUE},
 	{0,			"irc.anthrochat.net"},
 
 	{"ARCNet",	0},
-	{0,			"se1.arcnet.vapor.com"},
-	{0,			"us1.arcnet.vapor.com"},
-	{0,			"us2.arcnet.vapor.com"},
-	{0,			"us3.arcnet.vapor.com"},
-	{0,			"ca1.arcnet.vapor.com"},
-	{0,			"de1.arcnet.vapor.com"},
-	{0,			"de3.arcnet.vapor.com"},
-	{0,			"ch1.arcnet.vapor.com"},
-	{0,			"be1.arcnet.vapor.com"},
-	{0,			"nl3.arcnet.vapor.com"},
-	{0,			"uk1.arcnet.vapor.com"},
-	{0,			"uk2.arcnet.vapor.com"},
-	{0,			"fr1.arcnet.vapor.com"},
+	{0,			"arcnet-irc.org"},
+
+	{"AthemeNet", 0, 0, 0, LOGIN_SASL, 0, TRUE},
+	{0,			"irc.atheme.org"},
 
 	{"AustNet",	0},
-	{0,			"au.austnet.org"},
-	{0,			"us.austnet.org"},
+	{0,			"irc.austnet.org"},
 
 	{"AzzurraNet",	0},
 	{0,			"irc.azzurra.org"},
-	{0,			"crypto.azzurra.org"},
 
-	{"Canternet", 0, 0, 0, LOGIN_SASL},
-#ifdef USE_OPENSSL
-	{0, 		"irc.canternet.org/+6697"},
-#endif
+	{"Canternet", 0, 0, 0, LOGIN_SASL, 0, TRUE},
 	{0,			"irc.canternet.org"},
 
-	{"Chat4all", 0},
-#ifdef USE_OPENSSL
-	{0,			"irc.chat4all.org/+7001"},
-#endif
+	{"Chat4all", 0, 0, 0, 0, 0, TRUE},
 	{0,			"irc.chat4all.org"},
 
 	{"ChattingAway", 0},
@@ -139,26 +104,21 @@ static const struct defaultserver def[] =
 
 	{"ChatJunkies",	0},
 	{0,			"irc.chatjunkies.org"},
-	{0,			"nl.chatjunkies.org"},
 
 	{"ChatNet",	0},
-	{0,			"US.ChatNet.Org"},
+	{0,			"irc.chatnet.org"},
 
 	{"ChatSpike", 0},
 	{0,			"irc.chatspike.net"},
 
 	{"Criten", 0},
 	{0,			"irc.criten.net"},
-	{0,			"irc.eu.criten.net"},
 
 	{"DALnet", 0},
 	{0,			"irc.dal.net"},
-	{0,			"irc.eu.dal.net"},
 
 	{"Dark-Tou-Net",	0},
 	{0,			"irc.d-t-net.de"},
-	{0,			"bw.d-t-net.de"},
-	{0,			"nc.d-t-net.de"},
 
 	{"DarkMyst", 0, 0, 0, LOGIN_SASL},
 	{0,			"irc.darkmyst.org"},
@@ -177,66 +137,36 @@ static const struct defaultserver def[] =
 	{0,			"irc.lightning.net"},
 	{0,			"irc.servercentral.net"},
 
-	{"ElectroCode",		0},
-#ifdef USE_OPENSSL
-
-	{0,			"irc.electrocode.net/+6697"},
-#endif
+	{"ElectroCode", 0, 0, 0, 0, 0, TRUE},
 	{0,			"irc.electrocode.net"},
 
 	{"EnterTheGame",	0},
-	{0,			"IRC.EnterTheGame.Com"},
+	{0,			"irc.enterthegame.com"},
 
-	{"EntropyNet",	0, 0, 0, LOGIN_SASL},
-#ifdef USE_OPENSSL
-	{0,			"irc.entropynet.net/+6697"},
-#endif
+	{"EntropyNet",	0, 0, 0, LOGIN_SASL, 0, TRUE},
 	{0,			"irc.entropynet.net"},
-#ifdef USE_IPV6
-#ifdef USE_OPENSSL
-	{0,			"irc6.entropynet.net/+6697"},
-#endif
-	{0,			"irc6.entropynet.net"},
-#endif
 
-	{"EsperNet", 0, 0, 0, LOGIN_SASL},
-#ifdef USE_OPENSSL
-	{0,			"irc.esper.net/+6697"},
-#endif
+	{"EsperNet", 0, 0, 0, LOGIN_SASL, 0, TRUE},
 	{0,			"irc.esper.net"},
 
 	{"EUIrc",	0},
 	{0,			"irc.euirc.net"},
-	{0,			"irc.ham.de.euirc.net"},
-	{0,			"irc.ber.de.euirc.net"},
-	{0,			"irc.ffm.de.euirc.net"},
-	{0,			"irc.bre.de.euirc.net"},
-	{0,			"irc.hes.de.euirc.net"},
-	{0,			"irc.inn.at.euirc.net"},
-	{0,			"irc.bas.ch.euirc.net"},
 
 	{"EuropNet", 0},
 	{0,			"irc.europnet.org"},
 
 	{"FDFNet",	0},
 	{0,			"irc.fdfnet.net"},
-	{0,			"irc.eu.fdfnet.net"},
 
 	{"FEFNet", 0, 0, 0, LOGIN_SASL},
 	{0,			"irc.fef.net"},
 
-	{"freenode", 0, 0, 0, LOGIN_SASL},
-#ifdef USE_OPENSSL
-	{0,				"chat.freenode.net/+6697"},
-#endif
+	{"freenode", 0, 0, 0, LOGIN_SASL, 0, TRUE},
 	{0,				"chat.freenode.net"},
 	/* irc. points to chat. but many users and urls still reference it */
 	{0,				"irc.freenode.net"},
 
-	{"Furnet",	0},
-#ifdef USE_OPENSSL
-	{0,			"irc.furnet.org/+6697"},
-#endif
+	{"Furnet", 0, 0, 0, 0, 0, TRUE},
 	{0,			"irc.furnet.org"},
 
 	{"GalaxyNet",	0},
@@ -245,18 +175,14 @@ static const struct defaultserver def[] =
 	{"GameSurge", 0},
 	{0,			"irc.gamesurge.net"},
 	
-	{"GeeksIRC", 0, 0, 0, LOGIN_SASL},
-#ifdef USE_OPENSSL
-	{0,			"irc.geeksirc.net/+6697"},
-#endif
+	{"GeeksIRC", 0, 0, 0, LOGIN_SASL, 0, TRUE},
 	{0,			"irc.geeksirc.net"},
 
 	{"GeekShed",	0},
 	{0,			"irc.geekshed.net"},
 
 	{"German-Elite",	0},
-	{0,			"dominion.german-elite.net"},
-	{0,			"komatu.german-elite.net"},
+	{0,			"irc.german-elite.net"},
 
 	{"GIMPNet",		0},
 	{0,			"irc.gimp.org"},
@@ -268,22 +194,13 @@ static const struct defaultserver def[] =
 	{"IdleMonkeys", 0},
 	{0,			"irc.idlemonkeys.net"},
 
-	{"IndirectIRC", 0},
-#ifdef USE_OPENSSL
-	{0,			"irc.indirectirc.com/+6697"},
-#endif
+	{"IndirectIRC", 0, 0, 0, 0, 0, TRUE},
 	{0,			"irc.indirectirc.com"},
 	
-	{"Interlinked", 0, 0, 0, LOGIN_SASL},
-#ifdef USE_OPENSSL
-	{0,			"irc.interlinked.me/+6697"},
-#endif
+	{"Interlinked", 0, 0, 0, LOGIN_SASL, 0, TRUE},
 	{0,			"irc.interlinked.me"},
 
-	{"IRC4Fun", 0, 0, 0, LOGIN_SASL},
-#ifdef USE_OPENSSL
-	{0,				"irc.irc4fun.net/+6697"},
-#endif
+	{"IRC4Fun", 0, 0, 0, LOGIN_SASL, 0, TRUE},
 	{0,				"irc.irc4fun.net"},
 
 	{"IRCHighWay",	0},
@@ -294,28 +211,15 @@ static const struct defaultserver def[] =
 
 	{"IrcLink",	0},
 	{0,			"irc.irclink.net"},
-	{0,			"Alesund.no.eu.irclink.net"},
-	{0,			"Oslo.no.eu.irclink.net"},
-	{0,			"frogn.no.eu.irclink.net"},
-	{0,			"tonsberg.no.eu.irclink.net"},
 
 	{"IRCNet",		0},
 	{0,				"open.ircnet.net"},
-	{0,				"irc.de.ircnet.net"},
-	
-	{"IRCNode", 0, 0, 0, LOGIN_SASL},
-#ifdef USE_OPENSSL
-	{0,			"irc.ircnode.org/+6697"},
-#endif
-	{0,                     "irc.ircnode.org"},
 
 	{"Irctoo.net",	0},
 	{0,			"irc.irctoo.net"},
 
 	{"iZ-smart.net",	0},
-	{0,			"irc.iZ-smart.net/6666"},
-	{0,			"irc.iZ-smart.net/6667"},
-	{0,			"irc.iZ-smart.net/6668"},
+	{0,			"irc.iz-smart.net"},
 
 	{"Krstarica", 0},
 	{0,			"irc.krstarica.com"},
@@ -323,12 +227,6 @@ static const struct defaultserver def[] =
 #ifdef USE_OPENSSL
 	{"LinkNet",	0},
 	{0,			"irc.link-net.org/+7000"},
-	{0,			"as.link-net.org/+7000"},
-	{0,			"eu.link-net.org/+7000"},
-	{0,			"us.link-net.org/+7000"},
-#ifdef USE_IPV6
-	{0,			"irc6.link-net.org/+7000"},
-#endif
 #endif
 
 	{"MindForge",	0},
@@ -358,31 +256,14 @@ static const struct defaultserver def[] =
 	{"PIRC.PL",	0},
 	{0,			"irc.pirc.pl"},
 	
-	{"PonyChat", 0, 0, 0, LOGIN_SASL},
-#ifdef USE_OPENSSL
-	{0, 		"irc.ponychat.net/+6697"},
-#endif
+	{"PonyChat", 0, 0, 0, LOGIN_SASL, 0, TRUE},
 	{0,			"irc.ponychat.net"},
 
 	{"PTNet.org",   0},
-	{0,			"irc.PTNet.org"},
-	{0,			"world.PTnet.org"},
-	{0,			"netvisao.PTnet.org"},
-	{0,			"uevora.PTnet.org"},
-	{0,			"vianetworks.PTnet.org"},
-	{0,			"uc.PTnet.org"},
-	{0,			"nfsi.ptnet.org"},
-	{0,			"fctunl.ptnet.org"},
+	{0,			"irc.ptnet.org"},
 
 	{"QuakeNet", 0, 0, 0, LOGIN_CHALLENGEAUTH},
 	{0,			"irc.quakenet.org"},
-	{0,			"irc.se.quakenet.org"},
-	{0,			"irc.dk.quakenet.org"},
-	{0,			"irc.no.quakenet.org"},
-	{0,			"irc.fi.quakenet.org"},
-	{0,			"irc.be.quakenet.org"},
-	{0,			"irc.uk.quakenet.org"},
-	{0,			"irc.it.quakenet.org"},
 
 	{"Rizon", 0},
 	{0,			"irc.rizon.net"},
@@ -395,37 +276,21 @@ static const struct defaultserver def[] =
 
 	{"SceneNet",	0},
 	{0,			"irc.scene.org"},
-	{0,			"irc.eu.scene.org"},
-	{0,			"irc.us.scene.org"},
 
 	{"SeilEn.de",	0},
 	{0,			"irc.seilen.de"},
 
-	{"SeionIRC", 0, 0, 0, LOGIN_SASL},
-#ifdef USE_OPENSSL
-	{0,			"irc.seion.us/+6697"},
-#endif
-	{0,			"irc.seion.us"},
-
 	{"Serenity-IRC",	0},
 	{0,			"irc.serenity-irc.net"},
-	{0,			"eu.serenity-irc.net"},
-	{0,			"us.serenity-irc.net"},
 
 	{"SlashNET",	0},
 	{0,			"irc.slashnet.org"},
-	{0,			"area51.slashnet.org"},
-	{0,			"moo.slashnet.org"},
-	{0,			"radon.slashnet.org"},
 
-	{"Snoonet", 0, 0, 0, LOGIN_SASL},
-#ifdef USE_OPENSSL
-	{0,			"irc.snoonet.org/+6697"},
-#endif
-	{0,			"irc.snoonet.org/6667"},
+	{"Snoonet", 0, 0, 0, LOGIN_SASL, 0, TRUE},
+	{0,			"irc.snoonet.org"},
 
 	{"Snyde", 0},
-	{0,			"irc.snyde.net/6667"},
+	{0,			"irc.snyde.net"},
 
 	{"Sohbet.Net", 0},
 	{0,			"irc.sohbet.net"},
@@ -434,57 +299,28 @@ static const struct defaultserver def[] =
 	{0,			"irc.solidirc.com"},
 
 	{"SorceryNet", 0, 0, 0, LOGIN_SASL},
-	{0,			"irc.sorcery.net/9000"},
-	{0,			"irc.us.sorcery.net/9000"},
-	{0,			"irc.eu.sorcery.net/9000"},
+	{0,			"irc.sorcery.net"},
 	
-	{"SpotChat", 0, 0, 0, LOGIN_SASL},
-#ifdef USE_OPENSSL
-	{0,			"irc.spotchat.org/+6697"},
-#endif
-	{0,			"irc.spotchat.org/6667"},
+	{"SpotChat", 0, 0, 0, LOGIN_SASL, 0, TRUE},
+	{0,			"irc.spotchat.org"},
 
 	{"StarChat", 0},
 	{0,			"irc.starchat.net"},
-	{0,			"gainesville.starchat.net"},
-	{0,			"freebsd.starchat.net"},
-	{0,			"sunset.starchat.net"},
-	{0,			"revenge.starchat.net"},
-	{0,			"tahoma.starchat.net"},
-	{0,			"neo.starchat.net"},
 
-	{"StaticBox", 0, 0, 0, LOGIN_SASL},
-	{0,			"irc.staticbox.net"},
-
-	{"Station51", 0},
-#ifdef USE_OPENSSL
-	{0,			"irc.station51.net/+6697"},
-#endif
+	{"Station51", 0, 0, 0, 0, 0, TRUE},
 	{0,			"irc.station51.net"},
 
-	{"StormBit", 0, 0, 0, LOGIN_SASL},
-#ifdef USE_OPENSSL
-	{0,			"irc.stormbit.net/+6697"},
-#endif
+	{"StormBit", 0, 0, 0, LOGIN_SASL, 0, TRUE},
 	{0,			"irc.stormbit.net"},
 
-	{"SwiftIRC", 0},
-#ifdef USE_OPENSSL
-	{0,			"irc.swiftirc.net/+6697"},
-#endif
-	{0,			"irc.swiftirc.net/6667"},
+	{"SwiftIRC", 0, 0, 0, 0, 0, TRUE},
+	{0,			"irc.swiftirc.net"},
 
-	{"synIRC", 0},
-#ifdef USE_OPENSSL
-	{0, "irc.synirc.net/+6697"},
-#endif
-	{0, "irc.synirc.net/6667"},
+	{"synIRC", 0, 0, 0, 0, 0, TRUE},
+	{0, "irc.synirc.net"},
 
-	{"Techman's World IRC",	0, 0, 0, LOGIN_SASL},
-#ifdef USE_OPENSSL
-	{0,			"irc.techmansworld.com/+6697"},
-#endif
-	{0,			"irc.techmansworld.com/6667"},
+	{"Techtronix",	0, 0, 0, LOGIN_SASL, 0, TRUE},
+	{0,			"irc.techtronix.net"},
 
 	{"TinyCrab", 0, 0, 0, LOGIN_SASL},
 	{0,			"irc.tinycrab.net"},
@@ -508,16 +344,10 @@ static const struct defaultserver def[] =
 	{"Worldnet",		0},
 	{0,			"irc.worldnet.net"},
 
-	{"Windfyre",		0},
-#ifdef USE_OPENSSL
-	{0,			"irc.windfyre.net/+6697"},
-#endif
+	{"Windfyre", 0, 0, 0, 0, 0, TRUE},
 	{0,			"irc.windfyre.net"},
 
-	{"Xertion", 0, 0, 0, LOGIN_SASL},
-#ifdef USE_OPENSSL
-	{0,			"irc.xertion.org/+6697"},
-#endif
+	{"Xertion", 0, 0, 0, LOGIN_SASL, 0, TRUE},
 	{0,			"irc.xertion.org"},
 
 	{0,0}
@@ -566,9 +396,7 @@ servlist_favchan_copy (favchannel *fav)
 {
 	favchannel *newfav;
 
-	newfav = malloc (sizeof (favchannel));
-	memset (newfav, 0, sizeof (favchannel));
-
+	newfav = g_new (favchannel, 1);
 	newfav->name = g_strdup (fav->name);
 	newfav->key = g_strdup (fav->key);		/* g_strdup() can handle NULLs so no need to check it */
 
@@ -924,9 +752,8 @@ servlist_server_add (ircnet *net, char *name)
 {
 	ircserver *serv;
 
-	serv = malloc (sizeof (ircserver));
-	memset (serv, 0, sizeof (ircserver));
-	serv->hostname = strdup (name);
+	serv = g_new (ircserver, 1);
+	serv->hostname = g_strdup (name);
 
 	net->servlist = g_slist_append (net->servlist, serv);
 
@@ -938,9 +765,8 @@ servlist_command_add (ircnet *net, char *cmd)
 {
 	commandentry *entry;
 
-	entry = malloc (sizeof (commandentry));
-	memset (entry, 0, sizeof (commandentry));
-	entry->command = strdup (cmd);
+	entry = g_new (commandentry, 1);
+	entry->command = g_strdup (cmd);
 
 	net->commandlist = g_slist_append (net->commandlist, entry);
 
@@ -952,9 +778,7 @@ servlist_favchan_listadd (GSList *chanlist, char *channel, char *key)
 {
 	favchannel *chan;
 
-	chan = malloc (sizeof (favchannel));
-	memset (chan, 0, sizeof (favchannel));
-
+	chan = g_new (favchannel, 1);
 	chan->name = g_strdup (channel);
 	chan->key = g_strdup (key);
 	chanlist = g_slist_append (chanlist, chan);
@@ -990,8 +814,8 @@ servlist_favchan_add (ircnet *net, char *channel)
 void
 servlist_server_remove (ircnet *net, ircserver *serv)
 {
-	free (serv->hostname);
-	free (serv);
+	g_free (serv->hostname);
+	g_free (serv);
 	net->servlist = g_slist_remove (net->servlist, serv);
 }
 
@@ -1044,7 +868,7 @@ free_and_clear (char *str)
 		char *orig = str;
 		while (*str)
 			*str++ = 0;
-		free (orig);
+		g_free (orig);
 	}
 }
 
@@ -1072,25 +896,18 @@ servlist_net_remove (ircnet *net)
 	servlist_server_remove_all (net);
 	network_list = g_slist_remove (network_list, net);
 
-	if (net->nick)
-		free (net->nick);
-	if (net->nick2)
-		free (net->nick2);
-	if (net->user)
-		free (net->user);
-	if (net->real)
-		free (net->real);
+	g_free (net->nick);
+	g_free (net->nick2);
+	g_free (net->user);
+	g_free (net->real);
 	free_and_clear (net->pass);
 	if (net->favchanlist)
 		g_slist_free_full (net->favchanlist, (GDestroyNotify) servlist_favchan_free);
 	if (net->commandlist)
 		g_slist_free_full (net->commandlist, (GDestroyNotify) servlist_command_free);
-	if (net->comment)
-		free (net->comment);
-	if (net->encoding)
-		free (net->encoding);
-	free (net->name);
-	free (net);
+	g_free (net->encoding);
+	g_free (net->name);
+	g_free (net);
 
 	/* for safety */
 	list = serv_list;
@@ -1110,10 +927,8 @@ servlist_net_add (char *name, char *comment, int prepend)
 {
 	ircnet *net;
 
-	net = malloc (sizeof (ircnet));
-	memset (net, 0, sizeof (ircnet));
-	net->name = strdup (name);
-/*	net->comment = strdup (comment);*/
+	net = g_new0 (ircnet, 1);
+	net->name = g_strdup (name);
 	net->flags = FLAG_CYCLE | FLAG_USE_GLOBAL | FLAG_USE_PROXY;
 
 	if (prepend)
@@ -1156,6 +971,10 @@ servlist_load_defaults (void)
 			{
 				servlist_command_add (net, def[i].connectcmd);
 			}
+			if (def[i].ssl)
+			{
+				net->flags |= FLAG_USE_SSL;
+			}
 
 			if (g_str_hash (def[i].network) == def_hash)
 			{
@@ -1210,25 +1029,25 @@ servlist_load (void)
 			switch (buf[0])
 			{
 			case 'I':
-				net->nick = strdup (buf + 2);
+				net->nick = g_strdup (buf + 2);
 				break;
 			case 'i':
-				net->nick2 = strdup (buf + 2);
+				net->nick2 = g_strdup (buf + 2);
 				break;
 			case 'U':
-				net->user = strdup (buf + 2);
+				net->user = g_strdup (buf + 2);
 				break;
 			case 'R':
-				net->real = strdup (buf + 2);
+				net->real = g_strdup (buf + 2);
 				break;
 			case 'P':
-				net->pass = strdup (buf + 2);
+				net->pass = g_strdup (buf + 2);
 				break;
 			case 'L':
 				net->logintype = atoi (buf + 2);
 				break;
 			case 'E':
-				net->encoding = strdup (buf + 2);
+				net->encoding = servlist_check_encoding (buf + 2) ? g_strdup (buf + 2) : g_strdup ("UTF-8");
 				break;
 			case 'F':
 				net->flags = atoi (buf + 2);
@@ -1258,7 +1077,7 @@ servlist_load (void)
 			case 'A':
 				if (!net->pass)
 				{
-					net->pass = strdup (buf + 2);
+					net->pass = g_strdup (buf + 2);
 					if (!net->logintype)
 					{
 						net->logintype = LOGIN_SASL;
@@ -1267,7 +1086,7 @@ servlist_load (void)
 			case 'B':
 				if (!net->pass)
 				{
-					net->pass = strdup (buf + 2);
+					net->pass = g_strdup (buf + 2);
 					if (!net->logintype)
 					{
 						net->logintype = LOGIN_NICKSERV;
@@ -1302,13 +1121,6 @@ servlist_check_encoding (char *charset)
 	if (c)
 		c[0] = 0;
 
-	if (!g_ascii_strcasecmp (charset, "IRC")) /* special case */
-	{
-		if (c)
-			c[0] = ' ';
-		return TRUE;
-	}
-
 	gic = g_iconv_open (charset, "UTF-8");
 
 	if (c)
@@ -1379,8 +1191,7 @@ servlist_save (void)
 			fprintf (fp, "P=%s\n", net->pass);
 		if (net->logintype)
 			fprintf (fp, "L=%d\n", net->logintype);
-		if (net->encoding && g_ascii_strcasecmp (net->encoding, "System") &&
-			 g_ascii_strcasecmp (net->encoding, "System default"))
+		if (net->encoding)
 		{
 			fprintf (fp, "E=%s\n", net->encoding);
 			if (!servlist_check_encoding (net->encoding))
diff --git a/src/common/servlist.h b/src/common/servlist.h
index 6d6f1bd3..a305aede 100644
--- a/src/common/servlist.h
+++ b/src/common/servlist.h
@@ -45,7 +45,6 @@ typedef struct ircnet
 	char *real;
 	char *pass;
 	int logintype;
-	char *comment;
 	char *encoding;
 	GSList *servlist;
 	GSList *commandlist;
diff --git a/src/common/ssl.c b/src/common/ssl.c
index cfa9b6cf..f4e23665 100644
--- a/src/common/ssl.c
+++ b/src/common/ssl.c
@@ -25,18 +25,29 @@
 #include "inet.h"				  /* make it first to avoid macro redefinitions */
 #include <openssl/ssl.h>		  /* SSL_() */
 #include <openssl/err.h>		  /* ERR_() */
+#include <openssl/x509v3.h>
 #ifdef WIN32
 #include <openssl/rand.h>		  /* RAND_seed() */
 #endif
-#include "../../config.h"
+#include "config.h"
 #include <time.h>				  /* asctime() */
 #include <string.h>				  /* strncpy() */
 #include "ssl.h"				  /* struct cert_info */
 
 #include <glib.h>
 #include <glib/gprintf.h>
+#include <gio/gio.h>
 #include "util.h"
 
+/* If openssl was built without ec */
+#ifndef SSL_OP_SINGLE_ECDH_USE
+#define SSL_OP_SINGLE_ECDH_USE 0
+#endif
+
+#ifndef SSL_OP_NO_COMPRESSION
+#define SSL_OP_NO_COMPRESSION 0
+#endif
+
 /* globals */
 static struct chiper_info chiper_info;		/* static buffer for _SSL_get_cipher_info() */
 static char err_buf[256];			/* generic error buffer */
@@ -69,32 +80,29 @@ __SSL_critical_error (char *funcname)
 /* +++++ SSL functions +++++ */
 
 SSL_CTX *
-_SSL_context_init (void (*info_cb_func), int server)
+_SSL_context_init (void (*info_cb_func))
 {
 	SSL_CTX *ctx;
-#ifdef WIN32
-	int i, r;
-#endif
 
 	SSLeay_add_ssl_algorithms ();
 	SSL_load_error_strings ();
-	ctx = SSL_CTX_new (server ? SSLv23_server_method() : SSLv23_client_method ());
+	ctx = SSL_CTX_new (SSLv23_client_method ());
 
 	SSL_CTX_set_session_cache_mode (ctx, SSL_SESS_CACHE_BOTH);
 	SSL_CTX_set_timeout (ctx, 300);
+	SSL_CTX_set_options (ctx, SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3
+							  |SSL_OP_NO_COMPRESSION
+							  |SSL_OP_SINGLE_DH_USE|SSL_OP_SINGLE_ECDH_USE
+							  |SSL_OP_NO_TICKET
+							  |SSL_OP_CIPHER_SERVER_PREFERENCE);
+
+#if OPENSSL_VERSION_NUMBER >= 0x00908000L && !defined (OPENSSL_NO_COMP) /* workaround for OpenSSL 0.9.8 */
+	sk_SSL_COMP_zero(SSL_COMP_get_compression_methods());
+#endif
 
 	/* used in SSL_connect(), SSL_accept() */
 	SSL_CTX_set_info_callback (ctx, info_cb_func);
 
-#ifdef WIN32
-	/* under win32, OpenSSL needs to be seeded with some randomness */
-	for (i = 0; i < 128; i++)
-	{
-		r = rand ();
-		RAND_seed ((unsigned char *)&r, sizeof (r));
-	}
-#endif
-
 	return(ctx);
 }
 
@@ -329,3 +337,202 @@ _SSL_close (SSL * ssl)
 	SSL_free (ssl);
 	ERR_remove_state (0);		  /* free state buffer */
 }
+
+/* Hostname validation code based on OpenBSD's libtls. */
+
+static int
+_SSL_match_hostname (const char *cert_hostname, const char *hostname)
+{
+	const char *cert_domain, *domain, *next_dot;
+
+	if (g_ascii_strcasecmp (cert_hostname, hostname) == 0)
+		return 0;
+
+	/* Wildcard match? */
+	if (cert_hostname[0] == '*')
+	{
+		/*
+		 * Valid wildcards:
+		 * - "*.domain.tld"
+		 * - "*.sub.domain.tld"
+		 * - etc.
+		 * Reject "*.tld".
+		 * No attempt to prevent the use of eg. "*.co.uk".
+		 */
+		cert_domain = &cert_hostname[1];
+		/* Disallow "*"  */
+		if (cert_domain[0] == '\0')
+			return -1;
+		/* Disallow "*foo" */
+		if (cert_domain[0] != '.')
+			return -1;
+		/* Disallow "*.." */
+		if (cert_domain[1] == '.')
+			return -1;
+		next_dot = strchr (&cert_domain[1], '.');
+		/* Disallow "*.bar" */
+		if (next_dot == NULL)
+			return -1;
+		/* Disallow "*.bar.." */
+		if (next_dot[1] == '.')
+			return -1;
+
+		domain = strchr (hostname, '.');
+
+		/* No wildcard match against a hostname with no domain part. */
+		if (domain == NULL || strlen(domain) == 1)
+			return -1;
+
+		if (g_ascii_strcasecmp (cert_domain, domain) == 0)
+			return 0;
+	}
+
+	return -1;
+}
+
+static int
+_SSL_check_subject_altname (X509 *cert, const char *host)
+{
+	STACK_OF(GENERAL_NAME) *altname_stack = NULL;
+	GInetAddress *addr;
+	GSocketFamily family;
+	int type = GEN_DNS;
+	int count, i;
+	int rv = -1;
+
+	altname_stack = X509_get_ext_d2i (cert, NID_subject_alt_name, NULL, NULL);
+	if (altname_stack == NULL)
+		return -1;
+
+	addr = g_inet_address_new_from_string (host);
+	if (addr != NULL)
+	{
+		family = g_inet_address_get_family (addr);
+		if (family == G_SOCKET_FAMILY_IPV4 || family == G_SOCKET_FAMILY_IPV6)
+			type = GEN_IPADD;
+	}
+
+	count = sk_GENERAL_NAME_num(altname_stack);
+	for (i = 0; i < count; i++)
+	{
+		GENERAL_NAME *altname;
+
+		altname = sk_GENERAL_NAME_value (altname_stack, i);
+
+		if (altname->type != type)
+			continue;
+
+		if (type == GEN_DNS)
+		{
+			unsigned char *data;
+			int format;
+
+			format = ASN1_STRING_type (altname->d.dNSName);
+			if (format == V_ASN1_IA5STRING)
+			{
+				data = ASN1_STRING_data (altname->d.dNSName);
+
+				if (ASN1_STRING_length (altname->d.dNSName) != (int)strlen(data))
+				{
+					g_warning("NUL byte in subjectAltName, probably a malicious certificate.\n");
+					rv = -2;
+					break;
+				}
+
+				if (_SSL_match_hostname (data, host) == 0)
+				{
+					rv = 0;
+					break;
+				}
+			}
+			else
+				g_warning ("unhandled subjectAltName dNSName encoding (%d)\n", format);
+
+		}
+		else if (type == GEN_IPADD)
+		{
+			unsigned char *data;
+			const guint8 *addr_bytes;
+			int datalen, addr_len;
+
+			datalen = ASN1_STRING_length (altname->d.iPAddress);
+			data = ASN1_STRING_data (altname->d.iPAddress);
+
+			addr_bytes = g_inet_address_to_bytes (addr);
+			addr_len = (int)g_inet_address_get_native_size (addr);
+
+			if (datalen == addr_len && memcmp (data, addr_bytes, addr_len) == 0)
+			{
+				rv = 0;
+				break;
+			}
+		}
+	}
+
+	if (addr != NULL)
+		g_object_unref (addr);
+	sk_GENERAL_NAME_pop_free (altname_stack, GENERAL_NAME_free);
+	return rv;
+}
+
+static int
+_SSL_check_common_name (X509 *cert, const char *host)
+{
+	X509_NAME *name;
+	char *common_name = NULL;
+	int common_name_len;
+	int rv = -1;
+	GInetAddress *addr;
+
+	name = X509_get_subject_name (cert);
+	if (name == NULL)
+		return -1;
+
+	common_name_len = X509_NAME_get_text_by_NID (name, NID_commonName, NULL, 0);
+	if (common_name_len < 0)
+		return -1;
+
+	common_name = g_malloc0 (common_name_len + 1);
+
+	X509_NAME_get_text_by_NID (name, NID_commonName, common_name, common_name_len + 1);
+
+	/* NUL bytes in CN? */
+	if (common_name_len != (int)strlen(common_name))
+	{
+		g_warning ("NUL byte in Common Name field, probably a malicious certificate.\n");
+		rv = -2;
+		goto out;
+	}
+
+	if ((addr = g_inet_address_new_from_string (host)) != NULL)
+	{
+		/*
+		 * We don't want to attempt wildcard matching against IP
+		 * addresses, so perform a simple comparison here.
+		 */
+		if (g_strcmp0 (common_name, host) == 0)
+			rv = 0;
+		else
+			rv = -1;
+
+		g_object_unref (addr);
+	}
+	else if (_SSL_match_hostname (common_name, host) == 0)
+		rv = 0;
+
+out:
+	g_free(common_name);
+	return rv;
+}
+
+int
+_SSL_check_hostname (X509 *cert, const char *host)
+{
+	int rv;
+
+	rv = _SSL_check_subject_altname (cert, host);
+	if (rv == 0 || rv == -2)
+		return rv;
+
+	return _SSL_check_common_name (cert, host);
+}
diff --git a/src/common/ssl.h b/src/common/ssl.h
index 9c729855..e722f831 100644
--- a/src/common/ssl.h
+++ b/src/common/ssl.h
@@ -41,7 +41,7 @@ struct chiper_info {
     int chiper_bits;
 };
 
-SSL_CTX *_SSL_context_init (void (*info_cb_func), int server);
+SSL_CTX *_SSL_context_init (void (*info_cb_func));
 #define _SSL_context_free(a)	SSL_CTX_free(a);
 
 SSL *_SSL_socket (SSL_CTX *ctx, int sd);
@@ -52,7 +52,7 @@ char *_SSL_set_verify (SSL_CTX *ctx, void *(verify_callback), char *cacert);
     int SSL_get_fd(SSL *);
 */
 void _SSL_close (SSL * ssl);
-
+int _SSL_check_hostname(X509 *cert, const char *host);
 int _SSL_get_cert_info (struct cert_info *cert_info, SSL * ssl);
 struct chiper_info *_SSL_get_cipher_info (SSL * ssl);
 
diff --git a/src/common/text.c b/src/common/text.c
index 329ef37b..cd9ea26e 100644
--- a/src/common/text.c
+++ b/src/common/text.c
@@ -51,6 +51,9 @@
 #include <canberra.h>
 #endif
 
+const gchar* unicode_fallback_string = "\357\277\275"; /* The Unicode replacement character 0xFFFD */
+const gchar* arbitrary_encoding_fallback_string = "?";
+
 struct pevt_stage1
 {
 	int len;
@@ -83,7 +86,7 @@ scrollback_get_filename (session *sess)
 		buf = g_strdup_printf ("%s" G_DIR_SEPARATOR_S "scrollback" G_DIR_SEPARATOR_S "%s" G_DIR_SEPARATOR_S "%s.txt", get_xdir (), net, chan);
 	else
 		buf = NULL;
-	free (chan);
+	g_free (chan);
 
 	return buf;
 }
@@ -173,11 +176,11 @@ scrollback_shrink (session *sess)
 		p++;
 	}
 
-	fh = g_open (file, O_CREAT | O_TRUNC | O_APPEND | O_WRONLY, 0644);
+	fh = g_open (file, O_CREAT | O_TRUNC | O_APPEND | O_WRONLY | OFLAGS, 0644);
 	g_free (file);
 	if (fh == -1)
 	{
-		free (buf);
+		g_free (buf);
 		return;
 	}
 
@@ -200,14 +203,13 @@ scrollback_shrink (session *sess)
 	}
 
 	close (fh);
-	free (buf);
+	g_free (buf);
 }
 
 static void
-scrollback_save (session *sess, char *text)
+scrollback_save (session *sess, char *text, time_t stamp)
 {
 	char *buf;
-	time_t stamp;
 	int len;
 
 	if (sess->type == SESS_SERVER && prefs.hex_gui_tab_server == 1)
@@ -229,13 +231,14 @@ scrollback_save (session *sess, char *text)
 		if ((buf = scrollback_get_filename (sess)) == NULL)
 			return;
 
-		sess->scrollfd = g_open (buf, O_CREAT | O_APPEND | O_WRONLY, 0644);
+		sess->scrollfd = g_open (buf, O_CREAT | O_APPEND | O_WRONLY | OFLAGS, 0644);
 		g_free (buf);
 		if (sess->scrollfd == -1)
 			return;
 	}
 
-	stamp = time (0);
+	if (!stamp)
+		stamp = time(0);
 	if (sizeof (stamp) == 4)	/* gcc will optimize one of these out */
 		buf = g_strdup_printf ("T %d ", (int) stamp);
 	else
@@ -298,13 +301,6 @@ scrollback_load (session *sess)
 		{
 			char *buf_tmp;
 
-			/* If nothing but funny trailing matter e.g. 0x0d or 0x0d0a, toss it */
-			if (n_bytes >= 1 && buf[0] == 0x0d)
-			{
-				g_free (buf);
-				continue;
-			}
-
 			n_bytes--;
 			buf_tmp = buf;
 			buf = g_strndup (buf_tmp, n_bytes);
@@ -319,9 +315,9 @@ scrollback_load (session *sess)
 			if (buf[0] == 'T')
 			{
 				if (sizeof (time_t) == 4)
-					stamp = strtoul (buf + 2, NULL, 10);
+					stamp = g_ascii_strtoull (buf + 2, NULL, 10);
 				else
-					stamp = strtoull (buf + 2, NULL, 10); /* in case time_t is 64 bits */
+					stamp = g_ascii_strtoull (buf + 2, NULL, 10); /* in case time_t is 64 bits */
 				text = strchr (buf + 3, ' ');
 				if (text && text[1])
 				{
@@ -383,7 +379,7 @@ log_close (session *sess)
 	{
 		currenttime = time (NULL);
 		write (sess->logfd, obuf,
-			 snprintf (obuf, sizeof (obuf) - 1, _("**** ENDING LOGGING AT %s\n"),
+			 g_snprintf (obuf, sizeof (obuf) - 1, _("**** ENDING LOGGING AT %s\n"),
 						  ctime (&currenttime)));
 		close (sess->logfd);
 		sess->logfd = -1;
@@ -393,9 +389,7 @@ log_close (session *sess)
 static void
 mkdir_p (char *filename)
 {
-	char *dirname;
-	
-	dirname = g_path_get_dirname (filename);
+	char *dirname = g_path_get_dirname (filename);
 
 	g_mkdir_with_parents (dirname, 0700);
 
@@ -408,7 +402,7 @@ log_create_filename (char *channame)
 	char *tmp, *ret;
 	int mbl;
 
-	ret = tmp = strdup (channame);
+	ret = tmp = g_strdup (channame);
 	while (*tmp)
 	{
 		mbl = g_utf8_skip[((unsigned char *)tmp)[0]];
@@ -507,34 +501,6 @@ log_insert_vars (char *buf, int bufsize, char *fmt, char *c, char *n, char *s)
 	}
 }
 
-static int
-logmask_is_fullpath ()
-{
-	/* Check if final path/filename is absolute or relative.
-	 * If one uses log mask variables, such as "%c/...", %c will be empty upon
-	 * connecting since there's no channel name yet, so we have to make sure
-	 * we won't try to write to the FS root. On Windows we can be sure it's
-	 * full path if the 2nd character is a colon since Windows doesn't allow
-	 * colons in filenames.
-	 */
-#ifdef WIN32
-	/* Treat it as full path if it
-	 * - starts with '\' which denotes the root directory of the current drive letter
-	 * - starts with a drive letter and followed by ':'
-	 */
-	if (prefs.hex_irc_logmask[0] == '\\' || (((prefs.hex_irc_logmask[0] >= 'A' && prefs.hex_irc_logmask[0] <= 'Z') || (prefs.hex_irc_logmask[0] >= 'a' && prefs.hex_irc_logmask[0] <= 'z')) && prefs.hex_irc_logmask[1] == ':'))
-#else
-	if (prefs.hex_irc_logmask[0] == '/')
-#endif
-	{
-		return 1;
-	}
-	else
-	{
-		return 0;
-	}
-}
-
 static char *
 log_create_pathname (char *servname, char *channame, char *netname)
 {
@@ -544,7 +510,7 @@ log_create_pathname (char *servname, char *channame, char *netname)
 
 	if (!netname)
 	{
-		netname = strdup ("NETWORK");
+		netname = g_strdup ("NETWORK");
 	}
 	else
 	{
@@ -554,7 +520,7 @@ log_create_pathname (char *servname, char *channame, char *netname)
 	/* first, everything is in UTF-8 */
 	if (!rfc_casecmp (channame, servname))
 	{
-		channame = strdup ("server");
+		channame = g_strdup ("server");
 	}
 	else
 	{
@@ -562,27 +528,29 @@ log_create_pathname (char *servname, char *channame, char *netname)
 	}
 
 	log_insert_vars (fname, sizeof (fname), prefs.hex_irc_logmask, channame, netname, servname);
-	free (channame);
-	free (netname);
+	g_free (channame);
+	g_free (netname);
 
 	/* insert time/date */
 	now = time (NULL);
 	strftime_utf8 (fnametime, sizeof (fnametime), fname, now);
 
-	/* create final path/filename */
-	if (logmask_is_fullpath ())
+	/* If one uses log mask variables, such as "%c/...", %c will be empty upon
+	 * connecting since there's no channel name yet, so we have to make sure
+	 * we won't try to write to the FS root. */
+	if (g_path_is_absolute (prefs.hex_irc_logmask))
 	{
-		snprintf (fname, sizeof (fname), "%s", fnametime);
+		g_snprintf (fname, sizeof (fname), "%s", fnametime);
 	}
 	else	/* relative path */
 	{
-		snprintf (fname, sizeof (fname), "%s" G_DIR_SEPARATOR_S "logs" G_DIR_SEPARATOR_S "%s", get_xdir (), fnametime);
+		g_snprintf (fname, sizeof (fname), "%s" G_DIR_SEPARATOR_S "logs" G_DIR_SEPARATOR_S "%s", get_xdir (), fnametime);
 	}
 
 	/* create all the subdirectories */
 	mkdir_p (fname);
 
-	return g_strdup(fname);
+	return g_strdup (fname);
 }
 
 static int
@@ -597,18 +565,14 @@ log_open_file (char *servname, char *channame, char *netname)
 	if (!file)
 		return -1;
 
-#ifdef WIN32
-	fd = g_open (file, O_CREAT | O_APPEND | O_WRONLY, S_IREAD|S_IWRITE);
-#else
-	fd = g_open (file, O_CREAT | O_APPEND | O_WRONLY, 0644);
-#endif
+	fd = g_open (file, O_CREAT | O_APPEND | O_WRONLY | OFLAGS, 0644);
 	g_free (file);
 
 	if (fd == -1)
 		return -1;
 	currenttime = time (NULL);
 	write (fd, buf,
-			 snprintf (buf, sizeof (buf), _("**** BEGIN LOGGING AT %s\n"),
+			 g_snprintf (buf, sizeof (buf), _("**** BEGIN LOGGING AT %s\n"),
 						  ctime (&currenttime)));
 
 	return fd;
@@ -625,14 +589,15 @@ log_open (session *sess)
 
 	if (!log_error && sess->logfd == -1)
 	{
-		char *message;
+		char *filename = log_create_pathname (sess->server->servername, sess->channel, server_get_network (sess->server, FALSE));
+		char *message = g_strdup_printf (_("* Can't open log file(s) for writing. Check the\npermissions on %s"), filename);
 
-		message = g_strdup_printf (_("* Can't open log file(s) for writing. Check the\npermissions on %s"),
-			log_create_pathname (sess->server->servername, sess->channel, server_get_network (sess->server, FALSE)));
+		g_free (filename);
 
 		fe_message (message, FE_MSG_WAIT | FE_MSG_ERROR);
 
 		g_free (message);
+
 		log_error = TRUE;
 	}
 }
@@ -659,34 +624,29 @@ log_open_or_close (session *sess)
 int
 get_stamp_str (char *fmt, time_t tim, char **ret)
 {
-	char *loc = NULL;
 	char dest[128];
-	gsize len;
+	gsize len_locale;
+	gsize len_utf8;
 
-	/* strftime wants the format string in LOCALE! */
-	if (!prefs.utf8_locale)
-	{
-		const gchar *charset;
+	/* strftime requires the format string to be in locale encoding. */
+	fmt = g_locale_from_utf8 (fmt, -1, NULL, NULL, NULL);
 
-		g_get_charset (&charset);
-		loc = g_convert_with_fallback (fmt, -1, charset, "UTF-8", "?", 0, 0, 0);
-		if (loc)
-			fmt = loc;
-	}
+	len_locale = strftime_validated (dest, sizeof (dest), fmt, localtime (&tim));
 
-	len = strftime_validated (dest, sizeof (dest), fmt, localtime (&tim));
-	if (len)
+	g_free (fmt);
+
+	if (len_locale == 0)
 	{
-		if (prefs.utf8_locale)
-			*ret = g_strdup (dest);
-		else
-			*ret = g_locale_to_utf8 (dest, len, 0, &len, 0);
+		return 0;
 	}
 
-	if (loc)
-		g_free (loc);
+	*ret = g_locale_to_utf8 (dest, len_locale, NULL, &len_utf8, NULL);
+	if (*ret == NULL)
+	{
+		return 0;
+	}
 
-	return len;
+	return len_utf8;
 }
 
 static void
@@ -709,22 +669,32 @@ log_write (session *sess, char *text, time_t ts)
 	}
 
 	if (sess->logfd == -1)
+	{
 		log_open (sess);
+	}
 
 	/* change to a different log file? */
-	file = log_create_pathname (sess->server->servername, sess->channel,
-										 server_get_network (sess->server, FALSE));
+	file = log_create_pathname (sess->server->servername, sess->channel, server_get_network (sess->server, FALSE));
 	if (file)
 	{
 		if (g_access (file, F_OK) != 0)
 		{
-			close (sess->logfd);
-			sess->logfd = log_open_file (sess->server->servername, sess->channel,
-												  server_get_network (sess->server, FALSE));
+			if (sess->logfd != -1)
+			{
+				close (sess->logfd);
+			}
+
+			sess->logfd = log_open_file (sess->server->servername, sess->channel, server_get_network (sess->server, FALSE));
 		}
+
 		g_free (file);
 	}
 
+	if (sess->logfd == -1)
+	{
+		return;
+	}
+
 	if (prefs.hex_stamp_log)
 	{
 		if (!ts) ts = time(0);
@@ -735,6 +705,7 @@ log_write (session *sess, char *text, time_t ts)
 			g_free (stamp);
 		}
 	}
+
 	temp = strip_color (text, -1, STRIP_ALL);
 	len = strlen (temp);
 	write (sess->logfd, temp, len);
@@ -744,156 +715,104 @@ log_write (session *sess, char *text, time_t ts)
 	g_free (temp);
 }
 
-/* converts a CP1252/ISO-8859-1(5) hybrid to UTF-8                           */
-/* Features: 1. It never fails, all 00-FF chars are converted to valid UTF-8 */
-/*           2. Uses CP1252 in the range 80-9f because ISO doesn't have any- */
-/*              thing useful in this range and it helps us receive from mIRC */
-/*           3. The five undefined chars in CP1252 80-9f are replaced with   */
-/*              ISO-8859-15 control codes.                                   */
-/*           4. Handles 0xa4 as a Euro symbol ala ISO-8859-15.               */
-/*           5. Uses ISO-8859-1 (which matches CP1252) for everything else.  */
-/*           6. This routine measured 3x faster than g_convert :)            */
-
-static unsigned char *
-iso_8859_1_to_utf8 (unsigned char *text, int len, gsize *bytes_written)
+/**
+ * Converts a given string using the given iconv converter. This is similar to g_convert_with_fallback, except that it is tolerant of sequences in
+ * the original input that are invalid even in from_encoding. g_convert_with_fallback fails for such text, whereas this function replaces such a
+ * sequence with the fallback string.
+ *
+ * If len is -1, strlen(text) is used to calculate the length. Do not pass -1 if text is supposed to contain \0 bytes, such as if from_encoding is a
+ * multi-byte encoding like UTF-16.
+ */
+gchar *
+text_convert_invalid (const gchar* text, gssize len, GIConv converter, const gchar *fallback, gsize *len_out)
 {
-	unsigned int idx;
-	unsigned char *res, *output;
-	static const unsigned short lowtable[] = /* 74 byte table for 80-a4 */
-	{
-	/* compressed utf-8 table: if the first byte's 0x20 bit is set, it
-	   indicates a 2-byte utf-8 sequence, otherwise prepend a 0xe2. */
-		0x82ac, /* 80 Euro. CP1252 from here on... */
-		0xe281, /* 81 NA */
-		0x809a, /* 82 */
-		0xe692, /* 83 */
-		0x809e, /* 84 */
-		0x80a6, /* 85 */
-		0x80a0, /* 86 */
-		0x80a1, /* 87 */
-		0xeb86, /* 88 */
-		0x80b0, /* 89 */
-		0xe5a0, /* 8a */
-		0x80b9, /* 8b */
-		0xe592, /* 8c */
-		0xe28d, /* 8d NA */
-		0xe5bd, /* 8e */
-		0xe28f, /* 8f NA */
-		0xe290, /* 90 NA */
-		0x8098, /* 91 */
-		0x8099, /* 92 */
-		0x809c, /* 93 */
-		0x809d, /* 94 */
-		0x80a2, /* 95 */
-		0x8093, /* 96 */
-		0x8094, /* 97 */
-		0xeb9c, /* 98 */
-		0x84a2, /* 99 */
-		0xe5a1, /* 9a */
-		0x80ba, /* 9b */
-		0xe593, /* 9c */
-		0xe29d, /* 9d NA */
-		0xe5be, /* 9e */
-		0xe5b8, /* 9f */
-		0xe2a0, /* a0 */
-		0xe2a1, /* a1 */
-		0xe2a2, /* a2 */
-		0xe2a3, /* a3 */
-		0x82ac  /* a4 ISO-8859-15 Euro. */
-	};
+	gchar *result_part;
+	gsize result_part_len;
+	const gchar *end;
+	gsize invalid_start_pos;
+	GString *result;
+	const gchar *current_start;
 
 	if (len == -1)
+	{
 		len = strlen (text);
+	}
 
-	/* worst case scenario: every byte turns into 3 bytes */
-	res = output = g_malloc ((len * 3) + 1);
-	if (!output)
-		return NULL;
+	end = text + len;
 
-	while (len)
+	/* Find the first position of an invalid sequence. */
+	result_part = g_convert_with_iconv (text, len, converter, &invalid_start_pos, &result_part_len, NULL);
+	if (result_part != NULL)
 	{
-		if (G_LIKELY (*text < 0x80))
+		/* All text converted successfully on the first try. Return it. */
+
+		if (len_out != NULL)
 		{
-			*output = *text;	/* ascii maps directly */
+			*len_out = result_part_len;
 		}
-		else if (*text <= 0xa4)	/* 80-a4 use a lookup table */
+
+		return result_part;
+	}
+
+	/* One or more invalid sequences exist that need to be replaced with the fallback. */
+
+	result = g_string_sized_new (len);
+	current_start = text;
+
+	for (;;)
+	{
+		g_assert (current_start + invalid_start_pos < end);
+
+		/* Convert everything before the position of the invalid sequence. It should be successful. */
+		result_part = g_convert_with_iconv (current_start, invalid_start_pos, converter, &invalid_start_pos, &result_part_len, NULL);
+		g_assert (result_part != NULL);
+		g_string_append_len (result, result_part, result_part_len);
+		g_free (result_part);
+
+		/* Append the fallback */
+		g_string_append (result, fallback);
+
+		/* Now try converting everything after the invalid sequence. */
+		current_start += invalid_start_pos + 1;
+
+		result_part = g_convert_with_iconv (current_start, end - current_start, converter, &invalid_start_pos, &result_part_len, NULL);
+		if (result_part != NULL)
 		{
-			idx = *text - 0x80;
-			if (lowtable[idx] & 0x2000)
-			{
-				*output++ = (lowtable[idx] >> 8) & 0xdf; /* 2 byte utf-8 */
-				*output = lowtable[idx] & 0xff;
-			}
-			else
+			/* The rest of the text converted successfully. Append it and return the whole converted text. */
+
+			g_string_append_len (result, result_part, result_part_len);
+			g_free (result_part);
+
+			if (len_out != NULL)
 			{
-				*output++ = 0xe2;	/* 3 byte utf-8 */
-				*output++ = (lowtable[idx] >> 8) & 0xff;
-				*output = lowtable[idx] & 0xff;
+				*len_out = result->len;
 			}
+
+			return g_string_free (result, FALSE);
 		}
-		else if (*text < 0xc0)
-		{
-			*output++ = 0xc2;
-			*output = *text;
-		}
-		else
-		{
-			*output++ = 0xc3;
-			*output = *text - 0x40;
-		}
-		output++;
-		text++;
-		len--;
-	}
-	*output = 0;	/* terminate */
-	*bytes_written = output - res;
 
-	return res;
+		/* The rest of the text didn't convert successfully. invalid_start_pos has the position of the next invalid sequence. */
+	}
 }
 
-char *
-text_validate (char **text, int *len)
+/**
+ * Replaces any invalid UTF-8 in the given text with the unicode replacement character.
+ */
+gchar *
+text_fixup_invalid_utf8 (const gchar* text, gssize len, gsize *len_out)
 {
-	char *utf;
-	gsize utf_len;
-
-	/* valid utf8? */
-	if (g_utf8_validate (*text, *len, 0))
-		return NULL;
-
-#ifdef WIN32
-	if (GetACP () == 1252) /* our routine is better than iconv's 1252 */
-#else
-	if (prefs.utf8_locale)
-#endif
-		/* fallback to iso-8859-1 */
-		utf = iso_8859_1_to_utf8 (*text, *len, &utf_len);
-	else
+	static GIConv utf8_fixup_converter = NULL;
+	if (utf8_fixup_converter == NULL)
 	{
-		/* fallback to locale */
-		utf = g_locale_to_utf8 (*text, *len, 0, &utf_len, NULL);
-		if (!utf)
-			utf = iso_8859_1_to_utf8 (*text, *len, &utf_len);
+		utf8_fixup_converter = g_iconv_open ("UTF-8", "UTF-8");
 	}
 
-	if (!utf) 
-	{
-		*text = g_strdup ("%INVALID%");
-		*len = 9;
-	} else
-	{
-		*text = utf;
-		*len = utf_len;
-	}
-
-	return utf;
+	return text_convert_invalid (text, len, utf8_fixup_converter, unicode_fallback_string, len_out);
 }
 
 void
 PrintTextTimeStamp (session *sess, char *text, time_t timestamp)
 {
-	char *conv;
-
 	if (!sess)
 	{
 		if (!sess_list)
@@ -902,22 +821,19 @@ PrintTextTimeStamp (session *sess, char *text, time_t timestamp)
 	}
 
 	/* make sure it's valid utf8 */
-	if (text[0] == 0)
+	if (text[0] == '\0')
 	{
-		text = "\n";
-		conv = NULL;
-	} else
+		text = g_strdup ("\n");
+	}
+	else
 	{
-		int len = -1;
-		conv = text_validate ((char **)&text, &len);
+		text = text_fixup_invalid_utf8 (text, -1, NULL);
 	}
 
 	log_write (sess, text, timestamp);
-	scrollback_save (sess, text);
+	scrollback_save (sess, text, timestamp);
 	fe_print_text (sess, text, timestamp, FALSE);
-
-	if (conv)
-		g_free (conv);
+	g_free (text);
 }
 
 void
@@ -1004,7 +920,7 @@ PrintTextTimeStampf (session *sess, time_t timestamp, const char *format, ...)
    Each XP_TE_* signal is hard coded to call text_emit which calls
    display_event which decodes the data
 
-   This means that this system *should be faster* than snprintf because
+   This means that this system *should be faster* than g_snprintf because
    it always 'knows' that format of the string (basically is preparses much
    of the work)
 
@@ -1211,26 +1127,26 @@ static char * const pevt_chanrmlimit_help[] = {
 };
 
 static char * const pevt_chandeop_help[] = {
-	N_("The nick of the person of did the deop'ing"),
+	N_("The nick of the person who did the deop'ing"),
 	N_("The nick of the person who has been deop'ed"),
 };
 static char * const pevt_chandehop_help[] = {
-	N_("The nick of the person of did the dehalfop'ing"),
+	N_("The nick of the person who did the dehalfop'ing"),
 	N_("The nick of the person who has been dehalfop'ed"),
 };
 
 static char * const pevt_chandevoice_help[] = {
-	N_("The nick of the person of did the devoice'ing"),
+	N_("The nick of the person who did the devoice'ing"),
 	N_("The nick of the person who has been devoice'ed"),
 };
 
 static char * const pevt_chanunban_help[] = {
-	N_("The nick of the person of did the unban'ing"),
+	N_("The nick of the person who did the unban'ing"),
 	N_("The ban mask"),
 };
 
 static char * const pevt_chanunquiet_help[] = {
-	N_("The nick of the person of did the unquiet'ing"),
+	N_("The nick of the person who did the unquiet'ing"),
 	N_("The quiet mask"),
 };
 
@@ -1569,14 +1485,13 @@ pevent_load_defaults ()
 
 	for (i = 0; i < NUM_XP; i++)
 	{
-		if (pntevts_text[i])
-			free (pntevts_text[i]);
+		g_free (pntevts_text[i]);
 
 		/* make-te.c sets this 128 flag (DON'T call gettext() flag) */
 		if (te[i].num_args & 128)
-			pntevts_text[i] = strdup (te[i].def);
+			pntevts_text[i] = g_strdup (te[i].def);
 		else
-			pntevts_text[i] = strdup (_(te[i].def));
+			pntevts_text[i] = g_strdup (_(te[i].def));
 	}
 }
 
@@ -1588,19 +1503,18 @@ pevent_make_pntevts ()
 
 	for (i = 0; i < NUM_XP; i++)
 	{
-		if (pntevts[i] != NULL)
-			free (pntevts[i]);
+		g_free (pntevts[i]);
 		if (pevt_build_string (pntevts_text[i], &(pntevts[i]), &m) != 0)
 		{
-			snprintf (out, sizeof (out),
+			g_snprintf (out, sizeof (out),
 						 _("Error parsing event %s.\nLoading default."), te[i].name);
 			fe_message (out, FE_MSG_WARN);
-			free (pntevts_text[i]);
+			g_free (pntevts_text[i]);
 			/* make-te.c sets this 128 flag (DON'T call gettext() flag) */
 			if (te[i].num_args & 128)
-				pntevts_text[i] = strdup (te[i].def);
+				pntevts_text[i] = g_strdup (te[i].def);
 			else
-				pntevts_text[i] = strdup (_(te[i].def));
+				pntevts_text[i] = g_strdup (_(te[i].def));
 			if (pevt_build_string (pntevts_text[i], &(pntevts[i]), &m) != 0)
 			{
 				fprintf (stderr,
@@ -1622,22 +1536,17 @@ pevent_make_pntevts ()
 static void
 pevent_trigger_load (int *i_penum, char **i_text, char **i_snd)
 {
-	int penum = *i_penum, len;
+	int penum = *i_penum;
 	char *text = *i_text, *snd = *i_snd;
 
 	if (penum != -1 && text != NULL)
 	{
-		len = strlen (text) + 1;
-		if (pntevts_text[penum])
-			free (pntevts_text[penum]);
-		pntevts_text[penum] = malloc (len);
-		memcpy (pntevts_text[penum], text, len);
+		g_free (pntevts_text[penum]);
+		pntevts_text[penum] = g_strdup (text);
 	}
 
-	if (text)
-		free (text);
-	if (snd)
-		free (snd);
+	g_free (text);
+	g_free (snd);
 	*i_text = NULL;
 	*i_snd = NULL;
 	*i_penum = 0;
@@ -1690,7 +1599,7 @@ pevent_load (char *filename)
 		close (fd);
 		return 1;
 	}
-	ibuf = malloc (st.st_size);
+	ibuf = g_malloc (st.st_size);
 	read (fd, ibuf, st.st_size);
 	close (fd);
 
@@ -1706,8 +1615,6 @@ pevent_load (char *filename)
 			continue;
 		*ofs = 0;
 		ofs++;
-		/*if (*ofs == 0)
-			continue;*/
 
 		if (strcmp (buf, "event_name") == 0)
 		{
@@ -1717,53 +1624,16 @@ pevent_load (char *filename)
 			continue;
 		} else if (strcmp (buf, "event_text") == 0)
 		{
-			if (text)
-				free (text);
-
-#if 0
-			/* This allows updating of old strings. We don't use new defaults
-				if the user has customized the strings (.e.g a text theme).
-				Hash of the old default is enough to identify and replace it.
-				This only works in English. */
-
-			switch (g_str_hash (ofs))
-			{
-			case 0x526743a4:
-		/* %C08,02 Hostmask                  PRIV NOTI CHAN CTCP INVI UNIG %O */
-				text = strdup (te[XP_TE_IGNOREHEADER].def);
-				break;
-
-			case 0xe91bc9c2:
-		/* %C08,02                                                         %O */
-				text = strdup (te[XP_TE_IGNOREFOOTER].def);
-				break;
-
-			case 0x1fbfdf22:
-		/* -%C10-%C11-%O$tDCC RECV: Cannot open $1 for writing - aborting. */
-				text = strdup (te[XP_TE_DCCFILEERR].def);
-				break;
-
-			default:
-				text = strdup (ofs);
-			}
-#else
-			text = strdup (ofs);
-#endif
-
-			continue;
-		}/* else if (strcmp (buf, "event_sound") == 0)
-		{
-			if (snd)
-				free (snd);
-			snd = strdup (ofs);
+			g_free (text);
+			text = g_strdup (ofs);
 			continue;
-		}*/
+		}
 
 		continue;
 	}
 
 	pevent_trigger_load (&penum, &text, &snd);
-	free (ibuf);
+	g_free (ibuf);
 	return 0;
 }
 
@@ -1777,13 +1647,13 @@ pevent_check_all_loaded ()
 		if (pntevts_text[i] == NULL)
 		{
 			/*printf ("%s\n", te[i].name);
-			snprintf(out, sizeof(out), "The data for event %s failed to load. Reverting to defaults.\nThis may be because a new version of HexChat is loading an old config file.\n\nCheck all print event texts are correct", evtnames[i]);
+			g_snprintf(out, sizeof(out), "The data for event %s failed to load. Reverting to defaults.\nThis may be because a new version of HexChat is loading an old config file.\n\nCheck all print event texts are correct", evtnames[i]);
 			   gtkutil_simpledialog(out); */
 			/* make-te.c sets this 128 flag (DON'T call gettext() flag) */
 			if (te[i].num_args & 128)
-				pntevts_text[i] = strdup (te[i].def);
+				pntevts_text[i] = g_strdup (te[i].def);
 			else
-				pntevts_text[i] = strdup (_(te[i].def));
+				pntevts_text[i] = g_strdup (_(te[i].def));
 		}
 	}
 }
@@ -1808,9 +1678,10 @@ load_text_events ()
 #define ARG_FLAG(argn) (1 << (argn))
 
 void
-format_event (session *sess, int index, char **args, char *o, int sizeofo, unsigned int stripcolor_args)
+format_event (session *sess, int index, char **args, char *o, gsize sizeofo, unsigned int stripcolor_args)
 {
-	int len, oi, ii, numargs;
+	int len, ii, numargs;
+	gsize oi;
 	char *i, *ar, d, a, done_all = FALSE;
 
 	i = pntevts[index];
@@ -1868,19 +1739,10 @@ format_event (session *sess, int index, char **args, char *o, int sizeofo, unsig
 			done_all = TRUE;
 			continue;
 		case 3:
-/*			if (sess->type == SESS_DIALOG)
-			{
-				if (prefs.dialog_indent_nicks)
-					o[oi++] = '\t';
-				else
-					o[oi++] = ' ';
-			} else
-			{*/
-				if (prefs.hex_text_indent)
-					o[oi++] = '\t';
-				else
-					o[oi++] = ' ';
-			/*}*/
+			if (prefs.hex_text_indent)
+				o[oi++] = '\t';
+			else
+				o[oi++] = ' ';
 			break;
 		}
 	}
@@ -1908,7 +1770,7 @@ pevt_build_string (const char *input, char **output, int *max_arg)
 	int oi, ii, max = -1, len, x;
 
 	len = strlen (input);
-	i = malloc (len + 1);
+	i = g_malloc (len + 1);
 	memcpy (i, input, len + 1);
 	check_special_chars (i, TRUE);
 
@@ -1933,14 +1795,14 @@ pevt_build_string (const char *input, char **output, int *max_arg)
 		}
 		if (oi > 0)
 		{
-			s = (struct pevt_stage1 *) malloc (sizeof (struct pevt_stage1));
+			s = g_new (struct pevt_stage1, 1);
 			if (base == NULL)
 				base = s;
 			if (last != NULL)
 				last->next = s;
 			last = s;
 			s->next = NULL;
-			s->data = malloc (oi + sizeof (int) + 1);
+			s->data = g_malloc (oi + sizeof (int) + 1);
 			s->len = oi + sizeof (int) + 1;
 			clen += oi + sizeof (int) + 1;
 			s->data[0] = 0;
@@ -1951,12 +1813,12 @@ pevt_build_string (const char *input, char **output, int *max_arg)
 		if (ii == len)
 		{
 			fe_message ("String ends with a $", FE_MSG_WARN);
-			return 1;
+			goto err;
 		}
 		d = i[ii++];
 		if (d == 'a')
-		{								  /* Hex value */
-			x = 0;
+		{
+			/* Hex value */
 			if (ii == len)
 				goto a_len_error;
 			d = i[ii++];
@@ -1977,24 +1839,24 @@ pevt_build_string (const char *input, char **output, int *max_arg)
 			o[oi++] = x;
 			continue;
 
-		 a_len_error:
+		a_len_error:
 			fe_message ("String ends in $a", FE_MSG_WARN);
-			return 1;
-		 a_range_error:
+			goto err;
+		a_range_error:
 			fe_message ("$a value is greater than 255", FE_MSG_WARN);
-			return 1;
+			goto err;
 		}
 		if (d == 't')
 		{
 			/* Tab - if tabnicks is set then write '\t' else ' ' */
-			s = (struct pevt_stage1 *) malloc (sizeof (struct pevt_stage1));
+			s = g_new (struct pevt_stage1, 1);
 			if (base == NULL)
 				base = s;
 			if (last != NULL)
 				last->next = s;
 			last = s;
 			s->next = NULL;
-			s->data = malloc (1);
+			s->data = g_malloc (1);
 			s->len = 1;
 			clen += 1;
 			s->data[0] = 3;
@@ -2003,21 +1865,21 @@ pevt_build_string (const char *input, char **output, int *max_arg)
 		}
 		if (d < '1' || d > '9')
 		{
-			snprintf (o, sizeof (o), "Error, invalid argument $%c\n", d);
+			g_snprintf (o, sizeof (o), "Error, invalid argument $%c\n", d);
 			fe_message (o, FE_MSG_WARN);
-			return 1;
+			goto err;
 		}
 		d -= '0';
 		if (max < d)
 			max = d;
-		s = (struct pevt_stage1 *) malloc (sizeof (struct pevt_stage1));
+		s = g_new (struct pevt_stage1, 1);
 		if (base == NULL)
 			base = s;
 		if (last != NULL)
 			last->next = s;
 		last = s;
 		s->next = NULL;
-		s->data = malloc (2);
+		s->data = g_malloc (2);
 		s->len = 2;
 		clen += 2;
 		s->data[0] = 1;
@@ -2025,14 +1887,14 @@ pevt_build_string (const char *input, char **output, int *max_arg)
 	}
 	if (oi > 0)
 	{
-		s = (struct pevt_stage1 *) malloc (sizeof (struct pevt_stage1));
+		s = g_new (struct pevt_stage1, 1);
 		if (base == NULL)
 			base = s;
 		if (last != NULL)
 			last->next = s;
 		last = s;
 		s->next = NULL;
-		s->data = malloc (oi + sizeof (int) + 1);
+		s->data = g_malloc (oi + sizeof (int) + 1);
 		s->len = oi + sizeof (int) + 1;
 		clen += oi + sizeof (int) + 1;
 		s->data[0] = 0;
@@ -2040,39 +1902,54 @@ pevt_build_string (const char *input, char **output, int *max_arg)
 		memcpy (&(s->data[1 + sizeof (int)]), o, oi);
 		oi = 0;
 	}
-	s = (struct pevt_stage1 *) malloc (sizeof (struct pevt_stage1));
+	s = g_new (struct pevt_stage1, 1);
 	if (base == NULL)
 		base = s;
 	if (last != NULL)
 		last->next = s;
-	last = s;
 	s->next = NULL;
-	s->data = malloc (1);
+	s->data = g_malloc (1);
 	s->len = 1;
 	clen += 1;
 	s->data[0] = 2;
 
 	oi = 0;
 	s = base;
-	obuf = malloc (clen);
+	obuf = g_malloc (clen);
+
 	while (s)
 	{
 		next = s->next;
 		memcpy (&obuf[oi], s->data, s->len);
 		oi += s->len;
-		free (s->data);
-		free (s);
+		g_free (s->data);
+		g_free (s);
 		s = next;
 	}
 
-	free (i);
+	g_free (i);
 
 	if (max_arg)
 		*max_arg = max;
 	if (output)
 		*output = obuf;
+	else
+		g_free (obuf);
 
 	return 0;
+
+err:
+	while (s)
+	{
+		next = s->next;
+		g_free (s->data);
+		g_free (s);
+		s = next;
+	}
+
+	g_free(i);
+
+	return 1;
 }
 
 
@@ -2107,7 +1984,7 @@ text_emit (int index, session *sess, char *a, char *b, char *c, char *d,
 
 	if (prefs.hex_text_color_nicks && (index == XP_TE_CHANACTION || index == XP_TE_CHANMSG))
 	{
-		snprintf (tbuf, sizeof (tbuf), "\003%d%s", text_color_of (a), a);
+		g_snprintf (tbuf, sizeof (tbuf), "\003%d%s", text_color_of (a), a);
 		a = tbuf;
 		stripcolor_args &= ~ARG_FLAG(1);	/* don't strip color from this argument */
 	}
@@ -2239,9 +2116,9 @@ pevent_save (char *fn)
 
 	for (i = 0; i < NUM_XP; i++)
 	{
-		write (fd, buf, snprintf (buf, sizeof (buf),
+		write (fd, buf, g_snprintf (buf, sizeof (buf),
 										  "event_name=%s\n", te[i].name));
-		write (fd, buf, snprintf (buf, sizeof (buf),
+		write (fd, buf, g_snprintf (buf, sizeof (buf),
 										  "event_text=%s\n\n", pntevts_text[i]));
 	}
 
@@ -2257,7 +2134,7 @@ char *sound_files[NUM_XP];
 void
 sound_beep (session *sess)
 {
-	if (!prefs.hex_gui_focus_omitalerts || !fe_gui_info (sess, 0) == 1)
+	if (!prefs.hex_gui_focus_omitalerts || fe_gui_info (sess, 0) != 1)
 	{
 		if (sound_files[XP_TE_BEEP] && sound_files[XP_TE_BEEP][0])
 			/* user defined beep _file_ */
@@ -2283,12 +2160,8 @@ sound_play (const char *file, gboolean quiet)
 		return;
 	}
 
-#ifdef WIN32
 	/* check for fullpath */
-	if (file[0] == '\\' || (((file[0] >= 'A' && file[0] <= 'Z') || (file[0] >= 'a' && file[0] <= 'z')) && file[1] == ':'))
-#else
-	if (file[0] == '/')
-#endif
+	if (g_path_is_absolute (file))
 	{
 		wavfile = g_strdup (file);
 	}
@@ -2363,9 +2236,8 @@ sound_load_event (char *evt, char *file)
 
 	if (file[0] && pevent_find (evt, &i) != -1)
 	{
-		if (sound_files[i])
-			free (sound_files[i]);
-		sound_files[i] = strdup (file);
+		g_free (sound_files[i]);
+		sound_files[i] = g_strdup (file);
 	}
 }
 
@@ -2417,9 +2289,9 @@ sound_save ()
 	{
 		if (sound_files[i] && sound_files[i][0])
 		{
-			write (fd, buf, snprintf (buf, sizeof (buf),
+			write (fd, buf, g_snprintf (buf, sizeof (buf),
 											  "event=%s\n", te[i].name));
-			write (fd, buf, snprintf (buf, sizeof (buf),
+			write (fd, buf, g_snprintf (buf, sizeof (buf),
 											  "sound=%s\n\n", sound_files[i]));
 		}
 	}
diff --git a/src/common/text.h b/src/common/text.h
index 9a385167..28fc0c0d 100644
--- a/src/common/text.h
+++ b/src/common/text.h
@@ -57,11 +57,15 @@ void text_emit (int index, session *sess, char *a, char *b, char *c, char *d,
 		time_t timestamp);
 int text_emit_by_name (char *name, session *sess, time_t timestamp,
 					   char *a, char *b, char *c, char *d);
-char *text_validate (char **text, int *len);
+gchar *text_convert_invalid (const gchar* text, gssize len, GIConv converter, const gchar *fallback, gsize *len_out);
+gchar *text_fixup_invalid_utf8 (const gchar* text, gssize len, gsize *len_out);
 int get_stamp_str (char *fmt, time_t tim, char **ret);
-void format_event (session *sess, int index, char **args, char *o, int sizeofo, unsigned int stripcolor_args);
+void format_event (session *sess, int index, char **args, char *o, gsize sizeofo, unsigned int stripcolor_args);
 char *text_find_format_string (char *name);
- 
+
+extern const gchar* unicode_fallback_string;
+extern const gchar* arbitrary_encoding_fallback_string;
+
 void sound_play (const char *file, gboolean quiet);
 void sound_play_event (int i);
 void sound_beep (session *);
diff --git a/src/common/tree.c b/src/common/tree.c
index 587d15f0..b9a894d2 100644
--- a/src/common/tree.c
+++ b/src/common/tree.c
@@ -42,7 +42,7 @@ struct _tree
 tree *
 tree_new (tree_cmp_func *cmp, void *data)
 {
-	tree *t = calloc (1, sizeof (tree));
+	tree *t = g_new0 (tree, 1);
 	t->cmp = cmp;
 	t->data = data;
 	return t;
@@ -53,9 +53,8 @@ tree_destroy (tree *t)
 {
 	if (t)
 	{
-		if (t->array)
-			free (t->array);
-		free (t);
+		g_free (t->array);
+		g_free (t);
 	}
 }
 
diff --git a/src/common/tree.h b/src/common/tree.h
index 848f5abf..8cde93ea 100644
--- a/src/common/tree.h
+++ b/src/common/tree.h
@@ -20,6 +20,8 @@
 #ifndef HEXCHAT_TREE_H
 #define HEXCHAT_TREE_H
 
+#include <glib.h>
+
 typedef struct _tree tree;
 
 typedef int (tree_cmp_func) (const void *keya, const void *keyb, void *data);
diff --git a/src/common/url.c b/src/common/url.c
index 1321374f..0354d98c 100644
--- a/src/common/url.c
+++ b/src/common/url.c
@@ -53,7 +53,7 @@ static gboolean match_path (const char *word, int *start, int *end);
 static int
 url_free (char *url, void *data)
 {
-	free (url);
+	g_free (url);
 	return TRUE;
 }
 
@@ -124,13 +124,7 @@ url_add (char *urltext, int len)
 		return;
 	}
 
-	data = malloc (len + 1);
-	if (!data)
-	{
-		return;
-	}
-	memcpy (data, urltext, len);
-	data[len] = 0;
+	data = g_strndup (urltext, len);
 
 	if (data[len - 1] == '.')	/* chop trailing dot */
 	{
@@ -151,7 +145,7 @@ url_add (char *urltext, int len)
 	/* the URL is saved already, only continue if we need the URL grabber too */
 	if (!prefs.hex_url_grabber)
 	{
-		free (data);
+		g_free (data);
 		return;
 	}
 
@@ -163,7 +157,7 @@ url_add (char *urltext, int len)
 
 	if (url_find (data))
 	{
-		free (data);
+		g_free (data);
 		return;
 	}
 
@@ -180,7 +174,7 @@ url_add (char *urltext, int len)
 
 			pos = tree_remove_at_pos (url_tree, 0);
 			g_tree_remove (url_btree, pos);
-			free (pos);
+			g_free (pos);
 		}
 	}
 
@@ -332,7 +326,7 @@ static char *commands[] = {
 #define ARRAY_SIZE(a) (sizeof (a) / sizeof ((a)[0]))
 
 void
-url_check_line (char *buf, int len)
+url_check_line (char *buf)
 {
 	GRegex *re(void);
 	GMatchInfo *gmi;
@@ -415,7 +409,7 @@ regex_match (const GRegex *re, const char *word, int *start, int *end)
 }
 
 /*	Miscellaneous description --- */
-#define DOMAIN "[_\\pL\\pN][-_\\pL\\pN]*(\\.[-_\\pL\\pN]+)*"
+#define DOMAIN "[_\\pL\\pN\\pS][-_\\pL\\pN\\pS]*(\\.[-_\\pL\\pN\\pS]+)*"
 #define TLD "\\.[\\pL][-\\pL\\pN]*[\\pL]"
 #define IPADDR "[0-9]{1,3}(\\.[0-9]{1,3}){3}"
 #define IPV6GROUP "([0-9a-f]{0,4})"
@@ -429,7 +423,7 @@ regex_match (const GRegex *re, const char *word, int *start, int *end)
 #define OPT_PORT "(" PORT ")?"
 
 static GRegex *
-make_re (char *grist)
+make_re (const char *grist)
 {
 	GRegex *ret;
 	GError *err = NULL;
@@ -587,18 +581,14 @@ re_url (void)
 		
 		if (uri[i].flags & URI_PATH)
 		{
-			char *sep_escaped;
-			
-			sep_escaped = g_regex_escape_string (uri[i].path_sep, 
-							     strlen(uri[i].path_sep));
+			char *sep_escaped = g_regex_escape_string (uri[i].path_sep, strlen(uri[i].path_sep));
 
-			g_string_append_printf(grist_gstr, "(" "%s" PATH ")?",
-					       sep_escaped);
+			g_string_append_printf (grist_gstr, "(" "%s" PATH ")?", sep_escaped);
 
-			g_free(sep_escaped);
+			g_free (sep_escaped);
 		}
 
-		g_string_append(grist_gstr, ")");
+		g_string_append (grist_gstr, ")");
 	}
 
 	grist = g_string_free (grist_gstr, FALSE);
diff --git a/src/common/url.h b/src/common/url.h
index 676f9a6d..1b1deb3d 100644
--- a/src/common/url.h
+++ b/src/common/url.h
@@ -36,6 +36,6 @@ void url_clear (void);
 void url_save_tree (const char *fname, const char *mode, gboolean fullpath);
 int url_last (int *, int *);
 int url_check_word (const char *word);
-void url_check_line (char *buf, int len);
+void url_check_line (char *buf);
 
 #endif
diff --git a/src/common/userlist.c b/src/common/userlist.c
index e08cb857..54ed6f03 100644
--- a/src/common/userlist.c
+++ b/src/common/userlist.c
@@ -29,7 +29,7 @@
 #include "util.h"
 
 
-static int
+int
 nick_cmp_az_ops (server *serv, struct User *user1, struct User *user2)
 {
 	unsigned int access1 = user1->access;
@@ -52,30 +52,12 @@ nick_cmp_az_ops (server *serv, struct User *user1, struct User *user2)
 	return serv->p_cmp (user1->nick, user2->nick);
 }
 
-static int
+int
 nick_cmp_alpha (struct User *user1, struct User *user2, server *serv)
 {
 	return serv->p_cmp (user1->nick, user2->nick);
 }
 
-static int
-nick_cmp (struct User *user1, struct User *user2, server *serv)
-{
-	switch (prefs.hex_gui_ulist_sort)
-	{
-	case 0:
-		return nick_cmp_az_ops (serv, user1, user2);
-	case 1:
-		return serv->p_cmp (user1->nick, user2->nick);
-	case 2:
-		return -1 * nick_cmp_az_ops (serv, user1, user2);
-	case 3:
-		return -1 * serv->p_cmp (user1->nick, user2->nick);
-	default:
-		return -1;
-	}
-}
-
 /*
  insert name in appropriate place in linked list. Returns row number or:
   -1: duplicate
@@ -86,11 +68,9 @@ userlist_insertname (session *sess, struct User *newuser)
 {
 	if (!sess->usertree)
 	{
-		sess->usertree = tree_new ((tree_cmp_func *)nick_cmp, sess->server);
-		sess->usertree_alpha = tree_new ((tree_cmp_func *)nick_cmp_alpha, sess->server);
+		sess->usertree = tree_new ((tree_cmp_func *)nick_cmp_alpha, sess->server);
 	}
 
-	tree_insert (sess->usertree_alpha, newuser);
 	return tree_insert (sess->usertree, newuser);
 }
 
@@ -121,13 +101,12 @@ userlist_set_account (struct session *sess, char *nick, char *account)
 	user = userlist_find (sess, nick);
 	if (user)
 	{
-		if (user->account)
-			free (user->account);
+		g_free (user->account);
 			
 		if (strcmp (account, "*") == 0)
 			user->account = NULL;
 		else
-			user->account = strdup (account);
+			user->account = g_strdup (account);
 			
 		/* gui doesnt currently reflect login status, maybe later
 		fe_userlist_rehash (sess, user); */
@@ -148,14 +127,14 @@ userlist_add_hostname (struct session *sess, char *nick, char *hostname,
 		{
 			if (prefs.hex_gui_ulist_show_hosts)
 				do_rehash = TRUE;
-			user->hostname = strdup (hostname);
+			user->hostname = g_strdup (hostname);
 		}
 		if (!user->realname && realname && *realname)
-			user->realname = strdup (realname);
+			user->realname = g_strdup (realname);
 		if (!user->servername && servername)
-			user->servername = strdup (servername);
+			user->servername = g_strdup (servername);
 		if (!user->account && account && strcmp (account, "0") != 0)
-			user->account = strdup (account);
+			user->account = g_strdup (account);
 		if (away != 0xff)
 		{
 			if (user->away != away)
@@ -175,15 +154,11 @@ userlist_add_hostname (struct session *sess, char *nick, char *hostname,
 static int
 free_user (struct User *user, gpointer data)
 {
-	if (user->realname)
-		free (user->realname);
-	if (user->hostname)
-		free (user->hostname);
-	if (user->servername)
-		free (user->servername);
-	if (user->account)
-		free (user->account);
-	free (user);
+	g_free (user->realname);
+	g_free (user->hostname);
+	g_free (user->servername);
+	g_free (user->account);
+	g_free (user);
 
 	return TRUE;
 }
@@ -193,10 +168,8 @@ userlist_free (session *sess)
 {
 	tree_foreach (sess->usertree, (tree_traverse_func *)free_user, NULL);
 	tree_destroy (sess->usertree);
-	tree_destroy (sess->usertree_alpha);
 
 	sess->usertree = NULL;
-	sess->usertree_alpha = NULL;
 	sess->me = NULL;
 
 	sess->ops = 0;
@@ -224,8 +197,8 @@ userlist_find (struct session *sess, const char *name)
 {
 	int pos;
 
-	if (sess->usertree_alpha)
-		return tree_find (sess->usertree_alpha, name,
+	if (sess->usertree)
+		return tree_find (sess->usertree, name,
 								(tree_cmp_func *)find_cmp, sess->server, &pos);
 
 	return NULL;
@@ -248,7 +221,7 @@ userlist_find_global (struct server *serv, char *name)
 		}
 		list = list->next;
 	}
-	return 0;
+	return NULL;
 }
 
 static void
@@ -288,7 +261,7 @@ userlist_update_mode (session *sess, char *name, char mode, char sign)
 
 	/* remove from binary trees, before we loose track of it */
 	tree_remove (sess->usertree, user, &pos);
-	tree_remove (sess->usertree_alpha, user, &pos);
+	fe_userlist_remove (sess, user);
 
 	/* which bit number is affected? */
 	access = mode_access (sess->server, mode, &prefix);
@@ -318,11 +291,8 @@ userlist_update_mode (session *sess, char *name, char mode, char sign)
 	update_counts (sess, user, prefix, level, offset);
 
 	/* insert it back into its new place */
-	tree_insert (sess->usertree_alpha, user);
-	pos = tree_insert (sess->usertree, user);
-
-	/* let GTK move it too */
-	fe_userlist_move (sess, user, pos);
+	tree_insert (sess->usertree, user);
+	fe_userlist_insert (sess, user, FALSE);
 	fe_userlist_numbers (sess);
 }
 
@@ -335,14 +305,12 @@ userlist_change (struct session *sess, char *oldname, char *newname)
 	if (user)
 	{
 		tree_remove (sess->usertree, user, &pos);
-		tree_remove (sess->usertree_alpha, user, &pos);
+		fe_userlist_remove (sess, user);
 
 		safe_strcpy (user->nick, newname, NICKLEN);
 
-		tree_insert (sess->usertree_alpha, user);
-
-		fe_userlist_move (sess, user, tree_insert (sess->usertree, user));
-		fe_userlist_numbers (sess);
+		tree_insert (sess->usertree, user);
+		fe_userlist_insert (sess, user, FALSE);
 
 		return 1;
 	}
@@ -381,7 +349,6 @@ userlist_remove_user (struct session *sess, struct User *user)
 		sess->me = NULL;
 
 	tree_remove (sess->usertree, user, &pos);
-	tree_remove (sess->usertree_alpha, user, &pos);
 	free_user (user, NULL);
 }
 
@@ -397,8 +364,7 @@ userlist_add (struct session *sess, char *name, char *hostname,
 
 	notify_set_online (sess->server, name + prefix_chars, tags_data);
 
-	user = malloc (sizeof (struct User));
-	memset (user, 0, sizeof (struct User));
+	user = g_new0 (struct User, 1);
 
 	user->access = acc;
 
@@ -408,7 +374,7 @@ userlist_add (struct session *sess, char *name, char *hostname,
 
 	/* add it to our linked list */
 	if (hostname)
-		user->hostname = strdup (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))
@@ -417,9 +383,9 @@ userlist_add (struct session *sess, char *name, char *hostname,
 	if (sess->server->have_extjoin)
 	{
 		if (account && *account)
-			user->account = strdup (account);
+			user->account = g_strdup (account);
 		if (realname && *realname)
-			user->realname = strdup (realname);
+			user->realname = g_strdup (realname);
 	}
 
 	row = userlist_insertname (sess, user);
@@ -427,13 +393,10 @@ userlist_add (struct session *sess, char *name, char *hostname,
 	/* duplicate? some broken servers trigger this */
 	if (row == -1)
 	{
-		if (user->hostname)
-			free (user->hostname);
-		if (user->account)
-			free (user->account);
-		if (user->realname)
-			free (user->realname);
-		free (user);
+		g_free (user->hostname);
+		g_free (user->account);
+		g_free (user->realname);
+		g_free (user);
 		return;
 	}
 
@@ -451,7 +414,7 @@ userlist_add (struct session *sess, char *name, char *hostname,
 	if (user->me)
 		sess->me = user;
 
-	fe_userlist_insert (sess, user, row, FALSE);
+	fe_userlist_insert (sess, user, FALSE);
 	fe_userlist_numbers (sess);
 }
 
@@ -465,7 +428,7 @@ rehash_cb (struct User *user, session *sess)
 void
 userlist_rehash (session *sess)
 {
-	tree_foreach (sess->usertree_alpha, (tree_traverse_func *)rehash_cb, sess);
+	tree_foreach (sess->usertree, (tree_traverse_func *)rehash_cb, sess);
 }
 
 static int
@@ -480,7 +443,7 @@ userlist_flat_list (session *sess)
 {
 	GSList *list = NULL;
 
-	tree_foreach (sess->usertree_alpha, (tree_traverse_func *)flat_cb, &list);
+	tree_foreach (sess->usertree, (tree_traverse_func *)flat_cb, &list);
 	return g_slist_reverse (list);
 }
 
@@ -496,6 +459,6 @@ userlist_double_list(session *sess)
 {
 	GList *list = NULL;
 
-	tree_foreach (sess->usertree_alpha, (tree_traverse_func *)double_cb, &list);
+	tree_foreach (sess->usertree, (tree_traverse_func *)double_cb, &list);
 	return list;
 }
diff --git a/src/common/userlist.h b/src/common/userlist.h
index ebf95606..0c53dc71 100644
--- a/src/common/userlist.h
+++ b/src/common/userlist.h
@@ -61,5 +61,7 @@ void userlist_update_mode (session *sess, char *name, char mode, char sign);
 GSList *userlist_flat_list (session *sess);
 GList *userlist_double_list (session *sess);
 void userlist_rehash (session *sess);
+int nick_cmp_az_ops (server *serv, struct User *user1, struct User *user2);
+int nick_cmp_alpha (struct User *user1, struct User *user2, server *serv);
 
 #endif
diff --git a/src/common/util.c b/src/common/util.c
index b5ee1af2..be3dcac2 100644
--- a/src/common/util.c
+++ b/src/common/util.c
@@ -31,7 +31,6 @@
 
 #ifdef WIN32
 #include <sys/timeb.h>
-#include <process.h>
 #include <io.h>
 #include <VersionHelpers.h>
 #else
@@ -41,7 +40,7 @@
 #include <sys/utsname.h>
 #endif
 
-#include "../../config.h"
+#include "config.h"
 #include <fcntl.h>
 #include <errno.h>
 #include "hexchat.h"
@@ -52,9 +51,6 @@
 #if defined (USING_FREEBSD) || defined (__APPLE__)
 #include <sys/sysctl.h>
 #endif
-#ifdef SOCKS
-#include <socks.h>
-#endif
 
 /* SASL mechanisms */
 #ifdef USE_OPENSSL
@@ -67,10 +63,6 @@
 #endif
 #endif
 
-#ifndef HAVE_SNPRINTF
-#define snprintf g_snprintf
-#endif
-
 char *
 file_part (char *file)
 {
@@ -254,11 +246,11 @@ expand_homedir (char *file)
 
 	if (file[0] == '~')
 	{
+		char *slash_pos;
+
 		if (file[1] == '\0' || file[1] == '/')
 			return g_strconcat (g_get_home_dir (), &file[1], NULL);
 
-		char *slash_pos;
-
 		user = g_strdup(file);
 
 		slash_pos = strchr(user, '/');
@@ -370,13 +362,13 @@ strip_hidden_attribute (char *src, char *dst)
 	return len;
 }
 
-#if defined (USING_LINUX) || defined (USING_FREEBSD) || defined (__APPLE__)
+#if defined (USING_LINUX) || defined (USING_FREEBSD) || defined (__APPLE__) || defined (__CYGWIN__)
 
 static void
 get_cpu_info (double *mhz, int *cpus)
 {
 
-#ifdef USING_LINUX
+#if defined(USING_LINUX) || defined (__CYGWIN__)
 
 	char buf[256];
 	int fh;
@@ -508,7 +500,22 @@ get_sys_str (int with_cpu)
 	static char winver[20];
 	double mhz;
 
-	if (IsWindows8Point1OrGreater ())
+	/* Broken since major bumped to 10, should start to work eventually.
+	 * No, IsWindowsVersionOrGreater (10, 0, 0) doesn't work either.
+	 * TODO: replace with IsWindows10OrGreater() once added to the SDK.
+	 */
+	if (IsWindowsVersionOrGreater (6, 4, 0))
+	{
+		if (IsWindowsServer ())
+		{
+			strcpy (winver, "Server 10");
+		}
+		else
+		{
+			strcpy (winver, "10");
+		}
+	}
+	else if (IsWindows8Point1OrGreater ())
 	{
 		if (IsWindowsServer ())
 		{
@@ -610,7 +617,7 @@ get_sys_str (int with_cpu)
 char *
 get_sys_str (int with_cpu)
 {
-#if defined (USING_LINUX) || defined (USING_FREEBSD) || defined (__APPLE__)
+#if defined (USING_LINUX) || defined (USING_FREEBSD) || defined (__APPLE__) || defined (__CYGWIN__)
 	double mhz;
 #endif
 	int cpus = 1;
@@ -620,24 +627,24 @@ get_sys_str (int with_cpu)
 	if (buf)
 		return buf;
 
-	buf = malloc (128);
-
 	uname (&un);
 
-#if defined (USING_LINUX) || defined (USING_FREEBSD) || defined (__APPLE__)
+#if defined (USING_LINUX) || defined (USING_FREEBSD) || defined (__APPLE__) || defined (__CYGWIN__)
 	get_cpu_info (&mhz, &cpus);
 	if (mhz && with_cpu)
 	{
 		double cpuspeed = ( mhz > 1000 ) ? mhz / 1000 : mhz;
 		const char *cpuspeedstr = ( mhz > 1000 ) ? "GHz" : "MHz";
-		snprintf (buf, 128,
-					(cpus == 1) ? "%s %s [%s/%.2f%s]" : "%s %s [%s/%.2f%s/SMP]",
-					un.sysname, un.release, un.machine,
-					cpuspeed, cpuspeedstr);
+		buf = g_strdup_printf (
+			(cpus == 1) ? "%s %s [%s/%.2f%s]" : "%s %s [%s/%.2f%s/SMP]",
+			un.sysname, un.release, un.machine,
+			cpuspeed, cpuspeedstr);
 	}
 	else
+		buf = g_strdup_printf ("%s %s", un.sysname, un.release);
+#else
+	buf = g_strdup_printf ("%s %s", un.sysname, un.release);
 #endif
-		snprintf (buf, 128, "%s %s", un.sysname, un.release);
 
 	return buf;
 }
@@ -1217,80 +1224,6 @@ const unsigned char rfc_tolowertab[] =
 	0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
 };
 
-/*static unsigned char touppertab[] =
-	{ 0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa,
-	0xb, 0xc, 0xd, 0xe, 0xf, 0x10, 0x11, 0x12, 0x13, 0x14,
-	0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d,
-	0x1e, 0x1f,
-	' ', '!', '"', '#', '$', '%', '&', 0x27, '(', ')',
-	'*', '+', ',', '-', '.', '/',
-	'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
-	':', ';', '<', '=', '>', '?',
-	'@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I',
-	'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S',
-	'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^',
-	0x5f,
-	'`', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I',
-	'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S',
-	'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^',
-	0x7f,
-	0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
-	0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
-	0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99,
-	0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
-	0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9,
-	0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
-	0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9,
-	0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
-	0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9,
-	0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
-	0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9,
-	0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
-	0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9,
-	0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
-	0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9,
-	0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
-};*/
-
-/*static int
-rename_utf8 (char *oldname, char *newname)
-{
-	int sav, res;
-	char *fso, *fsn;
-
-	fso = hexchat_filename_from_utf8 (oldname, -1, 0, 0, 0);
-	if (!fso)
-		return FALSE;
-	fsn = hexchat_filename_from_utf8 (newname, -1, 0, 0, 0);
-	if (!fsn)
-	{
-		g_free (fso);
-		return FALSE;
-	}
-
-	res = rename (fso, fsn);
-	sav = errno;
-	g_free (fso);
-	g_free (fsn);
-	errno = sav;
-	return res;
-}
-
-static int
-unlink_utf8 (char *fname)
-{
-	int res;
-	char *fs;
-
-	fs = hexchat_filename_from_utf8 (fname, -1, 0, 0, 0);
-	if (!fs)
-		return FALSE;
-
-	res = unlink (fs);
-	g_free (fs);
-	return res;
-}*/
-
 static gboolean
 file_exists (char *fname)
 {
@@ -1526,7 +1459,7 @@ canonalize_key (char *key)
 }
 
 int
-portable_mode ()
+portable_mode (void)
 {
 #ifdef WIN32
 	if ((_access( "portable-mode", 0 )) != -1)
@@ -1543,7 +1476,7 @@ portable_mode ()
 }
 
 int
-unity_mode ()
+unity_mode (void)
 {
 #ifdef G_OS_UNIX
 	const char *env = g_getenv("XDG_CURRENT_DESKTOP");
@@ -1578,7 +1511,7 @@ parse_dh (char *str, DH **dh_out, unsigned char **secret_out, int *keysize_out)
 {
 	DH *dh;
 	guchar *data, *decoded_data;
-	guchar *secret;
+	guchar *secret = NULL;
 	gsize data_len;
 	guint size;
 	guint16 size16;
@@ -1630,7 +1563,7 @@ parse_dh (char *str, DH **dh_out, unsigned char **secret_out, int *keysize_out)
 	if (!(DH_generate_key (dh)))
 		goto fail;
 
-	secret = (unsigned char*)malloc (DH_size(dh));
+	secret = g_malloc (DH_size (dh));
 	key_size = DH_compute_key (secret, pubkey, dh);
 	if (key_size == -1)
 		goto fail;
@@ -1643,8 +1576,9 @@ parse_dh (char *str, DH **dh_out, unsigned char **secret_out, int *keysize_out)
 	return 1;
 
 fail:
-	if (decoded_data)
-		g_free (decoded_data);
+	g_free (secret);
+	g_free (decoded_data);
+
 	return 0;
 }
 
@@ -1652,7 +1586,7 @@ char *
 encode_sasl_pass_blowfish (char *user, char *pass, char *data)
 {
 	DH *dh;
-	char *response, *ret;
+	char *response, *ret = NULL;
 	unsigned char *secret;
 	unsigned char *encrypted_pass;
 	char *plain_pass;
@@ -1667,11 +1601,9 @@ encode_sasl_pass_blowfish (char *user, char *pass, char *data)
 		return NULL;
 	BF_set_key (&key, key_size, secret);
 
-	encrypted_pass = (guchar*)malloc (pass_len);
-	memset (encrypted_pass, 0, pass_len);
-	plain_pass = (char*)malloc (pass_len);
-	memset (plain_pass, 0, pass_len);
-	memcpy (plain_pass, pass, pass_len);
+	encrypted_pass = g_malloc0 (pass_len);
+	plain_pass = g_malloc0 (pass_len);
+	memcpy (plain_pass, pass, strlen(pass));
 	out_ptr = (char*)encrypted_pass;
 	in_ptr = (char*)plain_pass;
 
@@ -1680,7 +1612,7 @@ encode_sasl_pass_blowfish (char *user, char *pass, char *data)
 
 	/* Create response */
 	length = 2 + BN_num_bytes (dh->pub_key) + pass_len + user_len + 1;
-	response = (char*)malloc (length);
+	response = g_malloc0 (length);
 	out_ptr = response;
 
 	/* our key */
@@ -1699,11 +1631,12 @@ encode_sasl_pass_blowfish (char *user, char *pass, char *data)
 	
 	ret = g_base64_encode ((const guchar*)response, length);
 
-	DH_free (dh);
-	free (plain_pass);
-	free (encrypted_pass);
-	free (secret);
-	free (response);
+	g_free (response);
+
+	DH_free(dh);
+	g_free (plain_pass);
+	g_free (encrypted_pass);
+	g_free (secret);
 
 	return ret;
 }
@@ -1729,10 +1662,8 @@ encode_sasl_pass_aes (char *user, char *pass, char *data)
 	if (!parse_dh (data, &dh, &secret, &key_size))
 		return NULL;
 
-	encrypted_userpass = (guchar*)malloc (userpass_len);
-	memset (encrypted_userpass, 0, userpass_len);
-	plain_userpass = (guchar*)malloc (userpass_len);
-	memset (plain_userpass, 0, userpass_len);
+	encrypted_userpass = g_malloc0 (userpass_len);
+	plain_userpass = g_malloc0 (userpass_len);
 
 	/* create message */
 	/* format of: <username>\0<password>\0<padding> */
@@ -1763,7 +1694,7 @@ encode_sasl_pass_aes (char *user, char *pass, char *data)
 	/* Create response */
 	/* format of:  <size pubkey><pubkey><iv (always 16 bytes)><ciphertext> */
 	length = 2 + key_size + sizeof(iv) + userpass_len;
-	response = (char*)malloc (length);
+	response = g_malloc (length);
 	out_ptr = response;
 
 	/* our key */
@@ -1784,11 +1715,10 @@ encode_sasl_pass_aes (char *user, char *pass, char *data)
 
 end:
 	DH_free (dh);
-	free (plain_userpass);
-	free (encrypted_userpass);
-	free (secret);
-	if (response)
-		free (response);
+	g_free (plain_userpass);
+	g_free (encrypted_userpass);
+	g_free (secret);
+	g_free (response);
 
 	return ret;
 }
@@ -1866,9 +1796,7 @@ challengeauth_response (char *username, char *password, char *challenge)
 		g_string_append_printf (buf, "%02x", (unsigned int) digest[i]);
 	}
 
-	digest = (unsigned char *) g_string_free (buf, FALSE);
-
-	return (char *) digest;
+	return g_string_free (buf, FALSE);
 }
 #endif
 
diff --git a/src/common/util.h b/src/common/util.h
index 5231e56d..2c9f790c 100644
--- a/src/common/util.h
+++ b/src/common/util.h
@@ -73,12 +73,12 @@ guint32 str_hash (const char *key);
 guint32 str_ihash (const unsigned char *key);
 void safe_strcpy (char *dest, const char *src, int bytes_left);
 void canonalize_key (char *key);
-int portable_mode ();
-int unity_mode ();
+int portable_mode (void);
+int unity_mode (void);
 char *encode_sasl_pass_plain (char *user, char *pass);
 char *encode_sasl_pass_blowfish (char *user, char *pass, char *data);
 char *encode_sasl_pass_aes (char *user, char *pass, char *data);
 char *challengeauth_response (char *username, char *password, char *challenge);
 size_t strftime_validated (char *dest, size_t destsize, const char *format, const struct tm *time);
-size_t strftime_utf8 (char *dest, size_t destsize, const char *format, time_t time);
+gsize strftime_utf8 (char *dest, gsize destsize, const char *format, time_t time);
 #endif
diff --git a/src/dirent/dirent-win32.h b/src/dirent/dirent-win32.h
index cf3fe567..d1954c6b 100644
--- a/src/dirent/dirent-win32.h
+++ b/src/dirent/dirent-win32.h
@@ -198,13 +198,13 @@
  * only defined for compatibility.  These macros should always return false
  * on Windows.
  */
-#define	S_ISFIFO(mode) (((mode) & S_IFMT) == S_IFIFO)
-#define	S_ISDIR(mode)  (((mode) & S_IFMT) == S_IFDIR)
-#define	S_ISREG(mode)  (((mode) & S_IFMT) == S_IFREG)
-#define	S_ISLNK(mode)  (((mode) & S_IFMT) == S_IFLNK)
-#define	S_ISSOCK(mode) (((mode) & S_IFMT) == S_IFSOCK)
-#define	S_ISCHR(mode)  (((mode) & S_IFMT) == S_IFCHR)
-#define	S_ISBLK(mode)  (((mode) & S_IFMT) == S_IFBLK)
+#define S_ISFIFO(mode) (((mode) & S_IFMT) == S_IFIFO)
+#define S_ISDIR(mode)  (((mode) & S_IFMT) == S_IFDIR)
+#define S_ISREG(mode)  (((mode) & S_IFMT) == S_IFREG)
+#define S_ISLNK(mode)  (((mode) & S_IFMT) == S_IFLNK)
+#define S_ISSOCK(mode) (((mode) & S_IFMT) == S_IFSOCK)
+#define S_ISCHR(mode)  (((mode) & S_IFMT) == S_IFCHR)
+#define S_ISBLK(mode)  (((mode) & S_IFMT) == S_IFBLK)
 
 /* Return the exact length of d_namlen without zero terminator */
 #define _D_EXACT_NAMLEN(p) ((p)->d_namlen)
@@ -305,6 +305,7 @@ _wopendir(
 {
     _WDIR *dirp = NULL;
     int error;
+    DWORD n;
 
     /* Must have directory name */
     if (dirname == NULL  ||  dirname[0] == '\0') {
@@ -313,73 +314,58 @@ _wopendir(
     }
 
     /* Allocate new _WDIR structure */
-    dirp = (_WDIR*) malloc (sizeof (struct _WDIR));
-    if (dirp != NULL) {
-        DWORD n;
-
-        /* Reset _WDIR structure */
-        dirp->handle = INVALID_HANDLE_VALUE;
-        dirp->patt = NULL;
-        dirp->cached = 0;
-
-        /* Compute the length of full path plus zero terminator */
-        n = GetFullPathNameW (dirname, 0, NULL, NULL);
-
-        /* Allocate room for absolute directory name and search pattern */
-        dirp->patt = (wchar_t*) malloc (sizeof (wchar_t) * n + 16);
-        if (dirp->patt) {
-
-            /*
-             * Convert relative directory name to an absolute one.  This
-             * allows rewinddir() to function correctly even when current
-             * working directory is changed between opendir() and rewinddir().
-             */
-            n = GetFullPathNameW (dirname, n, dirp->patt, NULL);
-            if (n > 0) {
-                wchar_t *p;
-
-                /* Append search pattern \* to the directory name */
-                p = dirp->patt + n;
-                if (dirp->patt < p) {
-                    switch (p[-1]) {
-                    case '\\':
-                    case '/':
-                    case ':':
-                        /* Directory ends in path separator, e.g. c:\temp\ */
-                        /*NOP*/;
-                        break;
-
-                    default:
-                        /* Directory name doesn't end in path separator */
-                        *p++ = '\\';
-                    }
-                }
-                *p++ = '*';
-                *p = '\0';
-
-                /* Open directory stream and retrieve the first entry */
-                if (dirent_first (dirp)) {
-                    /* Directory stream opened successfully */
-                    error = 0;
-                } else {
-                    /* Cannot retrieve first entry */
-                    error = 1;
-                    dirent_set_errno (ENOENT);
-                }
-
-            } else {
-                /* Cannot retrieve full path name */
-                dirent_set_errno (ENOENT);
-                error = 1;
+    dirp = (_WDIR*) g_new (struct _WDIR, 1);
+    /* Reset _WDIR structure */
+    dirp->handle = INVALID_HANDLE_VALUE;
+    dirp->patt = NULL;
+    dirp->cached = 0;
+
+    /* Compute the length of full path plus zero terminator */
+    n = GetFullPathNameW (dirname, 0, NULL, NULL);
+
+    /* Allocate room for absolute directory name and search pattern */
+    dirp->patt = g_malloc (sizeof (wchar_t) * n + 16);
+    /*
+    * Convert relative directory name to an absolute one.  This
+    * allows rewinddir() to function correctly even when current
+    * working directory is changed between opendir() and rewinddir().
+    */
+    n = GetFullPathNameW (dirname, n, dirp->patt, NULL);
+    if (n > 0) {
+        wchar_t *p;
+
+        /* Append search pattern \* to the directory name */
+        p = dirp->patt + n;
+        if (dirp->patt < p) {
+            switch (p[-1]) {
+            case '\\':
+            case '/':
+            case ':':
+                /* Directory ends in path separator, e.g. c:\temp\ */
+                /*NOP*/;
+                break;
+
+            default:
+                /* Directory name doesn't end in path separator */
+                *p++ = '\\';
             }
+        }
+        *p++ = '*';
+        *p = '\0';
 
+        /* Open directory stream and retrieve the first entry */
+        if (dirent_first (dirp)) {
+            /* Directory stream opened successfully */
+            error = 0;
         } else {
-            /* Cannot allocate memory for search pattern */
+            /* Cannot retrieve first entry */
             error = 1;
+            dirent_set_errno (ENOENT);
         }
 
     } else {
-        /* Cannot allocate _WDIR structure */
+        /* Cannot retrieve full path name */
+        dirent_set_errno (ENOENT);
         error = 1;
     }
 
@@ -472,13 +458,11 @@ _wclosedir(
         }
 
         /* Release search pattern */
-        if (dirp->patt) {
-            free (dirp->patt);
-            dirp->patt = NULL;
-        }
+        g_free (dirp->patt);
+        dirp->patt = NULL;
 
         /* Release directory structure */
-        free (dirp);
+        g_free (dirp);
         ok = /*success*/0;
 
     } else {
@@ -579,6 +563,8 @@ opendir(
 {
     struct DIR *dirp;
     int error;
+    wchar_t wname[PATH_MAX + 1];
+    size_t n;
 
     /* Must have directory name */
     if (dirname == NULL  ||  dirname[0] == '\0') {
@@ -587,44 +573,36 @@ opendir(
     }
 
     /* Allocate memory for DIR structure */
-    dirp = (DIR*) malloc (sizeof (struct DIR));
-    if (dirp) {
-        wchar_t wname[PATH_MAX + 1];
-        size_t n;
-
-        /* Convert directory name to wide-character string */
-        error = dirent_mbstowcs_s(
-            &n, wname, PATH_MAX + 1, dirname, PATH_MAX);
-        if (!error) {
-
-            /* Open directory stream using wide-character name */
-            dirp->wdirp = _wopendir (wname);
-            if (dirp->wdirp) {
-                /* Directory stream opened */
-                error = 0;
-            } else {
-                /* Failed to open directory stream */
-                error = 1;
-            }
-
+    dirp = (DIR*) g_new (struct DIR, 1);
+
+    /* Convert directory name to wide-character string */
+    error = dirent_mbstowcs_s(
+        &n, wname, PATH_MAX + 1, dirname, PATH_MAX);
+    if (!error) {
+
+        /* Open directory stream using wide-character name */
+        dirp->wdirp = _wopendir (wname);
+        if (dirp->wdirp) {
+            /* Directory stream opened */
+            error = 0;
         } else {
-            /* 
-             * Cannot convert file name to wide-character string.  This
-             * occurs if the string contains invalid multi-byte sequences or
-             * the output buffer is too small to contain the resulting
-             * string.
-             */
+            /* Failed to open directory stream */
             error = 1;
         }
 
     } else {
-        /* Cannot allocate DIR structure */
+        /* 
+            * Cannot convert file name to wide-character string.  This
+            * occurs if the string contains invalid multi-byte sequences or
+            * the output buffer is too small to contain the resulting
+            * string.
+            */
         error = 1;
     }
 
     /* Clean up in case of error */
-    if (error  &&  dirp) {
-        free (dirp);
+    if (error != 0) {
+        g_free (dirp);
         dirp = NULL;
     }
 
@@ -733,14 +711,14 @@ closedir(
     DIR *dirp) 
 {
     int ok;
-    if (dirp) {
+    if (dirp != NULL) {
 
         /* Close wide-character directory stream */
         ok = _wclosedir (dirp->wdirp);
         dirp->wdirp = NULL;
 
         /* Release multi-byte character version */
-        free (dirp);
+        g_free (dirp);
 
     } else {
 
diff --git a/src/fe-gtk/Makefile.am b/src/fe-gtk/Makefile.am
index a8f43ac5..71179853 100644
--- a/src/fe-gtk/Makefile.am
+++ b/src/fe-gtk/Makefile.am
@@ -1,3 +1,6 @@
+
+include $(top_srcdir)/m4/clang-analyze.am
+
 localedir = $(datadir)/locale
 
 bin_PROGRAMS = hexchat
@@ -9,7 +12,7 @@ hexchat_LDADD = ../common/libhexchatcommon.a $(GUI_LIBS)
 EXTRA_DIST = \
 	ascii.h banlist.h chanlist.h chanview.h chanview-tabs.c \
 	chanview-tree.c custom-list.h editlist.h fe-gtk.h fkeys.h gtkutil.h joind.h \
-	maingui.h menu.h notifygui.h palette.h pixmaps.h \
+	maingui.h menu.h notifygui.h  notifications palette.h pixmaps.h plugin-notification.h \
 	plugin-tray.h plugingui.c plugingui.h rawlog.h sexy-iso-codes.h \
 	sexy-spell-entry.h textgui.h urlgrab.h userlistgui.h xtext.h \
 	../../data/hexchat.gresource.xml
@@ -26,12 +29,29 @@ if HAVE_ISO_CODES
 iso_codes_c = sexy-iso-codes.c
 endif
 
+if USE_LIBNOTIFY
+notify_c = notifications/notification-libnotify.c
+else
+if HAVE_GTK_MAC
+notify_c = notifications/notification-osx.m
+hexchat_LDFLAGS = -framework Foundation
+else
+notify_c = notifications/notification-dummy.c
+endif
+endif
+
 hexchat_SOURCES = ascii.c banlist.c chanlist.c chanview.c custom-list.c \
 	dccgui.c editlist.c fe-gtk.c fkeys.c gtkutil.c ignoregui.c joind.c menu.c \
-	maingui.c notifygui.c palette.c pixmaps.c plugin-tray.c $(plugingui_c) \
-	rawlog.c resources.c servlistgui.c setup.c $(iso_codes_c) \
+	maingui.c notifygui.c $(notify_c) palette.c pixmaps.c plugin-tray.c $(plugingui_c) \
+	plugin-notification.c rawlog.c resources.c servlistgui.c setup.c $(iso_codes_c) \
 	sexy-spell-entry.c textgui.c urlgrab.c userlistgui.c xtext.c
 hexchat_CPPFLAGS = $(AM_CPPFLAGS) -I$(top_builddir)/src/common
 
 resources.c: $(top_srcdir)/data/hexchat.gresource.xml $(shell $(GLIB_COMPILE_RESOURCES) --sourcedir=$(top_srcdir)/data --generate-dependencies $(top_srcdir)/data/hexchat.gresource.xml)
 	$(AM_V_GEN) $(GLIB_COMPILE_RESOURCES) --target=$@ --sourcedir=$(top_srcdir)/data --generate-source $<
+
+if DO_STATIC_ANALYSIS
+analyze_plists = $(hexchat_SOURCES:%.c=%.plist)
+all-local: $(analyze_plists)
+MOSTLYCLEANFILES = $(analyze_plists)
+endif
diff --git a/src/fe-gtk/banlist.c b/src/fe-gtk/banlist.c
index d6f44811..e10aaa67 100644
--- a/src/fe-gtk/banlist.c
+++ b/src/fe-gtk/banlist.c
@@ -491,7 +491,7 @@ banlist_unban_inner (gpointer none, banlist_info *banl, int mode_num)
 	if (!gtk_tree_model_get_iter_first (model, &iter))
 		return 0;
 
-	masks = g_malloc (sizeof (char *) * banl->line_ct);
+	masks = g_new (char *, banl->line_ct);
 	num_sel = 0;
 	do
 	{
@@ -577,17 +577,17 @@ static void
 banlist_add_selected_cb (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
 {
 	GSList **lp = data;
-	GSList *list = NULL;
 	GtkTreeIter *copy;
 
-	if (!lp) return;
-	list = *lp;
-	copy = g_malloc (sizeof (GtkTreeIter));
-	g_return_if_fail (copy != NULL);
+	if (lp == NULL)
+	{
+		return;
+	}
+
+	copy = g_new (GtkTreeIter, 1);
 	*copy = *iter;
 
-	list = g_slist_append (list, copy);
-	*(GSList **)data = list;
+	*lp = g_slist_append (*lp, copy);
 }
 
 static void
@@ -786,14 +786,9 @@ banlist_opengui (struct session *sess)
 		return;
 	}
 
-	if (!sess->res->banlist)
+	if (sess->res->banlist == NULL)
 	{
-		sess->res->banlist = g_malloc0 (sizeof (banlist_info));
-		if (!sess->res->banlist)
-		{
-			fe_message (_("Banlist initialization failed."), FE_MSG_ERROR);
-			return;
-		}
+		sess->res->banlist = g_new0 (banlist_info, 1);
 	}
 	banl = sess->res->banlist;
 	if (banl->window)
diff --git a/src/fe-gtk/chanlist.c b/src/fe-gtk/chanlist.c
index f3a2259d..f6ef46f3 100644
--- a/src/fe-gtk/chanlist.c
+++ b/src/fe-gtk/chanlist.c
@@ -94,7 +94,7 @@ chanlist_update_caption (server *serv)
 {
 	gchar tbuf[256];
 
-	snprintf (tbuf, sizeof tbuf,
+	g_snprintf (tbuf, sizeof tbuf,
 				 _("Displaying %d/%d users on %d/%d channels."),
 				 serv->gui->chanlist_users_shown_count,
 				 serv->gui->chanlist_users_found_count,
@@ -148,7 +148,7 @@ chanlist_data_free (server *serv)
 			data = rows->data;
 			g_free (data->topic);
 			g_free (data->collation_key);
-			free (data);
+			g_free (data);
 		}
 
 		g_slist_free (serv->gui->chanlist_data_stored_rows);
@@ -370,7 +370,7 @@ fe_add_chan_list (server *serv, char *chan, char *users, char *topic)
 	int len = strlen (chan) + 1;
 
 	/* we allocate the struct and channel string in one go */
-	next_row = malloc (sizeof (chanlistrow) + len);
+	next_row = g_malloc (sizeof (chanlistrow) + len);
 	memcpy (((char *)next_row) + sizeof (chanlistrow), chan, len);
 	next_row->topic = strip_color (topic, -1, STRIP_ALL);
 	next_row->collation_key = g_utf8_collate_key (chan, len-1);
@@ -456,7 +456,7 @@ chanlist_join (GtkWidget * wid, server *serv)
 	{
 		if (serv->connected && (strcmp (chan, "*") != 0))
 		{
-			snprintf (tbuf, sizeof (tbuf), "join %s", chan);
+			g_snprintf (tbuf, sizeof (tbuf), "join %s", chan);
 			handle_command (serv->server_session, tbuf, FALSE);
 		} else
 			gdk_beep ();
@@ -482,7 +482,7 @@ chanlist_filereq_done (server *serv, char *file)
 	if (fh == -1)
 		return;
 
-	snprintf (buf, sizeof buf, "HexChat Channel List: %s - %s\n",
+	g_snprintf (buf, sizeof buf, "HexChat Channel List: %s - %s\n",
 				 serv->servername, ctime (&t));
 	write (fh, buf, strlen (buf));
 
@@ -494,7 +494,7 @@ chanlist_filereq_done (server *serv, char *file)
 									  COL_CHANNEL, &chan,
 									  COL_USERS, &users,
 									  COL_TOPIC, &topic, -1);
-			snprintf (buf, sizeof buf, "%-16s %-5d%s\n", chan, users, topic);
+			g_snprintf (buf, sizeof buf, "%-16s %-5d%s\n", chan, users, topic);
 			g_free (chan);
 			g_free (topic);
 			write (fh, buf, strlen (buf));
@@ -717,7 +717,7 @@ chanlist_opengui (server *serv, int do_refresh)
 		return;
 	}
 
-	snprintf (tbuf, sizeof tbuf, _(DISPLAY_NAME": Channel List (%s)"),
+	g_snprintf (tbuf, sizeof tbuf, _(DISPLAY_NAME": Channel List (%s)"),
 				 server_get_network (serv, TRUE));
 
 	serv->gui->chanlist_pending_rows = NULL;
diff --git a/src/fe-gtk/chanview-tabs.c b/src/fe-gtk/chanview-tabs.c
index 8f940c24..5681f9d6 100644
--- a/src/fe-gtk/chanview-tabs.c
+++ b/src/fe-gtk/chanview-tabs.c
@@ -62,12 +62,13 @@ cv_tabs_sizerequest (GtkWidget *viewport, GtkRequisition *requisition, chanview
 static void
 cv_tabs_sizealloc (GtkWidget *widget, GtkAllocation *allocation, chanview *cv)
 {
+	GdkWindow *parent_win;
 	GtkAdjustment *adj;
 	GtkWidget *inner;
 	gint viewport_size;
 
 	inner = ((tabview *)cv)->inner;
-	GdkWindow *parent_win = gtk_widget_get_window (gtk_widget_get_parent (inner));
+	parent_win = gtk_widget_get_window (gtk_widget_get_parent (inner));
 
 	if (cv->vertical)
 	{
@@ -141,7 +142,7 @@ tab_scroll_left_up_clicked (GtkWidget *widget, chanview *cv)
 	gfloat new_value;
 	GtkWidget *inner;
 	GdkWindow *parent_win;
-	gfloat i;
+	gdouble i;
 
 	inner = ((tabview *)cv)->inner;
 	parent_win = gtk_widget_get_window (gtk_widget_get_parent (inner));
@@ -190,7 +191,7 @@ tab_scroll_right_down_clicked (GtkWidget *widget, chanview *cv)
 	gfloat new_value;
 	GtkWidget *inner;
 	GdkWindow *parent_win;
-	gfloat i;
+	gdouble i;
 
 	inner = ((tabview *)cv)->inner;
 	parent_win = gtk_widget_get_window (gtk_widget_get_parent (inner));
diff --git a/src/fe-gtk/chanview.c b/src/fe-gtk/chanview.c
index 4c50d922..e5556d9f 100644
--- a/src/fe-gtk/chanview.c
+++ b/src/fe-gtk/chanview.c
@@ -111,9 +111,8 @@ truncate_tab_name (char *name, int max)
 	if (max > 2 && g_utf8_strlen (name, -1) > max)
 	{
 		/* truncate long channel names */
-		buf = malloc (strlen (name) + 4);
-		strcpy (buf, name);
-		g_utf8_offset_to_pointer (buf, max)[0] = 0;
+		buf = g_malloc (strlen (name) + 4);
+		g_utf8_strncpy (buf, name, max);
 		strcat (buf, "..");
 		return buf;
 	}
@@ -231,7 +230,7 @@ chanview_free_ch (chanview *cv, GtkTreeIter *iter)
 	chan *ch;
 
 	gtk_tree_model_get (GTK_TREE_MODEL (cv->store), iter, COL_CHAN, &ch, -1);
-	free (ch);
+	g_free (ch);
 }
 
 static void
@@ -251,7 +250,7 @@ chanview_destroy (chanview *cv)
 		gtk_widget_destroy (cv->box);
 
 	chanview_destroy_store (cv);
-	free (cv);
+	g_free (cv);
 }
 
 static void
@@ -267,7 +266,7 @@ chanview_new (int type, int trunc_len, gboolean sort, gboolean use_icons,
 {
 	chanview *cv;
 
-	cv = calloc (1, sizeof (chanview));
+	cv = g_new0 (chanview, 1);
 	cv->store = gtk_tree_store_new (4, G_TYPE_STRING, G_TYPE_POINTER,
 											  PANGO_TYPE_ATTR_LIST, GDK_TYPE_PIXBUF);
 	cv->style = style;
@@ -368,7 +367,7 @@ chanview_add_real (chanview *cv, char *name, void *family, void *userdata,
 
 	if (!ch)
 	{
-		ch = calloc (1, sizeof (chan));
+		ch = g_new0 (chan, 1);
 		ch->userdata = userdata;
 		ch->family = family;
 		ch->cv = cv;
@@ -401,7 +400,7 @@ chanview_add (chanview *cv, char *name, void *family, void *userdata, gboolean a
 	ret = chanview_add_real (cv, new_name, family, userdata, allow_closure, tag, icon, NULL, NULL);
 
 	if (new_name != name)
-		free (new_name);
+		g_free (new_name);
 
 	return ret;
 }
@@ -492,7 +491,7 @@ chan_rename (chan *ch, char *name, int trunc_len)
 	ch->cv->trunc_len = trunc_len;
 
 	if (new_name != name)
-		free (new_name);
+		g_free (new_name);
 }
 
 /* this thing is overly complicated */
@@ -645,7 +644,7 @@ chan_remove (chan *ch, gboolean force)
 
 	ch->cv->size--;
 	gtk_tree_store_remove (ch->cv->store, &ch->iter);
-	free (ch);
+	g_free (ch);
 	return TRUE;
 }
 
diff --git a/src/fe-gtk/custom-list.c b/src/fe-gtk/custom-list.c
index a954b4a0..f1241947 100644
--- a/src/fe-gtk/custom-list.c
+++ b/src/fe-gtk/custom-list.c
@@ -134,7 +134,6 @@ custom_list_get_type (void)
 		return custom_list_type;
 
 	/* Some boilerplate type registration stuff */
-	if (1)
 	{
 		static const GTypeInfo custom_list_info = {
 			sizeof (CustomListClass),
@@ -154,7 +153,6 @@ custom_list_get_type (void)
 	}
 
 	/* Here we register our GtkTreeModel interface with the type system */
-	if (1)
 	{
 		static const GInterfaceInfo tree_model_info = {
 			(GInterfaceInitFunc) custom_list_tree_model_init,
@@ -167,7 +165,6 @@ custom_list_get_type (void)
 	}
 
 	/* Add GtkTreeSortable interface */
-	if (1)
 	{
 		static const GInterfaceInfo tree_sortable_info = {
 			(GInterfaceInitFunc) custom_list_sortable_init,
@@ -336,7 +333,7 @@ custom_list_get_iter (GtkTreeModel * tree_model,
 	gint n;
 
 	n = gtk_tree_path_get_indices (path)[0];
-	if (n >= custom_list->num_rows || n < 0)
+	if (n < 0 || (guint) n >= custom_list->num_rows)
 		return FALSE;
 
 	record = custom_list->rows[n];
@@ -533,7 +530,7 @@ custom_list_iter_nth_child (GtkTreeModel * tree_model,
 		return FALSE;
 
 	/* special case: if parent == NULL, set iter to n-th top-level row */
-	if (n >= custom_list->num_rows)
+	if (n < 0 || (guint) n >= custom_list->num_rows)
 		return FALSE;
 
 	iter->user_data = custom_list->rows[n];
@@ -730,7 +727,7 @@ custom_list_resort (CustomList * custom_list)
 							 custom_list);
 
 	/* let other objects know about the new order */
-	neworder = malloc (sizeof (gint) * custom_list->num_rows);
+	neworder = g_new (gint, custom_list->num_rows);
 
 	for (i = custom_list->num_rows - 1; i >= 0; i--)
 	{
@@ -747,7 +744,7 @@ custom_list_resort (CustomList * custom_list)
 	gtk_tree_model_rows_reordered (GTK_TREE_MODEL (custom_list), path, NULL,
 											 neworder);
 	gtk_tree_path_free (path);
-	free (neworder);
+	g_free (neworder);
 }
 
 void
diff --git a/src/fe-gtk/custom-list.h b/src/fe-gtk/custom-list.h
index 64f0535f..30a73919 100644
--- a/src/fe-gtk/custom-list.h
+++ b/src/fe-gtk/custom-list.h
@@ -77,10 +77,10 @@ struct _CustomList
 {
 	GObject parent;
 
-	guint num_rows;				  /* number of rows that we have used */
-	guint num_alloc;					/* number of rows allocated */
-	chanlistrow **rows;			  /* a dynamically allocated array of pointers to the
-										   *  CustomRecord structure for each row */
+	guint num_rows;     /* number of rows that we have used */
+	guint num_alloc;    /* number of rows allocated */
+	chanlistrow **rows; /* a dynamically allocated array of pointers to the
+	                     * CustomRecord structure for each row */
 
 	gint n_columns;
 	GType column_types[CUSTOM_LIST_N_COLUMNS];
diff --git a/src/fe-gtk/dccgui.c b/src/fe-gtk/dccgui.c
index 10ec8388..8c9dc8b4 100644
--- a/src/fe-gtk/dccgui.c
+++ b/src/fe-gtk/dccgui.c
@@ -88,7 +88,7 @@ struct my_dcc_send
 {
 	struct session *sess;
 	char *nick;
-	int maxcps;
+	gint64 maxcps;
 	int passive;
 };
 
@@ -105,7 +105,7 @@ static short view_mode;	/* 1=download 2=upload 3=both */
 
 
 static void
-proper_unit (DCC_SIZE size, char *buf, int buf_len)
+proper_unit (guint64 size, char *buf, size_t buf_len)
 {
 	gchar *formatted_str;
 	GFormatSizeFlags format_flags = G_FORMAT_SIZE_DEFAULT;
@@ -117,7 +117,7 @@ proper_unit (DCC_SIZE size, char *buf, int buf_len)
 		format_flags = G_FORMAT_SIZE_IEC_UNITS;
 #endif
 
-	formatted_str = g_format_size_full ((guint64)size, format_flags);
+	formatted_str = g_format_size_full (size, format_flags);
 	g_strlcpy (buf, formatted_str, buf_len);
 
 	g_free (formatted_str);
@@ -130,45 +130,45 @@ dcc_send_filereq_file (struct my_dcc_send *mdc, char *file)
 		dcc_send (mdc->sess, mdc->nick, file, mdc->maxcps, mdc->passive);
 	else
 	{
-		free (mdc->nick);
-		free (mdc);
+		g_free (mdc->nick);
+		g_free (mdc);
 	}
 }
 
 void
 fe_dcc_send_filereq (struct session *sess, char *nick, int maxcps, int passive)
 {
-	char tbuf[128];
-	struct my_dcc_send *mdc;
-	
-	mdc = malloc (sizeof (*mdc));
+	char* tbuf = g_strdup_printf (_("Send file to %s"), nick);
+
+	struct my_dcc_send *mdc = g_new (struct my_dcc_send, 1);
 	mdc->sess = sess;
-	mdc->nick = strdup (nick);
+	mdc->nick = g_strdup (nick);
 	mdc->maxcps = maxcps;
 	mdc->passive = passive;
 
-	snprintf (tbuf, sizeof tbuf, _("Send file to %s"), nick);
 	gtkutil_file_req (tbuf, dcc_send_filereq_file, mdc, prefs.hex_dcc_dir, NULL, FRF_MULTIPLE|FRF_FILTERISINITIAL);
+
+	g_free (tbuf);
 }
 
 static void
 dcc_prepare_row_chat (struct DCC *dcc, GtkListStore *store, GtkTreeIter *iter,
 							 gboolean update_only)
 {
-	static char pos[16], siz[16];
+	static char pos[16], size[16];
 	char *date;
 
 	date = ctime (&dcc->starttime);
 	date[strlen (date) - 1] = 0;	/* remove the \n */
 
 	proper_unit (dcc->pos, pos, sizeof (pos));
-	proper_unit (dcc->size, siz, sizeof (siz));
+	proper_unit (dcc->size, size, sizeof (size));
 
 	gtk_list_store_set (store, iter,
 							  CCOL_STATUS, _(dccstat[dcc->dccstat].name),
 							  CCOL_NICK, dcc->nick,
 							  CCOL_RECV, pos,
-							  CCOL_SENT, siz,
+							  CCOL_SENT, size,
 							  CCOL_START, date,
 							  CCOL_DCC, dcc,
 							  CCOL_COLOR,
@@ -194,13 +194,12 @@ dcc_prepare_row_send (struct DCC *dcc, GtkListStore *store, GtkTreeIter *iter,
 	per = (float) ((dcc->ack * 100.00) / dcc->size);
 	proper_unit (dcc->size, size, sizeof (size));
 	proper_unit (dcc->pos, pos, sizeof (pos));
-	snprintf (kbs, sizeof (kbs), "%.1f", ((float)dcc->cps) / 1024);
-/*	proper_unit (dcc->ack, ack, sizeof (ack));*/
-	snprintf (perc, sizeof (perc), "%.0f%%", per);
+	g_snprintf (kbs, sizeof (kbs), "%.1f", ((float)dcc->cps) / 1024);
+	g_snprintf (perc, sizeof (perc), "%.0f%%", per);
 	if (dcc->cps != 0)
 	{
 		to_go = (dcc->size - dcc->ack) / dcc->cps;
-		snprintf (eta, sizeof (eta), "%.2d:%.2d:%.2d",
+		g_snprintf (eta, sizeof (eta), "%.2d:%.2d:%.2d",
 					 to_go / 3600, (to_go / 60) % 60, to_go % 60);
 	} else
 		strcpy (eta, "--:--:--");
@@ -253,14 +252,14 @@ dcc_prepare_row_recv (struct DCC *dcc, GtkListStore *store, GtkTreeIter *iter,
 		proper_unit (dcc->resumable, pos, sizeof (pos));
 	else
 		proper_unit (dcc->pos, pos, sizeof (pos));
-	snprintf (kbs, sizeof (kbs), "%.1f", ((float)dcc->cps) / 1024);
+	g_snprintf (kbs, sizeof (kbs), "%.1f", ((float)dcc->cps) / 1024);
 	/* percentage recv'ed */
 	per = (float) ((dcc->pos * 100.00) / dcc->size);
-	snprintf (perc, sizeof (perc), "%.0f%%", per);
+	g_snprintf (perc, sizeof (perc), "%.0f%%", per);
 	if (dcc->cps != 0)
 	{
 		to_go = (dcc->size - dcc->pos) / dcc->cps;
-		snprintf (eta, sizeof (eta), "%.2d:%.2d:%.2d",
+		g_snprintf (eta, sizeof (eta), "%.2d:%.2d:%.2d",
 					 to_go / 3600, (to_go / 60) % 60, to_go % 60);
 	} else
 		strcpy (eta, "--:--:--");
@@ -526,7 +525,7 @@ resume_clicked (GtkWidget * wid, gpointer none)
 			fe_message (_("That file is not resumable."), FE_MSG_ERROR);
 			break;
 		case 1:
-			snprintf (buf, sizeof (buf),
+			g_snprintf (buf, sizeof (buf),
 						_(	"Cannot access file: %s\n"
 							"%s.\n"
 							"Resuming not possible."), dcc->destfile,	
@@ -607,7 +606,7 @@ browse_folder (char *dir)
 #else
 	char buf[512];
 
-	snprintf (buf, sizeof (buf), "file://%s", dir);
+	g_snprintf (buf, sizeof (buf), "file://%s", dir);
 	fe_open_url (buf);
 #endif
 }
@@ -640,7 +639,7 @@ dcc_details_populate (struct DCC *dcc)
 		gtk_label_set_text (GTK_LABEL (dccfwin.file_label), dcc->file);
 
 	/* address and port */
-	snprintf (buf, sizeof (buf), "%s : %d", net_ip (dcc->addr), dcc->port);
+	g_snprintf (buf, sizeof (buf), "%s : %d", net_ip (dcc->addr), dcc->port);
 	gtk_label_set_text (GTK_LABEL (dccfwin.address_label), buf);
 }
 
@@ -738,7 +737,7 @@ dcc_detail_label (char *text, GtkWidget *box, int num)
 	char buf[64];
 
 	label = gtk_label_new (NULL);
-	snprintf (buf, sizeof (buf), "<b>%s</b>", text);
+	g_snprintf (buf, sizeof (buf), "<b>%s</b>", text);
 	gtk_label_set_markup (GTK_LABEL (label), buf);
 	gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
 	gtk_table_attach (GTK_TABLE (box), label, 0, 1, 0 + num, 1 + num, GTK_FILL, GTK_FILL, 0, 0);
diff --git a/src/fe-gtk/editlist.c b/src/fe-gtk/editlist.c
index f7e22d52..4b236dc1 100644
--- a/src/fe-gtk/editlist.c
+++ b/src/fe-gtk/editlist.c
@@ -283,6 +283,7 @@ editlist_treeview_new (GtkWidget *box, char *title1, char *title2)
 	view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store));
 	gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (view), TRUE);
 	gtk_tree_view_set_enable_search (GTK_TREE_VIEW (view), FALSE);
+	gtk_tree_view_set_reorderable (GTK_TREE_VIEW (view), TRUE);
 	
 	g_signal_connect (G_OBJECT (view), "key_press_event",
 						G_CALLBACK (editlist_keypress), NULL);
@@ -313,7 +314,6 @@ editlist_treeview_new (GtkWidget *box, char *title1, char *title2)
 	gtk_tree_view_column_set_sizing (col, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
 	gtk_tree_view_column_set_resizable (col, TRUE);
 	gtk_tree_view_column_set_min_width (col, 100);
-	col = gtk_tree_view_get_column (GTK_TREE_VIEW (view), CMD_COLUMN);
 
 	gtk_container_add (GTK_CONTAINER (scroll), view);
 	gtk_container_add (GTK_CONTAINER (box), scroll);
diff --git a/src/fe-gtk/fe-gtk.c b/src/fe-gtk/fe-gtk.c
index 4b7d916f..8c163eb7 100644
--- a/src/fe-gtk/fe-gtk.c
+++ b/src/fe-gtk/fe-gtk.c
@@ -52,6 +52,7 @@
 #include "plugin-tray.h"
 #include "urlgrab.h"
 #include "setup.h"
+#include "plugin-notification.h"
 
 #ifdef USE_LIBCANBERRA
 #include <canberra.h>
@@ -224,7 +225,7 @@ fe_args (int argc, char *argv[])
 	/* cuts can. So we have to set the current dir manually, to the path  */
 	/* of the exe. */
 	{
-		char *tmp = strdup (argv[0]);
+		char *tmp = g_strdup (argv[0]);
 		char *sl;
 
 		sl = strrchr (tmp, G_DIR_SEPARATOR);
@@ -233,7 +234,7 @@ fe_args (int argc, char *argv[])
 			*sl = 0;
 			chdir (tmp);
 		}
-		free (tmp);
+		g_free (tmp);
 	}
 #endif
 
@@ -265,7 +266,7 @@ create_input_style (GtkStyle *style)
 	/* fall back */
 	if (pango_font_description_get_size (style->font_desc) == 0)
 	{
-		snprintf (buf, sizeof (buf), _("Failed to open font:\n\n%s"), prefs.hex_text_font);
+		g_snprintf (buf, sizeof (buf), _("Failed to open font:\n\n%s"), prefs.hex_text_font);
 		fe_message (buf, FE_MSG_ERROR);
 		pango_font_description_free (style->font_desc);
 		style->font_desc = pango_font_description_from_string ("sans 11");
@@ -381,6 +382,8 @@ fe_idle (gpointer data)
 {
 	session *sess = sess_list->data;
 
+	plugin_add (sess, NULL, NULL, notification_plugin_init, notification_plugin_deinit, NULL, FALSE);
+
 	plugin_add (sess, NULL, NULL, tray_plugin_init, tray_plugin_deinit, NULL, FALSE);
 
 	if (arg_minimize == 1)
@@ -424,8 +427,7 @@ fe_new_window (session *sess, int focus)
 void
 fe_new_server (struct server *serv)
 {
-	serv->gui = malloc (sizeof (struct server_gui));
-	memset (serv->gui, 0, sizeof (struct server_gui));
+	serv->gui = g_new0 (struct server_gui, 1);
 }
 
 void
@@ -510,18 +512,15 @@ fe_set_topic (session *sess, char *topic, char *stripped_topic)
 	}
 	else
 	{
-		if (sess->res->topic_text)
-		{
-			free (sess->res->topic_text);
-		}
+		g_free (sess->res->topic_text);
 
 		if (prefs.hex_text_stripcolor_topic)
 		{
-			sess->res->topic_text = strdup (stripped_topic);
+			sess->res->topic_text = g_strdup (stripped_topic);
 		}
 		else
 		{
-			sess->res->topic_text = strdup (topic);
+			sess->res->topic_text = g_strdup (topic);
 		}
 	}
 }
@@ -547,9 +546,8 @@ fe_update_mode_entry (session *sess, GtkWidget *entry, char **text, char *new_te
 	{
 		if (sess->gui->is_tab)
 		{
-			if (*text)
-				free (*text);
-			*text = strdup (new_text);
+			g_free (*text);
+			*text = g_strdup (new_text);
 		}
 	}
 }
@@ -721,7 +719,7 @@ fe_lastlog (session *sess, session *lastlog_sess, char *sstr, gtk_xtext_search_f
 		lbuf->search_lnee = strlen (lbuf->search_nee);
 	}
 	lbuf->search_flags = flags;
-	lbuf->search_text = strdup (sstr);
+	lbuf->search_text = g_strdup (sstr);
 	gtk_xtext_lastlog (lbuf, buf);
 }
 
@@ -751,9 +749,9 @@ fe_set_lag (server *serv, long lag)
 	if (per > 1.0)
 		per = 1.0;
 
-	snprintf (lagtext, sizeof (lagtext) - 1, "%s%ld.%lds",
+	g_snprintf (lagtext, sizeof (lagtext) - 1, "%s%ld.%lds",
 			  serv->lag_sent ? "+" : "", lag / 1000, (lag/100) % 10);
-	snprintf (lagtip, sizeof (lagtip) - 1, "Lag: %s%ld.%ld seconds",
+	g_snprintf (lagtip, sizeof (lagtip) - 1, "Lag: %s%ld.%ld seconds",
 				 serv->lag_sent ? "+" : "", lag / 1000, (lag/100) % 10);
 
 	while (list)
@@ -761,9 +759,8 @@ fe_set_lag (server *serv, long lag)
 		sess = list->data;
 		if (sess->server == serv)
 		{
-			if (sess->res->lag_tip)
-				free (sess->res->lag_tip);
-			sess->res->lag_tip = strdup (lagtip);
+			g_free (sess->res->lag_tip);
+			sess->res->lag_tip = g_strdup (lagtip);
 
 			if (!sess->gui->is_tab || current_tab == sess)
 			{
@@ -777,9 +774,8 @@ fe_set_lag (server *serv, long lag)
 			} else
 			{
 				sess->res->lag_value = per;
-				if (sess->res->lag_text)
-					free (sess->res->lag_text);
-				sess->res->lag_text = strdup (lagtext);
+				g_free (sess->res->lag_text);
+				sess->res->lag_text = g_strdup (lagtext);
 			}
 		}
 		list = list->next;
@@ -804,12 +800,11 @@ fe_set_throttle (server *serv)
 		sess = list->data;
 		if (sess->server == serv)
 		{
-			snprintf (tbuf, sizeof (tbuf) - 1, _("%d bytes"), serv->sendq_len);
-			snprintf (tip, sizeof (tip) - 1, _("Network send queue: %d bytes"), serv->sendq_len);
+			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);
 
-			if (sess->res->queue_tip)
-				free (sess->res->queue_tip);
-			sess->res->queue_tip = strdup (tip);
+			g_free (sess->res->queue_tip);
+			sess->res->queue_tip = g_strdup (tip);
 
 			if (!sess->gui->is_tab || current_tab == sess)
 			{
@@ -823,9 +818,8 @@ fe_set_throttle (server *serv)
 			} else
 			{
 				sess->res->queue_value = per;
-				if (sess->res->queue_text)
-					free (sess->res->queue_text);
-				sess->res->queue_text = strdup (tbuf);
+				g_free (sess->res->queue_text);
+				sess->res->queue_text = g_strdup (tbuf);
 			}
 		}
 		list = list->next;
@@ -882,11 +876,10 @@ fe_confirm (const char *message, void (*yesproc)(void *), void (*noproc)(void *)
 {
 	/* warning, assuming fe_confirm is used by DCC only! */
 	struct DCC *dcc = ud;
-	char *filepath;
 
 	if (dcc->file)
 	{
-		filepath = g_build_filename (prefs.hex_dcc_dir, dcc->file, NULL);
+		char *filepath = g_build_filename (prefs.hex_dcc_dir, dcc->file, NULL);
 		gtkutil_file_req (message, dcc_saveas_cb, ud, filepath, NULL,
 								FRF_WRITE|FRF_NOASKOVERWRITE|FRF_FILTERISINITIAL);
 		g_free (filepath);
@@ -978,9 +971,8 @@ fe_set_inputbox_contents (session *sess, char *text)
 		SPELL_ENTRY_SET_TEXT (sess->gui->input_box, text);
 	} else
 	{
-		if (sess->res->input_text)
-			free (sess->res->input_text);
-		sess->res->input_text = strdup (text);
+		g_free (sess->res->input_text);
+		sess->res->input_text = g_strdup (text);
 	}
 }
 
@@ -1092,9 +1084,9 @@ fe_open_url (const char *url)
 	/* the http:// part's missing, prepend it, otherwise it won't always work */
 	else if (strchr (url, ':') == NULL)
 	{
-		url = g_strdup_printf ("http://%s", url);
-		fe_open_url_inner (url);
-		g_free ((char *)url);
+		uri = g_strdup_printf ("http://%s", url);
+		fe_open_url_inner (uri);
+		g_free (uri);
 	}
 	/* we have a sane URL, send it to the browser untouched */
 	else
diff --git a/src/fe-gtk/fe-gtk.h b/src/fe-gtk/fe-gtk.h
index 17d1ab4d..ab776f63 100644
--- a/src/fe-gtk/fe-gtk.h
+++ b/src/fe-gtk/fe-gtk.h
@@ -20,7 +20,7 @@
 #ifndef HEXCHAT_FE_GTK_H
 #define HEXCHAT_FE_GTK_H
 
-#include "../../config.h"
+#include "config.h"
 
 #define DISPLAY_NAME "HexChat"
 
@@ -39,14 +39,13 @@
 
 #define flag_c flag_wid[0]
 #define flag_n flag_wid[1]
-#define flag_r flag_wid[2]
-#define flag_t flag_wid[3]
-#define flag_i flag_wid[4]
-#define flag_m flag_wid[5]
-#define flag_l flag_wid[6]
-#define flag_k flag_wid[7]
-#define flag_b flag_wid[8]
-#define NUM_FLAG_WIDS 9
+#define flag_t flag_wid[2]
+#define flag_i flag_wid[3]
+#define flag_m flag_wid[4]
+#define flag_l flag_wid[5]
+#define flag_k flag_wid[6]
+#define flag_b flag_wid[7]
+#define NUM_FLAG_WIDS 8
 
 #ifdef HAVE_GTK_MAC
 extern GtkosxApplication *osx_app;
@@ -92,9 +91,9 @@ struct server_gui
 	guint chanlist_channels_shown_count;	/* total number of displayed 
 														   channels */
 
-	int chanlist_maxusers;
-	int chanlist_minusers;
-	int chanlist_minusers_downloaded;	/* used by LIST IRC command */
+	guint32 chanlist_maxusers;
+	guint32 chanlist_minusers;
+	guint32 chanlist_minusers_downloaded;	/* used by LIST IRC command */
 	int chanlist_search_type;		/* 0=simple 1=pattern/wildcard 2=regexp */
 	gboolean chanlist_caption_is_stale;
 };
@@ -108,7 +107,7 @@ typedef struct restore_gui
 	void *tab;			/* (chan *) */
 
 	/* information stored when this tab isn't front-most */
-	void *user_model;	/* for filling the GtkTreeView */
+	GtkListStore *user_model;	/* for filling the GtkTreeView */
 	void *buffer;		/* xtext_Buffer */
 	char *input_text;	/* input text buffer (while not-front tab) */
 	char *topic_text;	/* topic GtkEntry buffer */
diff --git a/src/fe-gtk/fe-gtk.vcxproj b/src/fe-gtk/fe-gtk.vcxproj
index 59ab17c6..401518b4 100644
--- a/src/fe-gtk/fe-gtk.vcxproj
+++ b/src/fe-gtk/fe-gtk.vcxproj
@@ -2,6 +2,7 @@
 <Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

   <PropertyGroup Label="Configuration">

     <PlatformToolset>v120</PlatformToolset>

+    <ConfigurationType>Application</ConfigurationType>

   </PropertyGroup>

   <ItemGroup Label="ProjectConfigurations">

     <ProjectConfiguration Include="Release|Win32">

@@ -19,79 +20,38 @@
     <RootNamespace>fegtk</RootNamespace>

   </PropertyGroup>

   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />

-  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">

-    <ConfigurationType>Application</ConfigurationType>

-    <UseDebugLibraries>false</UseDebugLibraries>

-    <CharacterSet>MultiByte</CharacterSet>

-  </PropertyGroup>

-  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">

-    <ConfigurationType>Application</ConfigurationType>

-    <UseDebugLibraries>false</UseDebugLibraries>

-    <CharacterSet>MultiByte</CharacterSet>

-  </PropertyGroup>

   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />

-  <ImportGroup Label="ExtensionSettings">

-  </ImportGroup>

-  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">

-    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />

-    <Import Project="..\..\win32\hexchat.props" />

-  </ImportGroup>

-  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">

-    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />

-    <Import Project="..\..\win32\hexchat.props" />

-  </ImportGroup>

-  <PropertyGroup Label="UserMacros" />

-  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">

-    <LinkIncremental>false</LinkIncremental>

-    <TargetName>hexchat</TargetName>

-    <OutDir>$(HexChatBin)</OutDir>

-    <IntDir>$(HexChatObj)$(ProjectName)\</IntDir>

-  </PropertyGroup>

-  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">

-    <LinkIncremental>false</LinkIncremental>

+  <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />

+  <Import Project="..\..\win32\hexchat.props" />

+  <PropertyGroup>

     <TargetName>hexchat</TargetName>

-    <OutDir>$(HexChatBin)</OutDir>

-    <IntDir>$(HexChatObj)$(ProjectName)\</IntDir>

+    <OutDir>$(HexChatRel)</OutDir>

   </PropertyGroup>

   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">

     <ClCompile>

-      <PrecompiledHeader>

-      </PrecompiledHeader>

-      <FunctionLevelLinking>true</FunctionLevelLinking>

-      <IntrinsicFunctions>true</IntrinsicFunctions>

+      <!-- WholeProgramOptimization must be turned off for gresource constructors to work, otherwise the .CRT$XCU section is not emitted. -->

+      <WholeProgramOptimization>false</WholeProgramOptimization>

       <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;$(OwnFlags);%(PreprocessorDefinitions)</PreprocessorDefinitions>

-      <AdditionalIncludeDirectories>$(SolutionDir)..;$(DepsRoot)\include;$(Glib);$(Gtk);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>

-      <MultiProcessorCompilation>true</MultiProcessorCompilation>

+      <AdditionalIncludeDirectories>..\common;$(HexChatLib);$(DepsRoot)\include;$(Glib);$(Gtk);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>

       <DisableSpecificWarnings>4244;%(DisableSpecificWarnings)</DisableSpecificWarnings>

     </ClCompile>

     <Link>

-      <SubSystem>Windows</SubSystem>

-      <GenerateDebugInformation>true</GenerateDebugInformation>

-      <EnableCOMDATFolding>true</EnableCOMDATFolding>

-      <OptimizeReferences>true</OptimizeReferences>

-      <AdditionalLibraryDirectories>$(DepsRoot)\lib;$(OutDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>

-      <AdditionalDependencies>$(DepLibs);common.lib;%(AdditionalDependencies)</AdditionalDependencies>

+      <AdditionalLibraryDirectories>$(DepsRoot)\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>

+      <AdditionalDependencies>$(DepLibs);$(HexChatLib)common.lib;%(AdditionalDependencies)</AdditionalDependencies>

       <EntryPointSymbol>mainCRTStartup</EntryPointSymbol>

     </Link>

   </ItemDefinitionGroup>

   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">

     <ClCompile>

-      <PrecompiledHeader>

-      </PrecompiledHeader>

-      <FunctionLevelLinking>true</FunctionLevelLinking>

-      <IntrinsicFunctions>true</IntrinsicFunctions>

+      <!-- WholeProgramOptimization must be turned off for gresource constructors to work, otherwise the .CRT$XCU section is not emitted. -->

+      <WholeProgramOptimization>false</WholeProgramOptimization>

       <PreprocessorDefinitions>WIN32;_WIN64;_AMD64_;NDEBUG;_WINDOWS;$(OwnFlags);%(PreprocessorDefinitions)</PreprocessorDefinitions>

-      <AdditionalIncludeDirectories>$(SolutionDir)..;$(DepsRoot)\include;$(Glib);$(Gtk);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>

-      <MultiProcessorCompilation>true</MultiProcessorCompilation>

+      <AdditionalIncludeDirectories>..\common;$(HexChatLib);$(DepsRoot)\include;$(Glib);$(Gtk);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>

       <DisableSpecificWarnings>4244;4267;%(DisableSpecificWarnings)</DisableSpecificWarnings>

     </ClCompile>

     <Link>

-      <SubSystem>Windows</SubSystem>

-      <GenerateDebugInformation>true</GenerateDebugInformation>

-      <EnableCOMDATFolding>true</EnableCOMDATFolding>

-      <OptimizeReferences>true</OptimizeReferences>

-      <AdditionalLibraryDirectories>$(DepsRoot)\lib;$(OutDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>

-      <AdditionalDependencies>$(DepLibs);common.lib;%(AdditionalDependencies)</AdditionalDependencies>

+      <AdditionalLibraryDirectories>$(DepsRoot)\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>

+      <AdditionalDependencies>$(DepLibs);$(HexChatLib)common.lib;%(AdditionalDependencies)</AdditionalDependencies>

       <EntryPointSymbol>mainCRTStartup</EntryPointSymbol>

     </Link>

   </ItemDefinitionGroup>

@@ -99,10 +59,10 @@
     <PreBuildEvent>

       <Command><![CDATA[

 SET SOLUTIONDIR=$(SolutionDir)..\

-powershell -File "$(SolutionDir)..\win32\version-template.ps1" "$(SolutionDir)..\src\fe-gtk\hexchat.rc.tt" "$(SolutionDir)..\src\fe-gtk\hexchat.rc.utf8"

+powershell -File "$(SolutionDir)..\win32\version-template.ps1" "$(SolutionDir)..\src\fe-gtk\hexchat.rc.tt" "$(HexChatLib)hexchat.rc.utf8"

 REM hexchat.rc needs to be in UCS-2 or Resource Compiler will complain

-powershell "Get-Content -Encoding UTF8 '$(SolutionDir)..\src\fe-gtk\hexchat.rc.utf8' | Out-File '$(SolutionDir)..\src\fe-gtk\hexchat.rc'; Remove-Item '$(SolutionDir)..\src\fe-gtk\hexchat.rc.utf8'"

-"$(DepsRoot)\bin\glib-compile-resources.exe" --generate-source --sourcedir "$(DataDir)" --target "$(ProjectDir)resources.c" "$(DataDir)hexchat.gresource.xml"

+powershell "Get-Content -Encoding UTF8 '$(HexChatLib)hexchat.rc.utf8' | Out-File '$(HexChatLib)hexchat.rc'; Remove-Item '$(HexChatLib)hexchat.rc.utf8'"

+"$(DepsRoot)\bin\glib-compile-resources.exe" --generate-source --sourcedir "$(DataDir)" --target "$(HexChatLib)resources.c" "$(DataDir)hexchat.gresource.xml"

       ]]></Command>

       <Message>Build hexchat.rc and gresource file</Message>

     </PreBuildEvent>

@@ -120,6 +80,7 @@ powershell "Get-Content -Encoding UTF8 '$(SolutionDir)..\src\fe-gtk\hexchat.rc.u
     <ClInclude Include="joind.h" />

     <ClInclude Include="maingui.h" />

     <ClInclude Include="menu.h" />

+    <ClInclude Include="notifications\notification-backend.h" />

     <ClInclude Include="notifygui.h" />

     <ClInclude Include="palette.h" />

     <ClInclude Include="pixmaps.h" />

@@ -150,13 +111,15 @@ powershell "Get-Content -Encoding UTF8 '$(SolutionDir)..\src\fe-gtk\hexchat.rc.u
     <ClCompile Include="joind.c" />

     <ClCompile Include="maingui.c" />

     <ClCompile Include="menu.c" />

+    <ClCompile Include="notifications\notification-windows.c" />

     <ClCompile Include="notifygui.c" />

     <ClCompile Include="palette.c" />

     <ClCompile Include="pixmaps.c" />

+    <ClCompile Include="plugin-notification.c" />

     <ClCompile Include="plugin-tray.c" />

     <ClCompile Include="plugingui.c" />

     <ClCompile Include="rawlog.c" />

-    <ClCompile Include="resources.c" />

+    <ClCompile Include="$(HexChatLib)resources.c" />

     <ClCompile Include="servlistgui.c" />

     <ClCompile Include="setup.c" />

     <ClCompile Include="sexy-iso-codes.c" />

@@ -167,11 +130,11 @@ powershell "Get-Content -Encoding UTF8 '$(SolutionDir)..\src\fe-gtk\hexchat.rc.u
     <ClCompile Include="xtext.c" />

   </ItemGroup>

   <ItemGroup>

-    <Manifest Include="hexchat.exe.manifest" />

+    <Manifest Include="..\..\win32\hexchat.exe.manifest" />

   </ItemGroup>

   <ItemGroup>

     <None Include="hexchat.rc.tt" />

-    <ResourceCompile Include="hexchat.rc" />

+    <ResourceCompile Include="$(HexChatLib)hexchat.rc" />

   </ItemGroup>

   <ItemGroup>

     <None Include="..\..\data\icons\hexchat.ico" />

@@ -180,6 +143,4 @@ powershell "Get-Content -Encoding UTF8 '$(SolutionDir)..\src\fe-gtk\hexchat.rc.u
     <Xml Include="..\..\data\hexchat.gresource.xml" />

   </ItemGroup>

   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />

-  <ImportGroup Label="ExtensionTargets">

-  </ImportGroup>

-</Project>
\ No newline at end of file
+</Project>

diff --git a/src/fe-gtk/fe-gtk.vcxproj.filters b/src/fe-gtk/fe-gtk.vcxproj.filters
index 4598b1f2..fe211a2d 100644
--- a/src/fe-gtk/fe-gtk.vcxproj.filters
+++ b/src/fe-gtk/fe-gtk.vcxproj.filters
@@ -93,6 +93,9 @@
     <ClInclude Include="setup.h">

       <Filter>Header Files</Filter>

     </ClInclude>

+    <ClInclude Include="notifications\notification-backend.h">

+      <Filter>Header Files</Filter>

+    </ClInclude>

   </ItemGroup>

   <ItemGroup>

     <ClCompile Include="ascii.c">

@@ -179,17 +182,23 @@
     <ClCompile Include="xtext.c">

       <Filter>Source Files</Filter>

     </ClCompile>

-    <ClCompile Include="resources.c">

+    <ClCompile Include="$(HexChatLib)resources.c">

+      <Filter>Source Files</Filter>

+    </ClCompile>

+    <ClCompile Include="plugin-notification.c">

+      <Filter>Source Files</Filter>

+    </ClCompile>

+    <ClCompile Include="notifications\notification-windows.c">

       <Filter>Source Files</Filter>

     </ClCompile>

   </ItemGroup>

   <ItemGroup>

-    <Manifest Include="hexchat.exe.manifest">

+    <Manifest Include="..\..\win32\hexchat.exe.manifest">

       <Filter>Resource Files</Filter>

     </Manifest>

   </ItemGroup>

   <ItemGroup>

-    <ResourceCompile Include="hexchat.rc">

+    <ResourceCompile Include="$(HexChatLib)hexchat.rc">

       <Filter>Resource Files</Filter>

     </ResourceCompile>

   </ItemGroup>

diff --git a/src/fe-gtk/fkeys.c b/src/fe-gtk/fkeys.c
index 59086a5e..e762d208 100644
--- a/src/fe-gtk/fkeys.c
+++ b/src/fe-gtk/fkeys.c
@@ -198,6 +198,7 @@ static const struct key_action key_actions[KEY_MAX_ACTIONS + 1] = {
 	"ACCEL=Down\nNext Command\nD1!\nD2!\n\n"\
 	"ACCEL=Up\nLast Command\nD1!\nD2!\n\n"\
 	"ACCEL=Tab\nComplete nick/command\nD1!\nD2!\n\n"\
+	"ACCEL=<Shift>ISO_Left_Tab\nComplete nick/command\nD1:Previous\nD2!\n\n"\
 	"ACCEL=space\nCheck For Replace\nD1!\nD2!\n\n"\
 	"ACCEL=Return\nCheck For Replace\nD1!\nD2!\n\n"\
 	"ACCEL=KP_Enter\nCheck For Replace\nD1!\nD2!\n\n"\
@@ -241,10 +242,8 @@ key_free (gpointer data)
 
 	g_return_if_fail (kb != NULL);
 
-	if (kb->data1)
-		g_free (kb->data1);
-	if (kb->data2)
-		g_free (kb->data2);
+	g_free (kb->data1);
+	g_free (kb->data2);
 	g_free (kb);
 }
 
@@ -323,7 +322,7 @@ key_handle_key_press (GtkWidget *wid, GdkEventKey *evt, session *sess)
 		return FALSE;
 	current_sess = sess;
 
-	if (plugin_emit_keypress (sess, evt->state, evt->keyval, evt->length, evt->string))
+	if (plugin_emit_keypress (sess, evt->state, evt->keyval, gdk_keyval_to_unicode (evt->keyval)))
 		return 1;
 
 	/* maybe the plugin closed this tab? */
@@ -567,7 +566,7 @@ key_dialog_save (GtkWidget *wid, gpointer userdata)
 	{
 		do
 		{
-			kb = (struct key_binding *) g_malloc0 (sizeof (struct key_binding));
+			kb = g_new0 (struct key_binding, 1);
 
 			gtk_tree_model_get (GTK_TREE_MODEL (store), &iter, ACCEL_COLUMN, &accel,
 															ACTION_COLUMN, &actiontext,
@@ -598,7 +597,8 @@ key_dialog_save (GtkWidget *wid, gpointer userdata)
 			else
 				keybind_list = g_slist_append (keybind_list, kb);
 
-		} while (gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter));
+		}
+		while (gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter));
 	}
 
 	if (key_save_kbs () == 0)
@@ -666,6 +666,7 @@ key_dialog_treeview_new (GtkWidget *box)
 	view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store));
 	gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (view), TRUE);
 	gtk_tree_view_set_enable_search (GTK_TREE_VIEW (view), FALSE);
+	gtk_tree_view_set_reorderable (GTK_TREE_VIEW (view), TRUE);
 
 	g_signal_connect (G_OBJECT (view), "key-press-event",
 					G_CALLBACK (key_dialog_keypress), NULL);
@@ -850,7 +851,7 @@ key_save_kbs (void)
 									 0x180, XOF_DOMODE);
 	if (fd < 0)
 		return 1;
-	write (fd, buf, snprintf (buf, 510, "# HexChat key bindings config file\n\n"));
+	write (fd, buf, g_snprintf (buf, 510, "# HexChat key bindings config file\n\n"));
 
 	while (list)
 	{
@@ -858,17 +859,17 @@ key_save_kbs (void)
 
 		accel_text = gtk_accelerator_name (kb->keyval, kb->mod);
 
-		snprintf (buf, 510, "ACCEL=%s\n%s\n", accel_text, key_actions[kb->action].name);
+		g_snprintf (buf, 510, "ACCEL=%s\n%s\n", accel_text, key_actions[kb->action].name);
 		write (fd, buf, strlen (buf));
 		g_free (accel_text);
 
 		if (kb->data1 && kb->data1[0])
-			write (fd, buf, snprintf (buf, 510, "D1:%s\n", kb->data1));
+			write (fd, buf, g_snprintf (buf, 510, "D1:%s\n", kb->data1));
 		else
 			write (fd, "D1!\n", 4);
 
 		if (kb->data2 && kb->data2[0])
-			write (fd, buf, snprintf (buf, 510, "D2:%s\n", kb->data2));
+			write (fd, buf, g_snprintf (buf, 510, "D2:%s\n", kb->data2));
 		else
 			write (fd, "D2!\n", 4);
 
@@ -946,7 +947,7 @@ key_load_kbs (void)
 	fd = hexchat_open_file ("keybindings.conf", O_RDONLY, 0, 0);
 	if (fd < 0)
 	{
-		ibuf = strdup (default_kb_cfg);
+		ibuf = g_strdup (default_kb_cfg);
 		size = strlen (default_kb_cfg);
 	}
 	else
@@ -957,7 +958,7 @@ key_load_kbs (void)
 			return 1;
 		}
 
-		ibuf = malloc (st.st_size);
+		ibuf = g_malloc(st.st_size);
 		read (fd, ibuf, st.st_size);
 		size = st.st_size;
 		close (fd);
@@ -979,7 +980,7 @@ key_load_kbs (void)
 		switch (state)
 		{
 		case KBSTATE_MOD:
-			kb = (struct key_binding *) g_malloc0 (sizeof (struct key_binding));
+			kb = g_new0 (struct key_binding, 1);
 
 			/* New format */
 			if (strncmp (buf, "ACCEL=", 6) == 0)
@@ -1010,7 +1011,7 @@ key_load_kbs (void)
 			keyval = gdk_keyval_from_name (buf);
 			if (keyval == 0)
 			{
-				free (ibuf);
+				g_free (ibuf);
 				return 2;
 			}
 
@@ -1026,7 +1027,7 @@ key_load_kbs (void)
 
 			if (kb->action == KEY_MAX_ACTIONS + 1)
 			{
-				free (ibuf);
+				g_free (ibuf);
 				return 3;
 			}
 
@@ -1043,7 +1044,7 @@ key_load_kbs (void)
 
 			if (buf[0] != 'D')
 			{
-				free (ibuf);
+				g_free (ibuf);
 				return 4;
 			}
 
@@ -1069,12 +1070,10 @@ key_load_kbs (void)
 				len -= 3;
 				if (state == KBSTATE_DT1)
 				{
-					kb->data1 = g_malloc (len);
-					memcpy (kb->data1, &buf[3], len);
+					kb->data1 = g_strndup (&buf[3], len);
 				} else
 				{
-					kb->data2 = g_malloc (len);
-					memcpy (kb->data2, &buf[3], len);
+					kb->data2 = g_strndup (&buf[3], len);
 				}
 			} else if (buf[2] == '!')
 			{
@@ -1097,12 +1096,12 @@ key_load_kbs (void)
 			continue;
 		}
 	}
-	free (ibuf);
+	g_free (ibuf);
 	return 0;
 
 corrupt_file:
-	free (ibuf);
-	free (kb);
+	g_free (ibuf);
+	g_free (kb);
 	return 5;
 }
 
@@ -1410,9 +1409,6 @@ key_action_tab_clean(void)
 	}
 }
 
-/* Used in the followig completers */
-#define COMP_BUF 2048
-
 /* For use in sorting the user list for completion */
 static int
 talked_recent_cmp (struct User *a, struct User *b)
@@ -1426,16 +1422,31 @@ talked_recent_cmp (struct User *a, struct User *b)
 	return 0;
 }
 
+#define COMP_BUF 2048
+
+static inline glong
+len_to_offset (const char *str, glong len)
+{
+	return g_utf8_pointer_to_offset (str, str + len);
+}
+
+static inline glong
+offset_to_len (const char *str, glong offset)
+{
+	return g_utf8_offset_to_pointer (str, offset) - str;
+}
+
 static int
 key_action_tab_comp (GtkWidget *t, GdkEventKey *entry, char *d1, char *d2,
 							struct session *sess)
 {
-	int len = 0, elen = 0, i = 0, cursor_pos, ent_start = 0, comp = 0, found = 0,
-	    prefix_len, skip_len = 0, is_nick, is_cmd = 0;
-	char buf[COMP_BUF], ent[CHANLEN], *postfix = NULL, *result, *ch;
+	int len = 0, elen = 0, i = 0, cursor_pos, ent_start = 0, comp = 0, prefix_len, skip_len = 0;
+	gboolean is_nick = FALSE, is_cmd = FALSE, found = FALSE, has_nick_prefix = FALSE;
+	char ent[CHANLEN], *postfix = NULL, *result, *ch;
 	GList *list = NULL, *tmp_list = NULL;
 	const char *text;
 	GCompletion *gcomp = NULL;
+	GString *buf;
 
 	/* force the IM Context to reset */
 	SPELL_ENTRY_SET_EDITABLE (t, FALSE);
@@ -1449,8 +1460,6 @@ key_action_tab_comp (GtkWidget *t, GdkEventKey *entry, char *d1, char *d2,
 
 	cursor_pos = SPELL_ENTRY_GET_POS (t);
 
-	buf[0] = 0; /* make sure we don't get garbage in the buffer */
-
 	/* handle "nick: " or "nick " or "#channel "*/
 	ch = g_utf8_find_prev_char(text, g_utf8_offset_to_pointer(text,cursor_pos));
 	if (ch && ch[0] == ' ')
@@ -1489,15 +1498,23 @@ key_action_tab_comp (GtkWidget *t, GdkEventKey *entry, char *d1, char *d2,
 	if (ent_start == 0 && text[0] == prefs.hex_input_command_char[0])
 	{
 		ent_start++;
-		is_cmd = 1;
+		is_cmd = TRUE;
 	}
-	
+	else if (strchr (sess->server->chantypes, text[ent_start]) == NULL)
+	{
+		is_nick = TRUE;
+		if (strchr (sess->server->nick_prefixes, text[ent_start]) != NULL)
+		{
+			if (ent_start == 0)
+				has_nick_prefix = TRUE;
+			ent_start++;
+		}
+	}
+
 	prefix_len = ent_start;
 	elen = cursor_pos - ent_start;
 
 	g_utf8_strncpy (ent, g_utf8_offset_to_pointer (text, prefix_len), elen);
-
-	is_nick = (ent[0] == '#' || ent[0] == '&' || is_cmd) ? 0 : 1;
 	
 	if (sess->type == SESS_DIALOG && is_nick)
 	{
@@ -1505,7 +1522,7 @@ key_action_tab_comp (GtkWidget *t, GdkEventKey *entry, char *d1, char *d2,
 		if (rfc_ncasecmp (sess->channel, ent, elen) == 0)
 		{
 			result =  sess->channel;
-			is_nick = 0;
+			is_nick = FALSE;
 		}
 		else
 			return 2;
@@ -1562,7 +1579,7 @@ key_action_tab_comp (GtkWidget *t, GdkEventKey *entry, char *d1, char *d2,
 			{
 				if(rfc_ncasecmp(list->data, ent, elen) == 0)
 				{
-					found = 1;
+					found = TRUE;
 					break;
 				}
 				list = list->next;
@@ -1600,7 +1617,7 @@ key_action_tab_comp (GtkWidget *t, GdkEventKey *entry, char *d1, char *d2,
 			old_gcomp.elen = elen;
 
 			/* Get the first nick and put out the data for future nickcompletes */
-			if (prefs.hex_completion_amount && g_list_length (list) <= prefs.hex_completion_amount)
+			if (prefs.hex_completion_amount > 0 && g_list_length (list) <= (guint) prefs.hex_completion_amount)
 			{
 				g_free(result);
 				result = (char*)list->data;
@@ -1610,40 +1627,42 @@ key_action_tab_comp (GtkWidget *t, GdkEventKey *entry, char *d1, char *d2,
 				/* bash style completion */
 				if (g_list_next(list) != NULL)
 				{
+					buf = g_string_sized_new (MAX(COMP_BUF, len + NICKLEN));
 					if (strlen (result) > elen) /* the largest common prefix is larger than nick, change the data */
 					{
 						if (prefix_len)
-							g_utf8_strncpy (buf, text, prefix_len);
-						strncat (buf, result, COMP_BUF - prefix_len);
-						cursor_pos = strlen (buf);
+							g_string_append_len (buf, text, offset_to_len (text, prefix_len));
+						g_string_append (buf, result);
+						cursor_pos = buf->len;
 						g_free(result);
 						if (postfix)
 						{
-							strcat (buf, " ");
-							strncat (buf, postfix, COMP_BUF - cursor_pos -1);
+							g_string_append_c (buf, ' ');
+							g_string_append (buf, postfix);
 						}
-						SPELL_ENTRY_SET_TEXT (t, buf);
-						SPELL_ENTRY_SET_POS (t, g_utf8_pointer_to_offset(buf, buf + cursor_pos));
-						buf[0] = 0;
+						SPELL_ENTRY_SET_TEXT (t, buf->str);
+						SPELL_ENTRY_SET_POS (t, len_to_offset (buf->str, cursor_pos));
+						g_string_erase (buf, 0, -1);
 					}
 					else
 						g_free(result);
+
 					while (list)
 					{
-						len = strlen (buf);	/* current buffer */
+						len = buf->len;
 						elen = strlen (list->data);	/* next item to add */
 						if (len + elen + 2 >= COMP_BUF) /* +2 is space + null */
 						{
-							PrintText (sess, buf);
-							buf[0] = 0;
-							len = 0;
+							PrintText (sess, buf->str);
+							g_string_erase (buf, 0, -1);
 						}
-						strcpy (buf + len, (char *) list->data);
-						strcpy (buf + len + elen, " ");
+						g_string_append (buf, (char*)list->data);
+						g_string_append_c (buf, ' ');
 						list = list->next;
 					}
-					PrintText (sess, buf);
+					PrintText (sess, buf->str);
 					g_completion_free(gcomp);
+					g_string_free (buf, TRUE);
 					return 2;
 				}
 				/* Only one matching entry */
@@ -1655,17 +1674,19 @@ key_action_tab_comp (GtkWidget *t, GdkEventKey *entry, char *d1, char *d2,
 	
 	if(result)
 	{
+		buf = g_string_sized_new (len + NICKLEN);
 		if (prefix_len)
-			g_utf8_strncpy(buf, text, prefix_len);
-		strncat (buf, result, COMP_BUF - (prefix_len + 3)); /* make sure nicksuffix and space fits */
-		if(!prefix_len && is_nick)
-			strcat (buf, &prefs.hex_completion_suffix[0]);
-		strcat (buf, " ");
-		cursor_pos = strlen (buf);
+			g_string_append_len (buf, text, offset_to_len (text, prefix_len));
+		g_string_append (buf, result);
+		if((!prefix_len || has_nick_prefix) && is_nick && prefs.hex_completion_suffix[0] != '\0')
+			g_string_append_unichar (buf, g_utf8_get_char_validated (prefs.hex_completion_suffix, -1));
+		g_string_append_c (buf, ' ');
+		cursor_pos = buf->len;
 		if (postfix)
-			strncat (buf, postfix, COMP_BUF - cursor_pos - 2);
-		SPELL_ENTRY_SET_TEXT (t, buf);
-		SPELL_ENTRY_SET_POS (t, g_utf8_pointer_to_offset(buf, buf + cursor_pos));
+			g_string_append (buf, postfix);
+		SPELL_ENTRY_SET_TEXT (t, buf->str);
+		SPELL_ENTRY_SET_POS (t, len_to_offset (buf->str, cursor_pos));
+		g_string_free (buf, TRUE);
 	}
 	if (gcomp)
 		g_completion_free(gcomp);
@@ -1794,10 +1815,10 @@ replace_handle (GtkWidget *t)
 			memcpy (outbuf, text, xlen);
 			outbuf[xlen] = 0;
 			if (postfix_pnt == NULL)
-				snprintf (word, sizeof (word), "%s", pop->cmd);
+				g_snprintf (word, sizeof (word), "%s", pop->cmd);
 			else
-				snprintf (word, sizeof (word), "%s%s", pop->cmd, postfix);
-			strcat (outbuf, word);
+				g_snprintf (word, sizeof (word), "%s%s", pop->cmd, postfix);
+			g_strlcat (outbuf, word, sizeof(outbuf));
 			SPELL_ENTRY_SET_TEXT (t, outbuf);
 			SPELL_ENTRY_SET_POS (t, -1);
 			return;
diff --git a/src/fe-gtk/gtkutil.c b/src/fe-gtk/gtkutil.c
index eabe9c75..e2ca1192 100644
--- a/src/fe-gtk/gtkutil.c
+++ b/src/fe-gtk/gtkutil.c
@@ -62,7 +62,7 @@ static void
 gtkutil_file_req_destroy (GtkWidget * wid, struct file_req *freq)
 {
 	freq->callback (freq->userdata, NULL);
-	free (freq);
+	g_free (freq);
 }
 
 static void
@@ -255,7 +255,7 @@ gtkutil_file_req (const char *title, void *callback, void *userdata, char *filte
 
 	gtk_file_chooser_add_shortcut_folder (GTK_FILE_CHOOSER (dialog), get_xdir (), NULL);
 
-	freq = malloc (sizeof (struct file_req));
+	freq = g_new (struct file_req, 1);
 	freq->dialog = dialog;
 	freq->flags = flags;
 	freq->callback = callback;
diff --git a/src/fe-gtk/gtkutil.h b/src/fe-gtk/gtkutil.h
index 87beed08..9547cb3b 100644
--- a/src/fe-gtk/gtkutil.h
+++ b/src/fe-gtk/gtkutil.h
@@ -33,14 +33,6 @@ GtkWidget *gtkutil_button (GtkWidget *box, char *stock, char *tip, void *callbac
 void gtkutil_label_new (char *text, GtkWidget * box);
 GtkWidget *gtkutil_entry_new (int max, GtkWidget * box, void *callback,
 										gpointer userdata);
-GtkWidget *gtkutil_clist_new (int columns, char *titles[], GtkWidget * box,
-										int policy, void *select_callback,
-										gpointer select_userdata,
-										void *unselect_callback,
-										gpointer unselect_userdata, int selection_mode);
-int gtkutil_clist_selection (GtkWidget * clist);
-int gtkutil_clist_multiple_selection (GtkWidget * clist,
-													int ** rows, const int max_rows);
 void show_and_unfocus (GtkWidget * wid);
 void gtkutil_set_icon (GtkWidget *win);
 GtkWidget *gtkutil_window_new (char *title, char *role, int width, int height, int flags);
diff --git a/src/fe-gtk/hexchat.exe.manifest b/src/fe-gtk/hexchat.exe.manifest
deleted file mode 100644
index 39c4eb4c..00000000
--- a/src/fe-gtk/hexchat.exe.manifest
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
-  <assemblyIdentity
-    name="HexChat"
-    processorArchitecture="*"
-    version="1.0.0.0"
-    type="win32"
-  />
-  <description>HexChat IRC Client</description>
-  <dependency>
-    <dependentAssembly>
-      <assemblyIdentity
-        type="win32"
-        name="Microsoft.Windows.Common-Controls"
-        version="6.0.0.0"
-        processorArchitecture="*"
-        publicKeyToken="6595b64144ccf1df"
-        language="*"
-      />
-    </dependentAssembly>
-  </dependency>
-</assembly>
diff --git a/src/fe-gtk/hexchat.rc.tt b/src/fe-gtk/hexchat.rc.tt
index 684ddfdb..35b0e6aa 100644
--- a/src/fe-gtk/hexchat.rc.tt
+++ b/src/fe-gtk/hexchat.rc.tt
@@ -1,9 +1,9 @@
 #include <winver.h>

-#include "../../config.h"

+#include "config.h"

 

 #define COMMA_VERSION <#= [string]::Join(',', $versionParts) #>,0

 

-XC_ICON ICON "../../data/icons/hexchat.ico"

+XC_ICON ICON "<#= $env:SOLUTIONDIR -replace '\\', '/' #>data/icons/hexchat.ico"

 

 VS_VERSION_INFO VERSIONINFO

 	FILEVERSION    COMMA_VERSION

@@ -15,6 +15,7 @@ VS_VERSION_INFO VERSIONINFO
 			BEGIN

 				

 				VALUE "FileDescription", "HexChat IRC Client"

+				VALUE "CompanyName", "HexChat"

 				VALUE "ProductName", "HexChat"

 				VALUE "ProductVersion", PACKAGE_VERSION

 				VALUE "FileVersion", PACKAGE_VERSION

diff --git a/src/fe-gtk/joind.c b/src/fe-gtk/joind.c
index 61ce8828..2fdbeb32 100644
--- a/src/fe-gtk/joind.c
+++ b/src/fe-gtk/joind.c
@@ -151,16 +151,16 @@ joind_show_dialog (server *serv)
 	image1 = gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_LARGE_TOOLBAR);
 	gtk_widget_show (image1);
 	gtk_box_pack_start (GTK_BOX (hbox1), image1, FALSE, TRUE, 24);
-	gtk_misc_set_alignment (GTK_MISC (image1), 0.5, 0.06);
+	gtk_misc_set_alignment (GTK_MISC (image1), 0.5f, 0.06f);
 
 	vbox2 = gtk_vbox_new (FALSE, 10);
 	gtk_container_set_border_width (GTK_CONTAINER (vbox2), 6);
 	gtk_widget_show (vbox2);
 	gtk_box_pack_start (GTK_BOX (hbox1), vbox2, TRUE, TRUE, 0);
 
-	snprintf (buf2, sizeof (buf2), _("Connection to %s complete."),
+	g_snprintf (buf2, sizeof (buf2), _("Connection to %s complete."),
 				 server_get_network (serv, TRUE));
-	snprintf (buf, sizeof (buf), "\n<b>%s</b>", buf2);
+	g_snprintf (buf, sizeof (buf), "\n<b>%s</b>", buf2);
 	label = gtk_label_new (buf);
 	gtk_widget_show (label);
 	gtk_box_pack_start (GTK_BOX (vbox2), label, FALSE, FALSE, 0);
@@ -198,7 +198,7 @@ joind_show_dialog (server *serv)
 	gtk_widget_show (entry1);
 	gtk_box_pack_start (GTK_BOX (hbox2), entry1, TRUE, TRUE, 8);
 
-	snprintf (buf, sizeof (buf), "<small>     %s</small>",
+	g_snprintf (buf, sizeof (buf), "<small>     %s</small>",
 				 _("If you know the name of the channel you want to join, enter it here."));
 	label = gtk_label_new (buf);
 	gtk_widget_show (label);
@@ -210,9 +210,8 @@ joind_show_dialog (server *serv)
 	gtk_widget_show (radiobutton3);
 	gtk_box_pack_start (GTK_BOX (vbox2), radiobutton3, FALSE, FALSE, 0);
 	gtk_radio_button_set_group (GTK_RADIO_BUTTON (radiobutton3), radiobutton1_group);
-	radiobutton1_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (radiobutton3));
 
-	snprintf (buf, sizeof (buf), "<small>     %s</small>",
+	g_snprintf (buf, sizeof (buf), "<small>     %s</small>",
 				 _("Retrieving the Channel-List may take a minute or two."));
 	label = gtk_label_new (buf);
 	gtk_widget_show (label);
@@ -250,7 +249,6 @@ joind_show_dialog (server *serv)
 		if (g_ascii_strcasecmp(((ircnet*)serv->network)->name, "freenode") == 0)
 		{
 			gtk_entry_set_text (GTK_ENTRY (entry1), "#hexchat");
-			gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(radiobutton2), TRUE);
 		}
 
 	gtk_widget_grab_focus (okbutton1);
diff --git a/src/fe-gtk/maingui.c b/src/fe-gtk/maingui.c
index 2d50a98c..d718dba0 100644
--- a/src/fe-gtk/maingui.c
+++ b/src/fe-gtk/maingui.c
@@ -78,7 +78,7 @@ static void mg_link_irctab (session *sess, int focus);
 static session_gui static_mg_gui;
 static session_gui *mg_gui = NULL;	/* the shared irc tab */
 static int ignore_chanmode = FALSE;
-static const char chan_flags[] = { 'c', 'n', 'r', 't', 'i', 'm', 'l', 'k' };
+static const char chan_flags[] = { 'c', 'n', 't', 'i', 'm', 'l', 'k' };
 
 static chan *active_tab = NULL;	/* active tab */
 GtkWidget *parent_window = NULL;			/* the master window */
@@ -312,7 +312,7 @@ mg_inputbox_cb (GtkWidget *igad, session_gui *gui)
 	if (cmd[0] == 0)
 		return;
 
-	cmd = strdup (cmd);
+	cmd = g_strdup (cmd);
 
 	/* avoid recursive loop */
 	ignore = TRUE;
@@ -340,7 +340,7 @@ mg_inputbox_cb (GtkWidget *igad, session_gui *gui)
 	if (sess)
 		handle_multiline (sess, cmd, TRUE, FALSE);
 
-	free (cmd);
+	g_free (cmd);
 }
 
 static gboolean
@@ -393,42 +393,42 @@ fe_set_title (session *sess)
 	switch (type)
 	{
 	case SESS_DIALOG:
-		snprintf (tbuf, sizeof (tbuf), DISPLAY_NAME": %s %s @ %s",
+		g_snprintf (tbuf, sizeof (tbuf), DISPLAY_NAME": %s %s @ %s",
 					 _("Dialog with"), sess->channel, server_get_network (sess->server, TRUE));
 		break;
 	case SESS_SERVER:
-		snprintf (tbuf, sizeof (tbuf), DISPLAY_NAME": %s @ %s",
+		g_snprintf (tbuf, sizeof (tbuf), DISPLAY_NAME": %s @ %s",
 					 sess->server->nick, server_get_network (sess->server, TRUE));
 		break;
 	case SESS_CHANNEL:
 		/* don't display keys in the titlebar */
 		if (prefs.hex_gui_win_modes)
 		{
-			snprintf (tbuf, sizeof (tbuf),
+			g_snprintf (tbuf, sizeof (tbuf),
 						 DISPLAY_NAME": %s @ %s / %s (%s)",
 						 sess->server->nick, server_get_network (sess->server, TRUE),
 						 sess->channel, sess->current_modes ? sess->current_modes : "");
 		}
 		else
 		{
-			snprintf (tbuf, sizeof (tbuf),
+			g_snprintf (tbuf, sizeof (tbuf),
 						 DISPLAY_NAME": %s @ %s / %s",
 						 sess->server->nick, server_get_network (sess->server, TRUE),
 						 sess->channel);
 		}
 		if (prefs.hex_gui_win_ucount)
 		{
-			snprintf (tbuf + strlen (tbuf), 9, " (%d)", sess->total);
+			g_snprintf (tbuf + strlen (tbuf), 9, " (%d)", sess->total);
 		}
 		break;
 	case SESS_NOTICES:
 	case SESS_SNOTICES:
-		snprintf (tbuf, sizeof (tbuf), DISPLAY_NAME": %s @ %s (notices)",
+		g_snprintf (tbuf, sizeof (tbuf), DISPLAY_NAME": %s @ %s (notices)",
 					 sess->server->nick, server_get_network (sess->server, TRUE));
 		break;
 	default:
 	def:
-		snprintf (tbuf, sizeof (tbuf), DISPLAY_NAME);
+		g_snprintf (tbuf, sizeof (tbuf), DISPLAY_NAME);
 		gtk_window_set_title (GTK_WINDOW (sess->gui->window), tbuf);
 		return;
 	}
@@ -557,7 +557,7 @@ static int
 mg_progressbar_update (GtkWidget *bar)
 {
 	static int type = 0;
-	static float pos = 0;
+	static gdouble pos = 0;
 
 	pos += 0.05;
 	if (pos >= 0.99)
@@ -609,14 +609,14 @@ mg_unpopulate (session *sess)
 	gui = sess->gui;
 	res = sess->res;
 
-	res->input_text = strdup (SPELL_ENTRY_GET_TEXT (gui->input_box));
-	res->topic_text = strdup (gtk_entry_get_text (GTK_ENTRY (gui->topic_entry)));
-	res->limit_text = strdup (gtk_entry_get_text (GTK_ENTRY (gui->limit_entry)));
-	res->key_text = strdup (gtk_entry_get_text (GTK_ENTRY (gui->key_entry)));
+	res->input_text = g_strdup (SPELL_ENTRY_GET_TEXT (gui->input_box));
+	res->topic_text = g_strdup (gtk_entry_get_text (GTK_ENTRY (gui->topic_entry)));
+	res->limit_text = g_strdup (gtk_entry_get_text (GTK_ENTRY (gui->limit_entry)));
+	res->key_text = g_strdup (gtk_entry_get_text (GTK_ENTRY (gui->key_entry)));
 	if (gui->laginfo)
-		res->lag_text = strdup (gtk_label_get_text (GTK_LABEL (gui->laginfo)));
+		res->lag_text = g_strdup (gtk_label_get_text (GTK_LABEL (gui->laginfo)));
 	if (gui->throttleinfo)
-		res->queue_text = strdup (gtk_label_get_text (GTK_LABEL (gui->throttleinfo)));
+		res->queue_text = g_strdup (gtk_label_get_text (GTK_LABEL (gui->throttleinfo)));
 
 	for (i = 0; i < NUM_FLAG_WIDS - 1; i++)
 		res->flag_wid_state[i] = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (gui->flag_wid[i]));
@@ -645,7 +645,7 @@ mg_restore_label (GtkWidget *label, char **text)
 	if (*text)
 	{
 		gtk_label_set_text (GTK_LABEL (label), *text);
-		free (*text);
+		g_free (*text);
 		*text = NULL;
 	} else
 	{
@@ -659,7 +659,7 @@ mg_restore_entry (GtkWidget *entry, char **text)
 	if (*text)
 	{
 		gtk_entry_set_text (GTK_ENTRY (entry), *text);
-		free (*text);
+		g_free (*text);
 		*text = NULL;
 	} else
 	{
@@ -674,7 +674,7 @@ mg_restore_speller (GtkWidget *entry, char **text)
 	if (*text)
 	{
 		SPELL_ENTRY_SET_TEXT (entry, *text);
-		free (*text);
+		g_free (*text);
 		*text = NULL;
 	} else
 	{
@@ -1096,8 +1096,11 @@ mg_tab_close (session *sess)
 	else
 	{
 		for (i = 0, list = sess_list; list; list = list->next)
-			if (((session *)list->data)->server == sess->server)
+		{
+			session *s = (session*)list->data;
+			if (s->server == sess->server && (s->type == SESS_CHANNEL || s->type == SESS_DIALOG))
 				i++;
+		}
 		dialog = gtk_message_dialog_new (GTK_WINDOW (parent_window), 0,
 						GTK_MESSAGE_WARNING, GTK_BUTTONS_OK_CANCEL,
 						_("This server still has %d channels or dialogs associated with it. "
@@ -1331,8 +1334,7 @@ mg_close_gen (chan *ch, GtkWidget *box)
 {
 	char *title = g_object_get_data (G_OBJECT (box), "title");
 
-	if (title)
-		free (title);
+	g_free (title);
 	if (!ch)
 		ch = g_object_get_data (G_OBJECT (box), "ch");
 	if (ch)
@@ -1568,7 +1570,7 @@ mg_create_tabmenu (session *sess, GdkEventButton *event, chan *ch)
 	if (sess)
 	{
 		char *name = g_markup_escape_text (sess->channel[0] ? sess->channel : _("<none>"), -1);
-		snprintf (buf, sizeof (buf), "<span foreground=\"#3344cc\"><b>%s</b></span>", name);
+		g_snprintf (buf, sizeof (buf), "<span foreground=\"#3344cc\"><b>%s</b></span>", name);
 		g_free (name);
 
 		item = gtk_menu_item_new_with_label ("");
@@ -1617,7 +1619,7 @@ static gboolean
 mg_tab_contextmenu_cb (chanview *cv, chan *ch, int tag, gpointer ud, GdkEventButton *event)
 {
 	/* middle-click or shift-click to close a tab */
-	if ((event->button == 2 || (event->button == 1 && event->state & STATE_SHIFT))
+	if (((prefs.hex_gui_tab_middleclose && event->button == 2) || (event->button == 1 && event->state & STATE_SHIFT))
 		&& event->type == GDK_BUTTON_PRESS)
 	{
 		mg_xbutton_cb (cv, ch, tag, ud);
@@ -1640,7 +1642,7 @@ mg_dnd_drop_file (session *sess, char *target, char *uri)
 {
 	char *p, *data, *next, *fname;
 
-	p = data = strdup (uri);
+	p = data = g_strdup (uri);
 	while (*p)
 	{
 		next = strchr (p, '\r');
@@ -1652,7 +1654,7 @@ mg_dnd_drop_file (session *sess, char *target, char *uri)
 			if (fname)
 			{
 				/* dcc_send() expects utf-8 */
-				p = hexchat_filename_to_utf8 (fname, -1, 0, 0, 0);
+				p = g_filename_from_utf8 (fname, -1, 0, 0, 0);
 				if (p)
 				{
 					dcc_send (sess, target, p, prefs.hex_dcc_max_send_cps, 0);
@@ -1667,7 +1669,7 @@ mg_dnd_drop_file (session *sess, char *target, char *uri)
 		if (*p == '\n')
 			p++;
 	}
-	free (data);
+	g_free (data);
 
 }
 
@@ -1716,7 +1718,7 @@ mg_add_chan (session *sess)
 	{
 		sess->res->buffer = gtk_xtext_buffer_new (GTK_XTEXT (sess->gui->xtext));
 		gtk_xtext_set_time_stamp (sess->res->buffer, prefs.hex_stamp_text);
-		sess->res->user_model = userlist_create_model ();
+		sess->res->user_model = userlist_create_model (sess);
 	}
 }
 
@@ -1837,7 +1839,7 @@ mg_changui_destroy (session *sess)
 		/* it fixes: Gdk-CRITICAL **: gdk_colormap_get_screen: */
 		/*           assertion `GDK_IS_COLORMAP (cmap)' failed */
 		ret = sess->gui->window;
-		free (sess->gui);
+		g_free (sess->gui);
 		sess->gui = NULL;
 	}
 	return ret;
@@ -1941,7 +1943,7 @@ flagl_hit (GtkWidget * wid, struct session *sess)
 				gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (wid), FALSE);
 				return;
 			}
-			snprintf (modes, sizeof (modes), "+l %d", atoi (limit_str));
+			g_snprintf (modes, sizeof (modes), "+l %d", atoi (limit_str));
 			serv->p_mode (serv, sess->channel, modes);
 			serv->p_join_info (serv, sess->channel);
 		}
@@ -1957,7 +1959,7 @@ flagk_hit (GtkWidget * wid, struct session *sess)
 
 	if (serv->connected && sess->channel[0])
 	{
-		snprintf (modes, sizeof (modes), "-k %s", 
+		g_snprintf (modes, sizeof (modes), "-k %s", 
 			  gtk_entry_get_text (GTK_ENTRY (sess->gui->key_entry)));
 
 		if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (wid)))
@@ -2001,17 +2003,24 @@ mg_flagbutton_cb (GtkWidget *but, char *flag)
 static GtkWidget *
 mg_create_flagbutton (char *tip, GtkWidget *box, char *face)
 {
-	GtkWidget *wid;
+	GtkWidget *btn, *lbl;
+	char label_markup[16];
+
+	g_snprintf (label_markup, sizeof(label_markup), "<tt>%s</tt>", face);
+	lbl = gtk_label_new (NULL);
+	gtk_label_set_markup (GTK_LABEL(lbl), label_markup);
 
-	wid = gtk_toggle_button_new_with_label (face);
-	gtk_widget_set_size_request (wid, 18, 0);
-	gtk_widget_set_tooltip_text (wid, tip);
-	gtk_box_pack_start (GTK_BOX (box), wid, 0, 0, 0);
-	g_signal_connect (G_OBJECT (wid), "toggled",
+	btn = gtk_toggle_button_new ();
+	gtk_widget_set_size_request (btn, -1, 0);
+	gtk_widget_set_tooltip_text (btn, tip);
+	gtk_container_add (GTK_CONTAINER(btn), lbl);
+
+	gtk_box_pack_start (GTK_BOX (box), btn, 0, 0, 0);
+	g_signal_connect (G_OBJECT (btn), "toggled",
 							G_CALLBACK (mg_flagbutton_cb), face);
-	show_and_unfocus (wid);
+	show_and_unfocus (btn);
 
-	return wid;
+	return btn;
 }
 
 static void
@@ -2023,7 +2032,7 @@ mg_key_entry_cb (GtkWidget * igad, gpointer userdata)
 
 	if (serv->connected && sess->channel[0])
 	{
-		snprintf (modes, sizeof (modes), "+k %s",
+		g_snprintf (modes, sizeof (modes), "+k %s",
 				gtk_entry_get_text (GTK_ENTRY (igad)));
 		serv->p_mode (serv, sess->channel, modes);
 		serv->p_join_info (serv, sess->channel);
@@ -2046,7 +2055,7 @@ mg_limit_entry_cb (GtkWidget * igad, gpointer userdata)
 			gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (sess->gui->flag_l), FALSE);
 			return;
 		}
-		snprintf (modes, sizeof(modes), "+l %d", 
+		g_snprintf (modes, sizeof(modes), "+l %d", 
 				atoi (gtk_entry_get_text (GTK_ENTRY (igad))));
 		serv->p_mode (serv, sess->channel, modes);
 		serv->p_join_info (serv, sess->channel);
@@ -2066,7 +2075,6 @@ mg_create_chanmodebuttons (session_gui *gui, GtkWidget *box)
 {
 	gui->flag_c = mg_create_flagbutton (_("Filter Colors"), box, "c");
 	gui->flag_n = mg_create_flagbutton (_("No outside messages"), box, "n");
-	gui->flag_r = mg_create_flagbutton (_("Registered Only"), box, "r");
 	gui->flag_t = mg_create_flagbutton (_("Topic Protection"), box, "t");
 	gui->flag_i = mg_create_flagbutton (_("Invite Only"), box, "i");
 	gui->flag_m = mg_create_flagbutton (_("Moderated"), box, "m");
@@ -2287,7 +2295,7 @@ mg_word_clicked (GtkWidget *xtext, char *word, GdkEventButton *even)
 	case WORD_EMAIL:
 		word[end] = 0;
 		word += start;
-		tmp = g_strdup_printf("mailto:%s", word + (ispunct (*word)? 1: 0));
+		tmp = g_strdup_printf ("mailto:%s", word + (ispunct (*word) ? 1 : 0));
 		menu_urlmenu (even, tmp);
 		g_free (tmp);
 		break;
@@ -2597,7 +2605,7 @@ mg_change_nick (int cancel, char *text, gpointer userdata)
 
 	if (!cancel)
 	{
-		snprintf (buf, sizeof (buf), "nick %s", text);
+		g_snprintf (buf, sizeof (buf), "nick %s", text);
 		handle_command (current_sess, buf, FALSE);
 	}
 }
@@ -2940,7 +2948,7 @@ mg_create_search(session *sess, GtkWidget *box)
 	gtk_box_pack_start(GTK_BOX(gui->shbox), next, FALSE, FALSE, 0);
 	g_signal_connect(G_OBJECT(next), "clicked", G_CALLBACK(mg_search_handle_next), sess);
 
-	highlight = gtk_check_button_new_with_mnemonic (_("Highlight _all"));
+	highlight = gtk_check_button_new_with_mnemonic (_("_Highlight all"));
 	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(highlight), prefs.hex_text_search_highlight_all);
 	gtk_widget_set_can_focus (highlight, FALSE);
 	g_signal_connect (G_OBJECT (highlight), "toggled", G_CALLBACK (search_set_option), &prefs.hex_text_search_highlight_all);
@@ -3165,7 +3173,7 @@ mg_create_topwindow (session *sess)
 		sess->res->buffer = gtk_xtext_buffer_new (GTK_XTEXT (sess->gui->xtext));
 		gtk_xtext_buffer_show (GTK_XTEXT (sess->gui->xtext), sess->res->buffer, TRUE);
 		gtk_xtext_set_time_stamp (sess->res->buffer, prefs.hex_stamp_text);
-		sess->res->user_model = userlist_create_model ();
+		sess->res->user_model = userlist_create_model (sess);
 	}
 
 	userlist_show (sess);
@@ -3332,7 +3340,7 @@ mg_add_generic_tab (char *name, char *title, void *family, GtkWidget *box)
 	ch = chanview_add (mg_gui->chanview, name, NULL, box, TRUE, TAG_UTIL, pix_tree_util);
 	chan_set_color (ch, plain_list);
 	/* FIXME: memory leak */
-	g_object_set_data (G_OBJECT (box), "title", strdup (title));
+	g_object_set_data (G_OBJECT (box), "title", g_strdup (title));
 	g_object_set_data (G_OBJECT (box), "ch", ch);
 
 	if (prefs.hex_gui_tab_newtofront)
@@ -3395,7 +3403,7 @@ fe_clear_channel (session *sess)
 	{
 		if (sess->res->topic_text)
 		{
-			free (sess->res->topic_text);
+			g_free (sess->res->topic_text);
 			sess->res->topic_text = NULL;
 		}
 	}
@@ -3506,32 +3514,26 @@ mg_changui_new (session *sess, restore_gui *res, int tab, int focus)
 {
 	int first_run = FALSE;
 	session_gui *gui;
-	struct User *user = NULL;
 
-	if (!res)
+	if (res == NULL)
 	{
-		res = malloc (sizeof (restore_gui));
-		memset (res, 0, sizeof (restore_gui));
+		res = g_new0 (restore_gui, 1);
 	}
 
 	sess->res = res;
 
-	if (!sess->server->front_session)
+	if (sess->server->front_session == NULL)
+	{
 		sess->server->front_session = sess;
-
-	if (!is_channel (sess->server, sess->channel))
-		user = userlist_find_global (sess->server, sess->channel);
+	}
 
 	if (!tab)
 	{
-		gui = malloc (sizeof (session_gui));
-		memset (gui, 0, sizeof (session_gui));
+		gui = g_new0 (session_gui, 1);
 		gui->is_tab = FALSE;
 		sess->gui = gui;
 		mg_create_topwindow (sess);
 		fe_set_title (sess);
-		if (user && user->hostname)
-			set_topic (sess, user->hostname, user->hostname);
 		return;
 	}
 
@@ -3551,9 +3553,6 @@ mg_changui_new (session *sess, restore_gui *res, int tab, int focus)
 		gui->is_tab = TRUE;
 	}
 
-	if (user && user->hostname)
-		set_topic (sess, user->hostname, user->hostname);
-
 	mg_add_chan (sess);
 
 	if (first_run || (prefs.hex_gui_tab_newtofront == FOCUS_NEW_ONLY_ASKED && focus)
@@ -3631,8 +3630,8 @@ mg_set_title (GtkWidget *vbox, char *title) /* for non-irc tab/window only */
 	old = g_object_get_data (G_OBJECT (vbox), "title");
 	if (old)
 	{
-		g_object_set_data (G_OBJECT (vbox), "title", strdup (title));
-		free (old);
+		g_object_set_data (G_OBJECT (vbox), "title", g_strdup (title));
+		g_free (old);
 	} else
 	{
 		gtk_window_set_title (GTK_WINDOW (vbox), title);
@@ -3650,7 +3649,7 @@ fe_server_callback (server *serv)
 	if (serv->gui->rawlog_window)
 		mg_close_gen (NULL, serv->gui->rawlog_window);
 
-	free (serv->gui);
+	g_free (serv->gui);
 }
 
 /* called when a session is being killed */
@@ -3661,34 +3660,21 @@ fe_session_callback (session *sess)
 	if (sess->res->banlist && sess->res->banlist->window)
 		mg_close_gen (NULL, sess->res->banlist->window);
 
-	if (sess->res->input_text)
-		free (sess->res->input_text);
-
-	if (sess->res->topic_text)
-		free (sess->res->topic_text);
-
-	if (sess->res->limit_text)
-		free (sess->res->limit_text);
-
-	if (sess->res->key_text)
-		free (sess->res->key_text);
-
-	if (sess->res->queue_text)
-		free (sess->res->queue_text);
-	if (sess->res->queue_tip)
-		free (sess->res->queue_tip);
-
-	if (sess->res->lag_text)
-		free (sess->res->lag_text);
-	if (sess->res->lag_tip)
-		free (sess->res->lag_tip);
+	g_free (sess->res->input_text);
+	g_free (sess->res->topic_text);
+	g_free (sess->res->limit_text);
+	g_free (sess->res->key_text);
+	g_free (sess->res->queue_text);
+	g_free (sess->res->queue_tip);
+	g_free (sess->res->lag_text);
+	g_free (sess->res->lag_tip);
 
 	if (sess->gui->bartag)
 		fe_timeout_remove (sess->gui->bartag);
 
 	if (sess->gui != &static_mg_gui)
-		free (sess->gui);
-	free (sess->res);
+		g_free (sess->gui);
+	g_free (sess->res);
 }
 
 /* ===== DRAG AND DROP STUFF ===== */
diff --git a/src/fe-gtk/menu.c b/src/fe-gtk/menu.c
index 945f6360..902af92e 100644
--- a/src/fe-gtk/menu.c
+++ b/src/fe-gtk/menu.c
@@ -137,7 +137,7 @@ nick_command_parse (session *sess, char *cmd, char *nick, char *allnick)
 
 	/* this can't overflow, since popup->cmd is only 256 */
 	len = strlen (cmd) + strlen (nick) + strlen (allnick) + 512;
-	buf = malloc (len);
+	buf = g_malloc (len);
 
 	auto_insert (buf, len, cmd, 0, 0, allnick, sess->channel, "",
 					 server_get_network (sess->server, TRUE), host,
@@ -145,7 +145,7 @@ nick_command_parse (session *sess, char *cmd, char *nick, char *allnick)
 
 	nick_command (sess, buf);
 
-	free (buf);
+	g_free (buf);
 }
 
 /* userlist button has been clicked */
@@ -166,11 +166,12 @@ userlist_button_cb (GtkWidget * button, char *cmd)
 	if (sess->type == SESS_DIALOG)
 	{
 		/* fake a selection */
-		nicks = malloc (sizeof (char *) * 2);
+		nicks = g_new (char *, 2);
 		nicks[0] = g_strdup (sess->channel);
 		nicks[1] = NULL;
 		num_sel = 1;
-	} else
+	}
+	else
 	{
 		/* find number of selected rows */
 		nicks = userlist_selection_list (sess->gui->user_tree, &num_sel);
@@ -178,14 +179,13 @@ userlist_button_cb (GtkWidget * button, char *cmd)
 		{
 			nick_command_parse (sess, cmd, "", "");
 
-			if (nicks)
-				free (nicks);
+			g_free (nicks);
 			return;
 		}
 	}
 
 	/* create "allnicks" string */
-	allnicks = malloc (((NICKLEN + 1) * num_sel) + 1);
+	allnicks = g_malloc (((NICKLEN + 1) * num_sel) + 1);
 	*allnicks = 0;
 
 	i = 0;
@@ -218,8 +218,8 @@ userlist_button_cb (GtkWidget * button, char *cmd)
 		g_free (nicks[num_sel]);
 	}
 
-	free (nicks);
-	free (allnicks);
+	g_free (nicks);
+	g_free (allnicks);
 }
 
 /* a popup-menu-item has been selected */
@@ -393,9 +393,9 @@ toggle_cb (GtkWidget *item, char *pref_name)
 	char buf[256];
 
 	if (GTK_CHECK_MENU_ITEM (item)->active)
-		snprintf (buf, sizeof (buf), "set %s 1", pref_name);
+		g_snprintf (buf, sizeof (buf), "set %s 1", pref_name);
 	else
-		snprintf (buf, sizeof (buf), "set %s 0", pref_name);
+		g_snprintf (buf, sizeof (buf), "set %s 0", pref_name);
 
 	handle_command (current_sess, buf, FALSE);
 }
@@ -403,12 +403,11 @@ toggle_cb (GtkWidget *item, char *pref_name)
 static int
 is_in_path (char *cmd)
 {
-	char *prog = g_strdup (cmd + 1);	/* 1st char is "!" */
-	char *path, *orig;
+	char *orig = g_strdup (cmd + 1);	/* 1st char is "!" */
+	char *prog = orig;
 	char **argv;
 	int argc;
 
-	orig = prog; /* save for free()ing */
 	/* special-case these default entries. */
 	/*                  123456789012345678 */
 	if (strncmp (prog, "gnome-terminal -x ", 18) == 0)
@@ -417,15 +416,14 @@ is_in_path (char *cmd)
 
 	if (g_shell_parse_argv (prog, &argc, &argv, NULL))
 	{
-		path = g_find_program_in_path (argv[0]);
+		char *path = g_find_program_in_path (argv[0]);
+		g_strfreev (argv);
 		if (path)
 		{
 			g_free (path);
 			g_free (orig);
-			g_strfreev (argv);
 			return 1;
 		}
-		g_strfreev (argv);
 	}
 
 	g_free (orig);
@@ -588,7 +586,7 @@ menu_nickinfo_cb (GtkWidget *menu, session *sess)
 		return;
 
 	/* issue a /WHOIS */
-	snprintf (buf, sizeof (buf), "WHOIS %s %s", str_copy, str_copy);
+	g_snprintf (buf, sizeof (buf), "WHOIS %s %s", str_copy, str_copy);
 	handle_command (sess, buf, FALSE);
 	/* and hide the output */
 	sess->server->skip_next_whois = 1;
@@ -614,30 +612,30 @@ menu_create_nickinfo_menu (struct User *user, GtkWidget *submenu)
 
 	/* let the translators tweak this if need be */
 	fmt = _("<tt><b>%-11s</b></tt> %s");
-	snprintf (unknown, sizeof (unknown), "<i>%s</i>", _("Unknown"));
+	g_snprintf (unknown, sizeof (unknown), "<i>%s</i>", _("Unknown"));
 
 	if (user->realname)
 	{
 		real = strip_color (user->realname, -1, STRIP_ALL|STRIP_ESCMARKUP);
-		snprintf (buf, sizeof (buf), fmt, _("Real Name:"), real);
+		g_snprintf (buf, sizeof (buf), fmt, _("Real Name:"), real);
 		g_free (real);
 	} else
 	{
-		snprintf (buf, sizeof (buf), fmt, _("Real Name:"), unknown);
+		g_snprintf (buf, sizeof (buf), fmt, _("Real Name:"), unknown);
 	}
 	item = menu_quick_item (0, buf, submenu, XCMENU_MARKUP, 0, 0);
 	g_signal_connect (G_OBJECT (item), "activate",
 							G_CALLBACK (copy_to_clipboard_cb), 
 							user->realname ? user->realname : unknown);
 
-	snprintf (buf, sizeof (buf), fmt, _("User:"),
+	g_snprintf (buf, sizeof (buf), fmt, _("User:"),
 				 user->hostname ? user->hostname : unknown);
 	item = menu_quick_item (0, buf, submenu, XCMENU_MARKUP, 0, 0);
 	g_signal_connect (G_OBJECT (item), "activate",
 							G_CALLBACK (copy_to_clipboard_cb), 
 							user->hostname ? user->hostname : unknown);
 	
-	snprintf (buf, sizeof (buf), fmt, _("Account:"),
+	g_snprintf (buf, sizeof (buf), fmt, _("Account:"),
 				 user->account ? user->account : unknown);
 	item = menu_quick_item (0, buf, submenu, XCMENU_MARKUP, 0, 0);
 	g_signal_connect (G_OBJECT (item), "activate",
@@ -647,13 +645,13 @@ menu_create_nickinfo_menu (struct User *user, GtkWidget *submenu)
 	users_country = country (user->hostname);
 	if (users_country)
 	{
-		snprintf (buf, sizeof (buf), fmt, _ ("Country:"), users_country);
+		g_snprintf (buf, sizeof (buf), fmt, _ ("Country:"), users_country);
 		item = menu_quick_item (0, buf, submenu, XCMENU_MARKUP, 0, 0);
 		g_signal_connect (G_OBJECT (item), "activate",
 			G_CALLBACK (copy_to_clipboard_cb), users_country);
 	}
 
-	snprintf (buf, sizeof (buf), fmt, _("Server:"),
+	g_snprintf (buf, sizeof (buf), fmt, _("Server:"),
 				 user->servername ? user->servername : unknown);
 	item = menu_quick_item (0, buf, submenu, XCMENU_MARKUP, 0, 0);
 	g_signal_connect (G_OBJECT (item), "activate",
@@ -664,12 +662,12 @@ menu_create_nickinfo_menu (struct User *user, GtkWidget *submenu)
 	{
 		char min[96];
 
-		snprintf (min, sizeof (min), _("%u minutes ago"),
+		g_snprintf (min, sizeof (min), _("%u minutes ago"),
 					(unsigned int) ((time (0) - user->lasttalk) / 60));
-		snprintf (buf, sizeof (buf), fmt, _("Last Msg:"), min);
+		g_snprintf (buf, sizeof (buf), fmt, _("Last Msg:"), min);
 	} else
 	{
-		snprintf (buf, sizeof (buf), fmt, _("Last Msg:"), unknown);
+		g_snprintf (buf, sizeof (buf), fmt, _("Last Msg:"), unknown);
 	}
 	menu_quick_item (0, buf, submenu, XCMENU_MARKUP, 0, 0);
 
@@ -679,7 +677,7 @@ menu_create_nickinfo_menu (struct User *user, GtkWidget *submenu)
 		if (away)
 		{
 			char *msg = strip_color (away->message ? away->message : unknown, -1, STRIP_ALL|STRIP_ESCMARKUP);
-			snprintf (buf, sizeof (buf), fmt, _("Away Msg:"), msg);
+			g_snprintf (buf, sizeof (buf), fmt, _("Away Msg:"), msg);
 			g_free (msg);
 			item = menu_quick_item (0, buf, submenu, XCMENU_MARKUP, 0, 0);
 			g_signal_connect (G_OBJECT (item), "activate",
@@ -728,16 +726,15 @@ menu_nickmenu (session *sess, GdkEventButton *event, char *nick, int num_sel)
 	struct User *user;
 	GtkWidget *submenu, *menu = gtk_menu_new ();
 
-	if (str_copy)
-		free (str_copy);
-	str_copy = strdup (nick);
+	g_free (str_copy);
+	str_copy = g_strdup (nick);
 
 	submenu_list = 0;	/* first time through, might not be 0 */
 
 	/* more than 1 nick selected? */
 	if (num_sel > 1)
 	{
-		snprintf (buf, sizeof (buf), _("%d nicks selected."), num_sel);
+		g_snprintf (buf, sizeof (buf), _("%d nicks selected."), num_sel);
 		menu_quick_item (0, buf, menu, 0, 0, 0);
 		menu_quick_item (0, 0, menu, XCMENU_SHADED, 0, 0);
 	} else
@@ -938,7 +935,7 @@ open_url_cb (GtkWidget *item, char *url)
 	char buf[512];
 
 	/* pass this to /URL so it can handle irc:// */
-	snprintf (buf, sizeof (buf), "URL %s", url);
+	g_snprintf (buf, sizeof (buf), "URL %s", url);
 	handle_command (current_sess, buf, FALSE);
 }
 
@@ -948,20 +945,19 @@ menu_urlmenu (GdkEventButton *event, char *url)
 	GtkWidget *menu;
 	char *tmp, *chop;
 
-	if (str_copy)
-		free (str_copy);
-	str_copy = strdup (url);
+	g_free (str_copy);
+	str_copy = g_strdup (url);
 
 	menu = gtk_menu_new ();
 	/* more than 51 chars? Chop it */
 	if (g_utf8_strlen (str_copy, -1) >= 52)
 	{
-		tmp = strdup (str_copy);
+		tmp = g_strdup (str_copy);
 		chop = g_utf8_offset_to_pointer (tmp, 48);
 		chop[0] = chop[1] = chop[2] = '.';
 		chop[3] = 0;
 		menu_quick_item (0, tmp, menu, XCMENU_SHADED, 0, 0);
-		free (tmp);
+		g_free (tmp);
 	} else
 	{
 		menu_quick_item (0, str_copy, menu, XCMENU_SHADED, 0, 0);
@@ -988,7 +984,7 @@ menu_chan_cycle (GtkWidget * menu, char *chan)
 
 	if (current_sess)
 	{
-		snprintf (tbuf, sizeof tbuf, "CYCLE %s", chan);
+		g_snprintf (tbuf, sizeof tbuf, "CYCLE %s", chan);
 		handle_command (current_sess, tbuf, FALSE);
 	}
 }
@@ -1000,7 +996,7 @@ menu_chan_part (GtkWidget * menu, char *chan)
 
 	if (current_sess)
 	{
-		snprintf (tbuf, sizeof tbuf, "part %s", chan);
+		g_snprintf (tbuf, sizeof tbuf, "part %s", chan);
 		handle_command (current_sess, tbuf, FALSE);
 	}
 }
@@ -1012,7 +1008,7 @@ menu_chan_join (GtkWidget * menu, char *chan)
 
 	if (current_sess)
 	{
-		snprintf (tbuf, sizeof tbuf, "join %s", chan);
+		g_snprintf (tbuf, sizeof tbuf, "join %s", chan);
 		handle_command (current_sess, tbuf, FALSE);
 	}
 }
@@ -1026,9 +1022,8 @@ menu_chanmenu (struct session *sess, GdkEventButton * event, char *chan)
 	if (find_channel (sess->server, chan))
 		is_joined = TRUE;
 
-	if (str_copy)
-		free (str_copy);
-	str_copy = strdup (chan);
+	g_free (str_copy);
+	str_copy = g_strdup (chan);
 
 	menu = gtk_menu_new ();
 
@@ -1074,9 +1069,8 @@ menu_addfavoritemenu (server *serv, GtkWidget *menu, char *channel, gboolean ist
 
 	if (channel != str_copy)
 	{
-		if (str_copy)
-			free (str_copy);
-		str_copy = strdup (channel);
+		g_free (str_copy);
+		str_copy = g_strdup (channel);
 	}
 	
 	if (istree)
@@ -1717,7 +1711,7 @@ menu_about (GtkWidget *wid, gpointer sess)
 					"You should have received a copy of the GNU General Public License\n" \
 					"along with this program. If not, see <http://www.gnu.org/licenses/>";
 
-	g_snprintf  (comment, sizeof(comment), "Compiled: "__DATE__"\n"
+	g_snprintf  (comment, sizeof(comment), ""
 #ifdef WIN32
 				"Portable Mode: %s\n"
 				"Build Type: x%d\n"
diff --git a/src/fe-gtk/notifications/notification-backend.h b/src/fe-gtk/notifications/notification-backend.h
new file mode 100644
index 00000000..b60ced4e
--- /dev/null
+++ b/src/fe-gtk/notifications/notification-backend.h
@@ -0,0 +1,27 @@
+/* HexChat
+ * Copyright (C) 2015 Patrick Griffis.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#ifndef HEXCHAT_PLUGIN_NOTIFICATION_BACKEND_H
+#define HEXCHAT_PLUGIN_NOTIFICATION_BACKEND_H
+
+int notification_backend_supported (void);
+void notification_backend_show (const char *title, const char *text);
+int notification_backend_init (void);
+void notification_backend_deinit (void);
+
+#endif
diff --git a/src/fe-gtk/notifications/notification-dummy.c b/src/fe-gtk/notifications/notification-dummy.c
new file mode 100644
index 00000000..022443bf
--- /dev/null
+++ b/src/fe-gtk/notifications/notification-dummy.c
@@ -0,0 +1,39 @@
+/* HexChat
+ * Copyright (C) 2015 Patrick Griffis.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+void
+notification_backend_show (const char *title, const char *text)
+{
+}
+
+int
+notification_backend_init (void)
+{
+	return 0;
+}
+
+void
+notification_backend_deinit (void)
+{
+}
+
+int
+notification_backend_supported (void)
+{
+	return 0;
+}
diff --git a/src/fe-gtk/notifications/notification-libnotify.c b/src/fe-gtk/notifications/notification-libnotify.c
new file mode 100644
index 00000000..94f9679d
--- /dev/null
+++ b/src/fe-gtk/notifications/notification-libnotify.c
@@ -0,0 +1,72 @@
+/* HexChat
+ * Copyright (C) 2015 Patrick Griffis.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include "config.h"
+#include <glib.h>
+#include <libnotify/notify.h>
+
+static gboolean strip_markup = FALSE;
+
+void
+notification_backend_show (const char *title, const char *text)
+{
+	NotifyNotification *notification;
+
+	if (strip_markup)
+		text = g_markup_escape_text (text, -1);
+
+	notification = notify_notification_new (title, text, "hexchat");
+	notify_notification_set_hint (notification, "desktop-entry", g_variant_new_string ("hexchat"));
+
+	notify_notification_show (notification, NULL);
+
+	g_object_unref (notification);
+	if (strip_markup)
+		g_free ((char*)text);
+}
+
+int
+notification_backend_init (void)
+{
+	GList* server_caps;
+
+	if (!NOTIFY_CHECK_VERSION (0, 7, 0))
+		return 0;
+
+	if (!notify_init (PACKAGE_NAME))
+		return 0;
+
+	server_caps = notify_get_server_caps ();
+	if (g_list_find_custom (server_caps, "body-markup", (GCompareFunc)g_strcmp0))
+		strip_markup = TRUE;
+	g_list_free_full (server_caps, g_free);
+
+	return 1;
+}
+
+void
+notification_backend_deinit (void)
+{
+	notify_uninit ();
+}
+
+int
+notification_backend_supported (void)
+{
+	return notify_is_initted ();
+}
diff --git a/src/fe-gtk/notifications/notification-osx.m b/src/fe-gtk/notifications/notification-osx.m
new file mode 100644
index 00000000..c9ad72d0
--- /dev/null
+++ b/src/fe-gtk/notifications/notification-osx.m
@@ -0,0 +1,54 @@
+/* HexChat
+ * Copyright (C) 2015 Patrick Griffis.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#import <Cocoa/Cocoa.h>
+#include <gtkosxapplication.h>
+
+void
+notification_backend_show (const char *title, const char *text)
+{
+	NSString *str_title = [[NSString alloc] initWithUTF8String:title];
+	NSString *str_text = [[NSString alloc] initWithUTF8String:text];
+
+	NSUserNotification *userNotification = [NSUserNotification new];
+	userNotification.title = str_title;
+	userNotification.informativeText = str_text;
+
+	NSUserNotificationCenter *center = [NSUserNotificationCenter defaultUserNotificationCenter];
+	[center scheduleNotification:userNotification];
+
+	[str_title release];
+	[str_text release];
+}
+
+int
+notification_backend_init (void)
+{
+	return 1;
+}
+
+void
+notification_backend_deinit (void)
+{
+}
+
+int
+notification_backend_supported (void)
+{
+	return gtkosx_application_get_bundle_id () != NULL;
+}
diff --git a/src/fe-gtk/notifications/notification-windows.c b/src/fe-gtk/notifications/notification-windows.c
new file mode 100644
index 00000000..3fade306
--- /dev/null
+++ b/src/fe-gtk/notifications/notification-windows.c
@@ -0,0 +1,87 @@
+/* HexChat
+ * Copyright (C) 2015 Arnav Singh.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <gmodule.h>
+
+#include "hexchat.h"
+#include "plugin.h"
+
+#include <Windows.h>
+
+void (*winrt_notification_backend_show) (const char *title, const char *text) = NULL;
+int (*winrt_notification_backend_init) (void) = NULL;
+void (*winrt_notification_backend_deinit) (void) = NULL;
+int (*winrt_notification_backend_supported) (void) = NULL;
+
+void
+notification_backend_show (const char *title, const char *text)
+{
+	if (winrt_notification_backend_show == NULL)
+	{
+		return;
+	}
+
+	winrt_notification_backend_show (title, text);
+}
+
+int
+notification_backend_init (void)
+{
+	UINT original_error_mode;
+	GModule *module;
+
+	/* Temporarily suppress the "DLL could not be loaded" dialog box before trying to load hcnotifications-winrt.dll */
+	original_error_mode = GetErrorMode ();
+	SetErrorMode(SEM_FAILCRITICALERRORS);
+	module = module_load (HEXCHATLIBDIR "\\hcnotifications-winrt.dll");
+	SetErrorMode (original_error_mode);
+
+	if (module == NULL)
+	{
+		return 0;
+	}
+
+	g_module_symbol (module, "notification_backend_show", (gpointer *) &winrt_notification_backend_show);
+	g_module_symbol (module, "notification_backend_init", (gpointer *) &winrt_notification_backend_init);
+	g_module_symbol (module, "notification_backend_deinit", (gpointer *) &winrt_notification_backend_deinit);
+	g_module_symbol (module, "notification_backend_supported", (gpointer *) &winrt_notification_backend_supported);
+
+	return winrt_notification_backend_init ();
+}
+
+void
+notification_backend_deinit (void)
+{
+	if (winrt_notification_backend_deinit == NULL)
+	{
+		return;
+	}
+
+	winrt_notification_backend_deinit ();
+}
+
+int
+notification_backend_supported (void)
+{
+	if (winrt_notification_backend_supported == NULL)
+	{
+		return 0;
+	}
+
+	return winrt_notification_backend_supported ();
+}
diff --git a/src/fe-gtk/notifications/notification-winrt.cpp b/src/fe-gtk/notifications/notification-winrt.cpp
new file mode 100644
index 00000000..663f9c08
--- /dev/null
+++ b/src/fe-gtk/notifications/notification-winrt.cpp
@@ -0,0 +1,100 @@
+/* HexChat
+ * Copyright (c) 2014 Leetsoftwerx
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <string>
+#include <codecvt>
+
+#include <roapi.h>
+#include <windows.ui.notifications.h>
+
+using namespace Windows::UI::Notifications;
+using namespace Windows::Data::Xml::Dom;
+
+static ToastNotifier ^ notifier = nullptr;
+
+static std::wstring
+widen(const std::string & to_widen)
+{
+	std::wstring_convert<std::codecvt_utf8_utf16<wchar_t> > converter;
+	return converter.from_bytes(to_widen);
+}
+
+extern "C"
+{
+	__declspec (dllexport) void
+	notification_backend_show (const char *title, const char *text)
+	{
+		try
+		{
+			auto toastTemplate = ToastNotificationManager::GetTemplateContent (ToastTemplateType::ToastText02);
+			auto node_list = toastTemplate->GetElementsByTagName ("text");
+			UINT node_count = node_list->Length;
+
+			auto wtitle = widen (title);
+			node_list->GetAt (0)->AppendChild (
+				toastTemplate->CreateTextNode (Platform::StringReference (wtitle.c_str (), wtitle.size ())));
+
+			auto wtext = widen (text);
+			node_list->GetAt (1)->AppendChild (
+				toastTemplate->CreateTextNode (Platform::StringReference (wtext.c_str (), wtext.size ())));
+
+			// Mute sound, we already play our own
+			auto node = toastTemplate->SelectSingleNode ("/toast");
+			auto audio_elem = toastTemplate->CreateElement ("audio");
+			audio_elem->SetAttribute ("silent", "true");
+			static_cast<XmlElement^>(node)->AppendChild (audio_elem);
+
+			notifier->Show (ref new ToastNotification (toastTemplate));
+		}
+		catch (Platform::Exception ^ ex)
+		{
+		}
+		catch (...)
+		{
+		}
+	}
+
+	__declspec (dllexport) int
+	notification_backend_init (void)
+	{
+		if (!notifier)
+			notifier = ToastNotificationManager::CreateToastNotifier ("HexChat.Desktop.Notify");
+
+		if (FAILED (Windows::Foundation::Initialize (RO_INIT_SINGLETHREADED)))
+			return 0;
+
+		return 1;
+	}
+
+	__declspec (dllexport) 	void
+	notification_backend_deinit (void)
+	{
+		notifier = nullptr;
+		Windows::Foundation::Uninitialize ();
+	}
+
+	__declspec (dllexport) int
+	notification_backend_supported (void)
+	{
+		return 1;
+	}
+}
diff --git a/src/fe-gtk/notifications/notifications-winrt.vcxproj b/src/fe-gtk/notifications/notifications-winrt.vcxproj
new file mode 100644
index 00000000..dcd2a2b7
--- /dev/null
+++ b/src/fe-gtk/notifications/notifications-winrt.vcxproj
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="utf-8"?>

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

+  <ItemGroup Label="ProjectConfigurations">

+    <ProjectConfiguration Include="Release|Win32">

+      <Configuration>Release</Configuration>

+      <Platform>Win32</Platform>

+    </ProjectConfiguration>

+    <ProjectConfiguration Include="Release|x64">

+      <Configuration>Release</Configuration>

+      <Platform>x64</Platform>

+    </ProjectConfiguration>

+  </ItemGroup>

+  <ItemGroup>

+    <ClCompile Include="notification-winrt.cpp">

+      <CompileAsWinRT>true</CompileAsWinRT>

+    </ClCompile>

+  </ItemGroup>

+  <PropertyGroup Label="Globals">

+    <ProjectGuid>{C53145CC-D021-40C9-B97C-0249AB9A43C9}</ProjectGuid>

+    <Keyword>Win32Proj</Keyword>

+    <RootNamespace>notifications-winrt</RootNamespace>

+    <ProjectName>notifications-winrt</ProjectName>

+  </PropertyGroup>

+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />

+  <PropertyGroup Label="Configuration">

+    <PlatformToolset>v120</PlatformToolset>

+    <ConfigurationType>DynamicLibrary</ConfigurationType>

+    <CharacterSet>Unicode</CharacterSet>

+  </PropertyGroup>

+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />

+  <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />

+  <Import Project="..\..\..\win32\hexchat.props" />

+  <PropertyGroup>

+    <TargetName>hcnotifications-winrt</TargetName>

+    <OutDir>$(HexChatRel)plugins\</OutDir>

+  </PropertyGroup>

+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">

+    <ClCompile>

+      <PreprocessorDefinitions>WIN32;_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES;_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES_MEMORY;_CRT_SECURE_CPP_OVERLOAD_SECURE_NAMES_MEMORY;_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES_COUNT;NDEBUG;_WINDOWS;_USRDLL;NOTIFICATIONS_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>

+      <SDLCheck>true</SDLCheck>

+      <AdditionalUsingDirectories>$(VCInstallDir)vcpackages;$(FrameworkSdkDir)References\CommonConfiguration\Neutral;%(AdditionalUsingDirectories)</AdditionalUsingDirectories>

+    </ClCompile>

+    <Link>

+      <AdditionalDependencies>$(DepLibs);mincore.lib;runtimeobject.lib;%(AdditionalDependencies)</AdditionalDependencies>

+      <MinimumRequiredVersion>6.03</MinimumRequiredVersion>

+      <AdditionalLibraryDirectories>$(DepsRoot)\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>

+    </Link>

+  </ItemDefinitionGroup>

+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">

+    <ClCompile>

+      <PreprocessorDefinitions>WIN32;_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES;_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES_MEMORY;_CRT_SECURE_CPP_OVERLOAD_SECURE_NAMES_MEMORY;_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES_COUNT;NDEBUG;_WINDOWS;_USRDLL;NOTIFICATIONS_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>

+      <SDLCheck>true</SDLCheck>

+      <AdditionalUsingDirectories>$(VCInstallDir)vcpackages;$(FrameworkSdkDir)References\CommonConfiguration\Neutral;%(AdditionalUsingDirectories)</AdditionalUsingDirectories>

+    </ClCompile>

+    <Link>

+      <AdditionalDependencies>$(DepLibs);mincore.lib;runtimeobject.lib;%(AdditionalDependencies)</AdditionalDependencies>

+      <MinimumRequiredVersion>6.03</MinimumRequiredVersion>

+      <AdditionalLibraryDirectories>$(DepsRoot)\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>

+    </Link>

+  </ItemDefinitionGroup>

+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />

+</Project>

diff --git a/src/fe-gtk/notifications/notifications-winrt.vcxproj.filters b/src/fe-gtk/notifications/notifications-winrt.vcxproj.filters
new file mode 100644
index 00000000..06f4e558
--- /dev/null
+++ b/src/fe-gtk/notifications/notifications-winrt.vcxproj.filters
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <Filter Include="Source Files">
+      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+      <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+    </Filter>
+    <Filter Include="Header Files">
+      <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
+      <Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
+    </Filter>
+    <Filter Include="Resource Files">
+      <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
+      <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
+    </Filter>
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="notification-winrt.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+  </ItemGroup>
+</Project>
\ No newline at end of file
diff --git a/src/fe-gtk/notifygui.c b/src/fe-gtk/notifygui.c
index 5aa60d0a..ed16f44f 100644
--- a/src/fe-gtk/notifygui.c
+++ b/src/fe-gtk/notifygui.c
@@ -190,11 +190,11 @@ notify_gui_update (void)
 			{
 				lastseenminutes = (int)(time (0) - lastseen) / 60;
 				if (lastseenminutes < 60) 
-					snprintf (agobuf, sizeof (agobuf), _("%d minutes ago"), lastseenminutes);
+					g_snprintf (agobuf, sizeof (agobuf), _("%d minutes ago"), lastseenminutes);
 				else if (lastseenminutes < 120)
-					snprintf (agobuf, sizeof (agobuf), _("An hour ago"));
+					g_snprintf (agobuf, sizeof (agobuf), _("An hour ago"));
 				else
-					snprintf (agobuf, sizeof (agobuf), _("%d hours ago"), lastseenminutes / 60);
+					g_snprintf (agobuf, sizeof (agobuf), _("%d hours ago"), lastseenminutes / 60);
 				seen = agobuf;
 			}
 			if (!valid)	/* create new tree row if required */
@@ -219,7 +219,7 @@ notify_gui_update (void)
 						name = "";
 					server = server_get_network (servnot->server, TRUE);
 
-					snprintf (agobuf, sizeof (agobuf), _("%d minutes ago"), (int)(time (0) - lastseen) / 60);
+					g_snprintf (agobuf, sizeof (agobuf), _("%d minutes ago"), (int)(time (0) - lastseen) / 60);
 					seen = agobuf;
 
 					if (!valid)	/* create new tree row if required */
@@ -380,7 +380,7 @@ fe_notify_ask (char *nick, char *networks)
 	gtk_table_attach_defaults (GTK_TABLE (table), wid, 1, 2, 2, 3);
 
 	label = gtk_label_new (NULL);
-	snprintf (buf, sizeof (buf), "<i><span size=\"smaller\">%s</span></i>", _("Comma separated list of networks is accepted."));
+	g_snprintf (buf, sizeof (buf), "<i><span size=\"smaller\">%s</span></i>", _("Comma separated list of networks is accepted."));
 	gtk_label_set_markup (GTK_LABEL (label), buf);
 	gtk_table_attach_defaults (GTK_TABLE (table), label, 1, 2, 3, 4);
 
diff --git a/src/fe-gtk/palette.c b/src/fe-gtk/palette.c
index 435ba84b..17689756 100644
--- a/src/fe-gtk/palette.c
+++ b/src/fe-gtk/palette.c
@@ -106,45 +106,39 @@ palette_alloc (GtkWidget * widget)
 void
 palette_load (void)
 {
-	int i, j, l, fh;
+	int i, j, fh;
 	char prefname[256];
 	struct stat st;
 	char *cfg;
-	int red, green, blue;
+	guint16 red, green, blue;
 
 	fh = hexchat_open_file ("colors.conf", O_RDONLY, 0, 0);
 	if (fh != -1)
 	{
 		fstat (fh, &st);
-		cfg = malloc (st.st_size + 1);
-		if (cfg)
+		cfg = g_malloc0 (st.st_size + 1);
+		read (fh, cfg, st.st_size);
+
+		/* mIRC colors 0-31 are here */
+		for (i = 0; i < 32; i++)
+		{
+			g_snprintf (prefname, sizeof prefname, "color_%d", i);
+			cfg_get_color (cfg, prefname, &red, &green, &blue);
+			colors[i].red = red;
+			colors[i].green = green;
+			colors[i].blue = blue;
+		}
+
+		/* our special colors are mapped at 256+ */
+		for (i = 256, j = 32; j < MAX_COL+1; i++, j++)
 		{
-			cfg[0] = '\0';
-			l = read (fh, cfg, st.st_size);
-			if (l >= 0)
-				cfg[l] = '\0';
-
-			/* mIRC colors 0-31 are here */
-			for (i = 0; i < 32; i++)
-			{
-				snprintf (prefname, sizeof prefname, "color_%d", i);
-				cfg_get_color (cfg, prefname, &red, &green, &blue);
-				colors[i].red = red;
-				colors[i].green = green;
-				colors[i].blue = blue;
-			}
-
-			/* our special colors are mapped at 256+ */
-			for (i = 256, j = 32; j < MAX_COL+1; i++, j++)
-			{
-				snprintf (prefname, sizeof prefname, "color_%d", i);
-				cfg_get_color (cfg, prefname, &red, &green, &blue);
-				colors[j].red = red;
-				colors[j].green = green;
-				colors[j].blue = blue;
-			}
-			free (cfg);
+			g_snprintf (prefname, sizeof prefname, "color_%d", i);
+			cfg_get_color (cfg, prefname, &red, &green, &blue);
+			colors[j].red = red;
+			colors[j].green = green;
+			colors[j].blue = blue;
 		}
+		g_free (cfg);
 		close (fh);
 	}
 }
@@ -161,14 +155,14 @@ palette_save (void)
 		/* mIRC colors 0-31 are here */
 		for (i = 0; i < 32; i++)
 		{
-			snprintf (prefname, sizeof prefname, "color_%d", i);
+			g_snprintf (prefname, sizeof prefname, "color_%d", i);
 			cfg_put_color (fh, colors[i].red, colors[i].green, colors[i].blue, prefname);
 		}
 
 		/* our special colors are mapped at 256+ */
 		for (i = 256, j = 32; j < MAX_COL+1; i++, j++)
 		{
-			snprintf (prefname, sizeof prefname, "color_%d", i);
+			g_snprintf (prefname, sizeof prefname, "color_%d", i);
 			cfg_put_color (fh, colors[j].red, colors[j].green, colors[j].blue, prefname);
 		}
 
diff --git a/src/fe-gtk/pixmaps.c b/src/fe-gtk/pixmaps.c
index cbec6f71..053afaaf 100644
--- a/src/fe-gtk/pixmaps.c
+++ b/src/fe-gtk/pixmaps.c
@@ -87,10 +87,9 @@ pixmap_load_from_file (char *filename)
 static GdkPixbuf *
 load_pixmap (const char *filename)
 {
-	gchar *path;
 	GdkPixbuf *pixbuf;
 
-	path = g_strdup_printf ("%s" G_DIR_SEPARATOR_S "icons" G_DIR_SEPARATOR_S "%s.png", get_xdir (), filename);
+	gchar *path = g_strdup_printf ("%s" G_DIR_SEPARATOR_S "icons" G_DIR_SEPARATOR_S "%s.png", get_xdir (), filename);
 	pixbuf = gdk_pixbuf_new_from_file (path, 0);
 	g_free (path);
 
diff --git a/src/fe-gtk/plugin-notification.c b/src/fe-gtk/plugin-notification.c
new file mode 100644
index 00000000..04a64213
--- /dev/null
+++ b/src/fe-gtk/plugin-notification.c
@@ -0,0 +1,215 @@
+/* HexChat
+ * Copyright (C) 2015 Patrick Griffis.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include "config.h"
+#include <glib.h>
+
+#include "../common/hexchat-plugin.h"
+#include "../common/inbound.h" /* For alert_match_word() */
+#include "notifications/notification-backend.h"
+
+static hexchat_plugin *ph;
+
+static gboolean
+should_alert (void)
+{
+	int omit_away, omit_focused, omit_tray;
+
+	if (hexchat_get_prefs (ph, "gui_focus_omitalerts", NULL, &omit_focused) == 3 && omit_focused)
+	{
+		const char *status = hexchat_get_info (ph, "win_status");
+
+		if (status && !g_strcmp0 (status, "active"))
+			return FALSE;
+	}
+
+	if (hexchat_get_prefs (ph, "away_omit_alerts", NULL, &omit_away) == 3 && omit_away)
+	{
+		if (hexchat_get_info (ph, "away"))
+			return FALSE;
+	}
+
+	if (hexchat_get_prefs (ph, "gui_tray_quiet", NULL, &omit_tray) == 3 && omit_tray)
+	{
+		int tray_enabled;
+
+		if (hexchat_get_prefs (ph, "gui_tray", NULL, &tray_enabled) == 3 && tray_enabled)
+		{
+			const char *status = hexchat_get_info (ph, "win_status");
+
+			if (status && g_strcmp0 (status, "hidden") != 0)
+				return FALSE;
+		}
+	}
+
+	return TRUE;
+}
+
+static gboolean
+is_ignored (char *nick)
+{
+	const char *no_hilight;
+
+	if (hexchat_get_prefs (ph, "irc_no_hilight", &no_hilight, NULL) == 1 && no_hilight)
+	{
+		return alert_match_word (nick, (char*)no_hilight);
+	}
+	return FALSE;
+}
+
+static void
+show_notification (const char *title, const char *text)
+{
+	char *stripped_title, *stripped_text;
+
+	/* Strip all colors */
+	stripped_title = hexchat_strip (ph, title, -1, 7);
+	stripped_text = hexchat_strip (ph, text, -1, 7);
+	
+	notification_backend_show (stripped_title, stripped_text);
+
+	hexchat_free (ph, stripped_title);
+	hexchat_free (ph, stripped_text);
+}
+
+static void
+show_notificationf (const char *text, const char *format, ...)
+{
+	va_list args;
+	char *buf;
+
+	va_start (args, format);
+	buf = g_strdup_vprintf (format, args);
+	va_end (args);
+
+	show_notification (buf, text);
+	g_free (buf);
+}
+
+static int
+incoming_hilight_cb (char *word[], gpointer userdata)
+{
+	int hilight;
+
+	if (hexchat_get_prefs (ph, "input_balloon_hilight", NULL, &hilight) == 3 && hilight && should_alert())
+	{
+		show_notificationf (word[2], _("Highlighted message from: %s (%s)"), word[1], hexchat_get_info (ph, "channel"));
+	}
+	return HEXCHAT_EAT_NONE;
+}
+
+static int
+incoming_message_cb (char *word[], gpointer userdata)
+{
+	int message;
+
+	if (hexchat_get_prefs (ph, "input_balloon_chans", NULL, &message) == 3 && message && should_alert ())
+	{
+		show_notificationf (word[2], _("Channel message from: %s (%s)"), word[1], hexchat_get_info (ph, "channel"));
+	}
+	return HEXCHAT_EAT_NONE;
+}
+
+static int
+incoming_priv_cb (char *word[], gpointer userdata)
+{
+	int priv;
+
+	if (hexchat_get_prefs (ph, "input_balloon_priv", NULL, &priv) == 3 && priv && should_alert ())
+	{
+		const char *network = hexchat_get_info (ph, "network");
+		if (!network)
+			network = hexchat_get_info (ph, "server");
+
+		if (userdata != NULL) /* Special event */
+		{
+			if (GPOINTER_TO_INT (userdata) == 3)
+			{
+				if (!is_ignored (word[2]))
+					show_notificationf (word[1], _("File offer from: %s (%s)"), word[2], network);
+			}
+			else if (GPOINTER_TO_INT (userdata) == 2)
+			{
+				if (!is_ignored (word[2]))
+					show_notificationf (word[1], _("Invited to channel by: %s (%s)"), word[2], network);
+			}
+			else
+			{
+				if (!is_ignored (word[1]))
+					show_notificationf (word[2], _("Notice from: %s (%s)"), word[1], network);
+			}
+		}
+		else
+			show_notificationf (word[2], _("Private message from: %s (%s)"), word[1], network);
+	}
+	return HEXCHAT_EAT_NONE;
+}
+
+static int
+tray_cmd_cb (char *word[], char *word_eol[], gpointer userdata)
+{
+	if (word[2] && !g_ascii_strcasecmp (word[2], "-b") && word[3] && word[4])
+	{
+		if (should_alert ())
+			show_notification (word[3], word_eol[4]);
+		return HEXCHAT_EAT_ALL;
+	}
+
+	return HEXCHAT_EAT_NONE;
+}
+
+int
+notification_plugin_init (hexchat_plugin *plugin_handle, char **plugin_name, char **plugin_desc, char **plugin_version, char *arg)
+{
+	if (!notification_backend_init ())
+		return 0;
+
+	ph = plugin_handle;
+	*plugin_name = "";
+	*plugin_desc = "";
+	*plugin_version = "";
+
+	hexchat_hook_print (ph, "Channel Msg Hilight", HEXCHAT_PRI_LOWEST, incoming_hilight_cb, NULL);
+	hexchat_hook_print (ph, "Channel Action Hilight", HEXCHAT_PRI_LOWEST, incoming_hilight_cb, NULL);
+
+	hexchat_hook_print (ph, "Channel Message", HEXCHAT_PRI_LOWEST, incoming_message_cb, NULL);
+	hexchat_hook_print (ph, "Channel Action", HEXCHAT_PRI_LOWEST, incoming_message_cb, NULL);
+	hexchat_hook_print (ph, "Channel Notice", HEXCHAT_PRI_LOWEST, incoming_message_cb, NULL);
+
+	hexchat_hook_print (ph, "Private Message", HEXCHAT_PRI_LOWEST, incoming_priv_cb, NULL);
+	hexchat_hook_print (ph, "Private Message to Dialog", HEXCHAT_PRI_LOWEST, incoming_priv_cb, NULL);
+	hexchat_hook_print (ph, "Private Action", HEXCHAT_PRI_LOWEST, incoming_priv_cb, NULL);
+	hexchat_hook_print (ph, "Private Action to Dialog", HEXCHAT_PRI_LOWEST, incoming_priv_cb, NULL);
+
+	/* Special events treated as priv */
+	hexchat_hook_print (ph, "Notice", HEXCHAT_PRI_LOWEST, incoming_priv_cb, GINT_TO_POINTER (1));
+	hexchat_hook_print (ph, "Invited", HEXCHAT_PRI_LOWEST, incoming_priv_cb, GINT_TO_POINTER (2));
+	hexchat_hook_print (ph, "DCC Offer", HEXCHAT_PRI_LOWEST, incoming_priv_cb, GINT_TO_POINTER (3));
+
+	hexchat_hook_command (ph, "TRAY", HEXCHAT_PRI_HIGH, tray_cmd_cb, NULL, NULL);
+	
+	return 1;
+}
+
+
+int
+notification_plugin_deinit (void)
+{
+	notification_backend_deinit ();
+	return 1;
+}
diff --git a/src/fe-gtk/plugin-notification.h b/src/fe-gtk/plugin-notification.h
new file mode 100644
index 00000000..07ad1609
--- /dev/null
+++ b/src/fe-gtk/plugin-notification.h
@@ -0,0 +1,25 @@
+/* HexChat
+ * Copyright (C) 2015 Patrick Griffis.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#ifndef HEXCHAT_PLUGIN_NOTIFICATION_H
+#define HEXCHAT_PLUGIN_NOTIFICATION_H
+
+int notification_plugin_init (void *, char **, char **, char **, char *);
+int notification_plugin_deinit (void *);
+
+#endif
diff --git a/src/fe-gtk/plugin-tray.c b/src/fe-gtk/plugin-tray.c
index b3e34c0a..077a7c63 100644
--- a/src/fe-gtk/plugin-tray.c
+++ b/src/fe-gtk/plugin-tray.c
@@ -34,18 +34,6 @@
 #include <unistd.h>
 #endif
 
-#ifdef USE_LIBNOTIFY
-#include <libnotify/notify.h>
-#ifndef NOTIFY_CHECK_VERSION
-#define NOTIFY_CHECK_VERSION(x,y,z) 0
-#endif
-#if NOTIFY_CHECK_VERSION(0,7,0)
-#define XC_NOTIFY_NEW(a,b,c,d) notify_notification_new(a,b,c)
-#else
-#define XC_NOTIFY_NEW(a,b,c,d) notify_notification_new(a,b,c,d)
-#endif
-#endif
-
 typedef enum	/* current icon status */
 {
 	TS_NONE,
@@ -154,82 +142,6 @@ fe_tray_set_tooltip (const char *text)
 		gtk_status_icon_set_tooltip_text (sticon, text);
 }
 
-void
-fe_tray_set_balloon (const char *title, const char *text)
-{
-#ifndef WIN32
-#if 0
-	const char *argv[8];
-	const char *path;
-	char time[16];
-#endif
-	WinStatus ws;
-
-	/* no balloons if the window is focused */
-	ws = tray_get_window_status ();
-	if ((prefs.hex_away_omit_alerts && hexchat_get_info(ph, "away")) ||
-		(prefs.hex_gui_focus_omitalerts && ws == WS_FOCUSED))
-		return;
-
-	/* bit 1 of flags means "no balloons unless hidden/iconified" */
-	if (ws != WS_HIDDEN && prefs.hex_gui_tray_quiet)
-		return;
-
-	/* FIXME: this should close the current balloon */
-	if (!text)
-		return;
-
-#ifdef USE_LIBNOTIFY
-	static int notify_text_strip_flags = STRIP_ALL;
-	NotifyNotification *notification;
-	char *notify_text, *notify_title;
-
-	if (!notify_is_initted())
-	{
-		notify_init(PACKAGE_NAME);
-
-		GList* server_caps = notify_get_server_caps ();
-		if (g_list_find_custom (server_caps, "body-markup", (GCompareFunc)strcmp))
-		{
-			notify_text_strip_flags |= STRIP_ESCMARKUP;
-		}
-		g_list_free_full (server_caps, g_free);
-	}
-
-	notify_text = strip_color (text, -1, notify_text_strip_flags);
-	notify_title = strip_color (title, -1, STRIP_ALL);
-
-	notification = XC_NOTIFY_NEW (notify_title, notify_text, HEXCHATSHAREDIR "/icons/hicolor/scalable/apps/hexchat.svg", NULL);
-
-#if NOTIFY_CHECK_VERSION(0,7,0)
-	notify_notification_set_hint (notification, "desktop-entry", g_variant_new_string ("hexchat"));
-#endif
-
-	g_free ((char *)notify_title);
-	g_free ((char *)notify_text);
-
-	notify_notification_set_timeout (notification, prefs.hex_input_balloon_time*1000);
-	notify_notification_show (notification, NULL);
-
-	g_object_unref (notification);
-#endif
-#endif
-}
-
-static void
-tray_set_balloonf (const char *text, const char *format, ...)
-{
-	va_list args;
-	char *buf;
-
-	va_start (args, format);
-	buf = g_strdup_vprintf (format, args);
-	va_end (args);
-
-	fe_tray_set_balloon (buf, text);
-	g_free (buf);
-}
-
 static void
 tray_set_tipf (const char *format, ...)
 {
@@ -575,26 +487,32 @@ tray_menu_destroy (GtkWidget *menu, gpointer userdata)
 }
 
 #ifdef WIN32
-static void
+static gboolean
 tray_menu_enter_cb (GtkWidget *menu)
 {
 	tray_menu_inactivetime = 0;
+	return FALSE;
 }
 
-static void
+static gboolean
 tray_menu_left_cb (GtkWidget *menu)
 {
 	tray_menu_inactivetime = g_get_real_time ();
+	return FALSE;
 }
 
-static void
+static gboolean
 tray_check_hide (GtkWidget *menu)
 {
 	if (tray_menu_inactivetime && g_get_real_time () - tray_menu_inactivetime  >= 2000000)
 	{
 		tray_menu_destroy (menu, NULL);
+		return G_SOURCE_REMOVE;
 	}
+
+	return G_SOURCE_CONTINUE;
 }
+#endif
 
 static void
 tray_menu_settings (GtkWidget * wid, gpointer none)
@@ -602,7 +520,6 @@ tray_menu_settings (GtkWidget * wid, gpointer none)
 	extern void setup_open (void);
 	setup_open ();
 }
-#endif
 
 static void
 tray_menu_cb (GtkWidget *widget, guint button, guint time, gpointer userdata)
@@ -651,10 +568,9 @@ tray_menu_cb (GtkWidget *widget, guint button, guint time, gpointer userdata)
 		gtk_widget_set_sensitive (item, FALSE);
 
 	menu_add_plugin_items (menu, "\x5$TRAY", NULL);
-#ifdef WIN32
+
 	tray_make_item (menu, NULL, tray_menu_quit_cb, NULL);
 	mg_create_icon_item (_("_Preferences"), GTK_STOCK_PREFERENCES, menu, tray_menu_settings, NULL);
-#endif
 	tray_make_item (menu, NULL, tray_menu_quit_cb, NULL);
 	mg_create_icon_item (_("_Quit"), GTK_STOCK_QUIT, menu, tray_menu_quit_cb, NULL);
 
@@ -669,7 +585,7 @@ tray_menu_cb (GtkWidget *widget, guint button, guint time, gpointer userdata)
 	g_signal_connect (G_OBJECT (menu), "enter-notify-event",
 							G_CALLBACK (tray_menu_enter_cb), NULL);
 
-	tray_menu_timer = g_timeout_add(500, (GSourceFunc) tray_check_hide, menu);
+	tray_menu_timer = g_timeout_add (500, tray_check_hide, menu);
 #endif
 
 	gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL,
@@ -718,10 +634,6 @@ tray_hilight_cb (char *word[], void *userdata)
 								tray_hilight_count, word[1], hexchat_get_info (ph, "channel"));
 	}
 
-	if (prefs.hex_input_balloon_hilight)
-		tray_set_balloonf (word[2], _("Highlighted message from: %s (%s)"),
-								 word[1], hexchat_get_info (ph, "channel"));
-
 	return HEXCHAT_EAT_NONE;
 }
 
@@ -743,10 +655,6 @@ tray_message_cb (char *word[], void *userdata)
 			tray_set_tipf (_(DISPLAY_NAME": %u channel messages."), tray_pub_count);
 	}
 
-	if (prefs.hex_input_balloon_chans)
-		tray_set_balloonf (word[2], _("Channel message from: %s (%s)"),
-								 word[1], hexchat_get_info (ph, "channel"));
-
 	return HEXCHAT_EAT_NONE;
 }
 
@@ -774,10 +682,6 @@ tray_priv (char *from, char *text)
 			tray_set_tipf (_(DISPLAY_NAME": %u private messages, latest from: %s (%s)"),
 								tray_priv_count, from, network);
 	}
-
-	if (prefs.hex_input_balloon_priv)
-		tray_set_balloonf (text, _("Private message from: %s (%s)"),
-								 from, network);
 }
 
 static int
@@ -822,10 +726,6 @@ tray_dcc_cb (char *word[], void *userdata)
 								tray_file_count, word[1], network);
 	}
 
-	if (prefs.hex_input_balloon_priv && (!prefs.hex_away_omit_alerts || tray_find_away_status () != 1))
-		tray_set_balloonf ("", _("File offer from: %s (%s)"),
-								word[1], network);
-
 	return HEXCHAT_EAT_NONE;
 }
 
@@ -904,8 +804,6 @@ tray_plugin_deinit (hexchat_plugin *plugin_handle)
 {
 #ifdef WIN32
 	tray_cleanup ();
-#elif defined(USE_LIBNOTIFY)
-	notify_uninit ();
 #endif
 	return 1;
 }
diff --git a/src/fe-gtk/plugingui.c b/src/fe-gtk/plugingui.c
index 9b3186a6..83e05727 100644
--- a/src/fe-gtk/plugingui.c
+++ b/src/fe-gtk/plugingui.c
@@ -142,23 +142,21 @@ plugingui_load_cb (session *sess, char *file)
 {
 	if (file)
 	{
-		char *buf = malloc (strlen (file) + 9);
+		char *buf;
 
 		if (strchr (file, ' '))
-			sprintf (buf, "LOAD \"%s\"", file);
+			buf = g_strdup_printf ("LOAD \"%s\"", file);
 		else
-			sprintf (buf, "LOAD %s", file);
+			buf = g_strdup_printf ("LOAD %s", file);
 		handle_command (sess, buf, FALSE);
-		free (buf);
+		g_free (buf);
 	}
 }
 
 void
 plugingui_load (void)
 {
-	char *sub_dir;
-
-	sub_dir = g_build_filename (get_xdir(), "addons", NULL);
+	char *sub_dir = g_build_filename (get_xdir(), "addons", NULL);
 
 	gtkutil_file_req (_("Select a Plugin or Script to load"), plugingui_load_cb, current_sess,
 							sub_dir, "*."G_MODULE_SUFFIX";*.lua;*.pl;*.py;*.tcl;*.js", FRF_FILTERISINITIAL|FRF_EXTENSIONS);
@@ -175,7 +173,7 @@ plugingui_loadbutton_cb (GtkWidget * wid, gpointer unused)
 static void
 plugingui_unload (GtkWidget * wid, gpointer unused)
 {
-	char *modname, *file, *buf;
+	char *modname, *file;
 	GtkTreeView *view;
 	GtkTreeIter iter;
 	
@@ -188,16 +186,17 @@ plugingui_unload (GtkWidget * wid, gpointer unused)
 	{
 		if (plugin_kill (modname, FALSE) == 2)
 			fe_message (_("That plugin is refusing to unload.\n"), FE_MSG_ERROR);
-	} else
+	}
+	else
 	{
+		char *buf;
 		/* let python.so or perl.so handle it */
-		buf = malloc (strlen (file) + 10);
 		if (strchr (file, ' '))
-			sprintf (buf, "UNLOAD \"%s\"", file);
+			buf = g_strdup_printf ("UNLOAD \"%s\"", file);
 		else
-			sprintf (buf, "UNLOAD %s", file);
+			buf = g_strdup_printf ("UNLOAD %s", file);
 		handle_command (current_sess, buf, FALSE);
-		free (buf);
+		g_free (buf);
 	}
 
 	g_free (modname);
@@ -211,14 +210,14 @@ plugingui_reloadbutton_cb (GtkWidget *wid, GtkTreeView *view)
 
 	if (file)
 	{
-		char *buf = malloc (strlen (file) + 9);
+		char *buf;
 
 		if (strchr (file, ' '))
-			sprintf (buf, "RELOAD \"%s\"", file);
+			buf = g_strdup_printf ("RELOAD \"%s\"", file);
 		else
-			sprintf (buf, "RELOAD %s", file);
+			buf = g_strdup_printf ("RELOAD %s", file);
 		handle_command (current_sess, buf, FALSE);
-		free (buf);
+		g_free (buf);
 		g_free (file);
 	}
 }
diff --git a/src/fe-gtk/rawlog.c b/src/fe-gtk/rawlog.c
index f2527492..1d4bf9fd 100644
--- a/src/fe-gtk/rawlog.c
+++ b/src/fe-gtk/rawlog.c
@@ -109,7 +109,7 @@ open_rawlog (struct server *serv)
 		return;
 	}
 
-	snprintf (tbuf, sizeof tbuf, _(DISPLAY_NAME": Raw Log (%s)"), serv->servername);
+	g_snprintf (tbuf, sizeof tbuf, _(DISPLAY_NAME": Raw Log (%s)"), serv->servername);
 	serv->gui->rawlog_window =
 		mg_create_generic_tab ("RawLog", tbuf, FALSE, TRUE, close_rawlog, serv,
 							 640, 320, &vbox, serv);
@@ -146,7 +146,7 @@ fe_add_rawlog (server *serv, char *text, int len, int outbound)
 {
 	char **split_text;
 	char *new_text;
-	int i;
+	size_t i;
 
 	if (!serv->gui->rawlog_window)
 		return;
@@ -163,7 +163,7 @@ fe_add_rawlog (server *serv, char *text, int len, int outbound)
 		else
 			new_text = g_strconcat ("\0033>>\017 ", split_text[i], NULL);
 
-		gtk_xtext_append (GTK_XTEXT (serv->gui->rawlog_textlist)->buffer, new_text, strlen (new_text));
+		gtk_xtext_append (GTK_XTEXT (serv->gui->rawlog_textlist)->buffer, new_text, strlen (new_text), 0);
 
 		g_free (new_text);
 	}
diff --git a/src/fe-gtk/servlistgui.c b/src/fe-gtk/servlistgui.c
index f43a225a..f7909f72 100644
--- a/src/fe-gtk/servlistgui.c
+++ b/src/fe-gtk/servlistgui.c
@@ -90,7 +90,7 @@ static GtkWidget *servlist_open_edit (GtkWidget *parent, ircnet *net);
 static const char *pages[]=
 {
 	IRC_DEFAULT_CHARSET,
-	"IRC (Latin/Unicode Hybrid)",
+	"CP1252 (Windows-1252)",
 	"ISO-8859-15 (Western Europe)",
 	"ISO-8859-2 (Central Europe)",
 	"ISO-8859-7 (Greek)",
@@ -497,7 +497,7 @@ servlist_addnet_cb (GtkWidget *item, GtkTreeView *treeview)
 	ircnet *net;
 
 	net = servlist_net_add (_("New Network"), "", TRUE);
-	net->encoding = strdup (IRC_DEFAULT_CHARSET);
+	net->encoding = g_strdup (IRC_DEFAULT_CHARSET);
 	servlist_server_add (net, "newserver/6667");
 
 	store = (GtkListStore *)gtk_tree_view_get_model (treeview);
@@ -668,13 +668,12 @@ servlist_favor (GtkWidget *button, gpointer none)
 static void
 servlist_update_from_entry (char **str, GtkWidget *entry)
 {
-	if (*str)
-		free (*str);
+	g_free (*str);
 
 	if (gtk_entry_get_text (GTK_ENTRY (entry))[0] == 0)
 		*str = NULL;
 	else
-		*str = strdup (gtk_entry_get_text (GTK_ENTRY (entry)));
+		*str = g_strdup (gtk_entry_get_text (GTK_ENTRY (entry)));
 }
 
 static void
@@ -960,10 +959,10 @@ servlist_savegui (void)
 	if (!rfc_casecmp (nick1, nick2))
 		return 2;
 
-	strcpy (prefs.hex_irc_nick1, nick1);
-	strcpy (prefs.hex_irc_nick2, nick2);
-	strcpy (prefs.hex_irc_nick3, gtk_entry_get_text (GTK_ENTRY (entry_nick3)));
-	strcpy (prefs.hex_irc_user_name, gtk_entry_get_text (GTK_ENTRY (entry_guser)));
+	safe_strcpy (prefs.hex_irc_nick1, nick1, sizeof(prefs.hex_irc_nick1));
+	safe_strcpy (prefs.hex_irc_nick2, nick2, sizeof(prefs.hex_irc_nick2));
+	safe_strcpy (prefs.hex_irc_nick3, gtk_entry_get_text (GTK_ENTRY (entry_nick3)), sizeof(prefs.hex_irc_nick3));
+	safe_strcpy (prefs.hex_irc_user_name, gtk_entry_get_text (GTK_ENTRY (entry_guser)), sizeof(prefs.hex_irc_user_name));
 	sp = strchr (prefs.hex_irc_user_name, ' ');
 	if (sp)
 		sp[0] = 0;	/* spaces will break the login */
@@ -1203,9 +1202,9 @@ servlist_celledit_cb (GtkCellRendererText *cell, gchar *arg1, gchar *arg2,
 		}
 
 		netname = net->name;
-		net->name = strdup (arg2);
+		net->name = g_strdup (arg2);
 		gtk_list_store_set (GTK_LIST_STORE (model), &iter, 0, net->name, -1);
-		free (netname);
+		g_free (netname);
 	}
 
 	gtk_tree_path_free (path);
@@ -1311,7 +1310,7 @@ servlist_sanitize_hostname (char *host)
 {
 	char *ret, *c, *e;
 
-	ret = strdup (host);
+	ret = g_strdup (host);
 
 	c = strchr  (ret, ':');
 	e = strrchr (ret, ':');
@@ -1371,7 +1370,7 @@ servlist_editserver_cb (GtkCellRendererText *cell, gchar *name, gchar *newval, g
 		servname = serv->hostname;
 		serv->hostname = servlist_sanitize_hostname (newval);
 		gtk_list_store_set (GTK_LIST_STORE (model), &iter, 0, serv->hostname, -1);
-		free (servname);
+		g_free (servname);
 	}
 }
 
@@ -1409,7 +1408,7 @@ servlist_editcommand_cb (GtkCellRendererText *cell, gchar *name, gchar *newval,
 		cmd = entry->command;
 		entry->command = servlist_sanitize_command (newval);
 		gtk_list_store_set (GTK_LIST_STORE (model), &iter, 0, entry->command, -1);
-		free (cmd);
+		g_free (cmd);
 	}
 }
 
@@ -1508,9 +1507,8 @@ servlist_combo_cb (GtkEntry *entry, gpointer userdata)
 	if (!selected_net)
 		return;
 
-	if (selected_net->encoding)
-		free (selected_net->encoding);
-	selected_net->encoding = strdup (gtk_entry_get_text (entry));
+	g_free (selected_net->encoding);
+	selected_net->encoding = g_strdup (gtk_entry_get_text (entry));
 }
 
 /* Fills up the network's authentication type so that it's guaranteed to be either NULL or a valid value. */
@@ -1594,7 +1592,6 @@ servlist_create_charsetcombo (void)
 	int i;
 
 	cb = gtk_combo_box_text_new_with_entry ();
-	gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (cb), "System default");
 	i = 0;
 	while (pages[i])
 	{
@@ -1602,7 +1599,7 @@ servlist_create_charsetcombo (void)
 		i++;
 	}
 
-	gtk_entry_set_text (GTK_ENTRY (gtk_bin_get_child (GTK_BIN(cb))), selected_net->encoding ? selected_net->encoding : "System default");
+	gtk_entry_set_text (GTK_ENTRY (gtk_bin_get_child (GTK_BIN(cb))), selected_net->encoding ? selected_net->encoding : pages[0]);
 	
 	g_signal_connect (G_OBJECT (gtk_bin_get_child (GTK_BIN (cb))), "changed",
 							G_CALLBACK (servlist_combo_cb), NULL);
@@ -1660,7 +1657,7 @@ bold_label (char *text)
 	char buf[128];
 	GtkWidget *label;
 
-	snprintf (buf, sizeof (buf), "<b>%s</b>", text);
+	g_snprintf (buf, sizeof (buf), "<b>%s</b>", text);
 	label = gtk_label_new (buf);
 	gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
 	gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
@@ -1702,7 +1699,7 @@ servlist_open_edit (GtkWidget *parent, ircnet *net)
 
 	editwindow = gtk_window_new (GTK_WINDOW_TOPLEVEL);
 	gtk_container_set_border_width (GTK_CONTAINER (editwindow), 4);
-	snprintf (buf, sizeof (buf), _(DISPLAY_NAME": Edit %s"), net->name);
+	g_snprintf (buf, sizeof (buf), _(DISPLAY_NAME": Edit %s"), net->name);
 	gtk_window_set_title (GTK_WINDOW (editwindow), buf);
 	gtk_window_set_default_size (GTK_WINDOW (editwindow), netedit_win_width, netedit_win_height);
 	gtk_window_set_transient_for (GTK_WINDOW (editwindow), GTK_WINDOW (parent));
diff --git a/src/fe-gtk/setup.c b/src/fe-gtk/setup.c
index e4372dc9..dc469591 100644
--- a/src/fe-gtk/setup.c
+++ b/src/fe-gtk/setup.c
@@ -36,6 +36,7 @@
 #include "pixmaps.h"
 #include "menu.h"
 #include "plugin-tray.h"
+#include "notifications/notification-backend.h"
 
 #ifdef WIN32
 #include "../common/fe.h"
@@ -314,6 +315,7 @@ static const setting tabs_settings[] =
 	{ST_TOGGLE, N_("Show icons in the channel tree"), P_OFFINTNL(hex_gui_tab_icons), 0, 0, 0},
 	{ST_TOGGLE, N_("Show dotted lines in the channel tree"), P_OFFINTNL(hex_gui_tab_dots), 0, 0, 0},
 	{ST_TOGGLE, N_("Scroll mouse-wheel to change tabs"), P_OFFINTNL (hex_gui_tab_scrollchans), 0, 0, 0},
+	{ST_TOGGLE, N_("Middle click to close tab"), P_OFFINTNL(hex_gui_tab_middleclose), 0, 0, 0},
 	{ST_TOGGLE, N_("Smaller text"), P_OFFINTNL(hex_gui_tab_small), 0, 0, 0},
 	{ST_MENU,	N_("Focus new tabs:"), P_OFFINTNL(hex_gui_tab_newtofront), 0, focusnewtabsmenu, 0},
 	{ST_MENU,	N_("Placement of notices:"), P_OFFINTNL(hex_irc_notice_pos), 0, noticeposmenu, 0},
@@ -396,9 +398,47 @@ static const setting alert_settings[] =
 	{ST_HEADER,	N_("Alerts"),0,0,0},
 
 	{ST_ALERTHEAD},
-#if !defined (WIN32) && !defined (__APPLE__)
-	{ST_3OGGLE, N_("Show tray balloons on:"), 0, 0, (void *)balloonlist, 0},
+
+
+	{ST_3OGGLE, N_("Show notifications on:"), 0, 0, (void *)balloonlist, 0},
+	{ST_3OGGLE, N_("Blink tray icon on:"), 0, 0, (void *)trayblinklist, 0},
+	{ST_3OGGLE, N_("Blink task bar on:"), 0, 0, (void *)taskbarlist, 0},
+#ifdef WIN32
+	{ST_3OGGLE, N_("Make a beep sound on:"), 0, N_("Play the \"Instant Message Notification\" system sound upon the selected events"), (void *)beeplist, 0},
+#else
+#ifdef USE_LIBCANBERRA
+	{ST_3OGGLE, N_("Make a beep sound on:"), 0, N_("Play \"message-new-instant\" from the freedesktop.org sound theme upon the selected events"), (void *)beeplist, 0},
+#else
+	{ST_3OGGLE, N_("Make a beep sound on:"), 0, N_("Play a GTK beep upon the selected events"), (void *)beeplist, 0},
 #endif
+#endif
+
+	{ST_TOGGLE,	N_("Omit alerts when marked as being away"), P_OFFINTNL(hex_away_omit_alerts), 0, 0, 0},
+	{ST_TOGGLE,	N_("Omit alerts while the window is focused"), P_OFFINTNL(hex_gui_focus_omitalerts), 0, 0, 0},
+
+	{ST_HEADER,	N_("Tray Behavior"), 0, 0, 0},
+	{ST_TOGGLE,	N_("Enable system tray icon"), P_OFFINTNL(hex_gui_tray), 0, 0, 4},
+	{ST_TOGGLE,	N_("Minimize to tray"), P_OFFINTNL(hex_gui_tray_minimize), 0, 0, 0},
+	{ST_TOGGLE,	N_("Close to tray"), P_OFFINTNL(hex_gui_tray_close), 0, 0, 0},
+	{ST_TOGGLE,	N_("Automatically mark away/back"), P_OFFINTNL(hex_gui_tray_away), N_("Automatically change status when hiding to tray."), 0, 0},
+	{ST_TOGGLE,	N_("Only show notifications when hidden or iconified"), P_OFFINTNL(hex_gui_tray_quiet), 0, 0, 0},
+
+	{ST_HEADER,	N_("Highlighted Messages"),0,0,0},
+	{ST_LABEL,	N_("Highlighted messages are ones where your nickname is mentioned, but also:"), 0, 0, 0, 1},
+
+	{ST_ENTRY,	N_("Extra words to highlight:"), P_OFFSETNL(hex_irc_extra_hilight), 0, 0, sizeof prefs.hex_irc_extra_hilight},
+	{ST_ENTRY,	N_("Nick names not to highlight:"), P_OFFSETNL(hex_irc_no_hilight), 0, 0, sizeof prefs.hex_irc_no_hilight},
+	{ST_ENTRY,	N_("Nick names to always highlight:"), P_OFFSETNL(hex_irc_nick_hilight), 0, 0, sizeof prefs.hex_irc_nick_hilight},
+	{ST_LABEL,	N_("Separate multiple words with commas.\nWildcards are accepted.")},
+
+	{ST_END, 0, 0, 0, 0, 0}
+};
+
+static const setting alert_settings_nonotifications[] =
+{
+	{ST_HEADER,	N_("Alerts"),0,0,0},
+
+	{ST_ALERTHEAD},
 	{ST_3OGGLE, N_("Blink tray icon on:"), 0, 0, (void *)trayblinklist, 0},
 #ifdef HAVE_GTK_MAC
 	{ST_3OGGLE, N_("Bounce dock icon on:"), 0, 0, (void *)taskbarlist, 0},
@@ -421,17 +461,10 @@ static const setting alert_settings[] =
 	{ST_TOGGLE,	N_("Omit alerts while the window is focused"), P_OFFINTNL(hex_gui_focus_omitalerts), 0, 0, 0},
 
 	{ST_HEADER,	N_("Tray Behavior"), 0, 0, 0},
-#ifdef WIN32
-	{ST_TOGGLE,	N_("Enable system tray icon"), P_OFFINTNL(hex_gui_tray), 0, 0, 3},
-#else
 	{ST_TOGGLE,	N_("Enable system tray icon"), P_OFFINTNL(hex_gui_tray), 0, 0, 4},
-#endif
 	{ST_TOGGLE,	N_("Minimize to tray"), P_OFFINTNL(hex_gui_tray_minimize), 0, 0, 0},
 	{ST_TOGGLE,	N_("Close to tray"), P_OFFINTNL(hex_gui_tray_close), 0, 0, 0},
 	{ST_TOGGLE,	N_("Automatically mark away/back"), P_OFFINTNL(hex_gui_tray_away), N_("Automatically change status when hiding to tray."), 0, 0},
-#ifndef WIN32
-	{ST_TOGGLE,	N_("Only show tray balloons when hidden or iconified"), P_OFFINTNL(hex_gui_tray_quiet), 0, 0, 0},
-#endif
 
 	{ST_HEADER,	N_("Highlighted Messages"),0,0,0},
 	{ST_LABEL,	N_("Highlighted messages are ones where your nickname is mentioned, but also:"), 0, 0, 0, 1},
@@ -449,7 +482,7 @@ static const setting alert_settings_unity[] =
 	{ST_HEADER,	N_("Alerts"),0,0,0},
 
 	{ST_ALERTHEAD},
-	{ST_3OGGLE, N_("Show tray balloons on:"), 0, 0, (void *)balloonlist, 0},
+	{ST_3OGGLE, N_("Show notifications on:"), 0, 0, (void *)balloonlist, 0},
 	{ST_3OGGLE, N_("Blink task bar on:"), 0, 0, (void *)taskbarlist, 0},
 	{ST_3OGGLE, N_("Make a beep sound on:"), 0, 0, (void *)beeplist, 0},
 
@@ -467,6 +500,28 @@ static const setting alert_settings_unity[] =
 	{ST_END, 0, 0, 0, 0, 0}
 };
 
+static const setting alert_settings_unityandnonotifications[] =
+{
+	{ST_HEADER, N_("Alerts"), 0, 0, 0},
+
+	{ST_ALERTHEAD},
+	{ST_3OGGLE, N_("Blink task bar on:"), 0, 0, (void *)taskbarlist, 0},
+	{ST_3OGGLE, N_("Make a beep sound on:"), 0, 0, (void *)beeplist, 0},
+
+	{ST_TOGGLE, N_("Omit alerts when marked as being away"), P_OFFINTNL (hex_away_omit_alerts), 0, 0, 0},
+	{ST_TOGGLE, N_("Omit alerts while the window is focused"), P_OFFINTNL (hex_gui_focus_omitalerts), 0, 0, 0},
+
+	{ST_HEADER, N_("Highlighted Messages"), 0, 0, 0},
+	{ST_LABEL, N_("Highlighted messages are ones where your nickname is mentioned, but also:"), 0, 0, 0, 1},
+
+	{ST_ENTRY, N_("Extra words to highlight:"), P_OFFSETNL (hex_irc_extra_hilight), 0, 0, sizeof prefs.hex_irc_extra_hilight},
+	{ST_ENTRY, N_("Nick names not to highlight:"), P_OFFSETNL (hex_irc_no_hilight), 0, 0, sizeof prefs.hex_irc_no_hilight},
+	{ST_ENTRY, N_("Nick names to always highlight:"), P_OFFSETNL (hex_irc_nick_hilight), 0, 0, sizeof prefs.hex_irc_nick_hilight},
+	{ST_LABEL, N_("Separate multiple words with commas.\nWildcards are accepted.")},
+
+	{ST_END, 0, 0, 0, 0, 0}
+};
+
 static const setting general_settings[] =
 {
 	{ST_HEADER,	N_("Default Messages"),0,0,0},
@@ -559,9 +614,6 @@ static const char *const proxytypes[] =
 	N_("Socks4"),
 	N_("Socks5"),
 	N_("HTTP"),
-#ifdef USE_MSPROXY
-	N_("MS Proxy (ISA)"),
-#endif
 #ifdef USE_LIBPROXY
 	N_("Auto"),
 #endif
@@ -598,11 +650,7 @@ static const setting network_settings[] =
 	{ST_MENU,	N_("Use proxy for:"), P_OFFINTNL(hex_net_proxy_use), 0, proxyuse, 0},
 
 	{ST_HEADER,	N_("Proxy Authentication"), 0, 0, 0, 0},
-#ifdef USE_MSPROXY
-	{ST_TOGGLE,	N_("Use Authentication (MS Proxy, HTTP or Socks5 only)"), P_OFFINTNL(hex_net_proxy_auth), 0, 0, 0},
-#else
 	{ST_TOGGLE,	N_("Use Authentication (HTTP or Socks5 only)"), P_OFFINTNL(hex_net_proxy_auth), 0, 0, 0},
-#endif
 	{ST_ENTRY,	N_("Username:"), P_OFFSETNL(hex_net_proxy_user), 0, 0, sizeof prefs.hex_net_proxy_user},
 	{ST_ENTRY,	N_("Password:"), P_OFFSETNL(hex_net_proxy_pass), 0, GINT_TO_POINTER(1), sizeof prefs.hex_net_proxy_pass},
 
@@ -630,7 +678,7 @@ setup_headlabel (GtkWidget *tab, int row, int col, char *text)
 	char buf[128];
 	char *sp;
 
-	snprintf (buf, sizeof (buf), "<b><span size=\"smaller\">%s</span></b>", text);
+	g_snprintf (buf, sizeof (buf), "<b><span size=\"smaller\">%s</span></b>", text);
 	sp = strchr (buf + 17, ' ');
 	if (sp)
 		*sp = '\n';
@@ -752,7 +800,7 @@ setup_create_italic_label (char *text)
 	char buf[256];
 
 	label = gtk_label_new (NULL);
-	snprintf (buf, sizeof (buf), "<i><span size=\"smaller\">%s</span></i>", text);
+	g_snprintf (buf, sizeof (buf), "<i><span size=\"smaller\">%s</span></i>", text);
 	gtk_label_set_markup (GTK_LABEL (label), buf);
 	gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_CENTER);
 
@@ -1121,8 +1169,8 @@ setup_entry_cb (GtkEntry *entry, setting *set)
 {
 	int size;
 	int pos;
-	int len = gtk_entry_get_text_length (entry);
 	unsigned char *p = (unsigned char*)gtk_entry_get_text (entry);
+	int len = strlen (p);
 
 	/* need to truncate? */
 	if (len >= set->extra)
@@ -1220,9 +1268,9 @@ setup_create_header (GtkWidget *table, int row, char *labeltext)
 	char buf[128];
 
 	if (row == 0)
-		snprintf (buf, sizeof (buf), "<b>%s</b>", _(labeltext));
+		g_snprintf (buf, sizeof (buf), "<b>%s</b>", _(labeltext));
 	else
-		snprintf (buf, sizeof (buf), "\n<b>%s</b>", _(labeltext));
+		g_snprintf (buf, sizeof (buf), "\n<b>%s</b>", _(labeltext));
 
 	label = gtk_label_new (NULL);
 	gtk_label_set_markup (GTK_LABEL (label), buf);
@@ -1683,9 +1731,8 @@ setup_snd_changed_cb (GtkEntry *ent, GtkTreeView *tree)
 		return;
 
 	/* get the new sound file */
-	if (sound_files[n])
-		free (sound_files[n]);
-	sound_files[n] = strdup (gtk_entry_get_text (GTK_ENTRY (ent)));
+	g_free (sound_files[n]);
+	sound_files[n] = g_strdup (gtk_entry_get_text (GTK_ENTRY (ent)));
 
 	/* update the TreeView list */
 	store = (GtkListStore *)gtk_tree_view_get_model (tree);
@@ -1790,7 +1837,7 @@ setup_add_page (const char *title, GtkWidget *book, GtkWidget *tab)
 
 	/* label */
 	label = gtk_label_new (NULL);
-	snprintf (buf, sizeof (buf), "<b><big>%s</big></b>", _(title));
+	g_snprintf (buf, sizeof (buf), "<b><big>%s</big></b>", _(title));
 	gtk_label_set_markup (GTK_LABEL (label), buf);
 	gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
 	gtk_misc_set_padding (GTK_MISC (label), 2, 1);
@@ -1839,10 +1886,18 @@ setup_create_pages (GtkWidget *box)
 
 	setup_add_page (cata[8], book, setup_create_page (general_settings));
 
-	if (unity_mode ())
+	if (unity_mode () && !notification_backend_supported ())
+	{
+		setup_add_page (cata[9], book, setup_create_page (alert_settings_unityandnonotifications));
+	}
+	else if (unity_mode ())
 	{
 		setup_add_page (cata[9], book, setup_create_page (alert_settings_unity));
 	}
+	else if (!notification_backend_supported ())
+	{
+		setup_add_page (cata[9], book, setup_create_page (alert_settings_nonotifications));
+	}
 	else
 	{
 		setup_add_page (cata[9], book, setup_create_page (alert_settings));
@@ -2121,6 +2176,8 @@ setup_apply (struct hexchatprefs *pr)
 		noapply = TRUE;
 	if (DIFF (hex_gui_ulist_style))
 		noapply = TRUE;
+	if (DIFF (hex_gui_ulist_sort))
+		noapply = TRUE;
 
 	if (DIFF (hex_gui_tab_dots))
 		do_layout = TRUE;
@@ -2137,6 +2194,13 @@ setup_apply (struct hexchatprefs *pr)
 						" menu first."),
 						FE_MSG_WARN | FE_MSG_MARKUP);
 
+	/* format cannot be blank, there is already a setting for this */
+	if (pr->hex_stamp_text_format[0] == 0)
+	{
+		pr->hex_stamp_text = 0;
+		strcpy (pr->hex_stamp_text_format, prefs.hex_stamp_text_format);
+	}
+
 	memcpy (&prefs, pr, sizeof (prefs));
 
 #ifdef WIN32
diff --git a/src/fe-gtk/sexy-iso-codes.c b/src/fe-gtk/sexy-iso-codes.c
index e6acb726..06c8cd07 100644
--- a/src/fe-gtk/sexy-iso-codes.c
+++ b/src/fe-gtk/sexy-iso-codes.c
@@ -19,10 +19,11 @@
 *    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */
 
+#include "config.h"
+
 #include "sexy-iso-codes.h"
 #include <libintl.h>
 #include <string.h>
-#include "../../config.h"
 
 #define ISO_639_DOMAIN	"iso_639"
 #define ISO_3166_DOMAIN	"iso_3166"
diff --git a/src/fe-gtk/sexy-spell-entry.c b/src/fe-gtk/sexy-spell-entry.c
index bac1e2b5..f57c7f41 100644
--- a/src/fe-gtk/sexy-spell-entry.c
+++ b/src/fe-gtk/sexy-spell-entry.c
@@ -31,7 +31,12 @@
 #include <sys/types.h>
 #include <sys/stat.h>
 #include "sexy-iso-codes.h"
+
+#ifdef WIN32
+#include "marshal.h"
+#else
 #include "../common/marshal.h"
+#endif
 
 #ifdef WIN32
 #include "../common/typedef.h"
@@ -136,6 +141,8 @@ enum
 };
 static guint signals[LAST_SIGNAL] = {0};
 
+static PangoAttrList *empty_attrs_list = NULL;
+
 static gboolean
 spell_accumulator(GSignalInvocationHint *hint, GValue *return_accu, const GValue *handler_return, gpointer data)
 {
@@ -243,6 +250,11 @@ sexy_spell_entry_class_init(SexySpellEntryClass *klass)
 					   _hexchat_marshal_BOOLEAN__STRING,
 					   G_TYPE_BOOLEAN,
 					   1, G_TYPE_STRING);
+
+	if (empty_attrs_list == NULL)
+	{
+		empty_attrs_list = pango_attr_list_new ();
+	}
 }
 
 static void
@@ -292,7 +304,7 @@ insert_hiddenchar (SexySpellEntry *entry, guint start, guint end)
 	 * is 'hidden' */
 #if 0
 	PangoAttribute *hattr;
-	PangoRectangle *rect = g_malloc (sizeof (PangoRectangle));
+	PangoRectangle *rect = g_new (PangoRectangle, 1);
 
 	rect->x = 0;
 	rect->y = 0;
@@ -758,12 +770,9 @@ sexy_spell_entry_finalize(GObject *obj)
 		pango_attr_list_unref(entry->priv->attr_list);
 	if (entry->priv->dict_hash)
 		g_hash_table_destroy(entry->priv->dict_hash);
-	if (entry->priv->words)
-		g_strfreev(entry->priv->words);
-	if (entry->priv->word_starts)
-		g_free(entry->priv->word_starts);
-	if (entry->priv->word_ends)
-		g_free(entry->priv->word_ends);
+	g_strfreev(entry->priv->words);
+	g_free(entry->priv->word_starts);
+	g_free(entry->priv->word_ends);
 
 	if (have_enchant) {
 		if (entry->priv->broker) {
@@ -1038,7 +1047,7 @@ sexy_spell_entry_recheck_all(SexySpellEntry *entry)
 	{
 		/* Check for attributes */
 		text = gtk_entry_get_text (GTK_ENTRY (entry));
-		text_len = gtk_entry_get_text_length (GTK_ENTRY (entry));
+		text_len = strlen (text);
 		check_attributes (entry, text, text_len);
 	}
 
@@ -1078,7 +1087,14 @@ sexy_spell_entry_expose(GtkWidget *widget, GdkEventExpose *event)
 
 	
 	layout = gtk_entry_get_layout(gtk_entry);
-	pango_layout_set_attributes(layout, entry->priv->attr_list);
+	if (gtk_entry->preedit_length == 0)
+	{
+		pango_layout_set_attributes(layout, entry->priv->attr_list);
+	}
+	else
+	{
+		pango_layout_set_attributes(layout, empty_attrs_list);
+	}
 
 	return GTK_WIDGET_CLASS(parent_class)->expose_event (widget, event);
 }
diff --git a/src/fe-gtk/textgui.c b/src/fe-gtk/textgui.c
index 9956e9c6..b0f2f392 100644
--- a/src/fe-gtk/textgui.c
+++ b/src/fe-gtk/textgui.c
@@ -81,14 +81,14 @@ PrintTextLine (xtext_buffer *xtbuf, unsigned char *text, int len, int indent, ti
 				timet = time (0);
 
 			stamp_size = get_stamp_str (prefs.hex_stamp_text_format, timet, &stamp);
-			new_text = malloc (len + stamp_size + 1);
+			new_text = g_malloc (len + stamp_size + 1);
 			memcpy (new_text, stamp, stamp_size);
 			g_free (stamp);
 			memcpy (new_text + stamp_size, text, len);
-			gtk_xtext_append (xtbuf, new_text, len + stamp_size);
-			free (new_text);
+			gtk_xtext_append (xtbuf, new_text, len + stamp_size, timet);
+			g_free (new_text);
 		} else
-			gtk_xtext_append (xtbuf, text, len);
+			gtk_xtext_append (xtbuf, text, len, timet);
 		return;
 	}
 
@@ -173,13 +173,12 @@ pevent_edited (GtkCellRendererText *render, gchar *pathstr, gchar *new_text, gpo
 	}
 	if (m > (te[sig].num_args & 0x7f))
 	{
-		free (out);
-		out = malloc (4096);
-		snprintf (out, 4096,
-					_("This signal is only passed %d args, $%d is invalid"),
-					te[sig].num_args & 0x7f, m);
+		g_free (out);
+		out = g_strdup_printf (
+			_("This signal is only passed %d args, $%d is invalid"),
+			te[sig].num_args & 0x7f, m);
 		fe_message (out, FE_MSG_WARN);
-		free (out);
+		g_free (out);
 		return;
 	}
 
@@ -188,23 +187,20 @@ pevent_edited (GtkCellRendererText *render, gchar *pathstr, gchar *new_text, gpo
 	gtk_list_store_set (GTK_LIST_STORE (model), &iter, TEXT_COLUMN, new_text, -1);
 	gtk_tree_path_free (path);
 
-	if (pntevts_text[sig])
-		free (pntevts_text[sig]);
-	if (pntevts[sig])
-		free (pntevts[sig]);
+	g_free (pntevts_text[sig]);
+	g_free (pntevts[sig]);
 
-	pntevts_text[sig] = malloc (len + 1);
-	memcpy (pntevts_text[sig], text, len + 1);
+	pntevts_text[sig] = g_strdup (text);
 	pntevts[sig] = out;
 
-	out = malloc (len + 2);
+	out = g_malloc (len + 2);
 	memcpy (out, text, len + 1);
 	out[len] = '\n';
 	out[len + 1] = 0;
 	check_special_chars (out, TRUE);
 
 	PrintTextRaw (xtext->buffer, out, 0, 0);
-	free (out);
+	g_free (out);
 
 	/* Scroll to bottom */
 	gtk_adjustment_set_value (xtext->adj, gtk_adjustment_get_upper (xtext->adj));
@@ -328,14 +324,14 @@ pevent_test_cb (GtkWidget * wid, GtkWidget * twid)
 		text = _(pntevts_text[n]);
 		len = strlen (text);
 
-		out = malloc (len + 2);
+		out = g_malloc (len + 2);
 		memcpy (out, text, len + 1);
 		out[len] = '\n';
 		out[len + 1] = 0;
 		check_special_chars (out, TRUE);
 
 		PrintTextRaw (GTK_XTEXT (twid)->buffer, out, 0, 0);
-		free (out);
+		g_free (out);
 	}
 }
 
diff --git a/src/fe-gtk/userlistgui.c b/src/fe-gtk/userlistgui.c
index 19564ece..d06975ca 100644
--- a/src/fe-gtk/userlistgui.c
+++ b/src/fe-gtk/userlistgui.c
@@ -42,11 +42,11 @@
 
 enum
 {
-	COL_PIX=0,		// GdkPixbuf *
-	COL_NICK=1,		// char *
-	COL_HOST=2,		// char *
-	COL_USER=3,		// struct User *
-	COL_GDKCOLOR=4	// GdkColor *
+	COL_PIX=0,		/* GdkPixbuf * */
+	COL_NICK=1,		/* char * */
+	COL_HOST=2,		/* char * */
+	COL_USER=3,		/* struct User * */
+	COL_GDKCOLOR=4	/* GdkColor * */
 };
 
 
@@ -105,7 +105,7 @@ fe_userlist_numbers (session *sess)
 	{
 		if (sess->total)
 		{
-			snprintf (tbuf, sizeof (tbuf), _("%d ops, %d total"), sess->ops, sess->total);
+			g_snprintf (tbuf, sizeof (tbuf), _("%d ops, %d total"), sess->ops, sess->total);
 			tbuf[sizeof (tbuf) - 1] = 0;
 			gtk_label_set_text (GTK_LABEL (sess->gui->namelistinfo), tbuf);
 		} else
@@ -188,7 +188,7 @@ userlist_selection_list (GtkWidget *widget, int *num_ret)
 	if (num_sel < 1)
 		return NULL;
 
-	nicks = malloc (sizeof (char *) * (num_sel + 1));
+	nicks = g_new (char *, num_sel + 1);
 
 	i = 0;
 	gtk_tree_model_get_iter_first (model, &iter);
@@ -286,7 +286,7 @@ fe_userlist_remove (session *sess, struct User *user)
 	int sel;
 
 	iter = find_row (GTK_TREE_VIEW (sess->gui->user_tree),
-						  sess->res->user_model, user, &sel);
+						  GTK_TREE_MODEL(sess->res->user_model), user, &sel);
 	if (!iter)
 		return 0;
 
@@ -316,7 +316,7 @@ fe_userlist_rehash (session *sess, struct User *user)
 	int nick_color = 0;
 
 	iter = find_row (GTK_TREE_VIEW (sess->gui->user_tree),
-						  sess->res->user_model, user, &sel);
+						  GTK_TREE_MODEL(sess->res->user_model), user, &sel);
 	if (!iter)
 		return;
 
@@ -332,9 +332,9 @@ fe_userlist_rehash (session *sess, struct User *user)
 }
 
 void
-fe_userlist_insert (session *sess, struct User *newuser, int row, int sel)
+fe_userlist_insert (session *sess, struct User *newuser, gboolean sel)
 {
-	GtkTreeModel *model = sess->res->user_model;
+	GtkTreeModel *model = GTK_TREE_MODEL(sess->res->user_model);
 	GdkPixbuf *pix = get_user_icon (sess->server, newuser);
 	GtkTreeIter iter;
 	char *nick;
@@ -348,16 +348,16 @@ fe_userlist_insert (session *sess, struct User *newuser, int row, int sel)
 	nick = newuser->nick;
 	if (!prefs.hex_gui_ulist_icons)
 	{
-		nick = malloc (strlen (newuser->nick) + 2);
+		nick = g_malloc (strlen (newuser->nick) + 2);
 		nick[0] = newuser->prefix[0];
-		if (!nick[0] || nick[0] == ' ')
+		if (nick[0] == '\0' || nick[0] == ' ')
 			strcpy (nick, newuser->nick);
 		else
 			strcpy (nick + 1, newuser->nick);
 		pix = NULL;
 	}
 
-	gtk_list_store_insert_with_values (GTK_LIST_STORE (model), &iter, row,
+	gtk_list_store_insert_with_values (GTK_LIST_STORE (model), &iter, 0,
 									COL_PIX, pix,
 									COL_NICK, nick,
 									COL_HOST, newuser->hostname,
@@ -367,7 +367,7 @@ fe_userlist_insert (session *sess, struct User *newuser, int row, int sel)
 
 	if (!prefs.hex_gui_ulist_icons)
 	{
-		free (nick);
+		g_free (nick);
 	}
 
 	/* is it me? */
@@ -377,14 +377,6 @@ fe_userlist_insert (session *sess, struct User *newuser, int row, int sel)
 			mg_set_access_icon (sess->gui, pix, sess->server->is_away);
 	}
 
-#if 0
-	if (prefs.hilitenotify && notify_isnotify (sess, newuser->nick))
-	{
-		gtk_clist_set_foreground ((GtkCList *) sess->gui->user_clist, row,
-										  &colors[prefs.nu_color]);
-	}
-#endif
-
 	/* is it the front-most tab? */
 	if (gtk_tree_view_get_model (GTK_TREE_VIEW (sess->gui->user_tree))
 		 == model)
@@ -396,12 +388,6 @@ fe_userlist_insert (session *sess, struct User *newuser, int row, int sel)
 }
 
 void
-fe_userlist_move (session *sess, struct User *user, int new_row)
-{
-	fe_userlist_insert (sess, user, new_row, fe_userlist_remove (sess, user));
-}
-
-void
 fe_userlist_clear (session *sess)
 {
 	gtk_list_store_clear (sess->res->user_model);
@@ -459,11 +445,67 @@ userlist_dnd_leave (GtkTreeView *widget, GdkDragContext *context, guint ttime)
 	return TRUE;
 }
 
-void *
-userlist_create_model (void)
+static int
+userlist_alpha_cmp (GtkTreeModel *model, GtkTreeIter *iter_a, GtkTreeIter *iter_b, gpointer userdata)
+{
+	struct User *user_a, *user_b;
+
+	gtk_tree_model_get (model, iter_a, COL_USER, &user_a, -1);
+	gtk_tree_model_get (model, iter_b, COL_USER, &user_b, -1);
+
+	return nick_cmp_alpha (user_a, user_b, ((session*)userdata)->server);
+}
+
+static int
+userlist_ops_cmp (GtkTreeModel *model, GtkTreeIter *iter_a, GtkTreeIter *iter_b, gpointer userdata)
 {
-	return gtk_list_store_new (5, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING,
+	struct User *user_a, *user_b;
+
+	gtk_tree_model_get (model, iter_a, COL_USER, &user_a, -1);
+	gtk_tree_model_get (model, iter_b, COL_USER, &user_b, -1);
+
+	return nick_cmp_az_ops (((session*)userdata)->server, user_a, user_b);
+}
+
+GtkListStore *
+userlist_create_model (session *sess)
+{
+	GtkListStore *store;
+	GtkTreeIterCompareFunc cmp_func;
+	GtkSortType sort_type;
+
+	store = gtk_list_store_new (5, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING,
 										G_TYPE_POINTER, GDK_TYPE_COLOR);
+
+	switch (prefs.hex_gui_ulist_sort)
+	{
+	case 0:
+		cmp_func = userlist_ops_cmp;
+		sort_type = GTK_SORT_ASCENDING;
+		break;
+	case 1:
+		cmp_func = userlist_alpha_cmp;
+		sort_type = GTK_SORT_ASCENDING;
+		break;
+	case 2:
+		cmp_func = userlist_ops_cmp;
+		sort_type = GTK_SORT_DESCENDING;
+		break;
+	case 3:
+		cmp_func = userlist_alpha_cmp;
+		sort_type = GTK_SORT_DESCENDING;
+		break;
+	default:
+		/* No sorting */
+		gtk_tree_sortable_set_default_sort_func (GTK_TREE_SORTABLE(store), NULL, NULL, NULL);
+		return store;
+	}
+
+	gtk_tree_sortable_set_default_sort_func (GTK_TREE_SORTABLE(store), cmp_func, sess, NULL);
+	gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(store),
+						GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID, sort_type);
+
+	return store;
 }
 
 static void
@@ -525,7 +567,7 @@ userlist_click_cb (GtkWidget *widget, GdkEventButton *event, gpointer userdata)
 				i--;
 				g_free (nicks[i]);
 			}
-			free (nicks);
+			g_free (nicks);
 		}
 		return TRUE;
 	}
@@ -542,13 +584,13 @@ userlist_click_cb (GtkWidget *widget, GdkEventButton *event, gpointer userdata)
 				i--;
 				g_free (nicks[i]);
 			}
-			free (nicks);
+			g_free (nicks);
 			return TRUE;
 		}
 		if (nicks)
 		{
 			g_free (nicks[0]);
-			free (nicks);
+			g_free (nicks);
 		}
 
 		sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
@@ -567,7 +609,7 @@ userlist_click_cb (GtkWidget *widget, GdkEventButton *event, gpointer userdata)
 					i--;
 					g_free (nicks[i]);
 				}
-				free (nicks);
+				g_free (nicks);
 			}
 		} else
 		{
@@ -668,7 +710,7 @@ void
 userlist_show (session *sess)
 {
 	gtk_tree_view_set_model (GTK_TREE_VIEW (sess->gui->user_tree),
-									 sess->res->user_model);
+									 GTK_TREE_MODEL(sess->res->user_model));
 }
 
 void
diff --git a/src/fe-gtk/userlistgui.h b/src/fe-gtk/userlistgui.h
index 993fe8f0..e24f2ebc 100644
--- a/src/fe-gtk/userlistgui.h
+++ b/src/fe-gtk/userlistgui.h
@@ -23,7 +23,7 @@
 void userlist_set_value (GtkWidget *treeview, gfloat val);
 gfloat userlist_get_value (GtkWidget *treeview);
 GtkWidget *userlist_create (GtkWidget *box);
-void *userlist_create_model (void);
+GtkListStore *userlist_create_model (session *sess);
 void userlist_show (session *sess);
 void userlist_select (session *sess, char *name);
 char **userlist_selection_list (GtkWidget *widget, int *num_ret);
diff --git a/src/fe-gtk/xtext.c b/src/fe-gtk/xtext.c
index 6a499f52..6692b360 100644
--- a/src/fe-gtk/xtext.c
+++ b/src/fe-gtk/xtext.c
@@ -31,13 +31,19 @@
 #include <stdlib.h>
 #include <time.h>
 
-#include "../../config.h"
+#include "config.h"
 #include "../common/hexchat.h"
 #include "../common/fe.h"
 #include "../common/util.h"
 #include "../common/hexchatc.h"
 #include "../common/url.h"
+
+#ifdef WIN32
+#include "marshal.h"
+#else
 #include "../common/marshal.h"
+#endif
+
 #include "fe-gtk.h"
 #include "xtext.h"
 #include "fkeys.h"
@@ -484,7 +490,10 @@ gtk_xtext_adjustment_set (xtext_buffer *buf, int fire_signal)
 		adj->page_increment = adj->page_size;
 
 		if (adj->value > adj->upper - adj->page_size)
+		{
+			buf->scrollbar_down = TRUE;
 			adj->value = adj->upper - adj->page_size;
+		}
 
 		if (adj->value < 0)
 			adj->value = 0;
@@ -829,7 +838,6 @@ find_x (GtkXText *xtext, textentry *ent, int x, int subline, int indent)
 	int off, len, wid, mbl, mbw;
 
 	/* Skip to the first chunk of stuff for the subline */
-	list = ent->slp;
 	if (subline > 0)
 	{
 		suboff = GPOINTER_TO_INT (g_slist_nth_data (ent->sublines, subline - 1));
@@ -846,6 +854,8 @@ find_x (GtkXText *xtext, textentry *ent, int x, int subline, int indent)
 		list = ent->slp;
 	} 
 	/* Step to the first character of the subline */
+	if (list == NULL)
+		return 0;
 	meta = list->data;
 	off = meta->off;
 	len = meta->len;
@@ -934,12 +944,12 @@ gtk_xtext_find_x (GtkXText * xtext, int x, textentry * ent, int subline,
 }
 
 static textentry *
-gtk_xtext_find_char (GtkXText * xtext, int x, int y, int *off,
-							int *out_of_bounds, int *ret_subline)
+gtk_xtext_find_char (GtkXText * xtext, int x, int y, int *off, int *out_of_bounds)
 {
 	textentry *ent;
 	int line;
 	int subline;
+	int outofbounds;
 
 	/* Adjust y value for negative rounding, double to int */
 	if (y < 0)
@@ -948,13 +958,12 @@ gtk_xtext_find_char (GtkXText * xtext, int x, int y, int *off,
 	line = (y + xtext->pixel_offset) / xtext->fontsize;
 	ent = gtk_xtext_nth (xtext, line + (int)xtext->adj->value, &subline);
 	if (!ent)
-		return 0;
+		return NULL;
 
 	if (off)
-		*off = gtk_xtext_find_x (xtext, x, ent, subline, line, out_of_bounds);
-
-	if (ret_subline)
-		*ret_subline = subline;
+		*off = gtk_xtext_find_x (xtext, x, ent, subline, line, &outofbounds);
+	if (out_of_bounds)
+		*out_of_bounds = outofbounds;
 
 	return ent;
 }
@@ -1049,14 +1058,14 @@ gtk_xtext_paint (GtkWidget *widget, GdkRectangle *area)
 		return;
 	}
 
-	ent_start = gtk_xtext_find_char (xtext, area->x, area->y, NULL, NULL, NULL);
+	ent_start = gtk_xtext_find_char (xtext, area->x, area->y, NULL, NULL);
 	if (!ent_start)
 	{
 		xtext_draw_bg (xtext, area->x, area->y, area->width, area->height);
 		goto xit;
 	}
 	ent_end = gtk_xtext_find_char (xtext, area->x + area->width,
-											 area->y + area->height, NULL, NULL, NULL);
+											 area->y + area->height, NULL, NULL);
 	if (!ent_end)
 		ent_end = xtext->buffer->text_last;
 
@@ -1244,13 +1253,14 @@ lamejump:
 		}
 	}
 	/* marking upward? */
-	else if (xtext->buffer->last_ent_end == end_ent &&
+	else if (xtext->buffer->last_ent_start != NULL &&
+				xtext->buffer->last_ent_end == end_ent &&
 				xtext->buffer->last_offset_end == end_offset)
 	{
 		ent = end_ent;
 		while (ent)
 		{
-			if (ent == start_ent)
+			if (ent == start_ent && xtext->buffer->last_ent_start)
 			{
 				gtk_xtext_selection_up (xtext, xtext->buffer->last_ent_start, ent, start_offset);
 				/*gtk_xtext_render_ents (xtext, xtext->buffer->last_ent_start, ent);*/
@@ -1299,99 +1309,104 @@ gtk_xtext_selection_draw (GtkXText * xtext, GdkEventMotion * event, gboolean ren
 	textentry *ent;
 	textentry *ent_end;
 	textentry *ent_start;
-	int offset_start;
-	int offset_end;
-	int subline_start;
-	int subline_end;
-	int oob;
-	int marking_up = FALSE;
-	int len_start;
-	int len_end;
+	int offset_start = 0;
+	int offset_end = 0;
+	textentry *low_ent, *high_ent;
+	int low_x, low_y, low_offs;
+	int high_x, high_y, high_offs, high_len;
 
-	ent_start = gtk_xtext_find_char (xtext, xtext->select_start_x, xtext->select_start_y, &offset_start, &oob, &subline_start);
-	ent_end = gtk_xtext_find_char (xtext, xtext->select_end_x, xtext->select_end_y, &offset_end, &oob, &subline_end);
+	if (xtext->buffer->text_first == NULL)
+		return;
 
-	if ((!ent_start || !ent_end) && !xtext->buffer->text_last && xtext->adj->value != xtext->buffer->old_value)
-	{
-		gtk_xtext_render_page (xtext);
+	ent_start = gtk_xtext_find_char (xtext, xtext->select_start_x, xtext->select_start_y, &offset_start, NULL);
+	ent_end = gtk_xtext_find_char (xtext, xtext->select_end_x, xtext->select_end_y, &offset_end, NULL);
+	if (ent_start == NULL && ent_end == NULL)
 		return;
-	}
 
-	if (!ent_start)
+	if	((ent_start != ent_end && xtext->select_start_y > xtext->select_end_y) || /* different entries */
+		(ent_start == ent_end && offset_start > offset_end))	/* same entry, different character offsets */
 	{
-		ent_start = xtext->buffer->text_last;
-		offset_start = ent_start->str_len;
+		/* marking up */
+		low_ent = ent_end;
+		low_x = xtext->select_end_x;
+		low_y = xtext->select_end_y;
+		low_offs = offset_end;
+		high_ent = ent_start;
+		high_x = xtext->select_start_x;
+		high_y = xtext->select_start_y;
+		high_offs = offset_start;
 	}
-
-	if (!ent_end)
+	else
 	{
-		ent_end = xtext->buffer->text_last;
-		offset_end = ent_end->str_len;
+		/* marking down */
+		low_ent = ent_start;
+		low_x = xtext->select_start_x;
+		low_y = xtext->select_start_y;
+		low_offs = offset_start;
+		high_ent = ent_end;
+		high_x = xtext->select_end_x;
+		high_y = xtext->select_end_y;
+		high_offs = offset_end;
 	}
-
-	if ((ent_start != ent_end && xtext->select_start_y > xtext->select_end_y) || /* different entries */
-		(ent_start == ent_end && subline_start > subline_end) || /* different lines */
-		(ent_start == ent_end && subline_start == subline_end && xtext->select_start_x > xtext->select_end_x)) /* marking to the left */
+	if (low_ent == NULL)
+	{
+		low_ent = xtext->buffer->text_first;
+		low_offs = 0;
+	}
+	if (high_ent == NULL)
 	{
-		marking_up = TRUE;
+		high_ent = xtext->buffer->text_last;
+		high_offs = high_ent->str_len;
 	}
 
 	/* word selection */
 	if (xtext->word_select)
 	{
 		/* a word selection cannot be started if the cursor is out of bounds in gtk_xtext_button_press */
-		gtk_xtext_get_word (xtext, xtext->select_start_x, xtext->select_start_y, NULL, &offset_start, &len_start, NULL);
+		gtk_xtext_get_word (xtext, low_x, low_y, NULL, &low_offs, NULL, NULL);
 
 		/* in case the cursor is out of bounds we keep offset_end from gtk_xtext_find_char and fix the length */
-		if (gtk_xtext_get_word (xtext, xtext->select_end_x, xtext->select_end_y, NULL, &offset_end, &len_end, NULL) == NULL)
-			len_end = offset_end == ent_end->str_len? 0: -1; /* -1 for the space, 0 if at the end */
-
-		if (!marking_up)
-			offset_end += len_end;
-		else
-			offset_start += len_start;
+		if (gtk_xtext_get_word (xtext, high_x, high_y, NULL, &high_offs, &high_len, NULL) == NULL)
+			high_len = high_offs == high_ent->str_len? 0: -1; /* -1 for the space, 0 if at the end */
+		high_offs += high_len;
+		if (low_y < 0)
+			low_offs = xtext->buffer->last_offset_start;
+		if (high_y > xtext->buffer->window_height)
+			high_offs = xtext->buffer->last_offset_end;
 	}
 	/* line/ent selection */
 	else if (xtext->line_select)
 	{
-		offset_start = marking_up? ent_start->str_len: 0;
-		offset_end = marking_up? 0: ent_end->str_len;
+		low_offs = 0;
+		high_offs = high_ent->str_len;
 	}
-
-	if (marking_up)
+	/* character selection */
+	else
 	{
-		int temp;
-
-		/* ensure ent_start is above ent_end */
-		if (ent_start != ent_end)
-		{
-			ent = ent_start;
-			ent_start = ent_end;
-			ent_end = ent;
-		}
-
-		/* switch offsets as well */
-		temp = offset_start;
-		offset_start = offset_end;
-		offset_end = temp;
+		if (low_y < 0)
+			low_offs = xtext->buffer->last_offset_start;
+		if (high_y > xtext->buffer->window_height)
+			high_offs = xtext->buffer->last_offset_end;
 	}
 
 	/* set all the old mark_ fields to -1 */
 	gtk_xtext_selection_clear (xtext->buffer);
 
-	/* set the default values */
-	ent_start->mark_end = ent_start->str_len;
-	ent_end->mark_start = 0;
+	low_ent->mark_start = low_offs;
+	low_ent->mark_end = high_offs;
 
-	/* set the calculated values (this overwrites the default values if we're on the same ent) */
-	ent_start->mark_start = offset_start;
-	ent_end->mark_end = offset_end;
-
-	/* set all the mark_ fields of the ents within the selection */
-	if (ent_start != ent_end)
+	if (low_ent != high_ent)
 	{
-		ent = ent_start->next;
-		while (ent && ent != ent_end)
+		low_ent->mark_end = low_ent->str_len;
+		if (high_offs != 0)
+		{
+			high_ent->mark_start = 0;
+			high_ent->mark_end = high_offs;
+		}
+
+		/* set all the mark_ fields of the ents within the selection */
+		ent = low_ent->next;
+		while (ent && ent != high_ent)
 		{
 			ent->mark_start = 0;
 			ent->mark_end = ent->str_len;
@@ -1400,7 +1415,7 @@ gtk_xtext_selection_draw (GtkXText * xtext, GdkEventMotion * event, gboolean ren
 	}
 
 	if (render)
-		gtk_xtext_selection_render (xtext, ent_start, ent_end);
+		gtk_xtext_selection_render (xtext, low_ent, high_ent);
 }
 
 static int
@@ -1532,7 +1547,7 @@ gtk_xtext_get_word (GtkXText * xtext, int x, int y, textentry ** ret_ent,
 	int out_of_bounds = 0;
 	int len_to_offset = 0;
 
-	ent = gtk_xtext_find_char (xtext, x, y, &offset, &out_of_bounds, NULL);
+	ent = gtk_xtext_find_char (xtext, x, y, &offset, &out_of_bounds);
 	if (ent == NULL || out_of_bounds || offset < 0 || offset >= ent->str_len)
 		return NULL;
 
@@ -1585,11 +1600,11 @@ gtk_xtext_get_word (GtkXText * xtext, int x, int y, textentry ** ret_ent,
 
 		/* make sure we're not before the start of the match */
 		if (len_to_offset < start)
-			return 0;
+			return NULL;
 
 		/* and not after it */
 		if (len_to_offset - start >= end - start)
-			return 0;
+			return NULL;
 	}
 
 	return word;
@@ -1646,7 +1661,8 @@ gtk_xtext_check_mark_stamp (GtkXText *xtext, GdkModifierType mask)
 {
 	gboolean redraw = FALSE;
 
-	if (mask & STATE_SHIFT || prefs.hex_text_autocopy_stamp)
+	if ((mask & STATE_SHIFT || prefs.hex_text_autocopy_stamp)
+	    && (!prefs.hex_stamp_text || prefs.hex_text_indent))
 	{
 		if (!xtext->mark_stamp)
 		{
@@ -1707,7 +1723,7 @@ gtk_xtext_get_word_adjust (GtkXText *xtext, int x, int y, textentry **word_ent,
 			}
 		}
 	}
-	g_slist_free_full (slp, free);
+	g_slist_free_full (slp, g_free);
 
 	return word_type;
 }
@@ -1853,7 +1869,7 @@ gtk_xtext_set_clip_owner (GtkWidget * xtext, GdkEventButton * event)
 			gtk_selection_owner_set (xtext, GDK_SELECTION_SECONDARY, event ? event->time : GDK_CURRENT_TIME);
 		}
 
-		free (str);
+		g_free (str);
 	}
 }
 
@@ -2107,7 +2123,7 @@ gtk_xtext_selection_get_text (GtkXText *xtext, int *len_ret)
 		return NULL;
 
 	/* now allocate mem and copy buffer */
-	pos = txt = malloc (len);
+	pos = txt = g_malloc (len);
 	ent = buf->last_ent_start;
 	while (ent)
 	{
@@ -2147,10 +2163,11 @@ gtk_xtext_selection_get_text (GtkXText *xtext, int *len_ret)
 		/*stripped = gtk_xtext_conv_color (txt, strlen (txt), &len);*/
 		stripped = txt;
 		len = strlen (txt);
-	} else
+	}
+	else
 	{
 		stripped = gtk_xtext_strip_color (txt, strlen (txt), NULL, &len, NULL, FALSE);
-		free (txt);
+		g_free (txt);
 	}
 
 	*len_ret = len;
@@ -2205,7 +2222,7 @@ gtk_xtext_selection_get (GtkWidget * widget,
 		g_free (new_text);
 	}
 
-	free (stripped);
+	g_free (stripped);
 }
 
 static gboolean
@@ -2360,7 +2377,7 @@ xtext_do_chunk(chunk_t *c)
 	if (c->len1 == 0)
 		return;
 
-	meta = malloc (sizeof *meta);
+	meta = g_new (offlen_t, 1);
 	meta->off = c->off1;
 	meta->len = c->len1;
 	meta->emph = c->emph;
@@ -2383,7 +2400,7 @@ gtk_xtext_strip_color (unsigned char *text, int len, unsigned char *outbuf,
 	int mbl;	/* multi-byte length */
 
 	if (outbuf == NULL)
-		new_str = malloc (len + 2);
+		new_str = g_malloc (len + 2);
 	else
 		new_str = outbuf;
 
@@ -2459,7 +2476,7 @@ bad_utf8:		/* Normal ending sequence, and give up if bad utf8 */
 	if (slpp)
 		*slpp = c.slp;
 	else
-		g_slist_free_full (c.slp, free);
+		g_slist_free_full (c.slp, g_free);
 
 	return new_str;
 }
@@ -2475,7 +2492,7 @@ gtk_xtext_text_width_ent (GtkXText *xtext, textentry *ent)
 
 	if (ent->slp)
 	{
-		g_slist_free_full (ent->slp, free);
+		g_slist_free_full (ent->slp, g_free);
 		ent->slp = NULL;
 	}
 
@@ -2507,7 +2524,7 @@ gtk_xtext_text_width (GtkXText *xtext, unsigned char *text, int len)
 												&new_len, &slp, !xtext->ignore_hidden);
 
 	width =  backend_get_text_width_slp (xtext, new_buf, slp);
-	g_slist_free_full (slp, free);
+	g_slist_free_full (slp, g_free);
 
 	return width;
 }
@@ -3254,7 +3271,7 @@ gtk_xtext_render_stamp (GtkXText * xtext, textentry * ent,
 {
 	textentry tmp_ent;
 	int jo, ji, hs;
-	int xsize, y;
+	int xsize, y, emphasis;
 
 	/* trashing ent here, so make a backup first */
 	memcpy (&tmp_ent, ent, sizeof (tmp_ent));
@@ -3264,7 +3281,7 @@ gtk_xtext_render_stamp (GtkXText * xtext, textentry * ent,
 	xtext->jump_out_offset = 0;
 	xtext->jump_in_offset = 0;
 	xtext->hilight_start = 0xffff;	/* temp disable */
-	int emphasis = 0;
+	emphasis = 0;
 
 	if (xtext->mark_stamp)
 	{
@@ -3530,7 +3547,7 @@ gtk_xtext_save (GtkXText * xtext, int fh)
 											  &newlen, NULL, FALSE);
 		write (fh, buf, newlen);
 		write (fh, "\n", 1);
-		free (buf);
+		g_free (buf);
 		ent = ent->next;
 	}
 }
@@ -3641,7 +3658,7 @@ gtk_xtext_nth (GtkXText *xtext, int line, int *subline)
 					break;
 				lines -= g_slist_length (ent->sublines);
 			}
-			return 0;
+			return NULL;
 		}
 	}
 	/* -- end of optimization -- */
@@ -3656,7 +3673,7 @@ gtk_xtext_nth (GtkXText *xtext, int line, int *subline)
 		}
 		ent = ent->next;
 	}
-	return 0;
+	return NULL;
 }
 
 /* render enta (or an inclusive range enta->entb) */
@@ -3895,10 +3912,10 @@ gtk_xtext_kill_ent (xtext_buffer *buffer, textentry *ent)
 		gtk_xtext_search_textentry_del (buffer, ent);
 	}
 
-	g_slist_free_full (ent->slp, free);
+	g_slist_free_full (ent->slp, g_free);
 	g_slist_free (ent->sublines);
 
-	free (ent);
+	g_free (ent);
 	return visible;
 }
 
@@ -4033,7 +4050,7 @@ gtk_xtext_clear (xtext_buffer *buf, int lines)
 		while (buf->text_first)
 		{
 			next = buf->text_first->next;
-			free (buf->text_first);
+			g_free (buf->text_first);
 			buf->text_first = next;
 		}
 		buf->text_last = NULL;
@@ -4191,7 +4208,6 @@ gtk_xtext_search_textentry (xtext_buffer *buf, textentry *ent)
 
 		hay = match? g_strdup (str): g_utf8_casefold (str, lstr);
 		lhay = strlen (hay);
-		off = 0;
 
 		for (pos = hay, len = lhay; len;
 			  off += buf->search_lnee, pos = hay + off, len = lhay - off)
@@ -4210,7 +4226,7 @@ gtk_xtext_search_textentry (xtext_buffer *buf, textentry *ent)
 	}
 
 	/* Common processing --- */
-	g_slist_free_full (slp, free);
+	g_slist_free_full (slp, g_free);
 	return gl;
 }
 
@@ -4639,7 +4655,7 @@ gtk_xtext_append_indent (xtext_buffer *buf,
 	if (right_text[right_len-1] == '\n')
 		right_len--;
 
-	ent = malloc (left_len + right_len + 2 + sizeof (textentry));
+	ent = g_malloc (left_len + right_len + 2 + sizeof (textentry));
 	str = (unsigned char *) ent + sizeof (textentry);
 
 	memcpy (str, left_text, left_len);
@@ -4660,7 +4676,9 @@ gtk_xtext_append_indent (xtext_buffer *buf,
 		space = 0;
 
 	/* do we need to auto adjust the separator position? */
-	if (buf->xtext->auto_indent && ent->indent < MARGIN + space)
+	if (buf->xtext->auto_indent &&
+		 buf->indent < buf->xtext->max_auto_indent &&
+		 ent->indent < MARGIN + space)
 	{
 		tempindent = MARGIN + space + buf->xtext->space_width + left_width;
 
@@ -4681,7 +4699,7 @@ gtk_xtext_append_indent (xtext_buffer *buf,
 }
 
 void
-gtk_xtext_append (xtext_buffer *buf, unsigned char *text, int len)
+gtk_xtext_append (xtext_buffer *buf, unsigned char *text, int len, time_t stamp)
 {
 	textentry *ent;
 
@@ -4694,7 +4712,7 @@ gtk_xtext_append (xtext_buffer *buf, unsigned char *text, int len)
 	if (len >= sizeof (buf->xtext->scratch_buffer))
 		len = sizeof (buf->xtext->scratch_buffer) - 1;
 
-	ent = malloc (len + 1 + sizeof (textentry));
+	ent = g_malloc (len + 1 + sizeof (textentry));
 	ent->str = (unsigned char *) ent + sizeof (textentry);
 	ent->str_len = len;
 	if (len)
@@ -4703,7 +4721,7 @@ gtk_xtext_append (xtext_buffer *buf, unsigned char *text, int len)
 	ent->indent = 0;
 	ent->left_len = -1;
 
-	gtk_xtext_append_entry (buf, ent, 0);
+	gtk_xtext_append_entry (buf, ent, stamp);
 }
 
 gboolean
@@ -4738,11 +4756,14 @@ gtk_xtext_lastlog (xtext_buffer *out, xtext_buffer *search_area)
 			}
 			else
 			{
-				gtk_xtext_append (out, ent->str, ent->str_len);
+				gtk_xtext_append (out, ent->str, ent->str_len, 0);
 			}
 
-			out->text_last->stamp = ent->stamp;
-			gtk_xtext_search_textentry_add (out, out->text_last, gl, TRUE);
+			if (out->text_last)
+			{
+				out->text_last->stamp = ent->stamp;
+				gtk_xtext_search_textentry_add (out, out->text_last, gl, TRUE);
+			}
 		}
 		ent = ent->next;
 	}
@@ -4936,6 +4957,7 @@ gtk_xtext_buffer_show (GtkXText *xtext, xtext_buffer *buf, int render)
 		if (buf->window_width != w)
 		{
 			buf->window_width = w;
+			buf->window_height = h;
 			gtk_xtext_calc_lines (buf, FALSE);
 			if (buf->scrollbar_down)
 				gtk_adjustment_set_value (xtext->adj, xtext->adj->upper -
@@ -4959,8 +4981,7 @@ gtk_xtext_buffer_new (GtkXText *xtext)
 {
 	xtext_buffer *buf;
 
-	buf = malloc (sizeof (xtext_buffer));
-	memset (buf, 0, sizeof (xtext_buffer));
+	buf = g_new0 (xtext_buffer, 1);
 	buf->old_value = -1;
 	buf->xtext = xtext;
 	buf->scrollbar_down = TRUE;
@@ -4990,9 +5011,9 @@ gtk_xtext_buffer_free (xtext_buffer *buf)
 	while (ent)
 	{
 		next = ent->next;
-		free (ent);
+		g_free (ent);
 		ent = next;
 	}
 
-	free (buf);
+	g_free (buf);
 }
diff --git a/src/fe-gtk/xtext.h b/src/fe-gtk/xtext.h
index 73f5b52d..d6853f9f 100644
--- a/src/fe-gtk/xtext.h
+++ b/src/fe-gtk/xtext.h
@@ -252,7 +252,7 @@ struct _GtkXTextClass
 };
 
 GtkWidget *gtk_xtext_new (GdkColor palette[], int separator);
-void gtk_xtext_append (xtext_buffer *buf, unsigned char *text, int len);
+void gtk_xtext_append (xtext_buffer *buf, unsigned char *text, int len, time_t stamp);
 void gtk_xtext_append_indent (xtext_buffer *buf,
 										unsigned char *left_text, int left_len,
 										unsigned char *right_text, int right_len,
diff --git a/src/fe-text/fe-text.c b/src/fe-text/fe-text.c
index c8b64ab0..209a3d03 100644
--- a/src/fe-text/fe-text.c
+++ b/src/fe-text/fe-text.c
@@ -16,6 +16,8 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
+#include "config.h"
+
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -78,7 +80,6 @@ fe_new_window (struct session *sess, int focus)
 {
 	char buf[512];
 
-	sess->gui = malloc (4);
 	current_sess = sess;
 
 	if (!sess->server->front_session)
@@ -92,13 +93,11 @@ fe_new_window (struct session *sess, int focus)
 		return;
 	done_intro = 1;
 
-	snprintf (buf, sizeof (buf),
+	g_snprintf (buf, sizeof (buf),
 				"\n"
 				" \017HexChat-Text \00310"PACKAGE_VERSION"\n"
-				" \017Running on \00310%s \017glib \00310%d.%d.%d\n"
-				" \017This binary compiled \00310"__DATE__"\017\n",
-				get_sys_str (1),
-				glib_major_version, glib_minor_version, glib_micro_version);
+				" \017Running on \00310%s\n",
+				get_sys_str (1));
 	fe_print_text (sess, buf, 0, FALSE);
 
 	fe_print_text (sess, "\n\nCompiled in Features\0032:\017 "
@@ -111,9 +110,6 @@ fe_new_window (struct session *sess, int focus)
 #ifdef USE_OPENSSL
 	"OpenSSL "
 #endif
-#ifdef USE_IPV6
-	"IPv6"
-#endif
 	"\n\n", 0, FALSE);
 	fflush (stdout);
 }
@@ -140,7 +136,7 @@ timecat (char *buf, time_t stamp)
 
 /* Windows doesn't handle ANSI codes in cmd.exe, need to not display them */
 #ifndef WIN32
-/*                       0  1  2  3  4  5  6  7   8   9   10 11  12  13  14 15 */
+/*                               0  1  2  3  4  5  6  7   8   9  10  11  12  13  14 15 */
 static const short colconv[] = { 0, 7, 4, 2, 1, 3, 5, 11, 13, 12, 6, 16, 14, 15, 10, 7 };
 
 void
@@ -151,7 +147,7 @@ fe_print_text (struct session *sess, char *text, time_t stamp,
 	char num[8];
 	int reverse = 0, under = 0, bold = 0,
 		comma, k, i = 0, j = 0, len = strlen (text);
-	unsigned char *newtext = malloc (len + 1024);
+	unsigned char *newtext = g_malloc (len + 1024);
 
 	if (prefs.hex_stamp_text)
 	{
@@ -207,7 +203,7 @@ fe_print_text (struct session *sess, char *text, time_t stamp,
 						else
 							col = 30;
 						mirc = atoi (num);
-						mirc = colconv[mirc];
+						mirc = colconv[mirc % G_N_ELEMENTS(colconv)];
 						if (mirc > 9)
 						{
 							mirc += 50;
@@ -308,7 +304,7 @@ fe_print_text (struct session *sess, char *text, time_t stamp,
 
 	newtext[j] = 0;
 	write (STDOUT_FILENO, newtext, j);
-	free (newtext);
+	g_free (newtext);
 }
 #else
 /* The win32 version for cmd.exe */
@@ -319,7 +315,7 @@ fe_print_text (struct session *sess, char *text, time_t stamp,
 	int dotime = FALSE;
 	int comma, k, i = 0, j = 0, len = strlen (text);
 
-	unsigned char *newtext = malloc (len + 1024);
+	unsigned char *newtext = g_malloc (len + 1024);
 
 	if (prefs.hex_stamp_text)
 	{
@@ -403,7 +399,7 @@ fe_print_text (struct session *sess, char *text, time_t stamp,
 
 	newtext[j] = 0;
 	write (STDOUT_FILENO, newtext, j);
-	free (newtext);
+	g_free (newtext);
 }
 #endif
 
@@ -508,14 +504,14 @@ fe_args (int argc, char *argv[])
 	{
 #ifdef WIN32
 		/* see the chdir() below */
-		char *sl, *exe = strdup (argv[0]);
+		char *sl, *exe = g_strdup (argv[0]);
 		sl = strrchr (exe, '\\');
 		if (sl)
 		{
 			*sl = 0;
 			printf ("%s\\plugins\n", exe);
 		}
-		free (exe);
+		g_free (exe);
 #else
 		printf ("%s\n", HEXCHATLIBDIR);
 #endif
@@ -530,8 +526,7 @@ fe_args (int argc, char *argv[])
 
 	if (arg_cfgdir)	/* we want filesystem encoding */
 	{
-		if (xdir)
-			g_free (xdir);
+		g_free (xdir);
 		xdir = strdup (arg_cfgdir);
 		if (xdir[strlen (xdir) - 1] == '/')
 			xdir[strlen (xdir) - 1] = 0;
@@ -583,7 +578,6 @@ fe_exit (void)
 void
 fe_new_server (struct server *serv)
 {
-	serv->gui = malloc (4);
 }
 
 void
@@ -682,7 +676,7 @@ fe_progressbar_end (struct server *serv)
 {
 }
 void
-fe_userlist_insert (struct session *sess, struct User *newuser, int row, int sel)
+fe_userlist_insert (struct session *sess, struct User *newuser, gboolean sel)
 {
 }
 int
@@ -695,10 +689,6 @@ fe_userlist_rehash (struct session *sess, struct User *user)
 {
 }
 void
-fe_userlist_move (struct session *sess, struct User *user, int new_row)
-{
-}
-void
 fe_userlist_numbers (struct session *sess)
 {
 }
@@ -910,7 +900,6 @@ void fe_tray_set_flash (const char *filename1, const char *filename2, int timeou
 void fe_tray_set_file (const char *filename){}
 void fe_tray_set_icon (feicon icon){}
 void fe_tray_set_tooltip (const char *text){}
-void fe_tray_set_balloon (const char *title, const char *text){}
 void fe_userlist_update (session *sess, struct User *user){}
 void
 fe_open_chan_list (server *serv, char *filter, int do_refresh)
diff --git a/src/fe-text/fe-text.vcxproj b/src/fe-text/fe-text.vcxproj
index c59c57bc..1da7e733 100644
--- a/src/fe-text/fe-text.vcxproj
+++ b/src/fe-text/fe-text.vcxproj
@@ -2,6 +2,7 @@
 <Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

   <PropertyGroup Label="Configuration">

     <PlatformToolset>v120</PlatformToolset>

+    <ConfigurationType>Application</ConfigurationType>

   </PropertyGroup>

   <ItemGroup Label="ProjectConfigurations">

     <ProjectConfiguration Include="Release|Win32">

@@ -19,77 +20,38 @@
     <RootNamespace>fetext</RootNamespace>

   </PropertyGroup>

   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />

-  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">

-    <ConfigurationType>Application</ConfigurationType>

-    <UseDebugLibraries>false</UseDebugLibraries>

-    <WholeProgramOptimization>true</WholeProgramOptimization>

-    <CharacterSet>MultiByte</CharacterSet>

-  </PropertyGroup>

-  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">

-    <ConfigurationType>Application</ConfigurationType>

-    <UseDebugLibraries>false</UseDebugLibraries>

-    <WholeProgramOptimization>true</WholeProgramOptimization>

-    <CharacterSet>MultiByte</CharacterSet>

-  </PropertyGroup>

   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />

-  <ImportGroup Label="ExtensionSettings">

-  </ImportGroup>

-  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">

-    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />

-    <Import Project="..\..\win32\hexchat.props" />

-  </ImportGroup>

-  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">

-    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />

-    <Import Project="..\..\win32\hexchat.props" />

-  </ImportGroup>

-  <PropertyGroup Label="UserMacros" />

-  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">

-    <LinkIncremental>false</LinkIncremental>

-    <TargetName>hexchat-text</TargetName>

-    <OutDir>$(HexChatBin)</OutDir>

-    <IntDir>$(HexChatObj)$(ProjectName)\</IntDir>

-  </PropertyGroup>

-  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">

-    <LinkIncremental>false</LinkIncremental>

+  <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />

+  <Import Project="..\..\win32\hexchat.props" />

+  <PropertyGroup>

     <TargetName>hexchat-text</TargetName>

-    <OutDir>$(HexChatBin)</OutDir>

-    <IntDir>$(HexChatObj)$(ProjectName)\</IntDir>

+    <OutDir>$(HexChatRel)</OutDir>

   </PropertyGroup>

   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">

     <ClCompile>

-      <PrecompiledHeader>

-      </PrecompiledHeader>

-      <FunctionLevelLinking>true</FunctionLevelLinking>

-      <IntrinsicFunctions>true</IntrinsicFunctions>

       <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;$(OwnFlags);%(PreprocessorDefinitions)</PreprocessorDefinitions>

-      <AdditionalIncludeDirectories>$(SolutionDir)..;$(DepsRoot)\include;$(Glib);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>

-      <MultiProcessorCompilation>true</MultiProcessorCompilation>

+      <AdditionalIncludeDirectories>$(HexChatLib);$(DepsRoot)\include;$(Glib);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>

     </ClCompile>

     <Link>

       <SubSystem>Console</SubSystem>

       <GenerateDebugInformation>true</GenerateDebugInformation>

       <EnableCOMDATFolding>true</EnableCOMDATFolding>

       <OptimizeReferences>true</OptimizeReferences>

-      <AdditionalDependencies>$(DepLibs);"$(OutDir)\common.lib";%(AdditionalDependencies)</AdditionalDependencies>

+      <AdditionalDependencies>$(DepLibs);$(HexChatLib)common.lib;%(AdditionalDependencies)</AdditionalDependencies>

       <AdditionalLibraryDirectories>$(DepsRoot)\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>

     </Link>

   </ItemDefinitionGroup>

   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">

     <ClCompile>

-      <PrecompiledHeader>

-      </PrecompiledHeader>

-      <FunctionLevelLinking>true</FunctionLevelLinking>

-      <IntrinsicFunctions>true</IntrinsicFunctions>

       <PreprocessorDefinitions>WIN32;_WIN64;_AMD64_;NDEBUG;_CONSOLE;$(OwnFlags);%(PreprocessorDefinitions)</PreprocessorDefinitions>

-      <AdditionalIncludeDirectories>$(SolutionDir)..;$(DepsRoot)\include;$(Glib);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>

-      <MultiProcessorCompilation>true</MultiProcessorCompilation>

+      <AdditionalIncludeDirectories>$(HexChatLib);$(DepsRoot)\include;$(Glib);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>

     </ClCompile>

     <Link>

       <SubSystem>Console</SubSystem>

       <GenerateDebugInformation>true</GenerateDebugInformation>

       <EnableCOMDATFolding>true</EnableCOMDATFolding>

       <OptimizeReferences>true</OptimizeReferences>

-      <AdditionalDependencies>$(DepLibs);"$(OutDir)\common.lib";%(AdditionalDependencies)</AdditionalDependencies>

+      <AdditionalDependencies>$(DepLibs);$(HexChatLib)common.lib;%(AdditionalDependencies)</AdditionalDependencies>

       <AdditionalLibraryDirectories>$(DepsRoot)\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>

     </Link>

   </ItemDefinitionGroup>

@@ -100,6 +62,4 @@
     <ClCompile Include="fe-text.c" />

   </ItemGroup>

   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />

-  <ImportGroup Label="ExtensionTargets">

-  </ImportGroup>

-</Project>
\ No newline at end of file
+</Project>

diff --git a/src/htm/htm.csproj b/src/htm/htm.csproj
index eea953d2..d56f2188 100644
--- a/src/htm/htm.csproj
+++ b/src/htm/htm.csproj
@@ -61,22 +61,13 @@
   </PropertyGroup>

   <PropertyGroup />

   <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">

+    <PlatformTarget>x64</PlatformTarget>

+    <DebugType>pdbonly</DebugType>

+    <Optimize>true</Optimize>

     <OutputPath>..\..\..\hexchat-build\x64\bin\</OutputPath>

     <DefineConstants>TRACE</DefineConstants>

-    <Optimize>true</Optimize>

-    <DebugType>pdbonly</DebugType>

-    <PlatformTarget>x64</PlatformTarget>

-    <CodeAnalysisLogFile>bin\Release\thememan.exe.CodeAnalysisLog.xml</CodeAnalysisLogFile>

-    <CodeAnalysisUseTypeNameInSuppression>true</CodeAnalysisUseTypeNameInSuppression>

-    <CodeAnalysisModuleSuppressionsFile>GlobalSuppressions.cs</CodeAnalysisModuleSuppressionsFile>

     <ErrorReport>prompt</ErrorReport>

     <CodeAnalysisIgnoreGeneratedCode>false</CodeAnalysisIgnoreGeneratedCode>

-    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>

-    <CodeAnalysisRuleSetDirectories>;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets</CodeAnalysisRuleSetDirectories>

-    <CodeAnalysisIgnoreBuiltInRuleSets>false</CodeAnalysisIgnoreBuiltInRuleSets>

-    <CodeAnalysisRuleDirectories>;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules</CodeAnalysisRuleDirectories>

-    <CodeAnalysisIgnoreBuiltInRules>false</CodeAnalysisIgnoreBuiltInRules>

-    <CodeAnalysisFailOnMissingRules>false</CodeAnalysisFailOnMissingRules>

   </PropertyGroup>

   <ItemGroup>

     <Reference Include="System" />

diff --git a/src/libenchant_win8/libenchant_win8.def b/src/libenchant_win8/libenchant_win8.def
new file mode 100644
index 00000000..cf367651
--- /dev/null
+++ b/src/libenchant_win8/libenchant_win8.def
@@ -0,0 +1,2 @@
+EXPORTS 

+init_enchant_provider  

diff --git a/src/libenchant_win8/libenchant_win8.vcxproj b/src/libenchant_win8/libenchant_win8.vcxproj
new file mode 100644
index 00000000..aab7acc8
--- /dev/null
+++ b/src/libenchant_win8/libenchant_win8.vcxproj
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>

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

+  <ItemGroup Label="ProjectConfigurations">

+    <ProjectConfiguration Include="Release|Win32">

+      <Configuration>Release</Configuration>

+      <Platform>Win32</Platform>

+    </ProjectConfiguration>

+    <ProjectConfiguration Include="Release|x64">

+      <Configuration>Release</Configuration>

+      <Platform>x64</Platform>

+    </ProjectConfiguration>

+  </ItemGroup>

+  <PropertyGroup Label="Globals">

+    <ProjectGuid>{BF0EBC16-68AD-4CD1-864C-5B56836EBE2A}</ProjectGuid>

+    <Keyword>Win32Proj</Keyword>

+    <RootNamespace>libenchant_win8</RootNamespace>

+  </PropertyGroup>

+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />

+  <PropertyGroup Label="Configuration">

+    <ConfigurationType>DynamicLibrary</ConfigurationType>

+    <UseDebugLibraries>true</UseDebugLibraries>

+    <PlatformToolset>v120</PlatformToolset>

+  </PropertyGroup>

+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />

+  <Import Project="..\..\win32\hexchat.props" />

+  <ImportGroup Label="PropertySheets">

+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />

+  </ImportGroup>

+  <PropertyGroup>

+    <OutDir>$(HexChatRel)lib\enchant\</OutDir>

+    <TargetName>libenchant_win8</TargetName>

+  </PropertyGroup>

+  <ItemDefinitionGroup>

+    <ClCompile>

+      <AdditionalIncludeDirectories>..\common;$(HexChatLib);$(DepsRoot)\include\enchant;$(DepsRoot)\include;$(Glib);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>

+    </ClCompile>

+    <Link>

+      <AdditionalDependencies>$(DepLibs);%(AdditionalDependencies)</AdditionalDependencies>

+      <AdditionalLibraryDirectories>$(DepsRoot)\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>

+      <ModuleDefinitionFile>libenchant_win8.def</ModuleDefinitionFile>

+    </Link>

+  </ItemDefinitionGroup>

+  <ItemGroup>

+    <ClCompile Include="win8_provider.cpp" />

+  </ItemGroup>

+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />

+</Project>

diff --git a/src/libenchant_win8/libenchant_win8.vcxproj.filters b/src/libenchant_win8/libenchant_win8.vcxproj.filters
new file mode 100644
index 00000000..ff2f3024
--- /dev/null
+++ b/src/libenchant_win8/libenchant_win8.vcxproj.filters
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>

+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

+  <ItemGroup>

+    <Filter Include="Source Files">

+      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>

+      <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>

+    </Filter>

+    <Filter Include="Header Files">

+      <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>

+      <Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>

+    </Filter>

+    <Filter Include="Resource Files">

+      <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>

+      <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>

+    </Filter>

+  </ItemGroup>

+  <ItemGroup>

+    <ClCompile Include="win8_provider.cpp">

+      <Filter>Source Files</Filter>

+    </ClCompile>

+  </ItemGroup>

+</Project>

diff --git a/src/libenchant_win8/win8_provider.cpp b/src/libenchant_win8/win8_provider.cpp
new file mode 100644
index 00000000..73f16610
--- /dev/null
+++ b/src/libenchant_win8/win8_provider.cpp
@@ -0,0 +1,293 @@
+/* HexChat
+ * Copyright (c) 2015 Patrick Griffis
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include <Spellcheck.h>
+#include <glib.h>
+
+#include "typedef.h" // for ssize_t
+#include <enchant-provider.h>
+
+ENCHANT_PLUGIN_DECLARE ("win8")
+
+/* --------- Utils ----------*/
+
+static char *
+utf16_to_utf8 (const wchar_t * const str, gboolean from_bcp47)
+{
+	char *utf8 = g_utf16_to_utf8 ((gunichar2*)str, -1, nullptr, nullptr, nullptr);
+	if (utf8 && from_bcp47)
+	{
+		char *p = utf8;
+		/* bcp47 tags use syntax "en-US" while the myspell versions are "en_US" */
+		while (*p)
+		{
+			if (*p == '-')
+				*p = '_';
+			p++;
+		}
+	}
+	return utf8;
+}
+
+static wchar_t *
+utf8_to_utf16 (const char * const str, size_t len, gboolean to_bcp47)
+{
+	wchar_t *utf16 = (wchar_t*)g_utf8_to_utf16 (str, len, nullptr, nullptr, nullptr);
+	if (utf16 && to_bcp47)
+	{
+		wchar_t *p = utf16;
+		/* bcp47 tags use syntax "en-US" while the myspell versions are "en_US" */
+		while (*p)
+		{
+			if (*p == L'_')
+				*p = L'-';
+			p++;
+		}
+	}
+	return utf16;
+}
+
+static char **
+enumstring_to_chararray (IEnumString *strings, size_t *out_len, gboolean from_bcp47)
+{
+	char **chars = g_new (char*, 256); /* Hopefully large enough */
+	LPOLESTR wstr = nullptr;
+	size_t i = 0;
+
+	while (SUCCEEDED (strings->Next (1, &wstr, nullptr)) && i < 256 && wstr)
+	{
+		char *str = utf16_to_utf8 (wstr, from_bcp47);
+		if (str)
+		{
+			chars[i] = str;
+			i++;
+		}
+		CoTaskMemFree (wstr);
+	}
+	chars[i] = nullptr;
+	strings->Release ();
+
+	*out_len = i;
+	return chars;
+}
+
+/* ---------- Dict ------------ */
+
+static void
+win8_dict_add_to_personal (EnchantDict *dict, const char *const word, size_t len)
+{
+	auto checker = static_cast<ISpellChecker*>(dict->user_data);
+	wchar_t *wword = utf8_to_utf16 (word, len, FALSE);
+
+	checker->Add (wword);
+	g_free (wword);
+}
+
+static void
+win8_dict_add_to_session (EnchantDict *dict, const char *const word, size_t len)
+{
+	auto checker = static_cast<ISpellChecker*>(dict->user_data);
+	wchar_t *wword = utf8_to_utf16 (word, len, FALSE);
+
+	checker->Ignore (wword);
+	g_free (wword);
+}
+
+static int
+win8_dict_check (EnchantDict *dict, const char *const word, size_t len)
+{
+	auto checker = static_cast<ISpellChecker*>(dict->user_data);
+	wchar_t *wword = utf8_to_utf16 (word, len, FALSE);
+	IEnumSpellingError *errors;
+	ISpellingError *error = nullptr;
+	HRESULT hr;
+
+	hr = checker->Check (wword, &errors);
+	g_free (wword);
+
+	if (FAILED (hr))
+		return -1; /* Error */
+
+	if (errors->Next (&error) == S_OK)
+	{
+		error->Release ();
+		errors->Release ();
+		return 1; /* Spelling Issue */
+	}
+	else
+	{
+		errors->Release ();
+		return 0; /* Correct */
+	}
+}
+
+static char **
+win8_dict_suggest (EnchantDict *dict, const char *const word, size_t len, size_t *out_n_suggs)
+{
+	auto checker = static_cast<ISpellChecker*>(dict->user_data);
+	wchar_t *wword = utf8_to_utf16 (word, len, FALSE);
+	IEnumString *suggestions;
+	HRESULT hr;
+
+	hr = checker->Suggest (wword, &suggestions);
+	g_free (wword);
+
+	if (FAILED (hr))
+	{
+		*out_n_suggs = 0;
+		return nullptr;
+	}
+
+	return enumstring_to_chararray (suggestions, out_n_suggs, FALSE);
+}
+
+/* ---------- Provider ------------ */
+
+static EnchantDict *
+win8_provider_request_dict (EnchantProvider *provider, const char *const tag)
+{
+	auto factory = static_cast<ISpellCheckerFactory*>(provider->user_data);
+	ISpellChecker *checker;
+	EnchantDict *dict;
+	wchar_t *wtag = utf8_to_utf16 (tag, -1, TRUE);
+	HRESULT hr;
+
+	hr = factory->CreateSpellChecker (wtag, &checker);
+	g_free (wtag);
+
+	if (FAILED (hr))
+		return nullptr;
+
+	dict = g_new0 (EnchantDict, 1);
+	dict->suggest = win8_dict_suggest;
+	dict->check = win8_dict_check;
+	dict->add_to_personal = win8_dict_add_to_personal;
+	dict->add_to_exclude = win8_dict_add_to_personal; /* Basically the same */
+	dict->add_to_session = win8_dict_add_to_session;
+
+	dict->user_data = checker;
+
+	return dict;
+}
+
+static void
+win8_provider_dispose_dict (EnchantProvider *provider, EnchantDict *dict)
+{
+	if (dict)
+	{
+		auto checker = static_cast<ISpellChecker*>(dict->user_data);
+
+		checker->Release ();
+		g_free (dict);
+	}
+}
+
+static int
+win8_provider_dictionary_exists (EnchantProvider *provider, const char *const tag)
+{
+	auto factory = static_cast<ISpellCheckerFactory*>(provider->user_data);
+	wchar_t *wtag = utf8_to_utf16 (tag, -1, TRUE);
+
+	BOOL is_supported = FALSE;
+	factory->IsSupported (wtag, &is_supported);
+
+	g_free (wtag);
+	return is_supported;
+}
+
+
+static char **
+win8_provider_list_dicts (EnchantProvider *provider, size_t *out_n_dicts)
+{
+	auto factory = static_cast<ISpellCheckerFactory*>(provider->user_data);
+	IEnumString *dicts;
+
+	if (FAILED(factory->get_SupportedLanguages (&dicts)))
+	{
+		*out_n_dicts = 0;
+		return nullptr;
+	}
+
+	return enumstring_to_chararray (dicts, out_n_dicts, TRUE);
+}
+
+static void
+win8_provider_free_string_list (EnchantProvider *provider, char **str_list)
+{
+	g_strfreev (str_list);
+}
+
+static void
+win8_provider_dispose (EnchantProvider *provider)
+{
+	if (provider)
+	{
+		auto factory = static_cast<ISpellCheckerFactory*>(provider->user_data);
+
+		factory->Release();
+		g_free (provider);
+	}
+}
+
+static const char *
+win8_provider_identify (EnchantProvider *provider)
+{
+	return "win8";
+}
+
+static const char *
+win8_provider_describe (EnchantProvider *provider)
+{
+	return "Windows 8 SpellCheck Provider";
+}
+
+extern "C"
+{
+
+EnchantProvider *
+init_enchant_provider (void)
+{
+	EnchantProvider *provider;
+	ISpellCheckerFactory *factory;
+
+	if (FAILED (CoCreateInstance (__uuidof(SpellCheckerFactory), nullptr,
+				CLSCTX_INPROC_SERVER, IID_PPV_ARGS (&factory))))
+		return nullptr;
+
+	provider = g_new0 (EnchantProvider, 1);
+	provider->dispose = win8_provider_dispose;
+	provider->request_dict = win8_provider_request_dict;
+	provider->dispose_dict = win8_provider_dispose_dict;
+	provider->dictionary_exists = win8_provider_dictionary_exists;
+	provider->identify = win8_provider_identify;
+	provider->describe = win8_provider_describe;
+	provider->list_dicts = win8_provider_list_dicts;
+	provider->free_string_list = win8_provider_free_string_list;
+
+	provider->user_data = factory;
+
+	return provider;
+}
+
+}