Re: irssi and dccserv

Athan <[email protected]> Thu, 13 Feb 2003 11:31:00 +0000
Newsgroups gmane.network.irc.irssi.devel
Message-ID <[email protected]>
On Thu, Feb 13, 2003 at 09:42:55AM +0000, Athanasius wrote:
>   I've only just gotten the DCC GET working yesterday and haven't had
> chance to test vanilla CVS to see if the same problem is there.  I'll
> try and get to that today and post a patch whatever by the end of the
> day.

  OK, it didn't like the changes in fe-common/irc/dcc/module-formats.h
I'd made but actually never used, looks to be working now.  Patch
attached.

  This defines 5 new variables:

11:19 [dcc]
11:19 dcc_server_port = 9008
11:19 dcc_server_send = ON
11:19 dcc_server_fserve = OFF
11:19 dcc_server_chat = ON
11:19 dcc_server = ON

dcc_server turns the whole thing on or off.
dcc_server_port sets the port you listen on locally.  Either change this
to suit the fserve you're using, or you can do as I do if you have root
and simply redirect requisite ip:port's to this port which also means I
can make use of servers using port 59, for example.
dcc_server_chat controls whether or not the client accepts chats done
this way.
dcc_server_send controls whether or not the client accepts file
transfers done this way.
dcc_server_fserve is there but currently unsupported (I've not seen
anything using it).

As for the patch itself, as I said before it's HACKY.  Anyone more
familiar with the irssi DCC code can feel free to rework it to be less
hacky.  Possibly re-working all of the DCC CHAT/SEND to split into parts
that accept connection and then another that finishes setting things up
once you have the open connection.
  src/irc/dcc/dcc-server.c is the heart of things (and the mess ;), with
a few changes to other files in that directory, plus core/network.c to
support the changes.  Normal DCC CHAT/GEt should be entirely unaffected
by these changes.
  It's POSSIBLE I've still got a race condition or two in there between
reading a command on the initial TCP connection and converting it into a
chat/get, but I think I finally got that sorted out.
  dcc_server_nick_resolve() is a horrible hack to match up an incoming
connection to a server:nick we can currently see.
  convert_dcc_server_to_chat() and convert_dcc_server_to_send() are the
bits that munge the extant TCP connection into a DCC CHAT or GET as
required.

  I've actually only tested this locally so far, using mirc/sysreset
under wine and a local ircd, so if there are any race conditions or bugs
lurking in there using it on a full network will probably show them up.

-Ath
-- 
- Athanasius = Athanasius(at)miggy.org / http://www.miggy.org/
                  Finger athan(at)fysh.org for PGP key
	   "And it's me who is my enemy. Me who beats me up.
Me who makes the monsters. Me who strips my confidence." Paula Cole - ME
irssi-dccserver.diff (text/plain, 23.2 KB)
diff -r -u -N --exclude-from=irssi-dontdiff irssi-CVS-200302131108/src/core/network.c irssi-CVS/src/core/network.c
--- irssi-CVS-200302131108/src/core/network.c	Tue Dec 10 16:36:26 2002
+++ irssi-CVS/src/core/network.c	Thu Feb 13 10:28:42 2003
@@ -410,6 +410,26 @@
 	return 0;
 }
 
+/* Get socket peer address/port */
+int net_getpeername(GIOChannel *handle, IPADDR *addr, int *port)
+{
+	union sockaddr_union so;
+	socklen_t addrlen;
+
+	g_return_val_if_fail(handle != NULL, -1);
+	g_return_val_if_fail(addr != NULL, -1);
+
+	addrlen = sizeof(so);
+	if (getpeername(g_io_channel_unix_get_fd(handle),
+			(struct sockaddr *) &so, &addrlen) == -1)
+		return -1;
+
+        sin_get_ip(&so, addr);
+	if (port) *port = sin_get_port(&so);
+
+	return 0;
+}
+
 /* Get IP addresses for host, both IPv4 and IPv6 if possible.
    If ip->family is 0, the address wasn't found.
    Returns 0 = ok, others = error code for net_gethosterror() */
diff -r -u -N --exclude-from=irssi-dontdiff irssi-CVS-200302131108/src/irc/dcc/Makefile irssi-CVS/src/irc/dcc/Makefile
--- irssi-CVS-200302131108/src/irc/dcc/Makefile	Thu Feb 13 11:10:43 2003
+++ irssi-CVS/src/irc/dcc/Makefile	Thu Feb 13 10:55:15 2003
@@ -116,7 +116,7 @@
 INCLUDES =  	-I$(top_srcdir)/src 	-I$(top_srcdir)/src/core/ 	-I$(top_srcdir)/src/irc/core/ 	$(GLIB_CFLAGS)
 
 
