summary refs log tree commit diff stats
path: root/src/common
diff options
context:
space:
mode:
Diffstat (limited to 'src/common')
-rw-r--r--src/common/cfgfiles.c71
-rw-r--r--src/common/chanopt.c5
-rw-r--r--src/common/ctcp.c12
-rw-r--r--src/common/dcc.c8
-rw-r--r--src/common/dirent-win32.c199
-rw-r--r--src/common/dirent-win32.h28
-rw-r--r--src/common/identd.c100
-rw-r--r--src/common/identd.h1
-rw-r--r--src/common/ignore.c5
-rw-r--r--src/common/inbound.c5
-rw-r--r--src/common/inet.h4
-rw-r--r--src/common/makefile.mak45
-rw-r--r--src/common/msproxy.c5
-rw-r--r--src/common/network.c5
-rw-r--r--src/common/notify.c5
-rw-r--r--src/common/outbound.c22
-rw-r--r--src/common/plugin.c257
-rw-r--r--src/common/plugin.h15
-rw-r--r--src/common/proto-irc.c5
-rw-r--r--src/common/server.c57
-rw-r--r--src/common/servlist.c3
-rw-r--r--src/common/ssl.c2
-rw-r--r--src/common/text.c104
-rw-r--r--src/common/text.h3
-rw-r--r--src/common/thread.c33
-rw-r--r--src/common/thread.h10
-rw-r--r--src/common/util.c193
-rw-r--r--src/common/util.h9
-rw-r--r--src/common/xchat-plugin.h47
-rw-r--r--src/common/xchat.c33
-rw-r--r--src/common/xchat.h25
31 files changed, 1229 insertions, 87 deletions
diff --git a/src/common/cfgfiles.c b/src/common/cfgfiles.c
index 83f50e37..443538d2 100644
--- a/src/common/cfgfiles.c
+++ b/src/common/cfgfiles.c
@@ -17,7 +17,6 @@
  */
 
 #include <fcntl.h>
-#include <unistd.h>
 #include <stdlib.h>
 #include <string.h>
 #include <stdio.h>
@@ -31,12 +30,15 @@
 #include "text.h"
 #include "xchatc.h"
 
-#ifdef WIN32
-#define XCHAT_DIR "X-Chat 2"
-#else
+#ifndef WIN32
+#include <unistd.h>
 #define XCHAT_DIR ".xchat2"
 #endif
+
 #define DEF_FONT "Monospace 9"
+#ifdef WIN32
+#define DEF_FONT_ALTER "Arial Unicode MS,Lucida Sans Unicode"
+#endif
 
 void
 list_addentry (GSList ** list, char *cmd, char *name)
