diff --git a/src/sbbs3/gtkchat/GNUmakefile b/src/sbbs3/gtkchat/GNUmakefile
new file mode 100644
index 0000000000000000000000000000000000000000..ff73a4788c370dcc32476799d1b9febca6715e09
--- /dev/null
+++ b/src/sbbs3/gtkchat/GNUmakefile
@@ -0,0 +1,30 @@
+# GNUmakefile
+
+#########################################################################
+# Makefile for Synchronet monitor for Unix								#
+# For use with GNU make and GNU C Compiler or Borland Kylix C++			#
+# @format.tab-size 4, @format.use-tabs true								#
+#																		#
+# gcc: gmake															#
+# Borland (still in testing/debuging stage): gmake bcc=1				#
+#																		#
+# Optional build targets: dlls, utils, mono, all (default)				#
+#########################################################################
+
+# $Id$
+
+SRC_ROOT	=	../..
+include $(SRC_ROOT)/build/Common.gmake
+
+ifeq ($(os),sunos)
+ LDFLAGS	+=	-lnsl
+endif
+
+vpath %.c ..
+
+CFLAGS	+=	-I.. $(SMBLIB_CFLAGS) $(XPDEV-MT_CFLAGS) `pkg-config gtk+-2.0 --cflags`
+LDFLAGS	+=	$(SMBLIB_LDFLAGS) $(XPDEV-MT_LDFLAGS) $(MT_LDFLAGS) `pkg-config gtk+-2.0 --libs`
+
+$(GTKCHAT): $(OBJS)
+	@echo Linking $@
+	$(QUIET)$(CC) $(LDFLAGS) $(OBJS) -o $@ $(SMBLIB_LIBS) $(XPDEV-MT_LIBS)
diff --git a/src/sbbs3/gtkchat/callbacks.c b/src/sbbs3/gtkchat/callbacks.c
new file mode 100644
index 0000000000000000000000000000000000000000..7867e2346de82389e8e7fe60d4e8deb20b47a268
--- /dev/null
+++ b/src/sbbs3/gtkchat/callbacks.c
@@ -0,0 +1,145 @@
+#include <gtk/gtk.h>
+
+#include "chatfuncs.h"
+#include "callbacks.h"
+#include "interface.h"
+#include "support.h"
+
+gint
+get_from_remote(gpointer data)
+{
+	int		ch;
+	gchar	*outstr;
+	gchar	instr[2];
+	int		inbytes;
+	int		outbytes;
+
+	instr[1]=0;
+	switch(chat_check_remote()) {
+		case 2:		/* Chat active */
+			while(ch=chat_read_byte()) {
+				if(ch==-1) {
+					chat_close();
+					gtk_main_quit();
+					return(FALSE);
+				}
+				if(ch==8 || ch==127) {
+					GtkTextIter		start;
+					GtkTextIter		end;
+
+					gtk_text_buffer_get_iter_at_mark(
+							 gtk_text_view_get_buffer(GTK_TEXT_VIEW(data))
+							,&start
+							,gtk_text_buffer_get_insert(
+									gtk_text_view_get_buffer(GTK_TEXT_VIEW(data))
+							)
+					);
+					end=start;
+					gtk_text_iter_backward_cursor_position(&end);
+					gtk_text_buffer_delete(
+							 gtk_text_view_get_buffer(GTK_TEXT_VIEW(data))
+							,&start
+							,&end
+					);
+				}
+				else {
+					instr[0]=ch;
+					outstr=g_convert(instr, 1, "UTF-8", "CP437", &inbytes, &outbytes, NULL);
+					gtk_text_buffer_insert_at_cursor(
+							 gtk_text_view_get_buffer(GTK_TEXT_VIEW(data))
+							,outstr
+							,1
+					);
+					g_free(outstr);
+				}
+			}
+			return(TRUE);
+	}
+	chat_close();
+	return(FALSE);
+}
+
+
+gint
+connect_wait(gpointer data)
+{
+	GtkWidget *MainWindow;
+
+	switch(chat_check_remote()) {
+		case -1:	/* Error */
+		case 0:		/* Remote has gone away */
+			chat_close();
+			return(FALSE);
+		case 1:		/* Waiting for remote */
+			return(TRUE);
+		case 2:		/* Chat active */
+			MainWindow = create_MainWindow ();
+			gtk_widget_hide (GTK_WIDGET(data));
+			gtk_widget_show (MainWindow);
+			return(FALSE);
+	}
+	return(TRUE);
+}
+
+
+void
+on_MainWindow_destroy                  (GtkObject       *object,
+                                        gpointer         user_data)
+{
+	chat_close();
+	gtk_main_quit();
+}
+
+
+gboolean
+on_LocalText_key_press_event           (GtkWidget       *widget,
+                                        GdkEventKey     *event,
+                                        gpointer         user_data)
+{
+	gchar	*outstr;
+	gchar	instr[2];
+	int		inbytes;
+	int		outbytes;
+
+	if(event->keyval=='\b' || event->keyval==127) {
+		GtkTextIter		start;
+		GtkTextIter		end;
+
+		chat_write_byte(event->keyval);
+		gtk_text_buffer_get_iter_at_mark(
+				 gtk_text_view_get_buffer(GTK_TEXT_VIEW(widget))
+				,&start
+				,gtk_text_buffer_get_insert(
+						gtk_text_view_get_buffer(GTK_TEXT_VIEW(widget))
+				)
+		);
+		end=start;
+		gtk_text_iter_backward_cursor_position(&end);
+		gtk_text_buffer_delete(
+				 gtk_text_view_get_buffer(GTK_TEXT_VIEW(widget))
+				,&start
+				,&end
+		);
+	}
+	if(event->keyval >= 32 && event->keyval < 127) {
+		instr[1]=0;
+		instr[0]=event->keyval;
+		outstr=g_convert(instr, 1, "UTF-8", "CP437", &inbytes, &outbytes, NULL);
+		gtk_text_buffer_insert_at_cursor(
+				 gtk_text_view_get_buffer(GTK_TEXT_VIEW(widget))
+				,outstr
+				,1
+		);
+		g_free(outstr);
+	}
+	return FALSE;
+}
+
+
+void
+on_CancelButton_clicked                (GtkButton       *button,
+                                        gpointer         user_data)
+{
+	chat_close();
+	gtk_main_quit();
+}
diff --git a/src/sbbs3/gtkchat/callbacks.h b/src/sbbs3/gtkchat/callbacks.h
new file mode 100644
index 0000000000000000000000000000000000000000..944a5a0adc2600ed7f8d11ee0c39caa345edfe1f
--- /dev/null
+++ b/src/sbbs3/gtkchat/callbacks.h
@@ -0,0 +1,22 @@
+#include <gtk/gtk.h>
+
+
+void
+on_MainWindow_destroy                  (GtkObject       *object,
+                                        gpointer         user_data);
+
+gboolean
+on_LocalText_key_press_event           (GtkWidget       *widget,
+                                        GdkEventKey     *event,
+                                        gpointer         user_data);
+
+void
+on_CancelButton_clicked                (GtkButton       *button,
+                                        gpointer         user_data);
+
+gint
+get_from_remote(gpointer data);
+
+
+gint
+connect_wait(gpointer data);
diff --git a/src/sbbs3/gtkchat/chatfuncs.c b/src/sbbs3/gtkchat/chatfuncs.c
new file mode 100644
index 0000000000000000000000000000000000000000..a030ba6df18a19ccc3de30c128846520ef2a39dd
--- /dev/null
+++ b/src/sbbs3/gtkchat/chatfuncs.c
@@ -0,0 +1,178 @@
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <string.h>
+#include <time.h>
+#include <utime.h>
+#include <unistd.h>
+
+#include "sbbs.h"
+#include "chatfuncs.h"
+
+#define PCHAT_LEN 1000
+
+char			usrname[128];
+
+static node_t	node;
+static int		nodenum;
+static int		in,out;
+static char		inpath[MAX_PATH+1];
+static char		outpath[MAX_PATH+1];
+static scfg_t	cfg;
+
+static int togglechat(int on)
+{
+    static int  org_act;
+	int nodefile;
+
+	if(getnodedat(&cfg,nodenum,&node,&nodefile))
+		return(-1);
+    if(on) {
+        org_act=node.action;
+        if(org_act==NODE_PCHT)
+            org_act=NODE_MAIN;
+        node.misc|=NODE_LCHAT;
+    }
+	else {
+        node.action=org_act;
+        node.misc&=~NODE_LCHAT;
+    }
+	if(putnodedat(&cfg,nodenum,&node,nodefile))
+		return(-1);
+    return(0);
+}
+
+int chat_open(int node_num, char *ctrl_dir)
+{
+	char	*p;
+	char	str[1024];
+
+    /* Read .cfg files here */
+    memset(&cfg,0,sizeof(cfg));
+    cfg.size=sizeof(cfg);
+    SAFECOPY(cfg.ctrl_dir,ctrl_dir);
+    if(!load_cfg(&cfg, NULL, TRUE, str))
+        return(-1);
+
+	nodenum=node_num;
+
+	if(getnodedat(&cfg,nodenum,&node,NULL))
+		return(-1);
+
+	username(&cfg,node.useron,usrname);
+
+	sprintf(outpath,"%slchat.dab",cfg.node_path[nodenum-1]);
+	if((out=sopen(outpath,O_RDWR|O_CREAT|O_BINARY,O_DENYNONE
+		,S_IREAD|S_IWRITE))==-1) {
+		return(-1);
+	}
+
+	sprintf(inpath,"%schat.dab",cfg.node_path[nodenum-1]);
+	if((in=sopen(inpath,O_RDWR|O_CREAT|O_BINARY,O_DENYNONE
+		,S_IREAD|S_IWRITE))==-1) {
+		close(out);
+		return(-1);
+    }
+
+	if((p=(char *)malloc(PCHAT_LEN))==NULL) {
+		close(in);
+		close(out);
+		return(-1);
+    }
+	memset(p,0,PCHAT_LEN);
+	write(in,p,PCHAT_LEN);
+	write(out,p,PCHAT_LEN);
+	free(p);
+	lseek(in,0,SEEK_SET);
+	lseek(out,0,SEEK_SET);
+
+	if(togglechat(TRUE))
+		return(-1);
+	return(0);
+}
+
+int chat_check_remote(void)
+{
+	time_t			now;
+	static time_t	last_nodechk=0;
+
+	now=time(NULL);
+	if(now!=last_nodechk) {
+		if(getnodedat(&cfg,nodenum,&node,NULL)!=0)
+			return(-1);			/* Failed to read nodedat! */
+		last_nodechk=now;
+	}
+	if(node.misc&NODE_LCHAT)
+		return(1);				/* Still Waiting */
+
+	if(node.status==NODE_WFC || node.status>NODE_QUIET || node.action!=NODE_PCHT)
+		return(0);				/* Remote has gone away */
+
+	if(in==-1)
+		return(0);				/* Remote has gone away */
+
+	if(out==-1)
+		return(-1);				/* Write error or some such */
+
+	return(2);					/* Everything is good! */
+}
+
+int chat_read_byte(void)
+{
+	unsigned char ch=0;
+
+	if(in==-1)
+		return(-1);
+	utime(inpath,NULL);
+	switch(read(in,&ch,1)) {
+		case -1:
+			close(in);
+			in=-1;
+			return(-1);
+		case 0:
+			if(lseek(in,0,SEEK_SET)==-1);	/* Wrapped */
+				return(-1);
+			switch(read(in,&ch,1)) {
+				case -1:
+					close(in);
+					in=-1;
+					return(-1);
+			}
+			/* Fall-through */
+		case 1:
+			lseek(in,-1L,SEEK_CUR);
+			if(ch) {
+				write(in,"",1);
+				return(ch);
+			}
+	}
+
+	return(0);
+}
+
+int chat_write_byte(unsigned char ch)
+{
+	if(out==-1)
+		return(-1);
+	if(lseek(out,0,SEEK_CUR)>=PCHAT_LEN)
+		lseek(out,0,SEEK_SET);
+	switch(write(out,&ch,1)) {
+		case -1:
+			close(out);
+			out=-1;
+			return(-1);
+	}
+	utime(outpath,NULL);
+	return(0);
+}
+
+int chat_close(void)
+{
+	if(in != -1)
+		close(in);
+	if(out != -1)
+		close(out);
+	return(togglechat(FALSE));
+}
diff --git a/src/sbbs3/gtkchat/chatfuncs.h b/src/sbbs3/gtkchat/chatfuncs.h
new file mode 100644
index 0000000000000000000000000000000000000000..94f1af0cfccfbd5af7558cc4c5cbb4d935331d68
--- /dev/null
+++ b/src/sbbs3/gtkchat/chatfuncs.h
@@ -0,0 +1,11 @@
+#ifndef _CHATFUNCS_H_
+#define _CHATFUNCS_H_
+
+extern char			usrname[128];
+int chat_open(int node_num, char *ctrl_dir);
+int chat_check_remote(void);
+int chat_read_byte(void);
+int chat_write_byte(unsigned char ch);
+int chat_close(void);
+
+#endif
diff --git a/src/sbbs3/gtkchat/gtk-chat.glade b/src/sbbs3/gtkchat/gtk-chat.glade
new file mode 100644
index 0000000000000000000000000000000000000000..02c3efb0116ca37ebeb8c918ce52293c72226954
--- /dev/null
+++ b/src/sbbs3/gtkchat/gtk-chat.glade
@@ -0,0 +1,216 @@
+<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
+<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
+
+<glade-interface>
+
+<widget class="GtkWindow" id="MainWindow">
+  <property name="width_request">400</property>
+  <property name="height_request">300</property>
+  <property name="visible">True</property>
+  <property name="title" translatable="yes">Synchronet Sysop Chat</property>
+  <property name="type">GTK_WINDOW_TOPLEVEL</property>
+  <property name="window_position">GTK_WIN_POS_NONE</property>
+  <property name="modal">False</property>
+  <property name="resizable">True</property>
+  <property name="destroy_with_parent">False</property>
+  <property name="icon_name">stock_help-chat</property>
+  <property name="decorated">True</property>
+  <property name="skip_taskbar_hint">False</property>
+  <property name="skip_pager_hint">False</property>
+  <property name="type_hint">GDK_WINDOW_TYPE_HINT_UTILITY</property>
+  <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+  <property name="focus_on_map">True</property>
+  <property name="urgency_hint">False</property>
+  <signal name="destroy" handler="on_MainWindow_destroy" last_modification_time="Wed, 08 Mar 2006 07:15:35 GMT"/>
+
+  <child>
+    <widget class="GtkVPaned" id="SplitPane">
+      <property name="visible">True</property>
+      <property name="can_focus">True</property>
+      <property name="position">148</property>
+
+      <child>
+	<widget class="GtkTextView" id="RemoteText">
+	  <property name="visible">True</property>
+	  <property name="tooltip" translatable="yes">Remote Text Window</property>
+	  <property name="editable">False</property>
+	  <property name="overwrite">False</property>
+	  <property name="accepts_tab">True</property>
+	  <property name="justification">GTK_JUSTIFY_LEFT</property>
+	  <property name="wrap_mode">GTK_WRAP_WORD</property>
+	  <property name="cursor_visible">True</property>
+	  <property name="pixels_above_lines">0</property>
+	  <property name="pixels_below_lines">0</property>
+	  <property name="pixels_inside_wrap">0</property>
+	  <property name="left_margin">0</property>
+	  <property name="right_margin">0</property>
+	  <property name="indent">0</property>
+	  <property name="text" translatable="yes"></property>
+	</widget>
+	<packing>
+	  <property name="shrink">True</property>
+	  <property name="resize">False</property>
+	</packing>
+      </child>
+
+      <child>
+	<widget class="GtkTextView" id="LocalText">
+	  <property name="visible">True</property>
+	  <property name="can_focus">True</property>
+	  <property name="has_focus">True</property>
+	  <property name="editable">False</property>
+	  <property name="overwrite">False</property>
+	  <property name="accepts_tab">True</property>
+	  <property name="justification">GTK_JUSTIFY_LEFT</property>
+	  <property name="wrap_mode">GTK_WRAP_WORD</property>
+	  <property name="cursor_visible">True</property>
+	  <property name="pixels_above_lines">0</property>
+	  <property name="pixels_below_lines">0</property>
+	  <property name="pixels_inside_wrap">0</property>
+	  <property name="left_margin">0</property>
+	  <property name="right_margin">0</property>
+	  <property name="indent">0</property>
+	  <property name="text" translatable="yes"></property>
+	  <signal name="key_press_event" handler="on_LocalText_key_press_event" last_modification_time="Wed, 08 Mar 2006 07:13:25 GMT"/>
+	</widget>
+	<packing>
+	  <property name="shrink">True</property>
+	  <property name="resize">True</property>
+	</packing>
+      </child>
+    </widget>
+  </child>
+</widget>
+
+<widget class="GtkWindow" id="WaitWindow">
+  <property name="visible">True</property>
+  <property name="title" translatable="yes">Waiting for user</property>
+  <property name="type">GTK_WINDOW_TOPLEVEL</property>
+  <property name="window_position">GTK_WIN_POS_NONE</property>
+  <property name="modal">False</property>
+  <property name="resizable">True</property>
+  <property name="destroy_with_parent">False</property>
+  <property name="decorated">True</property>
+  <property name="skip_taskbar_hint">False</property>
+  <property name="skip_pager_hint">False</property>
+  <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
+  <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+  <property name="focus_on_map">True</property>
+  <property name="urgency_hint">False</property>
+
+  <child>
+    <widget class="GtkFixed" id="fixed1">
+      <property name="width_request">400</property>
+      <property name="height_request">40</property>
+      <property name="visible">True</property>
+
+      <child>
+	<widget class="GtkLabel" id="MessageLabel">
+	  <property name="width_request">400</property>
+	  <property name="height_request">16</property>
+	  <property name="visible">True</property>
+	  <property name="label" translatable="yes">Waiting for user to connect.</property>
+	  <property name="use_underline">False</property>
+	  <property name="use_markup">False</property>
+	  <property name="justify">GTK_JUSTIFY_LEFT</property>
+	  <property name="wrap">False</property>
+	  <property name="selectable">False</property>
+	  <property name="xalign">0.5</property>
+	  <property name="yalign">0.5</property>
+	  <property name="xpad">0</property>
+	  <property name="ypad">0</property>
+	  <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+	  <property name="width_chars">-1</property>
+	  <property name="single_line_mode">False</property>
+	  <property name="angle">0</property>
+	</widget>
+	<packing>
+	  <property name="x">0</property>
+	  <property name="y">0</property>
+	</packing>
+      </child>
+
+      <child>
+	<widget class="GtkButton" id="CancelButton">
+	  <property name="width_request">88</property>
+	  <property name="height_request">24</property>
+	  <property name="visible">True</property>
+	  <property name="can_focus">True</property>
+	  <property name="relief">GTK_RELIEF_NORMAL</property>
+	  <property name="focus_on_click">True</property>
+	  <signal name="clicked" handler="on_CancelButton_clicked" last_modification_time="Thu, 09 Mar 2006 02:41:33 GMT"/>
+
+	  <child>
+	    <widget class="GtkAlignment" id="alignment1">
+	      <property name="visible">True</property>
+	      <property name="xalign">0.5</property>
+	      <property name="yalign">0.5</property>
+	      <property name="xscale">0</property>
+	      <property name="yscale">0</property>
+	      <property name="top_padding">0</property>
+	      <property name="bottom_padding">0</property>
+	      <property name="left_padding">0</property>
+	      <property name="right_padding">0</property>
+
+	      <child>
+		<widget class="GtkHBox" id="hbox1">
+		  <property name="visible">True</property>
+		  <property name="homogeneous">False</property>
+		  <property name="spacing">2</property>
+
+		  <child>
+		    <widget class="GtkImage" id="image1">
+		      <property name="visible">True</property>
+		      <property name="stock">gtk-cancel</property>
+		      <property name="icon_size">4</property>
+		      <property name="xalign">0.5</property>
+		      <property name="yalign">0.5</property>
+		      <property name="xpad">0</property>
+		      <property name="ypad">0</property>
+		    </widget>
+		    <packing>
+		      <property name="padding">0</property>
+		      <property name="expand">False</property>
+		      <property name="fill">False</property>
+		    </packing>
+		  </child>
+
+		  <child>
+		    <widget class="GtkLabel" id="label2">
+		      <property name="visible">True</property>
+		      <property name="label" translatable="yes">Cancel</property>
+		      <property name="use_underline">True</property>
+		      <property name="use_markup">False</property>
+		      <property name="justify">GTK_JUSTIFY_LEFT</property>
+		      <property name="wrap">False</property>
+		      <property name="selectable">False</property>
+		      <property name="xalign">0.5</property>
+		      <property name="yalign">0.5</property>
+		      <property name="xpad">0</property>
+		      <property name="ypad">0</property>
+		      <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+		      <property name="width_chars">-1</property>
+		      <property name="single_line_mode">False</property>
+		      <property name="angle">0</property>
+		    </widget>
+		    <packing>
+		      <property name="padding">0</property>
+		      <property name="expand">False</property>
+		      <property name="fill">False</property>
+		    </packing>
+		  </child>
+		</widget>
+	      </child>
+	    </widget>
+	  </child>
+	</widget>
+	<packing>
+	  <property name="x">160</property>
+	  <property name="y">16</property>
+	</packing>
+      </child>
+    </widget>
+  </child>
+</widget>
+
+</glade-interface>
diff --git a/src/sbbs3/gtkchat/gtk-chat.gladep b/src/sbbs3/gtkchat/gtk-chat.gladep
new file mode 100644
index 0000000000000000000000000000000000000000..3d6dc000aa11c73fa8a6dfddf2edd25d450ebff1
--- /dev/null
+++ b/src/sbbs3/gtkchat/gtk-chat.gladep
@@ -0,0 +1,10 @@
+<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
+<!DOCTYPE glade-project SYSTEM "http://glade.gnome.org/glade-project-2.0.dtd">
+
+<glade-project>
+  <name>Synchronet Sysop Chat</name>
+  <program_name>gtk-chat</program_name>
+  <source_directory></source_directory>
+  <gnome_support>FALSE</gnome_support>
+  <gettext_support>FALSE</gettext_support>
+</glade-project>
diff --git a/src/sbbs3/gtkchat/interface.c b/src/sbbs3/gtkchat/interface.c
new file mode 100644
index 0000000000000000000000000000000000000000..99e4938c8cdc7f0cc11672739cf24bd8d24231d4
--- /dev/null
+++ b/src/sbbs3/gtkchat/interface.c
@@ -0,0 +1,142 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+
+#include <gdk/gdkkeysyms.h>
+#include <gtk/gtk.h>
+
+#include "callbacks.h"
+#include "interface.h"
+#include "support.h"
+
+#define GLADE_HOOKUP_OBJECT(component,widget,name) \
+  g_object_set_data_full (G_OBJECT (component), name, \
+    gtk_widget_ref (widget), (GDestroyNotify) gtk_widget_unref)
+
+#define GLADE_HOOKUP_OBJECT_NO_REF(component,widget,name) \
+  g_object_set_data (G_OBJECT (component), name, widget)
+
+GtkWidget *MessageLabel;
+
+GtkWidget*
+create_MainWindow (void)
+{
+  GtkWidget *MainWindow;
+  GtkWidget *SplitPane;
+  GtkWidget *RemoteText;
+  GtkWidget *LocalText;
+  GtkTooltips *tooltips;
+
+  tooltips = gtk_tooltips_new ();
+
+  MainWindow = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+  gtk_widget_set_size_request (MainWindow, 400, 300);
+  gtk_window_set_title (GTK_WINDOW (MainWindow), "Synchronet Sysop Chat");
+  gtk_window_set_icon_name (GTK_WINDOW (MainWindow), "stock_help-chat");
+  gtk_window_set_type_hint (GTK_WINDOW (MainWindow), GDK_WINDOW_TYPE_HINT_UTILITY);
+
+  SplitPane = gtk_vpaned_new ();
+  gtk_widget_show (SplitPane);
+  gtk_container_add (GTK_CONTAINER (MainWindow), SplitPane);
+  gtk_paned_set_position (GTK_PANED (SplitPane), 148);
+
+  RemoteText = gtk_text_view_new ();
+  gtk_widget_show (RemoteText);
+  gtk_paned_pack1 (GTK_PANED (SplitPane), RemoteText, FALSE, TRUE);
+  GTK_WIDGET_UNSET_FLAGS (RemoteText, GTK_CAN_FOCUS);
+  gtk_tooltips_set_tip (tooltips, RemoteText, "Remote Text Window", NULL);
+  gtk_text_view_set_editable (GTK_TEXT_VIEW (RemoteText), FALSE);
+  gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (RemoteText), GTK_WRAP_WORD);
+
+  LocalText = gtk_text_view_new ();
+  gtk_widget_show (LocalText);
+  gtk_paned_pack2 (GTK_PANED (SplitPane), LocalText, TRUE, TRUE);
+  gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (LocalText), GTK_WRAP_WORD);
+  gtk_text_view_set_editable (GTK_TEXT_VIEW (LocalText), FALSE);
+
+  g_signal_connect ((gpointer) MainWindow, "destroy",
+                    G_CALLBACK (on_MainWindow_destroy),
+                    NULL);
+  g_signal_connect ((gpointer) LocalText, "key_press_event",
+                    G_CALLBACK (on_LocalText_key_press_event),
+                    NULL);
+
+  /* Store pointers to all widgets, for use by lookup_widget(). */
+  GLADE_HOOKUP_OBJECT_NO_REF (MainWindow, MainWindow, "MainWindow");
+  GLADE_HOOKUP_OBJECT (MainWindow, SplitPane, "SplitPane");
+  GLADE_HOOKUP_OBJECT (MainWindow, RemoteText, "RemoteText");
+  GLADE_HOOKUP_OBJECT (MainWindow, LocalText, "LocalText");
+  GLADE_HOOKUP_OBJECT_NO_REF (MainWindow, tooltips, "tooltips");
+
+  gtk_widget_grab_focus (LocalText);
+
+  gtk_timeout_add(50, get_from_remote, RemoteText);
+
+  return MainWindow;
+}
+
+GtkWidget*
+create_WaitWindow (void)
+{
+  GtkWidget *WaitWindow;
+  GtkWidget *fixed1;
+  GtkWidget *CancelButton;
+  GtkWidget *alignment1;
+  GtkWidget *hbox1;
+  GtkWidget *image1;
+  GtkWidget *label2;
+
+  WaitWindow = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+  gtk_window_set_title (GTK_WINDOW (WaitWindow), "Waiting for user");
+
+  fixed1 = gtk_fixed_new ();
+  gtk_widget_show (fixed1);
+  gtk_container_add (GTK_CONTAINER (WaitWindow), fixed1);
+  gtk_widget_set_size_request (fixed1, 400, 40);
+
+  MessageLabel = gtk_label_new ("Waiting for user to connect.");
+  gtk_widget_show (MessageLabel);
+  gtk_fixed_put (GTK_FIXED (fixed1), MessageLabel, 0, 0);
+  gtk_widget_set_size_request (MessageLabel, 400, 16);
+
+  CancelButton = gtk_button_new ();
+  gtk_widget_show (CancelButton);
+  gtk_fixed_put (GTK_FIXED (fixed1), CancelButton, 160, 16);
+  gtk_widget_set_size_request (CancelButton, 88, 24);
+
+  alignment1 = gtk_alignment_new (0.5, 0.5, 0, 0);
+  gtk_widget_show (alignment1);
+  gtk_container_add (GTK_CONTAINER (CancelButton), alignment1);
+
+  hbox1 = gtk_hbox_new (FALSE, 2);
+  gtk_widget_show (hbox1);
+  gtk_container_add (GTK_CONTAINER (alignment1), hbox1);
+
+  image1 = gtk_image_new_from_stock ("gtk-cancel", GTK_ICON_SIZE_BUTTON);
+  gtk_widget_show (image1);
+  gtk_box_pack_start (GTK_BOX (hbox1), image1, FALSE, FALSE, 0);
+
+  label2 = gtk_label_new_with_mnemonic ("Cancel");
+  gtk_widget_show (label2);
+  gtk_box_pack_start (GTK_BOX (hbox1), label2, FALSE, FALSE, 0);
+
+  g_signal_connect ((gpointer) CancelButton, "clicked",
+                    G_CALLBACK (on_CancelButton_clicked),
+                    NULL);
+
+  /* Store pointers to all widgets, for use by lookup_widget(). */
+  GLADE_HOOKUP_OBJECT_NO_REF (WaitWindow, WaitWindow, "WaitWindow");
+  GLADE_HOOKUP_OBJECT (WaitWindow, fixed1, "fixed1");
+  GLADE_HOOKUP_OBJECT (WaitWindow, MessageLabel, "MessageLabel");
+  GLADE_HOOKUP_OBJECT (WaitWindow, CancelButton, "CancelButton");
+  GLADE_HOOKUP_OBJECT (WaitWindow, alignment1, "alignment1");
+  GLADE_HOOKUP_OBJECT (WaitWindow, hbox1, "hbox1");
+  GLADE_HOOKUP_OBJECT (WaitWindow, image1, "image1");
+  GLADE_HOOKUP_OBJECT (WaitWindow, label2, "label2");
+
+  gtk_timeout_add(50, connect_wait, WaitWindow);
+  return WaitWindow;
+}
+
diff --git a/src/sbbs3/gtkchat/interface.h b/src/sbbs3/gtkchat/interface.h
new file mode 100644
index 0000000000000000000000000000000000000000..347c8407c3167833ba10e6cfd5c62122a1dc7a6b
--- /dev/null
+++ b/src/sbbs3/gtkchat/interface.h
@@ -0,0 +1,7 @@
+/*
+ * DO NOT EDIT THIS FILE - it is generated by Glade.
+ */
+
+extern GtkWidget *MessageLabel;
+GtkWidget* create_MainWindow (void);
+GtkWidget* create_WaitWindow (void);
diff --git a/src/sbbs3/gtkchat/main.c b/src/sbbs3/gtkchat/main.c
new file mode 100644
index 0000000000000000000000000000000000000000..d5a9b1ad4a2ea783f9b6077a54786c845979f8a1
--- /dev/null
+++ b/src/sbbs3/gtkchat/main.c
@@ -0,0 +1,56 @@
+#include <stdlib.h>
+
+#include <gtk/gtk.h>
+
+#include "dirwrap.h"
+
+#include "chatfuncs.h"
+#include "interface.h"
+#include "support.h"
+
+int lprintf(int level, char *fmt, ...)
+{
+	/* ToDo: Log output */
+	return(0);
+}
+
+
+int
+main (int argc, char *argv[])
+{
+  int		node;
+  char		*ctrl_dir;
+  GtkWidget *WaitWindow;
+
+  gtk_set_locale ();
+  gtk_init (&argc, &argv);
+
+/*  add_pixmap_directory (PACKAGE_DATA_DIR "/" PACKAGE "/pixmaps"); */
+
+  ctrl_dir=getenv("SBBSCTRL");
+  if(ctrl_dir==NULL)
+  	return(1);
+
+  if(argc<1)
+    return(1);
+
+  node=atoi(argv[1]);
+
+  if(node<1)
+    return(1);
+
+  /*
+   * Request a chat
+   */
+  if(chat_open(node, ctrl_dir))
+  	return(1);
+
+  /*
+   * Show "waiting" window
+   */
+  WaitWindow = create_WaitWindow ();
+  gtk_widget_show (WaitWindow);
+
+  gtk_main ();
+  return 0;
+}
diff --git a/src/sbbs3/gtkchat/objects.mk b/src/sbbs3/gtkchat/objects.mk
new file mode 100644
index 0000000000000000000000000000000000000000..67dc2399faaa840a6e04c0256161dcc4963fb134
--- /dev/null
+++ b/src/sbbs3/gtkchat/objects.mk
@@ -0,0 +1,21 @@
+OBJS	:=	\
+			$(MTOBJODIR)$(DIRSEP)ars$(OFILE) \
+			$(MTOBJODIR)$(DIRSEP)dat_rec$(OFILE) \
+			$(MTOBJODIR)$(DIRSEP)date_str$(OFILE) \
+			$(MTOBJODIR)$(DIRSEP)load_cfg$(OFILE) \
+			$(MTOBJODIR)$(DIRSEP)nopen$(OFILE) \
+			$(MTOBJODIR)$(DIRSEP)scfglib1$(OFILE) \
+			$(MTOBJODIR)$(DIRSEP)scfglib2$(OFILE) \
+			$(MTOBJODIR)$(DIRSEP)userdat$(OFILE) \
+			$(MTOBJODIR)$(DIRSEP)str_util$(OFILE) \
+			$(MTOBJODIR)$(DIRSEP)chatfuncs$(OFILE) \
+			$(MTOBJODIR)$(DIRSEP)interface$(OFILE) \
+			$(MTOBJODIR)$(DIRSEP)support$(OFILE) \
+			$(MTOBJODIR)$(DIRSEP)callbacks$(OFILE) \
+			$(MTOBJODIR)$(DIRSEP)main$(OFILE) \
+
+#			$(MTOBJODIR)$(DIRSEP)chat$(OFILE) \
+#			$(MTOBJODIR)$(DIRSEP)getmail$(OFILE) \
+#			$(MTOBJODIR)$(DIRSEP)getstats$(OFILE) \
+#			$(MTOBJODIR)$(DIRSEP)sbbs_ini$(OFILE) \
+
diff --git a/src/sbbs3/gtkchat/support.c b/src/sbbs3/gtkchat/support.c
new file mode 100644
index 0000000000000000000000000000000000000000..601777a1164102d853be394bc2515d57706667c7
--- /dev/null
+++ b/src/sbbs3/gtkchat/support.c
@@ -0,0 +1,136 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+
+#include <gtk/gtk.h>
+
+#include "support.h"
+
+GtkWidget*
+lookup_widget                          (GtkWidget       *widget,
+                                        const gchar     *widget_name)
+{
+  GtkWidget *parent, *found_widget;
+
+  for (;;)
+    {
+      if (GTK_IS_MENU (widget))
+        parent = gtk_menu_get_attach_widget (GTK_MENU (widget));
+      else
+        parent = widget->parent;
+      if (!parent)
+        parent = (GtkWidget*) g_object_get_data (G_OBJECT (widget), "GladeParentKey");
+      if (parent == NULL)
+        break;
+      widget = parent;
+    }
+
+  found_widget = (GtkWidget*) g_object_get_data (G_OBJECT (widget),
+                                                 widget_name);
+  if (!found_widget)
+    g_warning ("Widget not found: %s", widget_name);
+  return found_widget;
+}
+
+static GList *pixmaps_directories = NULL;
+
+/* Use this function to set the directory containing installed pixmaps. */
+void
+add_pixmap_directory                   (const gchar     *directory)
+{
+  pixmaps_directories = g_list_prepend (pixmaps_directories,
+                                        g_strdup (directory));
+}
+
+/* This is an internally used function to find pixmap files. */
+static gchar*
+find_pixmap_file                       (const gchar     *filename)
+{
+  GList *elem;
+
+  /* We step through each of the pixmaps directory to find it. */
+  elem = pixmaps_directories;
+  while (elem)
+    {
+      gchar *pathname = g_strdup_printf ("%s%s%s", (gchar*)elem->data,
+                                         G_DIR_SEPARATOR_S, filename);
+      if (g_file_test (pathname, G_FILE_TEST_EXISTS))
+        return pathname;
+      g_free (pathname);
+      elem = elem->next;
+    }
+  return NULL;
+}
+
+/* This is an internally used function to create pixmaps. */
+GtkWidget*
+create_pixmap                          (GtkWidget       *widget,
+                                        const gchar     *filename)
+{
+  gchar *pathname = NULL;
+  GtkWidget *pixmap;
+
+  if (!filename || !filename[0])
+      return gtk_image_new ();
+
+  pathname = find_pixmap_file (filename);
+
+  if (!pathname)
+    {
+      g_warning ("Couldn't find pixmap file: %s", filename);
+      return gtk_image_new ();
+    }
+
+  pixmap = gtk_image_new_from_file (pathname);
+  g_free (pathname);
+  return pixmap;
+}
+
+/* This is an internally used function to create pixmaps. */
+GdkPixbuf*
+create_pixbuf                          (const gchar     *filename)
+{
+  gchar *pathname = NULL;
+  GdkPixbuf *pixbuf;
+  GError *error = NULL;
+
+  if (!filename || !filename[0])
+      return NULL;
+
+  pathname = find_pixmap_file (filename);
+
+  if (!pathname)
+    {
+      g_warning ("Couldn't find pixmap file: %s", filename);
+      return NULL;
+    }
+
+  pixbuf = gdk_pixbuf_new_from_file (pathname, &error);
+  if (!pixbuf)
+    {
+      fprintf (stderr, "Failed to load pixbuf file: %s: %s\n",
+               pathname, error->message);
+      g_error_free (error);
+    }
+  g_free (pathname);
+  return pixbuf;
+}
+
+/* This is used to set ATK action descriptions. */
+void
+glade_set_atk_action_description       (AtkAction       *action,
+                                        const gchar     *action_name,
+                                        const gchar     *description)
+{
+  gint n_actions, i;
+
+  n_actions = atk_action_get_n_actions (action);
+  for (i = 0; i < n_actions; i++)
+    {
+      if (!strcmp (atk_action_get_name (action, i), action_name))
+        atk_action_set_description (action, i, description);
+    }
+}
+
diff --git a/src/sbbs3/gtkchat/support.h b/src/sbbs3/gtkchat/support.h
new file mode 100644
index 0000000000000000000000000000000000000000..5c1a63f922f0e24caf3e751ec7b891a4412596db
--- /dev/null
+++ b/src/sbbs3/gtkchat/support.h
@@ -0,0 +1,36 @@
+#include <gtk/gtk.h>
+
+/*
+ * Public Functions.
+ */
+
+/*
+ * This function returns a widget in a component created by Glade.
+ * Call it with the toplevel widget in the component (i.e. a window/dialog),
+ * or alternatively any widget in the component, and the name of the widget
+ * you want returned.
+ */
+GtkWidget*  lookup_widget              (GtkWidget       *widget,
+                                        const gchar     *widget_name);
+
+
+/* Use this function to set the directory containing installed pixmaps. */
+void        add_pixmap_directory       (const gchar     *directory);
+
+
+/*
+ * Private Functions.
+ */
+
+/* This is used to create the pixmaps used in the interface. */
+GtkWidget*  create_pixmap              (GtkWidget       *widget,
+                                        const gchar     *filename);
+
+/* This is used to create the pixbufs used in the interface. */
+GdkPixbuf*  create_pixbuf              (const gchar     *filename);
+
+/* This is used to set ATK action descriptions. */
+void        glade_set_atk_action_description (AtkAction       *action,
+                                              const gchar     *action_name,
+                                              const gchar     *description);
+
diff --git a/src/sbbs3/gtkchat/targets.mk b/src/sbbs3/gtkchat/targets.mk
new file mode 100644
index 0000000000000000000000000000000000000000..91ab785e46ff02f40b1e135101fcc260e94416cd
--- /dev/null
+++ b/src/sbbs3/gtkchat/targets.mk
@@ -0,0 +1,5 @@
+GTKCHAT	=	$(EXEODIR)$(DIRSEP)gtkchat$(EXEFILE)
+
+all: xpdev-mt smblib $(MTOBJODIR) $(EXEODIR) $(GTKCHAT)
+
+$(GTKCHAT):	$(XPDEV-MT_LIB) $(SMBLIB)