-libirc_dcc_a_SOURCES =  	dcc.c 	dcc-chat.c 	dcc-get.c 	dcc-send.c 	dcc-resume.c 	dcc-autoget.c 	dcc-queue.c
+libirc_dcc_a_SOURCES =  	dcc.c 	dcc-chat.c 	dcc-get.c 	dcc-send.c 	dcc-resume.c 	dcc-autoget.c 	dcc-queue.c 	dcc-server.c
 
 
 noinst_HEADERS =  	dcc-rec.h 	dcc-file-rec.h 	dcc.h 	dcc-file.h 	dcc-chat.h 	dcc-get.h 	dcc-send.h 	dcc-queue.h 	module.h
@@ -134,7 +134,7 @@
 libirc_dcc_a_LIBADD = 
 libirc_dcc_a_OBJECTS =  dcc.$(OBJEXT) dcc-chat.$(OBJEXT) \
 dcc-get.$(OBJEXT) dcc-send.$(OBJEXT) dcc-resume.$(OBJEXT) \
-dcc-autoget.$(OBJEXT) dcc-queue.$(OBJEXT)
+dcc-autoget.$(OBJEXT) dcc-queue.$(OBJEXT) dcc-server.$(OBJEXT)
 AR = ar
 CFLAGS =  
 COMPILE = $(CC) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
@@ -151,7 +151,8 @@
 TAR = tar
 GZIP_ENV = --best
 DEP_FILES =  .deps/dcc-autoget.P .deps/dcc-chat.P .deps/dcc-get.P \
-.deps/dcc-queue.P .deps/dcc-resume.P .deps/dcc-send.P .deps/dcc.P
+.deps/dcc-queue.P .deps/dcc-resume.P .deps/dcc-send.P \
+.deps/dcc-server.P .deps/dcc.P
 SOURCES = $(libirc_dcc_a_SOURCES)
 OBJECTS = $(libirc_dcc_a_OBJECTS)
 
diff -r -u -N --exclude-from=irssi-dontdiff irssi-CVS-200302131108/src/irc/dcc/Makefile.am irssi-CVS/src/irc/dcc/Makefile.am
--- irssi-CVS-200302131108/src/irc/dcc/Makefile.am	Mon Nov 11 06:35:12 2002
+++ irssi-CVS/src/irc/dcc/Makefile.am	Thu Feb 13 10:28:42 2003
@@ -13,7 +13,8 @@
 	dcc-send.c \
 	dcc-resume.c \
 	dcc-autoget.c \
-	dcc-queue.c
+	dcc-queue.c \
+	dcc-server.c
 
 noinst_HEADERS = \
 	dcc-rec.h \
diff -r -u -N --exclude-from=irssi-dontdiff irssi-CVS-200302131108/src/irc/dcc/Makefile.in irssi-CVS/src/irc/dcc/Makefile.in
--- irssi-CVS-200302131108/src/irc/dcc/Makefile.in	Thu Feb 13 11:10:38 2003
+++ irssi-CVS/src/irc/dcc/Makefile.in	Thu Feb 13 10:28:42 2003
@@ -116,7 +116,7 @@
 INCLUDES =  	-I$(top_srcdir)/src 	-I$(top_srcdir)/src/core/ 	-I$(top_srcdir)/src/irc/core/ 	$(GLIB_CFLAGS)
 
 
-libirc_dcc_a_SOURCES =  	dcc.c 	dcc-chat.c 	dcc-get.c 	dcc-send.c 	dcc-resume.c 	dcc-autoget.c 	dcc-queue.c
+libirc_dcc_a_SOURCES =  	dcc.c 	dcc-chat.c 	dcc-get.c 	dcc-send.c 	dcc-resume.c 	dcc-autoget.c 	dcc-queue.c 	dcc-server.c
 
 
 noinst_HEADERS =  	dcc-rec.h 	dcc-file-rec.h 	dcc.h 	dcc-file.h 	dcc-chat.h 	dcc-get.h 	dcc-send.h 	dcc-queue.h 	module.h
@@ -134,7 +134,7 @@
 libirc_dcc_a_LIBADD = 
 libirc_dcc_a_OBJECTS =  dcc.$(OBJEXT) dcc-chat.$(OBJEXT) \
 dcc-get.$(OBJEXT) dcc-send.$(OBJEXT) dcc-resume.$(OBJEXT) \