@@ -166,9 +168,13 @@ list_delentry (GSList ** list, char *name)
 char *
 cfg_get_str (char *cfg, char *var, char *dest, int dest_len)
 {
+	char buffer[128];	/* should be plenty for a variable name */
+
+	sprintf (buffer, "%s ", var);	/* add one space, this way it works against var - var2 checks too */
+
 	while (1)
 	{
-		if (!strncasecmp (var, cfg, strlen (var)))
+		if (!strncasecmp (buffer, cfg, strlen (var) + 1))
 		{
 			char *value, t;
 			cfg += strlen (var);
@@ -308,12 +314,19 @@ get_xdir_fs (void)
 {
 	if (!xdir_fs)
 	{
-		char out[256];
+		if (portable_mode ())
+		{
+			xdir_fs = ".\\config";
+		}
+		else
+		{
+			char out[256];
 
-		if (!get_reg_str ("Software\\Microsoft\\Windows\\CurrentVersion\\"
-				"Explorer\\Shell Folders", "AppData", out, sizeof (out)))
-			return "./config";
-		xdir_fs = g_strdup_printf ("%s\\" XCHAT_DIR, out);
+			if (!get_reg_str ("Software\\Microsoft\\Windows\\CurrentVersion\\"
+					"Explorer\\Shell Folders", "AppData", out, sizeof (out)))
+				return "./config";
+			xdir_fs = g_strdup_printf ("%s\\" "X-Chat 2", out);
+		}
 	}
 	return xdir_fs;
 }
@@ -393,7 +406,9 @@ const struct prefs vars[] = {
 	{"dcc_blocksize", P_OFFINT (dcc_blocksize), TYPE_INT},
 	{"dcc_completed_dir", P_OFFSET (dcc_completed_dir), TYPE_STR},
 	{"dcc_dir", P_OFFSET (dccdir), TYPE_STR},
+#ifndef WIN32
 	{"dcc_fast_send", P_OFFINT (fastdccsend), TYPE_BOOL},
+#endif
 	{"dcc_global_max_get_cps", P_OFFINT (dcc_global_max_get_cps), TYPE_INT},
 	{"dcc_global_max_send_cps", P_OFFINT (dcc_global_max_send_cps), TYPE_INT},
 	{"dcc_ip", P_OFFSET (dcc_ip_str), TYPE_STR},
@@ -429,7 +444,11 @@ const struct prefs vars[] = {
 	{"gui_input_style", P_OFFINT (style_inputbox), TYPE_BOOL},
 	{"gui_join_dialog", P_OFFINT (gui_join_dialog), TYPE_BOOL},
 	{"gui_lagometer", P_OFFINT (lagometer), TYPE_INT},
+	{"gui_license", P_OFFSET (gui_license), TYPE_STR},
 	{"gui_mode_buttons", P_OFFINT (chanmodebuttons), TYPE_BOOL},
+#ifdef WIN32
+	{"gui_one_instance", P_OFFINT (gui_one_instance), TYPE_BOOL},
+#endif
 	{"gui_pane_left_size", P_OFFINT (gui_pane_left_size), TYPE_INT},
 	{"gui_pane_right_size", P_OFFINT (gui_pane_right_size), TYPE_INT},
 	{"gui_quit_dialog", P_OFFINT (gui_quit_dialog), TYPE_BOOL},
@@ -536,6 +555,9 @@ const struct prefs vars[] = {
 
 	{"tab_chans", P_OFFINT (tabchannels), TYPE_BOOL},
 	{"tab_dialogs", P_OFFINT (privmsgtab), TYPE_BOOL},
+#ifdef WIN32
+	{"tab_icons", P_OFFINT (tab_icons), TYPE_BOOL},
+#endif
 	{"tab_layout", P_OFFINT (tab_layout), TYPE_INT},
 	{"tab_new_to_front", P_OFFINT (newtabstofront), TYPE_INT},
 	{"tab_notices", P_OFFINT (notices_tabs), TYPE_BOOL},
@@ -546,16 +568,30 @@ const struct prefs vars[] = {
 	{"tab_sort", P_OFFINT (tab_sort), TYPE_BOOL},
 	{"tab_trunc", P_OFFINT (truncchans), TYPE_INT},
 	{"tab_utils", P_OFFINT (windows_as_tabs), TYPE_BOOL},
+#ifdef WIN32
+	{"tab_xp", P_OFFINT (tab_xp), TYPE_BOOL},
+#endif
 
+	{"text_auto_copy_color", P_OFFINT (autocopy_color), TYPE_BOOL},	
+	{"text_auto_copy_stamp", P_OFFINT (autocopy_stamp), TYPE_BOOL},
+	{"text_auto_copy_text", P_OFFINT (autocopy_text), TYPE_BOOL},
 	{"text_background", P_OFFSET (background), TYPE_STR},
 	{"text_color_nicks", P_OFFINT (colorednicks), TYPE_BOOL},
+#ifdef WIN32
+	{"text_emoticons", P_OFFINT (emoticons), TYPE_BOOL},
+#endif
 	{"text_font", P_OFFSET (font_normal), TYPE_STR},
+#ifdef WIN32
+	{"text_font_main", P_OFFSET (font_main), TYPE_STR},
+	{"text_font_alternative", P_OFFSET (font_alternative), TYPE_STR},
+#endif
 	{"text_indent", P_OFFINT (indent_nicks), TYPE_BOOL},
 	{"text_max_indent", P_OFFINT (max_auto_indent), TYPE_INT},
 	{"text_max_lines", P_OFFINT (max_lines), TYPE_INT},
 	{"text_replay", P_OFFINT (text_replay), TYPE_BOOL},
 	{"text_show_marker", P_OFFINT (show_marker), TYPE_BOOL},
 	{"text_show_sep", P_OFFINT (show_separator), TYPE_BOOL},
+	{"text_spell_langs", P_OFFSET (spell_langs), TYPE_STR},
 	{"text_stripcolor", P_OFFINT (stripcolor), TYPE_BOOL},
 	{"text_thin_sep", P_OFFINT (thin_separator), TYPE_BOOL},
 	{"text_tint_blue", P_OFFINT (tint_blue), TYPE_INT},
@@ -626,13 +662,16 @@ load_config (void)
 	prefs.indent_nicks = 1;
 	prefs.thin_separator = 1;
 	prefs._tabs_position = 2; /* 2 = left */
+#ifndef WIN32
 	prefs.fastdccsend = 1;
+#endif
 	prefs.wordwrap = 1;
 	prefs.autosave = 1;
 	prefs.autodialog = 1;
 	prefs.gui_input_spell = 1;
 	prefs.autoreconnect = 1;
 	prefs.recon_delay = 10;
+	prefs.autocopy_text = 1;
 	prefs.text_replay = 1;
 	prefs.tabchannels = 1;
 	prefs.tab_layout = 2;	/* 0=Tabs 1=Reserved 2=Tree */
@@ -687,6 +726,8 @@ load_config (void)
 #ifdef WIN32
 	prefs.identd = 1;
 #endif
+	strcpy (prefs.gui_license, "");
+	strcpy (prefs.spell_langs, g_getenv ("LC_ALL") ? g_getenv ("LC_ALL") : "en_US");
 	strcpy (prefs.stamp_format, "[%H:%M] ");
 	strcpy (prefs.timestamp_log_format, "%b %d %H:%M:%S ");
 	strcpy (prefs.logmask, "%n-%c.log");
@@ -719,6 +760,10 @@ load_config (void)
 	strcpy (prefs.quitreason, _("Leaving"));
 	strcpy (prefs.partreason, prefs.quitreason);
 	strcpy (prefs.font_normal, DEF_FONT);
+#ifdef WIN32
+	strcpy (prefs.font_main, DEF_FONT);
+	strcpy (prefs.font_alternative, DEF_FONT_ALTER);
+#endif
 	strcpy (prefs.dnsprogram, "host");
 	strcpy (prefs.irc_no_hilight, "NickServ,ChanServ");
 
@@ -1071,7 +1116,13 @@ cmd_set (struct session *sess, char *tbuf, char *word[], char *word_eol[])
 	while (vars[i].name);
 
 	if (!finds && !quiet)
+	{
 		PrintText (sess, "No such variable.\n");
+	}
+	else if (prefs.autosave && !save_config ())
+	{
+		PrintText (sess, "Error saving changes to disk.\n");
+	}
 
 	return TRUE;
 }
diff --git a/src/common/chanopt.c b/src/common/chanopt.c
index a4fd8faa..f8e16e8b 100644
--- a/src/common/chanopt.c
+++ b/src/common/chanopt.c
@@ -3,12 +3,15 @@
 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
-#include <unistd.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
 #include <errno.h>
 
+#ifndef WIN32
+#include <unistd.h>
+#endif
+
 #include "xchat.h"
 
 #include "cfgfiles.h"
diff --git a/src/common/ctcp.c b/src/common/ctcp.c
index 574cda79..2bb75746 100644
--- a/src/common/ctcp.c
+++ b/src/common/ctcp.c
@@ -18,9 +18,12 @@
 
 #include <stdio.h>
 #include <string.h>
-#include <unistd.h>
 #include <stdlib.h>
 
+#ifndef WIN32
+#include <unistd.h>
+#endif
+
 #include "xchat.h"
 #include "cfgfiles.h"
 #include "util.h"
@@ -134,8 +137,13 @@ ctcp_handle (session *sess, char *to, char *nick, char *ip,
 
 	if (!strcasecmp (msg, "VERSION") && !prefs.hidever)
 	{
-		snprintf (outbuf, sizeof (outbuf), "VERSION xchat "PACKAGE_VERSION" %s",
+#ifdef WIN32
+		snprintf (outbuf, sizeof (outbuf), "VERSION XChat-WDK "PACKAGE_VERSION" [x%d] / %s",
+					 get_cpu_arch (), get_cpu_str ());
+#else
+		snprintf (outbuf, sizeof (outbuf), "VERSION XChat-WDK "PACKAGE_VERSION" %s",
 					 get_cpu_str ());
+#endif
 		serv->p_nctcp (serv, nick, outbuf);
 	}
 
diff --git a/src/common/dcc.c b/src/common/dcc.c
index 8f289342..d91b7e8e 100644
--- a/src/common/dcc.c
+++ b/src/common/dcc.c
@@ -31,7 +31,6 @@
 #include <time.h>
 #include <errno.h>
 #include <sys/stat.h>
-#include <unistd.h>
 #include <fcntl.h>
 
 #define WANTSOCKET
@@ -41,6 +40,8 @@
 
 #ifdef WIN32
 #include <windows.h>
+#else
+#include <unistd.h>
 #endif
 
 #include "xchat.h"
@@ -57,6 +58,9 @@
 
 #ifdef USE_DCC64
 #define BIG_STR_TO_INT(x) strtoull(x,NULL,10)
+#ifdef WIN32
+#define stat _stat64
+#endif
 #else
 #define BIG_STR_TO_INT(x) strtoul(x,NULL,10)
 #endif
@@ -1984,7 +1988,7 @@ is_same_file (struct DCC *dcc, struct DCC *new_dcc)
 
 	/* now handle case-insensitive Filesystems: HFS+, FAT */
 #ifdef WIN32
-#warning no win32 implementation - behaviour may be unreliable
+	/* warning no win32 implementation - behaviour may be unreliable */
 #else
 	/* this fstat() shouldn't really fail */
 	if ((dcc->fp == -1 ? stat (dcc->destfile_fs, &st_a) : fstat (dcc->fp, &st_a)) == -1)
diff --git a/src/common/dirent-win32.c b/src/common/dirent-win32.c
new file mode 100644
index 00000000..273c6732
--- /dev/null
+++ b/src/common/dirent-win32.c
@@ -0,0 +1,199 @@
+/*****************************************************************************
+ * dirent.h - dirent API for Microsoft Visual Studio
+ *
+ * Copyright (C) 2006 Toni Ronkko
+ *
+ * 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 TONI RONKKO 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.
+ *
+ * Dec 15, 2009, John Cunningham
+ * Added rewinddir member function
+ *
+ * Jan 18, 2008, Toni Ronkko
+ * Using FindFirstFileA and WIN32_FIND_DATAA to avoid converting string
+ * between multi-byte and unicode representations.  This makes the
+ * code simpler and also allows the code to be compiled under MingW.  Thanks
+ * to Azriel Fasten for the suggestion.
+ *
+ * Mar 4, 2007, Toni Ronkko
+ * Bug fix: due to the strncpy_s() function this file only compiled in
+ * Visual Studio 2005.  Using the new string functions only when the
+ * compiler version allows.
+ *
+ * Nov  2, 2006, Toni Ronkko
+ * Major update: removed support for Watcom C, MS-DOS and Turbo C to
+ * simplify the file, updated the code to compile cleanly on Visual
+ * Studio 2005 with both unicode and multi-byte character strings,
+ * removed rewinddir() as it had a bug.
+ *
+ * Aug 20, 2006, Toni Ronkko
+ * Removed all remarks about MSVC 1.0, which is antiqued now.  Simplified
+ * comments by removing SGML tags.
+ *
+ * May 14 2002, Toni Ronkko
+ * Embedded the function definitions directly to the header so that no
+ * source modules need to be included in the Visual Studio project.  Removed
+ * all the dependencies to other projects so that this very header can be
+ * used independently.
+ *
+ * May 28 1998, Toni Ronkko
+ * First version.
+ *****************************************************************************/
+
+#include "dirent-win32.h"
+
+/* Use the new safe string functions introduced in Visual Studio 2005 */
+#if defined(_MSC_VER) && _MSC_VER >= 1400
+# define STRNCPY(dest,src,size) strncpy_s((dest),(size),(src),_TRUNCATE)
+#else
+# define STRNCPY(dest,src,size) strncpy((dest),(src),(size))
+#endif
+
+
+/*****************************************************************************
+ * Open directory stream DIRNAME for read and return a pointer to the
+ * internal working area that is used to retrieve individual directory
+ * entries.
+ */
+DIR *opendir(const char *dirname)
+{
+   DIR *dirp;
+   assert (dirname != NULL);
+   assert (strlen (dirname) < MAX_PATH);
+
+   /* construct new DIR structure */
+   dirp = (DIR*) malloc (sizeof (struct DIR));
+   if (dirp != NULL) {
+      char *p;
+
+      /* take directory name... */
+      STRNCPY (dirp->patt, dirname, sizeof(dirp->patt));
+      dirp->patt[MAX_PATH] = '\0';
+
+      /* ... and append search pattern to it */
+      p = strchr (dirp->patt, '\0');
+      if (dirp->patt < p  &&  *(p-1) != '\\'  &&  *(p-1) != ':') {
+         *p++ = '\\';
+      }
+      *p++ = '*';
+      *p = '\0';
+
+      /* open stream and retrieve first file */
+      dirp->search_handle = FindFirstFileA (dirp->patt, &dirp->current.data);
+      if (dirp->search_handle == INVALID_HANDLE_VALUE) {
+         /* invalid search pattern? */
+         free (dirp);
+         return NULL;
+      }
+
+      /* there is an un-processed directory entry in memory now */
+      dirp->cached = 1;
+   }
+
+   return dirp;
+}
+
+
+/*****************************************************************************
+ * Read a directory entry, and return a pointer to a dirent structure
+ * containing the name of the entry in d_name field.  Individual directory
+ * entries returned by this very function include regular files,
+ * sub-directories, pseudo-directories "." and "..", but also volume labels,
+ * hidden files and system files may be returned.
+ */
+struct dirent *readdir(DIR *dirp)
+{
+   assert (dirp != NULL);
+
+   if (dirp->search_handle == INVALID_HANDLE_VALUE) {
+      /* directory stream was opened/rewound incorrectly or ended normally */
+      return NULL;
+   }
+
+   /* get next directory entry */
+   if (dirp->cached != 0) {
+      /* a valid directory entry already in memory */
+      dirp->cached = 0;
+   } else {
+      /* read next directory entry from disk */
+      if (FindNextFileA (dirp->search_handle, &dirp->current.data) == FALSE) {
+         /* the very last file has been processed or an error occured */
+         FindClose (dirp->search_handle);
+         dirp->search_handle = INVALID_HANDLE_VALUE;
+         return NULL;
+      }
+   }
+
+   /* copy as a multibyte character string */
+   STRNCPY ( dirp->current.d_name,
+             dirp->current.data.cFileName,
+             sizeof(dirp->current.d_name) );
+   dirp->current.d_name[MAX_PATH] = '\0';
+
+   return &dirp->current;
+}
+
+
+/*****************************************************************************
+ * Close directory stream opened by opendir() function.  Close of the
+ * directory stream invalidates the DIR structure as well as any previously
+ * read directory entry.
+ */
+int closedir(DIR *dirp)
+{
+   assert (dirp != NULL);
+
+   /* release search handle */
+   if (dirp->search_handle != INVALID_HANDLE_VALUE) {
+      FindClose (dirp->search_handle);
+      dirp->search_handle = INVALID_HANDLE_VALUE;
+   }
+
+   /* release directory handle */
+   free (dirp);
+   return 0;
+}
+
+
+/*****************************************************************************
+ * Resets the position of the directory stream to which dirp refers to the
+ * beginning of the directory. It also causes the directory stream to refer
+ * to the current state of the corresponding directory, as a call to opendir()
+ * would have done. If dirp does not refer to a directory stream, the effect
+ * is undefined.
+ */
+void rewinddir(DIR* dirp)
+{
+   /* release search handle */
+   if (dirp->search_handle != INVALID_HANDLE_VALUE) {
+      FindClose (dirp->search_handle);
+      dirp->search_handle = INVALID_HANDLE_VALUE;
+   }
+
+   /* open new search handle and retrieve first file */
+   dirp->search_handle = FindFirstFileA (dirp->patt, &dirp->current.data);
+   if (dirp->search_handle == INVALID_HANDLE_VALUE) {
+      /* invalid search pattern? */
+      free (dirp);
+      return;
+   }
+
+   /* there is an un-processed directory entry in memory now */
+   dirp->cached = 1;
+}
diff --git a/src/common/dirent-win32.h b/src/common/dirent-win32.h
new file mode 100644
index 00000000..cbb753e6
--- /dev/null
+++ b/src/common/dirent-win32.h
@@ -0,0 +1,28 @@
+#ifndef DIRENT_H
+#define DIRENT_H
+
+#include <windows.h>
+#include <string.h>
+#include <assert.h>
+
+typedef struct dirent
+{
+   char d_name[MAX_PATH + 1]; /* current dir entry (multi-byte char string) */
+   WIN32_FIND_DATAA data;     /* file attributes */
+}  dirent;
+
+typedef struct DIR
+{
+   dirent current;            /* Current directory entry */
+   int    cached;             /* Indicates un-processed entry in memory */
+   HANDLE search_handle;      /* File search handle */
+   char   patt[MAX_PATH + 3]; /* search pattern (3 = pattern + "\\*\0") */
+} DIR;
+
+/* Forward declarations */
+DIR *opendir (const char *dirname);
+struct dirent *readdir (DIR *dirp);
+int closedir (DIR *dirp);
+void rewinddir(DIR* dirp);
+
+#endif /*DIRENT_H*/
diff --git a/src/common/identd.c b/src/common/identd.c
index 919282ea..430c7e8f 100644
--- a/src/common/identd.c
+++ b/src/common/identd.c
@@ -1,8 +1,13 @@
 /* simple identd server for xchat under win32 */
 
+#include "inet.h"
+#include "xchat.h"
+#include "xchatc.h"
 
 static int identd_is_running = FALSE;
-
+#ifdef USE_IPV6
+static int identd_ipv6_is_running = FALSE;
+#endif
 
 static int
 identd (char *username)
@@ -75,11 +80,102 @@ identd (char *username)
 	return 0;
 }
 
-static void
+#ifdef USE_IPV6
+static int
+identd_ipv6 (char *username)
+{
+	int sok, read_sok, len;
+	char *p;
+	char buf[256];
+	char outbuf[256];
+	char ipv6buf[60];
+	DWORD ipv6buflen = sizeof (ipv6buf);
+	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;
+
+	if (WSAAddressToString ((struct sockaddr *) &addr, sizeof (addr), NULL, &ipv6buf, &ipv6buflen) == SOCKET_ERROR)
+	{
+		snprintf (ipv6buf, sizeof (ipv6buf) - 1, "[SOCKET ERROR: 0x%X]", WSAGetLastError ());
+	}
+
+	snprintf (outbuf, sizeof (outbuf), "%%\tServicing ident request from %s\n", ipv6buf);
+	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;
diff --git a/src/common/identd.h b/src/common/identd.h
new file mode 100644
index 00000000..636f9641
--- /dev/null
+++ b/src/common/identd.h
@@ -0,0 +1 @@
+void identd_start (char *username);
diff --git a/src/common/ignore.c b/src/common/ignore.c
index c3544f30..0ed23daa 100644
--- a/src/common/ignore.c
+++ b/src/common/ignore.c
@@ -19,11 +19,14 @@
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
-#include <unistd.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
 
+#ifndef WIN32
+#include <unistd.h>
+#endif
+
 #include "xchat.h"
 #include "ignore.h"
 #include "cfgfiles.h"
diff --git a/src/common/inbound.c b/src/common/inbound.c
index 1d621583..59ffaf2b 100644
--- a/src/common/inbound.c
+++ b/src/common/inbound.c
@@ -21,9 +21,12 @@
 #include <stdlib.h>
 #include <stdio.h>
 #include <sys/types.h>
-#include <unistd.h>
 #include <time.h>
 
+#ifndef WIN32
+#include <unistd.h>
+#endif
+
 #define WANTARPA
 #define WANTDNS
 #include "inet.h"
diff --git a/src/common/inet.h b/src/common/inet.h
index b420c9c6..8995569c 100644
--- a/src/common/inet.h
+++ b/src/common/inet.h
@@ -21,12 +21,12 @@
 
 #else
 
+#include "../../config.h"
 #ifdef USE_IPV6
 #include <winsock2.h>
 #include <ws2tcpip.h>
-#include <tpipv6.h>
 #else
-#include <winsock.h>
+#include <winsock2.h>
 #endif
 
 #define set_blocking(sok)	{ \
diff --git a/src/common/makefile.mak b/src/common/makefile.mak
new file mode 100644
index 00000000..d6df2608
--- /dev/null
+++ b/src/common/makefile.mak
@@ -0,0 +1,45 @@
+include "..\makeinc.mak"
+
+COMMON_OBJECTS = \
+cfgfiles.obj \
+chanopt.obj \
+ctcp.obj \
+dcc.obj \
+dirent-win32.obj \
+history.obj \
+identd.obj \
+ignore.obj \
+inbound.obj \
+modes.obj \
+network.obj \
+notify.obj \
+outbound.obj \
+plugin.obj \
+plugin-timer.obj \
+proto-irc.obj \
+server.obj \
+servlist.obj \
+ssl.obj \
+text.obj \
+thread.obj \
+tree.obj \
+url.obj \
+userlist.obj \
+util.obj \
+xchat.obj
+
+all: $(COMMON_OBJECTS) xchatcommon.lib dirent-win32.lib
+
+xchatcommon.lib: $(COMMON_OBJECTS)
+	lib /nologo /out:xchatcommon.lib $(COMMON_OBJECTS)
+
+dirent-win32.lib: dirent-win32.obj
+	lib /nologo /out:dirent-win32.lib dirent-win32.obj
+
+.c.obj::
+	$(CC) $(CFLAGS) $(GLIB) $<
+
+clean:
+	@del *.obj
+	@del xchatcommon.lib
+	@del dirent-win32.lib
diff --git a/src/common/msproxy.c b/src/common/msproxy.c
index 9c85394d..5103233a 100644
--- a/src/common/msproxy.c
+++ b/src/common/msproxy.c
@@ -26,9 +26,12 @@
 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
-#include <unistd.h>
 #include <fcntl.h>
 
+#ifndef WIN32
+#include <unistd.h>
+#endif
+
 #define WANTSOCKET
 #define WANTARPA
 #include "inet.h"
diff --git a/src/common/network.c b/src/common/network.c
index 0c409506..eba24b05 100644
--- a/src/common/network.c
+++ b/src/common/network.c
@@ -21,9 +21,12 @@
 #include <stdlib.h>
 #include <string.h>
 #include <stdio.h>
-#include <unistd.h>
 #include <glib.h>
 
+#ifndef WIN32
+#include <unistd.h>
+#endif
+
 #include "../../config.h"				  /* grab USE_IPV6 and LOOKUPD defines */
 
 #define WANTSOCKET
diff --git a/src/common/notify.c b/src/common/notify.c
index 04795849..9c6e54de 100644
--- a/src/common/notify.c
+++ b/src/common/notify.c
@@ -22,9 +22,12 @@
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
-#include <unistd.h>
 #include <time.h>
 
+#ifndef WIN32
+#include <unistd.h>
+#endif
+
 #include "xchat.h"
 #include "notify.h"
 #include "cfgfiles.h"
diff --git a/src/common/outbound.c b/src/common/outbound.c
index c1270e96..a09fdce4 100644
--- a/src/common/outbound.c
+++ b/src/common/outbound.c
@@ -30,9 +30,9 @@
 
 #ifndef WIN32
 #include <sys/wait.h>
+#include <unistd.h>
 #endif
 
-#include <unistd.h>
 #include <time.h>
 #include <signal.h>
 #include <sys/stat.h>
@@ -2881,6 +2881,23 @@ cmd_recv (struct session *sess, char *tbuf, char *word[], char *word_eol[])
 	return FALSE;
 }
 
+#if 0 /* manual command for flushing prefs to disk, but we use an autosave-upon-set approach instead */
+static int
+cmd_saveconf (struct session *sess, char *tbuf, char *word[], char *word_eol[])
+{
+	if (save_config ())
+	{
+		PrintText (sess, "Settings have been saved successfully.\n");
+	}
+	else
+	{
+		PrintText (sess, "Error saving settings.\n");
+	}
+
+	return TRUE;
+}
+#endif
+
 static int
 cmd_say (struct session *sess, char *tbuf, char *word[], char *word_eol[])
 {
@@ -3633,6 +3650,9 @@ const struct commands xc_cmds[] = {
 #endif
 	{"RECV", cmd_recv, 1, 0, 1, N_("RECV <text>, send raw data to xchat, as if it was received from the irc server")},
 
+#if 0
+	{"SAVECONF", cmd_saveconf, 0, 0, 1, N_("SAVECONF, saves the current settings to disk")},
+#endif
 	{"SAY", cmd_say, 0, 0, 1,
 	 N_("SAY <text>, sends the text to the object in the current window")},
 	{"SEND", cmd_send, 0, 0, 1, N_("SEND <nick> [<file>]")},
diff --git a/src/common/plugin.c b/src/common/plugin.c
index ada4d3be..2e81c1f1 100644
--- a/src/common/plugin.c
+++ b/src/common/plugin.c
@@ -20,6 +20,14 @@
 #include <string.h>
 #include <stdarg.h>
 #include <stdio.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+
+#ifdef WIN32
+#include <io.h>
+#else
+#include <unistd.h>
+#endif
 
 #include "xchat.h"
 #include "fe.h"
@@ -262,6 +270,12 @@ plugin_add (session *sess, char *filename, void *handle, void *init_func,
 		pl->xchat_send_modes = xchat_send_modes;
 		pl->xchat_strip = xchat_strip;
 		pl->xchat_free = xchat_free;
+		pl->xchat_pluginpref_set_str = xchat_pluginpref_set_str;
+		pl->xchat_pluginpref_get_str = xchat_pluginpref_get_str;
+		pl->xchat_pluginpref_set_int = xchat_pluginpref_set_int;
+		pl->xchat_pluginpref_get_int = xchat_pluginpref_get_int;
+		pl->xchat_pluginpref_delete = xchat_pluginpref_delete;
+		pl->xchat_pluginpref_list = xchat_pluginpref_list;
 
 		/* incase new plugins are loaded on older xchat */
 		pl->xchat_dummy4 = xchat_dummy;
@@ -996,13 +1010,20 @@ xchat_get_info (xchat_plugin *ph, const char *id)
 		return XCHATLIBDIR;
 
 	case 0x14f51cd8: /* version */
+#ifdef WIN32
+		return XCHAT_RELEASE;
+#else
 		return PACKAGE_VERSION;
+#endif
 
 	case 0xdd9b1abd:	/* xchatdir */
 		return get_xdir_utf8 ();
 
 	case 0xe33f6c4a:	/* xchatdirfs */
 		return get_xdir_fs ();
+
+	case 0x3d1e70d7:	/* wdk_version */
+		return PACKAGE_VERSION;
 	}
 
 	sess = ph->context;
@@ -1567,3 +1588,239 @@ xchat_free (xchat_plugin *ph, void *ptr)
 {
 	g_free (ptr);
 }
+
+static int
+xchat_pluginpref_set_str_real (xchat_plugin *pl, const char *var, const char *value, int mode) /* mode: 0 = delete, 1 = save */
+{
+	FILE *fpIn;
+	int fhOut;
+	int prevSetting;
+	char confname[64];
+	char confname_tmp[69];
+	char buffer[512];		/* the same as in cfg_put_str */
+	char buffer_tmp[512];
+	char *canon;
+
+	canon = g_strdup (pl->name);
+	canonalize_key (canon);
+	sprintf (confname, "plugin_%s.conf", canon);
+	g_free (canon);
+	sprintf (confname_tmp, "%s.new", confname);
+
+	fhOut = xchat_open_file (confname_tmp, O_TRUNC | O_WRONLY | O_CREAT, 0600, XOF_DOMODE);
+	fpIn = xchat_fopen_file (confname, "r", 0);
+
+	if (fhOut == -1)		/* unable to save, abort */
+	{
+		return 0;
+	}
+	else if (fpIn == NULL)	/* no previous config file, no parsing */
+	{
+		if (mode)
+		{
+			sprintf (buffer, "%s = %s\n", var, value);
+			write (fhOut, buffer, strlen (buffer));
+			close (fhOut);
+
+			sprintf (buffer, "%s/%s", get_xdir_fs (), confname);
+			sprintf (buffer_tmp, "%s/%s", get_xdir_fs (), confname_tmp);
+
+#ifdef WIN32
+			unlink (buffer);
+#endif
+
+			if (rename (buffer_tmp, buffer) == 0)
+			{
+				return 1;
+			}
+			else
+			{
+				return 0;
+			}
+		}
+		else
+		{
+			/* mode = 0, we want to delete but the config file and thus the given setting does not exist, we're ready */
+			close (fhOut);
+			return 1;
+		}
+	}
+	else	/* existing config file, preserve settings and find & replace current var value if any */
+	{
+		prevSetting = 0;
+
+		while (fscanf (fpIn, " %[^\n]", &buffer) != EOF)	/* read whole lines including whitespaces */
+		{
+			sprintf (buffer_tmp, "%s ", var);				/* add one space, this way it works against var - var2 checks too */
+
+			if (strncmp (buffer_tmp, buffer, strlen (var) + 1) == 0)	/* given setting already exists */
+			{
+				if (mode)									/* overwrite the existing matching setting if we are in save mode */
+				{
+					sprintf (buffer, "%s = %s\n", var, value);
+				}
+				else										/* erase the setting in delete mode */
+				{
+					strcpy (buffer, "");
+				}
+
+				prevSetting = 1;
+			}
+			else
+			{
+				strcat (buffer, "\n");						/* preserve the existing different settings */
+			}
+
+			write (fhOut, buffer, strlen (buffer));
+		}
+
+		fclose (fpIn);
+
+		if (!prevSetting && mode)	/* var doesn't exist currently, append if we're in save mode */
+		{
+			sprintf (buffer, "%s = %s\n", var, value);
+			write (fhOut, buffer, strlen (buffer));
+		}
+
+		close (fhOut);
+
+		sprintf (buffer, "%s/%s", get_xdir_fs (), confname);
+		sprintf (buffer_tmp, "%s/%s", get_xdir_fs (), confname_tmp);
+
+#ifdef WIN32
+		unlink (buffer);
+#endif
+
+		if (rename (buffer_tmp, buffer) == 0)
+		{
+			return 1;
+		}
+		else
+		{
+			return 0;
+		}
+	}
+}
+
+int
+xchat_pluginpref_set_str (xchat_plugin *pl, const char *var, const char *value)
+{
+	return xchat_pluginpref_set_str_real (pl, var, value, 1);
+}
+
+int
+xchat_pluginpref_get_str (xchat_plugin *pl, const char *var, char *dest)
+{
+	int fh;
+	int l;
+	char confname[64];
+	char *canon;
+	char *cfg;
+	struct stat st;
+
+	canon = g_strdup (pl->name);
+	canonalize_key (canon);
+	sprintf (confname, "plugin_%s.conf", canon);
+	g_free (canon);
+
+	/* partly borrowed from palette.c */
+	fh = xchat_open_file (confname, O_RDONLY, 0, 0);
+
+	if (fh == -1)
+	{
+		return 0;
+	}
+
+	fstat (fh, &st);
+	cfg = malloc (st.st_size + 1);
+
+	if (!cfg)
+	{
+		close (fh);
+		return 0;
+	}
+
+	cfg[0] = '\0';
+	l = read (fh, cfg, st.st_size);
+
+	if (l >= 0)
+	{
+		cfg[l] = '\0';
+	}
+
+	if (!cfg_get_str (cfg, var, dest, 512)) /* dest_len is the same as buffer size in set */
+	{
+		free (cfg);
+		close (fh);
+		return 0;
+	}
+
+	free (cfg);
+	close (fh);
+	return 1;
+}
+
+int
+xchat_pluginpref_set_int (xchat_plugin *pl, const char *var, int value)
+{
+	char buffer[12];
+
+	sprintf (buffer, "%d", value);
+	return xchat_pluginpref_set_str_real (pl, var, buffer, 1);
+}
+
+int
+xchat_pluginpref_get_int (xchat_plugin *pl, const char *var)
+{
+	char buffer[12];
+
+	if (xchat_pluginpref_get_str (pl, var, buffer))
+	{
+		return atoi (buffer);
+	}
+	else
+	{
+		return -1;
+	}
+}
+
+int
+xchat_pluginpref_delete (xchat_plugin *pl, const char *var)
+{
+	return xchat_pluginpref_set_str_real (pl, var, 0, 0);
+}
+
+int
+xchat_pluginpref_list (xchat_plugin *pl, char* dest)
+{
+	FILE *fpIn;
+	char confname[64];
+	char buffer[512];										/* the same as in cfg_put_str */
+	char *token;
+
+	token = g_strdup (pl->name);
+	canonalize_key (token);
+	sprintf (confname, "plugin_%s.conf", token);
+	g_free (token);
+
+	fpIn = xchat_fopen_file (confname, "r", 0);
+
+	if (fpIn == NULL)										/* no existing config file, no parsing */
+	{
+		return 0;
+	}
+	else													/* existing config file, get list of settings */
+	{
+		strcpy (dest, "");									/* clean up garbage */
+		while (fscanf (fpIn, " %[^\n]", &buffer) != EOF)	/* read whole lines including whitespaces */
+		{
+			token = strtok (buffer, "=");
+			strncat (dest, token, strlen (token) - 1);
+			strcat (dest, ",");
+		}
+
+		fclose (fpIn);
+	}
+
+	return 1;
+}
diff --git a/src/common/plugin.h b/src/common/plugin.h
index b0c89d1b..8c347d51 100644
--- a/src/common/plugin.h
+++ b/src/common/plugin.h
@@ -98,6 +98,21 @@ struct _xchat_plugin
 	     int flags);
 	void (*xchat_free) (xchat_plugin *ph,
 	    void *ptr);
+	int (*xchat_pluginpref_set_str) (xchat_plugin *ph,
+		const char *var,
+		const char *value);
+	int (*xchat_pluginpref_get_str) (xchat_plugin *ph,
+		const char *var,
+		char *dest);
+	int (*xchat_pluginpref_set_int) (xchat_plugin *ph,
+		const char *var,
+		int value);
+	int (*xchat_pluginpref_get_int) (xchat_plugin *ph,
+		const char *var);
+	int (*xchat_pluginpref_delete) (xchat_plugin *ph,
+		const char *var);
+	int (*xchat_pluginpref_list) (xchat_plugin *ph,
+		char *dest);
 	void *(*xchat_dummy4) (xchat_plugin *ph);
 	void *(*xchat_dummy3) (xchat_plugin *ph);
 	void *(*xchat_dummy2) (xchat_plugin *ph);
diff --git a/src/common/proto-irc.c b/src/common/proto-irc.c
index 338a3b18..a5ebc366 100644
--- a/src/common/proto-irc.c
+++ b/src/common/proto-irc.c
@@ -18,13 +18,16 @@
 
 /* IRC RFC1459(+commonly used extensions) protocol implementation */
 
-#include <unistd.h>
 #include <string.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <ctype.h>
 #include <stdarg.h>
 
+#ifndef WIN32
+#include <unistd.h>
+#endif
+
 #include "xchat.h"
 #include "ctcp.h"
 #include "fe.h"
diff --git a/src/common/server.c b/src/common/server.c
index db0af9ca..919d9119 100644
--- a/src/common/server.c
+++ b/src/common/server.c
@@ -26,7 +26,6 @@
 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
-#include <unistd.h>
 #include <errno.h>
 #include <fcntl.h>
 
@@ -37,6 +36,7 @@
 #ifndef WIN32
 #include <signal.h>
 #include <sys/wait.h>
+#include <unistd.h>
 #else
 #include <winbase.h>
 #endif
@@ -67,7 +67,7 @@
 #endif
 
 #ifdef WIN32
-#include "identd.c"
+#include "identd.h"
 #endif
 
 #ifdef USE_LIBPROXY
@@ -310,6 +310,11 @@ server_inline (server *serv, char *line, int len)
 {
 	char *utf_line_allocated = NULL;
 
+#ifdef WIN32
+	char *cleaned_line;
+	int cleaned_len;
+#endif
+
 	/* 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 */
@@ -396,11 +401,23 @@ server_inline (server *serv, char *line, int len)
 		}
 	}
 
+#ifdef WIN32
+	cleaned_line = text_replace_non_bmp (line, len, &cleaned_len);
+	if (cleaned_line != NULL ) {
+		line = cleaned_line;
+		len = cleaned_len;
+	}
+#endif
+
 	fe_add_rawlog (serv, line, len, FALSE);
 
 	/* let proto-irc.c handle it */
 	serv->p_inline (serv, line, len);
 
+#ifdef WIN32
+	g_free (cleaned_line);
+#endif
+
 	if (utf_line_allocated != NULL) /* only if a special copy was allocated */
 		g_free (utf_line_allocated);
 }
@@ -844,33 +861,6 @@ server_flush_queue (server *serv)
 	fe_set_throttle (serv);
 }
 
-#ifdef WIN32
-
-static int
-waitline2 (GIOChannel *source, char *buf, int bufsize)
-{
-	int i = 0;
-	int len;
-
-	while (1)
-	{
-		if (g_io_channel_read (source, &buf[i], 1, &len) != G_IO_ERROR_NONE)
-			return -1;
-		if (buf[i] == '\n' || bufsize == i + 1)
-		{
-			buf[i] = 0;
-			return i;
-		}
-		i++;
-	}
-}
-
-#else
-
-#define waitline2(source,buf,size) waitline(serv->childread,buf,size,0)
-
-#endif
-
 /* connect() successed */
 
 static void
@@ -1394,12 +1384,7 @@ base64_encode (char *to, char *from, unsigned int len)
 static int
 http_read_line (int print_fd, int sok, char *buf, int len)
 {
-#ifdef WIN32
-	/* make sure waitline() uses recv() or it'll fail on win32 */
-	len = waitline (sok, buf, len, FALSE);
-#else
 	len = waitline (sok, buf, len, TRUE);
-#endif
 	if (len >= 1)
 	{
 		/* print the message out (send it to the parent process) */
@@ -1795,7 +1780,11 @@ server_connect (server *serv, char *hostname, int port, int no_login)
 	}
 #endif
 	serv->childpid = pid;
+#ifdef WIN32
+	serv->iotag = fe_input_add (serv->childread, FIA_READ|FIA_FD, server_read_child,
+#else
 	serv->iotag = fe_input_add (serv->childread, FIA_READ, server_read_child,
+#endif
 										 serv);
 }
 
diff --git a/src/common/servlist.c b/src/common/servlist.c
index 66bc8115..c9d4b6a8 100644
--- a/src/common/servlist.c
+++ b/src/common/servlist.c
@@ -21,7 +21,10 @@
 #include <string.h>
 #include <sys/types.h>
 #include <sys/stat.h>
+
+#ifndef WIN32
 #include <unistd.h>
+#endif
 
 #include "xchat.h"
 #include <glib.h>
diff --git a/src/common/ssl.c b/src/common/ssl.c
index a18ad47c..daa7416b 100644
--- a/src/common/ssl.c
+++ b/src/common/ssl.c
@@ -17,12 +17,12 @@
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
  */
 
+#include "inet.h"				  /* make it first to avoid macro redefinitions */
 #include <openssl/ssl.h>		  /* SSL_() */
 #include <openssl/err.h>		  /* ERR_() */
 #include <time.h>					  /* asctime() */
 #include <string.h>				  /* strncpy() */
 #include "ssl.h"					  /* struct cert_info */
-#include "inet.h"
 #include "../../config.h"		  /* HAVE_SNPRINTF */
 
 #ifndef HAVE_SNPRINTF
diff --git a/src/common/text.c b/src/common/text.c
index 6b111742..f8afc4b3 100644
--- a/src/common/text.c
+++ b/src/common/text.c
@@ -19,13 +19,16 @@
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
-#include <unistd.h>
 #include <ctype.h>
 #include <time.h>
 #include <sys/types.h>
 #include <fcntl.h>
 #include <sys/stat.h>
+
+#ifndef WIN32
+#include <unistd.h>
 #include <sys/mman.h>
+#endif
 
 #include "xchat.h"
 #include <glib.h>
@@ -271,9 +274,15 @@ scrollback_load (session *sess)
 	char *text;
 	time_t stamp;
 	int lines;
+
+#ifdef WIN32
+	char *cleaned_text;
+	int cleaned_len;
+#else
 	char *map, *end_map;
 	struct stat statbuf;
 	const char *begin, *eol;
+#endif
 
 	if (sess->text_scrollback == SET_DEFAULT)
 	{
@@ -293,6 +302,7 @@ scrollback_load (session *sess)
 	if (fh == -1)
 		return;
 
+#ifndef WIN32
 	if (fstat (fh, &statbuf) < 0)
 		return;
 
@@ -301,7 +311,7 @@ scrollback_load (session *sess)
 		return;
 
 	end_map = map + statbuf.st_size;
-	
+
 	lines = 0;
 	begin = map;
 	while (begin < end_map)
@@ -314,11 +324,11 @@ scrollback_load (session *sess)
 			eol = end_map;
 
 		n_bytes = MIN (eol - begin, sizeof (buf) - 1);
-		
+
 		strncpy (buf, begin, n_bytes);
 
 		buf[n_bytes] = 0;
-		
+
 		if (buf[0] == 'T')
 		{
 			if (sizeof (time_t) == 4)
@@ -350,6 +360,45 @@ scrollback_load (session *sess)
 	}
 
 	munmap (map, statbuf.st_size);
+#else
+	lines = 0;
+	while (waitline (fh, buf, sizeof buf, FALSE) != -1)
+	{
+		if (buf[0] == 'T')
+		{
+			if (sizeof (time_t) == 4)
+				stamp = strtoul (buf + 2, NULL, 10);
+			else
+				stamp = strtoull (buf + 2, NULL, 10); /* just incase time_t is 64 bits */
+			text = strchr (buf + 3, ' ');
+			if (text)
+			{
+				text = strip_color (text + 1, -1, STRIP_COLOR);
+				cleaned_text = text_replace_non_bmp (text, -1, &cleaned_len);
+				if (cleaned_text != NULL)
+				{
+					g_free (text);
+					text = cleaned_text;
+				}
+				fe_print_text (sess, text, stamp);
+				g_free (text);
+			}
+			lines++;
+		}
+	}
+
+	sess->scrollwritten = lines;
+
+	if (lines)
+	{
+		text = ctime (&stamp);
+		text[24] = 0;	/* get rid of the \n */
+		snprintf (buf, sizeof (buf), "\n*\t%s %s\n\n", _("Loaded log from"), text);
+		fe_print_text (sess, buf, 0);
+		/*EMIT_SIGNAL (XP_TE_GENMSG, sess, "*", buf, NULL, NULL, NULL, 0);*/
+	}
+#endif
+
 	close (fh);
 }
 
@@ -637,6 +686,13 @@ get_stamp_str (char *fmt, time_t tim, char **ret)
 	}
 
 	len = strftime (dest, sizeof (dest), fmt, localtime (&tim));
+#ifdef WIN32
+	if (!len)
+	{
+		/* use failsafe format until a correct one is specified */
+		len = strftime (dest, sizeof (dest), "[%H:%M]", localtime (&tim));
+	}
+#endif
 	if (len)
 	{
 		if (prefs.utf8_locale)
@@ -812,6 +868,46 @@ iso_8859_1_to_utf8 (unsigned char *text, int len, gsize *bytes_written)
 	return res;
 }
 
+#ifdef WIN32
+/* replace characters outside of the Basic Multilingual Plane with
+ * replacement characters (0xFFFD) */
+char *
+text_replace_non_bmp (char *utf8_input, int input_length, glong *output_length)
+{
+	gunichar *ucs4_text;
+	gunichar suspect;
+	gchar *utf8_text;
+	glong ucs4_length;
+	glong index;
+
+	ucs4_text = g_utf8_to_ucs4_fast (utf8_input, input_length, &ucs4_length);
+
+	/* replace anything not in the Basic Multilingual Plane
+	 * (code points above 0xFFFF) with the replacement
+	 * character */
+	for (index = 0; index < ucs4_length; index++)
+	{
+		suspect = ucs4_text[index];
+		if ((suspect >= 0x1D173 && suspect <= 0x1D17A)
+			|| (suspect >= 0xE0001 && suspect <= 0xE007F))
+		{
+			ucs4_text[index] = 0xFFFD; /* replacement character */
+		}
+	}
+
+	utf8_text = g_ucs4_to_utf8 (
+		ucs4_text,
+		ucs4_length,
+		NULL,
+		output_length,
+		NULL
+	);
+	g_free (ucs4_text);
+
+	return utf8_text;
+}
+#endif
+
 char *
 text_validate (char **text, int *len)
 {
diff --git a/src/common/text.h b/src/common/text.h
index 150821ae..6d5ac03e 100644
--- a/src/common/text.h
+++ b/src/common/text.h
@@ -28,6 +28,9 @@ int pevent_load (char *filename);
 void pevent_make_pntevts (void);
 void text_emit (int index, session *sess, char *a, char *b, char *c, char *d);
 int text_emit_by_name (char *name, session *sess, char *a, char *b, char *c, char *d);
+#ifdef WIN32
+char *text_replace_non_bmp (char *utf8_input, int input_length, glong *output_length);
+#endif
 char *text_validate (char **text, int *len);
 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);
diff --git a/src/common/thread.c b/src/common/thread.c
new file mode 100644
index 00000000..02b17cfb
--- /dev/null
+++ b/src/common/thread.c
@@ -0,0 +1,33 @@
+#include <fcntl.h>
+#include "thread.h"
+
+thread *
+thread_new (void)
+{
+	thread *th;
+
+	th = calloc (1, sizeof (*th));
+	if (!th)
+	{
+		return NULL;
+	}
+
+	if (_pipe (th->pipe_fd, 4096, _O_BINARY) == -1)
+	{
+		free (th);
+		return NULL;
+	}
+
+	return th;
+}
+
+int
+thread_start (thread *th, void *(*start_routine)(void *), void *arg)
+{
+	DWORD id;
+
+	CloseHandle (CreateThread (NULL, 0, (LPTHREAD_START_ROUTINE)start_routine, arg, 0, (DWORD *)&id));
+	th->threadid = id;
+
+	return 1;
+}
diff --git a/src/common/thread.h b/src/common/thread.h
new file mode 100644
index 00000000..7ca0f937
--- /dev/null
+++ b/src/common/thread.h
@@ -0,0 +1,10 @@
+#include <windows.h>
+
+typedef struct
+{
+	DWORD threadid;
+	int pipe_fd[2];
+} thread;
+
+thread *thread_new (void);
+int thread_start (thread *th, void *(*start_routine)(void *), void *arg);
diff --git a/src/common/util.c b/src/common/util.c
index 5a0ab6c5..9d9c2cff 100644
--- a/src/common/util.c
+++ b/src/common/util.c
@@ -16,26 +16,32 @@
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
  */
 
+#define WANTSOCKET
+#include "inet.h"				/* make it first to avoid macro redefinitions */
+
 #define __APPLE_API_STRICT_CONFORMANCE
 
 #define _FILE_OFFSET_BITS 64
 #include <stdio.h>
-#include <unistd.h>
 #include <string.h>
 #include <stdlib.h>
 #include <sys/types.h>
 #include <sys/stat.h>
+
 #ifdef WIN32
 #include <sys/timeb.h>
 #include <process.h>
+#include <io.h>
+#include "dirent-win32.h"
 #else
-#include <sys/types.h>
+#include <unistd.h>
 #include <pwd.h>
 #include <sys/time.h>
 #include <sys/utsname.h>
+#include <dirent.h>
 #endif
+
 #include <fcntl.h>
-#include <dirent.h>
 #include <errno.h>
 #include "xchat.h"
 #include "xchatc.h"
@@ -44,9 +50,6 @@
 #include "util.h"
 #include "../../config.h"
 
-#define WANTSOCKET
-#include "inet.h"
-
 #if defined (USING_FREEBSD) || defined (__APPLE__)
 #include <sys/sysctl.h>
 #endif
@@ -54,9 +57,11 @@
 #include <socks.h>
 #endif
 
+#ifndef ENABLE_NLS
 #ifndef HAVE_SNPRINTF
 #define snprintf g_snprintf
 #endif
+#endif
 
 #ifdef USE_DEBUG
 
@@ -383,6 +388,28 @@ waitline (int sok, char *buf, int bufsize, int use_recv)
 	}
 }
 
+#ifdef WIN32
+/* waitline2 using win32 file descriptor and glib instead of _read. win32 can't _read() sok! */
+int
+waitline2 (GIOChannel *source, char *buf, int bufsize)
+{
+	int i = 0;
+	int len;
+
+	while (1)
+	{
+		if (g_io_channel_read (source, &buf[i], 1, &len) != G_IO_ERROR_NONE)
+			return -1;
+		if (buf[i] == '\n' || bufsize == i + 1)
+		{
+			buf[i] = 0;
+			return i;
+		}
+		i++;
+	}
+}
+#endif
+
 /* checks for "~" in a file and expands */
 
 char *
@@ -624,30 +651,110 @@ get_mhz (void)
 	return 0;	/* fails on Win9x */
 }
 
+int
+get_cpu_arch (void)
+{
+	SYSTEM_INFO si;
+
+	GetSystemInfo (&si);
+
+	if (si.wProcessorArchitecture == 9)
+	{
+		return 64;
+	}
+	else
+	{
+		return 86;
+	}
+}
+
 char *
 get_cpu_str (void)
 {
 	static char verbuf[64];
-	OSVERSIONINFO osvi;
-	SYSTEM_INFO si;
+	static char winver[20];
+	OSVERSIONINFOEX osvi;
 	double mhz;
 
-	osvi.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
+	osvi.dwOSVersionInfoSize = sizeof (OSVERSIONINFOEX);
 	GetVersionEx (&osvi);
-	GetSystemInfo (&si);
+
+	switch (osvi.dwMajorVersion)
+	{
+		case 5:
+			switch (osvi.dwMinorVersion)
+			{
+				case 1:
+					strcpy (winver, "XP");
+					break;
+				case 2:
+					if (osvi.wProductType == VER_NT_WORKSTATION)
+					{
+						strcpy (winver, "XP x64 Edition");
+					}
+					else
+					{
+						if (GetSystemMetrics(SM_SERVERR2) == 0)
+						{
+							strcpy (winver, "Server 2003");
+						}
+						else
+						{
+							strcpy (winver, "Server 2003 R2");
+						}
+					}
+					break;
+			}
+			break;
+		case 6:
+			switch (osvi.dwMinorVersion)
+			{
+				case 0:
+					if (osvi.wProductType == VER_NT_WORKSTATION)
+					{
+						strcpy (winver, "Vista");
+					}
+					else
+					{
+						strcpy (winver, "Server 2008");
+					}
+					break;
+				case 1:
+					if (osvi.wProductType == VER_NT_WORKSTATION)
+					{
+						strcpy (winver, "7");
+					}
+					else
+					{
+						strcpy (winver, "Server 2008 R2");
+					}
+					break;
+				case 2:
+					if (osvi.wProductType == VER_NT_WORKSTATION)
+					{
+						strcpy (winver, "8");
+					}
+					else
+					{
+						strcpy (winver, "8 Server");
+					}
+					break;
+			}
+			break;
+	}
 
 	mhz = get_mhz ();
 	if (mhz)
 	{
 		double cpuspeed = ( mhz > 1000 ) ? mhz / 1000 : mhz;
 		const char *cpuspeedstr = ( mhz > 1000 ) ? "GHz" : "MHz";
-		sprintf (verbuf, "Windows %ld.%ld [i%d86/%.2f%s]",
-					osvi.dwMajorVersion, osvi.dwMinorVersion, si.wProcessorLevel, 
-					cpuspeed, cpuspeedstr);
-	} else
-		sprintf (verbuf, "Windows %ld.%ld [i%d86]",
-			osvi.dwMajorVersion, osvi.dwMinorVersion, si.wProcessorLevel);
-
+		sprintf (verbuf, "Windows %s [%.2f%s]",	winver, cpuspeed, cpuspeedstr);
+	}
+	else
+	{
+		sprintf (verbuf, "Windows %s", winver);
+	}
+	
 	return verbuf;
 }
 
@@ -1727,3 +1834,55 @@ safe_strcpy (char *dest, const char *src, int bytes_left)
 		}
 	}
 }