-dcc-autoget.$(OBJEXT) dcc-queue.$(OBJEXT)
+dcc-autoget.$(OBJEXT) dcc-queue.$(OBJEXT) dcc-server.$(OBJEXT)
 AR = ar
 CFLAGS = @CFLAGS@
 COMPILE = $(CC) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
@@ -151,7 +151,8 @@
 TAR = tar
 GZIP_ENV = --best
 DEP_FILES =  .deps/dcc-autoget.P .deps/dcc-chat.P .deps/dcc-get.P \
-.deps/dcc-queue.P .deps/dcc-resume.P .deps/dcc-send.P .deps/dcc.P
+.deps/dcc-queue.P .deps/dcc-resume.P .deps/dcc-send.P \
+.deps/dcc-server.P .deps/dcc.P
 SOURCES = $(libirc_dcc_a_SOURCES)
 OBJECTS = $(libirc_dcc_a_OBJECTS)
 
diff -r -u -N --exclude-from=irssi-dontdiff irssi-CVS-200302131108/src/irc/dcc/dcc-chat.c irssi-CVS/src/irc/dcc/dcc-chat.c
--- irssi-CVS-200302131108/src/irc/dcc/dcc-chat.c	Sun May 26 18:39:35 2002
+++ irssi-CVS/src/irc/dcc/dcc-chat.c	Thu Feb 13 10:28:42 2003
@@ -34,7 +34,7 @@
 
 #include "dcc-chat.h"
 
-static char *dcc_chat_get_new_id(const char *nick)
+char *dcc_chat_get_new_id(const char *nick)
 {
         char *id;
 	int num;
@@ -285,7 +285,7 @@
 }
 
 /* input function: DCC CHAT received some data.. */
-static void dcc_chat_input(CHAT_DCC_REC *dcc)
+void dcc_chat_input(CHAT_DCC_REC *dcc)
 {
         char tmpbuf[512], *str;
 	int recvlen, ret;
diff -r -u -N --exclude-from=irssi-dontdiff irssi-CVS-200302131108/src/irc/dcc/dcc-get.c irssi-CVS/src/irc/dcc/dcc-get.c
--- irssi-CVS-200302131108/src/irc/dcc/dcc-get.c	Thu Nov 21 17:48:40 2002
+++ irssi-CVS/src/irc/dcc/dcc-get.c	Thu Feb 13 10:28:42 2003
@@ -172,7 +172,7 @@
 }
 
 /* callback: net_connect() finished for DCC GET */
-static void sig_dccget_connected(GET_DCC_REC *dcc)
+void sig_dccget_connected(GET_DCC_REC *dcc)
 {
 	struct stat statbuf;
 	char *fname, *tempfname;
diff -r -u -N --exclude-from=irssi-dontdiff irssi-CVS-200302131108/src/irc/dcc/dcc-resume.c irssi-CVS/src/irc/dcc/dcc-resume.c
--- irssi-CVS-200302131108/src/irc/dcc/dcc-resume.c	Thu Nov 21 17:48:40 2002
+++ irssi-CVS/src/irc/dcc/dcc-resume.c	Thu Feb 13 10:28:42 2003
@@ -123,7 +123,7 @@
 }
 
 /* Resume a DCC GET */
-static void dcc_send_resume(GET_DCC_REC *dcc)
+void dcc_send_resume(GET_DCC_REC *dcc)
 {
         off_t pos;
 	char *str;
diff -r -u -N --exclude-from=irssi-dontdiff irssi-CVS-200302131108/src/irc/dcc/dcc-server.c irssi-CVS/src/irc/dcc/dcc-server.c
--- irssi-CVS-200302131108/src/irc/dcc/dcc-server.c	Thu Jan  1 01:00:00 1970
+++ irssi-CVS/src/irc/dcc/dcc-server.c	Thu Feb 13 10:13:42 2003
@@ -0,0 +1,536 @@
+/*
+ dcc-server.c : irssi
+
+    Copyright (C) 1999-2001 Timo Sirainen
+
+    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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include <ctype.h>
+
+#include "module.h"
+#include "signals.h"
+#include "commands.h"
+#include "network.h"
+#include "net-nonblock.h"
+#include "net-sendbuffer.h"
+#include "line-split.h"
+#include "misc.h"
+#include "settings.h"
+
+#include "irc-servers.h"
+#include "irc-queries.h"
+#include "masks.h"
+#include "nicklist.h"
+
+#include "dcc-server.h"
+#include "dcc-chat.h"
+#include "dcc-get.h"
+
+/* extern functions */
+extern char *dcc_chat_get_new_id(const char *nick);
+extern void dcc_chat_input(CHAT_DCC_REC *dcc);
+extern void sig_dccget_connected(GET_DCC_REC *dcc);
+
+/* Prototype local functions */
+
+void dcc_server_stop(void);
+static void sig_dcc_server_listen(void *data);
+static void dcc_server_input(DCC_SERVER_REC *rec);
+void dcc_server_destroy(DCC_SERVER_REC *rec);
+void convert_dcc_server_to_chat(DCC_SERVER_REC *rec);
+void convert_dcc_server_to_fserve(DCC_SERVER_REC *rec);
+void convert_dcc_server_to_send(DCC_SERVER_REC *rec, char *filesize,
+                                char *filename);
+
+static int dcc_server;
+static u_int16_t dcc_server_port;
+static int dcc_server_chat;
+static int dcc_server_fserve;
+static int dcc_server_send;
+static int running = FALSE;
+static u_int16_t running_port = 0;
+GIOChannel *dcc_server_listen_handle = NULL;
+int dcc_server_listen_tag;
+
+void dcc_server_start(void)
+{
+	int port;
+
+	if (running)
+	{
+	/* Already running, might be able to leave it as-is, may need
+	 * to make changes
+	 */
+		if (running_port != dcc_server_port)
+		{
+		/* Need to restart on new port */
+			dcc_server_stop();
+		} else
+		{
+			return;
+		}
+	}
+
+	running = FALSE;
+	if (dcc_server_port < IPPORT_RESERVED)
+	{
+		g_warning("dcc_server_port must be at least %d\n", IPPORT_RESERVED);
+		return;
+	}
+	port = dcc_server_port;
+	dcc_server_listen_handle = net_listen(NULL, &port);
+	if (dcc_server_listen_handle == NULL)
+	{
+		g_warning("Couldn't start listening dcc server\n");
+		return;
+	}
+
+	running = TRUE;
+	running_port = dcc_server_port;
+	dcc_server_listen_tag = g_input_add(dcc_server_listen_handle,
+	                         G_INPUT_READ,
+	                         (GInputFunction) sig_dcc_server_listen, NULL);
+}
+
+void dcc_server_stop(void)
+{
+	if (!running)
+	{
+		return;
+	}
+
+	net_disconnect(dcc_server_listen_handle);
+	running = FALSE;
+	running_port = 0;
+}
+
+static void sig_dcc_server_listen(void *data)
+{
+	DCC_SERVER_REC *rec;
+	IPADDR ip;
+	GIOChannel *handle;
+	int ret;
+
+	handle = net_accept(dcc_server_listen_handle, &ip, NULL);
+	if (handle == NULL)
+	{
+		return;
+	}
+
+	rec = g_new0(DCC_SERVER_REC, 1);
+	rec->handle = handle;
+	rec->created = time(NULL);
+	rec->addr = ip;
+	net_ip2host(&rec->addr, rec->ipaddrstr);
+	if ((ret = net_gethostbyaddr(&rec->addr, &rec->addrstr)))
+	{
+		g_error("DCC Server: accepting connection, error - %s\n", net_gethosterror(ret));
+	}
+	net_getsockname(handle, &rec->addr, &rec->port);
+	rec->sendbuf = net_sendbuffer_create(handle, 0);
+	rec->tagread = g_input_add(handle, G_INPUT_READ,
+	                           (GInputFunction) dcc_server_input, rec);
+
+	signal_emit("dcc server connected", 1, rec);
+}
+
+static void dcc_server_input(DCC_SERVER_REC *rec)
+{
+	char tmpbuf[1024], *str;
+	int ret, recvlen;
+
+	recvlen = rec->handle == NULL ? -1 :
+		net_receive(rec->handle, tmpbuf, sizeof(tmpbuf));
+	if (recvlen == -1)
+	{
+		return;
+	}
+	ret = line_split(tmpbuf, recvlen, &str, &rec->readbuf);
+
+	if (ret != 0)
+	{
+		if (ret == -1)
+		{
+			/* connection lost */
+			dcc_server_destroy(rec);
+		} else
+		{
+			signal_emit("dcc server event", 2, rec, str);
+			memset(tmpbuf, 0, 1024);
+		}
+	}
+}
+
+void dcc_server_destroy(DCC_SERVER_REC *rec)
+{
+	line_split_free(rec->readbuf);
+	net_sendbuffer_destroy(rec->sendbuf, 0);
+	net_disconnect(rec->handle);
+}
+
+extern GSList *servers;
+
+int dcc_server_nick_resolve(DCC_SERVER_REC *rec, char *nick)
+{
+	GSList *s, *nicklist;
+	char *h;
+
+	g_return_val_if_fail(rec != NULL, FALSE);
+	rec->nickname = g_strdup(nick); /* some limit on nick length */
+	rec->id = g_strdup(nick);
+
+	/* Walk the list of servers */
+	for (s = servers ; s != NULL ; s = s->next)
+	{
+		/* Lookup rec->nick on this server */
+		SERVER_REC *server = s->data;
+		if (NULL != (nicklist = nicklist_get_same(server, rec->nickname)))
+		{
+			GSList *n;
+			NICK_REC *nick;
+			for (n = nicklist ; n != NULL ; n = n->next)
+			{
+			/* Does the nick's host match what's in rec->addr */
+				nick = n->data;
+				if (NULL == (h = strchr(nick->host, '@')))
+				{
+					continue;
+				}
+				h++;
+				if (strstr(rec->addrstr, h)
+				    || strstr(rec->ipaddrstr, h))
+				{
+					rec->server = IRC_SERVER(server);
+					rec->nick = nick;
+					return TRUE;
+				}
+			}
+		}
+	}
+
+	return FALSE;
+}
+
+/* The heart of the /dccserver protocol handling:
+ *
+ * Chat Protocol
+ * Client connects to Server and sends:
+ * 100 <client nickname>
+ * When Server receives this, it sends:
+ * 101 <server nickname>
+ * Connection is established, users can now chat.
+ *
+ * Fserve Protocol
+ * Client connects to Server and sends:
+ * 110 <client nickname>
+ * When Server receives this, it sends:
+ * 111 <server nickname>
+ * Connection is established, user can now access fserve.
+ *
+ * Send Protocol
+ * Client connects to Server and sends:
+ * 120 <client nickname> <file size> <filename>
+ * When Server receives this, it sends:
+ * 121 <server nickname> <resume position>
+ *
+ * Where resume position is between 0 and file size, and is required.
+ * Connection is established, and Server dcc gets the file.
+ *
+ * Get Protocol
+ * Client connects to Server and sends:
+ * 130 <client nickname> <filename>
+ * When Server receives this, it sends:
+ * 131 <server nickname> <file size>
+ * When Client receives this, it sends:
+ * 132 <client nickname> <resume position>
+ * Where resume position is between 0 and file size, and is required.
+ * Connection is established, and Server dcc sends the file.
+ *
+ * Other
+ * If server receives unexpected information, or doesn't receive info 15
+ * seconds after initial connection, it closes the connection.
+ *
+ * If service is unavailable, server sends:
+ * 150 unavailable
+ *
+ * If server rejects connection, it sends:
+ * 151 rejected 
+ *
+ * Note the 'str' passed here has been through line_split() which strips
+ *  any \n on the end, so we don't have to worry about that here.
+ */
+static void dcc_server_event(DCC_SERVER_REC *rec, char *str)
+{
+	char reply[1024];
+
+	if (!isdigit(str[0]))
+	{
+	/* Messages start with a numeric */
+		dcc_server_destroy(rec);
+		return;
+	}
+	if (!strncmp(str, "100 ", 4))
+	{
+	/* Chat request */
+		if (!settings_get_bool("dcc_server_chat"))
+		{
+		/* We're not accepting CHATs this way */
+			strncpy(reply, "150 unavailable\n", sizeof(reply) - 1);
+			net_transmit(rec->handle, reply, strlen(reply));
+			dcc_server_destroy(rec);
+			return;
+		}
+		str += 4;	/* Now pointing to the nick they claim */
+		if (!dcc_server_nick_resolve(rec, str))
+		{
+			strncpy(reply, "151 rejected\n", sizeof(reply) - 1);
+			net_transmit(rec->handle, reply, strlen(reply));
+			dcc_server_destroy(rec);
+			return;
+		}
+		convert_dcc_server_to_chat(rec);
+	} else if (!strncmp(str, "110 ", 4))
+	{
+	/* Fserve request */
+		/* We don't support this */
+		strncpy(reply, "150 unavailable\n", sizeof(reply) - 1);
+		net_transmit(rec->handle, reply, strlen(reply));
+		dcc_server_destroy(rec);
+		return;
+#if 0
+		if (!settings_get_bool("dcc_server_fserve"))
+		{
+		/* We're not accepting CHATs this way */
+			strncpy(reply, "150 unavailable\n", sizeof(reply) - 1);
+			net_transmit(rec->handle, reply, strlen(reply));
+			dcc_server_destroy(rec);
+			return;
+		}
+		str += 4;
+		convert_dcc_server_to_fserve(rec);
+#endif
+	} else if (!strncmp(str, "120 ", 4))
+	{
+	/* Send request */
+		char *nick;
+		char *filesize;
+		char *filename;
+		if (!settings_get_bool("dcc_server_send"))
+		{
+		/* We're not accepting SENDs this way */
+			strncpy(reply, "150 unavailable\n", sizeof(reply) - 1);
+			net_transmit(rec->handle, reply, strlen(reply));
+			dcc_server_destroy(rec);
+			return;
+		}
+		str += 4;	/* Now pointing to the nick they claim */
+		/* 120 <client nickname> <file size> <filename> */
+		nick = str;
+		if (NULL == (filesize = strchr(nick, ' ')))
+		{
+			strncpy(reply, "151 rejected\n", sizeof(reply) - 1);
+			net_transmit(rec->handle, reply, strlen(reply));
+			dcc_server_destroy(rec);
+			return;
+		}
+		*filesize++ = '\0';
+		if (NULL == (filename = strchr(filesize, ' ')))
+		{
+			strncpy(reply, "151 rejected\n", sizeof(reply) - 1);
+			net_transmit(rec->handle, reply, strlen(reply));
+			dcc_server_destroy(rec);
+			return;
+		}
+		*filename++ = '\0';
+		if (!dcc_server_nick_resolve(rec, nick))
+		{
+			strncpy(reply, "151 rejected\n", sizeof(reply) - 1);
+			net_transmit(rec->handle, reply, strlen(reply));
+			dcc_server_destroy(rec);
+			return;
+		}
+		convert_dcc_server_to_send(rec, filesize, filename);
+	} else if (!strncmp(str, "130 ", 4))
+	{
+	/* Get request */
+		/* We don't support this */
+		strncpy(reply, "150 unavailable\n", sizeof(reply) - 1);
+		net_transmit(rec->handle, reply, strlen(reply));
+		dcc_server_destroy(rec);
+		return;
+		/*
+		str += 4;
+		convert_dcc_server_to_get(rec);
+		*/
+	} else
+	{
+	/* Unknown message */
+		strncpy(reply, "150 unavailable\n", sizeof(reply) - 1);
+		net_transmit(rec->handle, reply, strlen(reply));
+		dcc_server_destroy(rec);
+	}
+}
+
+/* The magical morphing functions, turn our DCC_SERVER_REC into
+ * one of the 'real' DCC types, without disconnecting or starting from
+ * scratch
+ */
+void convert_dcc_server_to_chat(DCC_SERVER_REC *rec)
+{
+	char reply[1024];
+	CHAT_DCC_REC *dcc;
+
+	dcc = g_new0(CHAT_DCC_REC, 1);
+	dcc->orig_type = dcc->type = DCC_CHAT_TYPE;
+	dcc->mirc_ctcp = settings_get_bool("dcc_mirc_ctcp");
+	dcc->id = dcc_chat_get_new_id(rec->nickname);
+
+	dcc_init_rec(DCC(dcc), rec->server, NULL, rec->nickname, "chat");
+
+	dcc->starttime = time(NULL);
+	dcc->handle = rec->handle;
+	dcc->sendbuf = net_sendbuffer_create(dcc->handle, 0);
+	memcpy(&dcc->addr, &rec->addr, sizeof(IPADDR));
+	net_ip2host(&dcc->addr, dcc->addrstr);
+	dcc->port = settings_get_int("dcc_server_port");
+	dcc->tagread = g_input_add(dcc->handle, G_INPUT_READ,
+	                           (GInputFunction) dcc_chat_input, dcc);
+	signal_emit("dcc connected", 1, dcc);
+
+	/* Done with the DCC_SERVER_REC *rec now */
+	g_source_remove(rec->tagread);
+	g_source_remove_by_user_data(rec);
+	line_split_free(rec->readbuf);
+	rec->readbuf = NULL;
+	net_sendbuffer_destroy(rec->sendbuf, 0);
+	rec->sendbuf = NULL;
+	g_free(rec);
+
+	/* Safe to send our response */
+	g_snprintf(reply, sizeof(reply), "101 %s\n", dcc->server->nick);
+	net_transmit(dcc->handle, reply, strlen(reply));
+}
+
+void convert_dcc_server_to_fserve(DCC_SERVER_REC *rec)
+{
+}
+
+void convert_dcc_server_to_send(DCC_SERVER_REC *rec, char *filesize, char *filename)
+{
+	char reply[1024];
+	GET_DCC_REC *dcc;
+	IPADDR ipaddr;
+	size_t fsize = atoi(filesize);
+	int len, quoted = FALSE;
+
+	len = strlen(filename);
+	if (len > 1 && *filename == '"' && filename[len-1] == '"') {
+		/* "file name" - MIRC sends filenames with spaces like
+		 * this */
+		filename[len-1] = '\0';
+		g_memmove(filename, filename+1, len);
+		quoted = TRUE;
+	}
+	/* dcc_get_create() equivalent BEGIN */
+	dcc = g_new0(GET_DCC_REC, 1);
+	dcc->orig_type = module_get_uniq_id_str("DCC", "SEND");
+	dcc->type = module_get_uniq_id_str("DCC", "GET");
+	dcc->fhandle = -1;
+
+	dcc_init_rec(DCC(dcc), rec->server, NULL, rec->nickname, filename);
+
+	dcc->starttime = time(NULL);
+	dcc->handle = rec->handle;
+	/* dcc_get_create() equivalent END */
+
+	dcc->target = g_strdup(rec->nickname);
+	memcpy(&dcc->addr, &rec->addr, sizeof(IPADDR));
+	if (dcc->addr.family == AF_INET)
+	{
+		net_ip2host(&dcc->addr, dcc->addrstr);
+	} 
+#if 0
+	else
+	{
+		/* with IPv6, show it to us as it was sent */
+		strncpy(dcc->addrstr, address, sizeof(dcc->addrstr)-1);
+		dcc->addrstr[sizeof(dcc->addrstr)-1] = '\0';
+	}
+#endif
+	net_getpeername(dcc->handle, &ipaddr, &dcc->port);
+	dcc->size = str_to_uofft(filesize);
+	dcc->file_quoted = quoted;
+
+	/* Done with the DCC_SERVER_REC *rec now */
+	g_source_remove(rec->tagread);
+	g_source_remove_by_user_data(rec);
+	line_split_free(rec->readbuf);
+	rec->readbuf = NULL;
+	net_sendbuffer_destroy(rec->sendbuf, 0);
+	rec->sendbuf = NULL;
+	g_free(rec);
+
+	/* Start the actual GET to receive it */
+	if (settings_get_bool("dcc_autoresume")) {
+		dcc_send_resume(dcc);
+	}
+	dcc->tagconn = g_input_add(dcc->handle,
+	                           G_INPUT_WRITE | G_INPUT_READ,
+	                           (GInputFunction) sig_dccget_connected,
+	                           dcc);
+	/* Safe to send our response
+	 * 121 <server nickname> <resume position>
+	 */
+	g_snprintf(reply, sizeof(reply), "121 %s %d\n", dcc->server->nick,
+	           dcc->skipped);
+	net_transmit(dcc->handle, reply, strlen(reply));
+}
+
+static void read_settings(void)
+{
+	dcc_server = settings_get_bool("dcc_server");
+	dcc_server_port = settings_get_int("dcc_server_port");
+	dcc_server_chat = settings_get_bool("dcc_server_chat");
+	dcc_server_fserve = settings_get_bool("dcc_server_fserve");
+	dcc_server_send = settings_get_bool("dcc_server_send");
+
+	if (dcc_server)
+	{
+		dcc_server_start();
+	} else
+	{
+		dcc_server_stop();
+	}
+}
+
+void dcc_server_init(void)
+{
+	settings_add_int("dcc", "dcc_server_port", 9007);
+	settings_add_bool("dcc", "dcc_server_chat", FALSE);
+	settings_add_bool("dcc", "dcc_server_fserve", FALSE);
+	settings_add_bool("dcc", "dcc_server_send", FALSE);
+
+	read_settings();
+
+	signal_add("setup changed", (SIGNAL_FUNC) read_settings);
+	signal_add("dcc server event", (SIGNAL_FUNC) dcc_server_event);
+}
+
+void dcc_server_deinit(void)
+{
+	signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
+	signal_remove("dcc server event", (SIGNAL_FUNC) dcc_server_event);
+}
diff -r -u -N --exclude-from=irssi-dontdiff irssi-CVS-200302131108/src/irc/dcc/dcc-server.h irssi-CVS/src/irc/dcc/dcc-server.h
--- irssi-CVS-200302131108/src/irc/dcc/dcc-server.h	Thu Jan  1 01:00:00 1970
+++ irssi-CVS/src/irc/dcc/dcc-server.h	Thu Feb 13 10:13:42 2003
@@ -0,0 +1,34 @@
+#ifndef __DCC_SERVER_H
+#define __DCC_SERVER_H
+
+#include "dcc.h"
+
+struct DCC_SERVER_REC {
+	char *id; /* unique identifier - usually same as nick. */
+
+	/* To pass through to DCC CHAT/SEND */
+	time_t created;
+	IRC_SERVER_REC *server;
+	NICK_REC *nick;
+	char *nickname;
+	IPADDR addr; /* address we're connected in */
+	char *addrstr; /* in readable form */
+	char ipaddrstr[MAX_IP_LEN]; /* in readable form */
+	int port; /* port we're connected in */
+	GIOChannel *handle; /* socket handle */
+
+        LINEBUF_REC *readbuf;
+	int tagread;
+	NET_SENDBUF_REC *sendbuf;
+
+	unsigned int connection_lost:1; /* other side closed connection */
+};
+
+#define DCC_SERVER_TYPE module_get_uniq_id_str("DCC", "SERVER")
+
+CHAT_DCC_REC *dcc_server_find_id(const char *id);
+
+void dcc_server_init(void);
+void dcc_server_deinit(void);
+
+#endif
diff -r -u -N --exclude-from=irssi-dontdiff irssi-CVS-200302131108/src/irc/dcc/dcc.c irssi-CVS/src/irc/dcc/dcc.c
--- irssi-CVS-200302131108/src/irc/dcc/dcc.c	Sat Dec 28 17:54:13 2002
+++ irssi-CVS/src/irc/dcc/dcc.c	Thu Feb 13 10:28:42 2003
@@ -41,6 +41,9 @@
 void dcc_autoget_init(void);
 void dcc_autoget_deinit(void);
 
+void dcc_server_init(void);
+void dcc_server_deinit(void);
+
 GSList *dcc_conns;
 
 static GSList *dcc_types;
@@ -516,6 +519,7 @@
 	settings_add_str("dcc", "dcc_port", "0");
 	settings_add_time("dcc", "dcc_timeout", "5min");
 	settings_add_str("dcc", "dcc_own_ip", "");
+	settings_add_bool("dcc", "dcc_server", FALSE);
 
 	signal_add("event connected", (SIGNAL_FUNC) sig_connected);
 	signal_add("server disconnected", (SIGNAL_FUNC) sig_server_disconnected);
@@ -534,6 +538,7 @@
 	dcc_send_init();
 	dcc_resume_init();
 	dcc_autoget_init();
+	dcc_server_init();
 
 	module_register("dcc", "irc");
 }