+
+void
+canonalize_key (char *key)
+{
+	char *pos, token;
+
+	for (pos = key; (token = *pos) != 0; pos++)
+	{
+		if (token != '_' && (token < '0' || token > '9') && (token < 'A' || token > 'Z') && (token < 'a' || token > 'z'))
+		{
+			*pos = '_';
+		}
+		else
+		{
+			*pos = tolower(token);
+		}
+	}
+}
+
+int
+portable_mode ()
+{
+#ifdef WIN32
+	if ((_access( "portable-mode", 0 )) != -1)
+	{
+		return 1;
+	}
+	else
+	{
+		return 0;
+	}
+#else
+	return 0;
+#endif
+}
+
+int
+xtray_mode ()
+{
+#ifdef WIN32
+	if ((_access( "plugins/xtray.dll", 0 )) != -1)
+	{
+		return 1;
+	}
+	else
+	{
+		return 0;
+	}
+#else
+	return 0;
+#endif
+}
diff --git a/src/common/util.h b/src/common/util.h
index fce45def..82d74366 100644
--- a/src/common/util.h
+++ b/src/common/util.h
@@ -43,6 +43,12 @@ int strip_color2 (const char *src, int len, char *dst, int flags);
 int strip_hidden_attribute (char *src, char *dst);
 char *errorstring (int err);
 int waitline (int sok, char *buf, int bufsize, int);