@@ -548,6 +553,7 @@
 	dcc_send_deinit();
 	dcc_resume_deinit();
 	dcc_autoget_deinit();
+	dcc_server_deinit();
 
 	signal_remove("event connected", (SIGNAL_FUNC) sig_connected);
 	signal_remove("server disconnected", (SIGNAL_FUNC) sig_server_disconnected);
diff -r -u -N --exclude-from=irssi-dontdiff irssi-CVS-200302131108/src/irc/dcc/dcc.h irssi-CVS/src/irc/dcc/dcc.h
--- irssi-CVS-200302131108/src/irc/dcc/dcc.h	Sun Nov 25 17:35:47 2001
+++ irssi-CVS/src/irc/dcc/dcc.h	Thu Feb 13 10:28:42 2003
@@ -7,6 +7,7 @@
 #define DCC(dcc) ((DCC_REC *) (dcc))
 
 typedef struct CHAT_DCC_REC CHAT_DCC_REC;
+typedef struct DCC_SERVER_REC DCC_SERVER_REC;
 
 typedef struct {
 #include "dcc-rec.h"
signature.asc (application/pgp-signature, 240 B)
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.0.6 (GNU/Linux)
Comment: For info see http://www.gnupg.org

iEYEARECAAYFAj5LgfQACgkQzbc+I5XfxKdJLwCggl1p0Q1nrygel+bFlGeLvZe6
UG8An31ZYi1jQKLUz9A8Ptxloe4XFgfg
=n/pu
-----END PGP SIGNATURE-----