+#ifdef WIN32
+int waitline2 (GIOChannel *source, char *buf, int bufsize);
+int get_cpu_arch (void);
+#else
+#define waitline2(source,buf,size) waitline(serv->childread,buf,size,0)
+#endif
 unsigned long make_ping_time (void);
 void move_file_utf8 (char *src_dir, char *dst_dir, char *fname, int dccpermissions);
 int mkdir_utf8 (char *dir);
@@ -50,5 +56,8 @@ int token_foreach (char *str, char sep, int (*callback) (char *str, void *ud), v
 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 xtray_mode ();
 
 #endif
diff --git a/src/common/xchat-plugin.h b/src/common/xchat-plugin.h
index 30b19295..1b7da8fb 100644
--- a/src/common/xchat-plugin.h
+++ b/src/common/xchat-plugin.h
@@ -137,6 +137,21 @@ struct _xchat_plugin
 	     int flags);
 	void (*xchat_free) (xchat_plugin *ph,
 	    void *ptr);
+	int (*xchat_pluginpref_set_str) (xchat_plugin *ph,
+		const char *var,
+		const char *value);
+	int (*xchat_pluginpref_get_str) (xchat_plugin *ph,
+		const char *var,
+		char *dest);
+	int (*xchat_pluginpref_set_int) (xchat_plugin *ph,
+		const char *var,
+		int value);
+	int (*xchat_pluginpref_get_int) (xchat_plugin *ph,
+		const char *var);
+	int (*xchat_pluginpref_delete) (xchat_plugin *ph,
+		const char *var);
+	int (*xchat_pluginpref_list) (xchat_plugin *ph,
+		char *dest);
 };
 #endif
 
@@ -292,6 +307,32 @@ void
 xchat_free (xchat_plugin *ph,
 	    void *ptr);
 
+int
+xchat_pluginpref_set_str (xchat_plugin *ph,
+		const char *var,
+		const char *value);
+
+int
+xchat_pluginpref_get_str (xchat_plugin *ph,
+		const char *var,
+		char *dest);
+
+int
+xchat_pluginpref_set_int (xchat_plugin *ph,
+		const char *var,
+		int value);
+int
+xchat_pluginpref_get_int (xchat_plugin *ph,
+		const char *var);
+
+int
+xchat_pluginpref_delete (xchat_plugin *ph,
+		const char *var);
+
+int
+xchat_pluginpref_list (xchat_plugin *ph,
+		char *dest);
+
 #if !defined(PLUGIN_C) && defined(WIN32)
 #ifndef XCHAT_PLUGIN_HANDLE
 #define XCHAT_PLUGIN_HANDLE (ph)
@@ -326,6 +367,12 @@ xchat_free (xchat_plugin *ph,
 #define xchat_send_modes ((XCHAT_PLUGIN_HANDLE)->xchat_send_modes)
 #define xchat_strip ((XCHAT_PLUGIN_HANDLE)->xchat_strip)
 #define xchat_free ((XCHAT_PLUGIN_HANDLE)->xchat_free)
+#define xchat_pluginpref_set_str ((XCHAT_PLUGIN_HANDLE)->xchat_pluginpref_set_str)
+#define xchat_pluginpref_get_str ((XCHAT_PLUGIN_HANDLE)->xchat_pluginpref_get_str)
+#define xchat_pluginpref_set_int ((XCHAT_PLUGIN_HANDLE)->xchat_pluginpref_set_int)
+#define xchat_pluginpref_get_int ((XCHAT_PLUGIN_HANDLE)->xchat_pluginpref_get_int)
+#define xchat_pluginpref_delete ((XCHAT_PLUGIN_HANDLE)->xchat_pluginpref_delete)
+#define xchat_pluginpref_list ((XCHAT_PLUGIN_HANDLE)->xchat_pluginpref_list)
 #endif
 
 #ifdef __cplusplus
diff --git a/src/common/xchat.c b/src/common/xchat.c
index afac9a0e..22c4eddb 100644
--- a/src/common/xchat.c
+++ b/src/common/xchat.c
@@ -22,14 +22,16 @@
 #include <time.h>
 #include <sys/types.h>
 #include <sys/stat.h>
-#include <unistd.h>
 
 #define WANTSOCKET
 #include "inet.h"
 
-#ifndef WIN32
+#ifdef WIN32
+#include <windows.h>
+#else
 #include <sys/wait.h>
 #include <signal.h>
+#include <unistd.h>
 #endif
 
 #include "xchat.h"
@@ -590,6 +592,7 @@ static char defaultconf_commands[] =
 	"NAME DMSG\n"			"CMD msg =%2 &3\n\n"\
 	"NAME EXIT\n"			"CMD quit\n\n"\
 	"NAME GREP\n"			"CMD lastlog -r &2\n\n"\
+	"NAME IGNALL\n"			"CMD ignore %2!*@* ALL\n\n"\
 	"NAME J\n"				"CMD join &2\n\n"\
 	"NAME KILL\n"			"CMD quote KILL %2 :&3\n\n"\
 	"NAME LEAVE\n"			"CMD part &2\n\n"\
@@ -903,7 +906,11 @@ int
 main (int argc, char *argv[])
 {
 	int ret;
-	
+
+#ifdef WIN32
+	HANDLE mutex;
+#endif
+
 	srand (time (0));	/* CL: do this only once! */
 
 #ifdef SOCKS
@@ -920,6 +927,21 @@ main (int argc, char *argv[])
 
 	load_config ();
 
+#ifdef WIN32
+	if (prefs.gui_one_instance && !portable_mode ())
+	{
+		DWORD error;
+
+		mutex = CreateMutex (NULL, TRUE, "Local\xchat");
+		error = GetLastError ();
+
+		if (error == ERROR_ALREADY_EXISTS || mutex == NULL)
+		{
+			return 1;
+		}
+	}
+#endif
+
 #ifdef USE_LIBPROXY
 	libproxy_factory = px_proxy_factory_new();
 #endif
@@ -945,6 +967,11 @@ main (int argc, char *argv[])
 
 #ifdef WIN32
 	WSACleanup ();
+
+	if (prefs.gui_one_instance && !portable_mode ())
+	{
+		CloseHandle (mutex);
+	}
 #endif
 
 	return 0;
diff --git a/src/common/xchat.h b/src/common/xchat.h
index 6e31d7c5..95fb1324 100644
--- a/src/common/xchat.h
+++ b/src/common/xchat.h
@@ -8,12 +8,14 @@
 
 #include "history.h"
 
+#ifndef ENABLE_NLS
 #ifndef HAVE_SNPRINTF
 #define snprintf g_snprintf
 #endif
 
 #ifndef HAVE_VSNPRINTF
-#define vsnprintf g_vsnprintf
+#define vsnprintf _vsnprintf
+#endif
 #endif
 
 #ifdef USE_DEBUG
@@ -54,7 +56,7 @@ void *xchat_realloc (char *old, int len, char *file, int line);
 
 #ifdef WIN32						/* for win32 */
 #define OFLAGS O_BINARY
-#define sleep(t) _sleep(t*1000)
+#define sleep(t) Sleep(t*1000)
 #include <direct.h>
 #define	F_OK	0
 #define	X_OK	1
@@ -113,8 +115,16 @@ struct xchatprefs
 	char awayreason[256];
 	char quitreason[256];
 	char partreason[256];
+#ifdef WIN32
+	char font_normal[4 * FONTNAMELEN + 1];
+	char font_main[FONTNAMELEN + 1];
+	char font_alternative[3 * FONTNAMELEN + 1];
+#else
 	char font_normal[FONTNAMELEN + 1];
+#endif
 	char doubleclickuser[256];
+	char gui_license[64];
+	char spell_langs[64];
 	char sounddir[PATHLEN + 1];
 	char soundcmd[PATHLEN + 1];
 	char background[PATHLEN + 1];
@@ -178,6 +188,9 @@ struct xchatprefs
 	int gui_usermenu;
 	int gui_join_dialog;
 	int gui_quit_dialog;
+#ifdef WIN32
+	int gui_one_instance;
+#endif
 	int dialog_left;
 	int dialog_top;
 	int dialog_width;
@@ -205,6 +218,9 @@ struct xchatprefs
 	unsigned int wallops;
 	unsigned int skipmotd;
 	unsigned int autorejoin;
+	unsigned int autocopy_text;
+	unsigned int autocopy_stamp;
+	unsigned int autocopy_color;
 	unsigned int colorednicks;
 	unsigned int chanmodebuttons;
 	unsigned int userlistbuttons;
@@ -292,7 +308,12 @@ struct xchatprefs
 	unsigned int wait_on_exit;
 	unsigned int confmode;
 	unsigned int utf8_locale;
+#ifdef WIN32
 	unsigned int identd;
+	unsigned int emoticons;
+	unsigned int tab_icons;
+	unsigned int tab_xp;
+#endif
 
 	unsigned int ctcp_number_limit;	/*flood */
 	unsigned int ctcp_time_limit;	/*seconds of floods */