Hi!

I've written a patch for irssi-0.8.9 to allow DCCs from behind a
NAT/Firewall (passive DCCs, as mIRC calls them).

This patch features:

1) Support for passive GET (GET of DCC with port 0)
2) Support for passive SEND ("/dcc send -passive nick filename")
3) Support for passive CHAT ("/dcc chat -passive nick" to send a
request, "/dcc chat nick" as usual to accept a passive request)
4) Support for RESUME in passive SEND/GET
5) Updated /help dcc
6) Autoget and autochat still work, but dcc_autoaccept_lowports must be
enabled in order to autoget files and autoaccept chats with passive
protocol.

I've decided to keep the warning about lowports when receiving a request
on port 0 but this is just a cosmetic issue. :-)

The whole thing is achieved by supporting the extended protocol that
mIRC uses to send DCC from behind a firewall, but without the need to
use a real firewall, and should be 100% compatible with both mIRC and
xchat (the same feature is supported in xchat since version 2.0.6
through /DCC PSEND).

I've been running this patch for a couple of weeks and it seems to be
stable. I only had a problem once sending a file with spaces in its
filename: the other side was replying with underscores in place of
spaces and irssi didn't recognize the DCC reply. I don't think this is a
bug, since the other side should reply with exactly the same filename,
anyway a suitable workaround for this problem is: 

/set dcc_send_replace_space_with_underscore ON

Feel free to try this patch if you need some of the features it adds,
and let me know what you think about it. ;-)

Bye
-- 
Francesco Fracassi <[EMAIL PROTECTED]> - OpenPGP Key ID: 48E5E542
Available at http://www.azzurra.org/pgp/mav.asc and pgp.mit.edu
diff -ruN irssi-0.8.9.orig/docs/help/dcc irssi-0.8.9/docs/help/dcc
--- irssi-0.8.9.orig/docs/help/dcc	2004-03-10 21:21:24.000000000 +0100
+++ irssi-0.8.9/docs/help/dcc	2004-03-10 21:21:50.000000000 +0100
@@ -11,19 +11,25 @@
     - Shows all the open DCC connections.
 /DCC RESUME [<nick> [<file>]]
     - Resumes a DCC SEND/GET connection.
-/DCC CHAT [<nick>]
+/DCC CHAT [-passive] [<nick>]
     - Sends a chat connection request to remote client or accepts 
       a chat connection if the remote end has already sent a request.
+      If -passive is used then the passive DCC protocol is used (as mIRC
+      can do). This is useful to bypass a NAT/firewall which limit your
+      possibility in listening for remote connections.
 /DCC GET [<nick> [<file>]]
     - Gets the file offered by remote client. The file is downloaded and
       saved into the current working directory.
-/DCC SEND [-append | -prepend | -flush | -rmtail | -rmhead]
+/DCC SEND [-passive] [-append | -prepend | -flush | -rmtail | -rmhead]
           <nick> <file> [<file> ...]
     - Sends a DCC SEND request to remote client. Remote end has to accept
       the request before the transmission can be started. Giving multiple files
       queues them. File names may contain shell expansion characters: * ? [] ~
       (~ expansion may not be supported on all platforms). Files with spaces
-      in their names need to be quoted (eg. "file name").
+      in their names need to be quoted (eg. "file name"). If -passive is used
+      then the passive DCC protocol is used (as mIRC and xchat > 2.0.7 can do). 
+      This is useful to bypass a NAT/firewall which limit your possibility in 
+      listening for remote connections.
 /DCC SERVER [<+|-scf> <port>]
     - Starts a DCC SERVER on the specified port. The remote can connect to this
       server and initiate chat, send and fserve requests. You can specify + or -
diff -ruN irssi-0.8.9.orig/src/irc/dcc/dcc-chat.c irssi-0.8.9/src/irc/dcc/dcc-chat.c
--- irssi-0.8.9.orig/src/irc/dcc/dcc-chat.c	2004-03-10 21:21:24.000000000 +0100
+++ irssi-0.8.9/src/irc/dcc/dcc-chat.c	2004-03-10 21:21:50.000000000 +0100
@@ -388,36 +388,87 @@
 	}
 }
 
-/* SYNTAX: DCC CHAT [<nick>] */
+static void dcc_chat_passive(CHAT_DCC_REC *dcc)
+{
+    IPADDR own_ip;
+    int port;
+    GIOChannel *handle;
+    char host[MAX_IP_LEN];
+    
+    g_return_if_fail(IS_DCC_CHAT(dcc));
+
+    if (dcc->addrstr[0] == '\0' ||
+        dcc->starttime != 0 || dcc->handle != NULL) {
+        /* already sent a chat request / already chatting */
+        return;
+    }
+    
+    handle = dcc_listen(net_sendbuffer_handle(dcc->server->handle),
+                        &own_ip, &port);
+    if (handle == NULL)
+        cmd_return_error(CMDERR_ERRNO);
+
+    dcc->handle = handle;
+    dcc->tagconn = g_input_add(dcc->handle, G_INPUT_READ,
+                    (GInputFunction) dcc_chat_listen, dcc);
+
+    /* Let's send the reply to the other client! - FF */
+    dcc_ip2str(&own_ip, host);
+    irc_send_cmdv(dcc->server, "PRIVMSG %s :\001DCC CHAT CHAT %s %d %d\001",
+                dcc->nick, host, port, dcc->pasv_id);
+         
+}
+
+/* SYNTAX: DCC CHAT [-passive] [<nick>] */
 static void cmd_dcc_chat(const char *data, IRC_SERVER_REC *server)
 {
 	void *free_arg;
 	CHAT_DCC_REC *dcc;
 	IPADDR own_ip;
-        GIOChannel *handle;
-	char *nick, host[MAX_IP_LEN];
+    GIOChannel *handle;
+	GHashTable *optlist;
+    int passive = FALSE;
+    int p_id;
+    char *nick, host[MAX_IP_LEN];
 	int port;
 
 	g_return_if_fail(data != NULL);
 
-	if (!cmd_get_params(data, &free_arg, 1, &nick))
+	if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS,
+                "dcc chat", &optlist, &nick))
 		return;
 
+    if (g_hash_table_lookup(optlist, "passive") != NULL) passive = TRUE;
+    
 	if (*nick == '\0') {
 		dcc = DCC_CHAT(dcc_find_request_latest(DCC_CHAT_TYPE));
-		if (dcc != NULL)
-			dcc_chat_connect(dcc);
+		if (dcc != NULL) {
+			if (!dcc_is_passive(dcc)) {
+                dcc_chat_connect(dcc);
+            } else {
+                dcc_chat_passive(dcc);
+            }
+        }
 		cmd_params_free(free_arg);
 		return;
 	}
 
 	dcc = dcc_chat_find_id(nick);
 	if (dcc != NULL && dcc_is_waiting_user(dcc)) {
-		/* found from dcc chat requests,
-		   we're the connecting side */
-		dcc_chat_connect(dcc);
-		cmd_params_free(free_arg);
-		return;
+		if (!dcc_is_passive(dcc)) {
+		    /* found from dcc chat requests,
+		   	   we're the connecting side */
+		    dcc_chat_connect(dcc);
+		    cmd_params_free(free_arg);
+		    return;
+		}
+
+		/* We are accepting a passive DCC CHAT. - FF */
+        dcc_chat_passive(dcc);
+        
+        /* We are done... return! - FF */
+        cmd_params_free(free_arg);
+        return;
 	}
 
 	if (dcc != NULL && dcc_is_listening(dcc) &&
@@ -427,27 +478,52 @@
                 dcc_destroy(DCC(dcc));
 	}
 
-	/* start listening  */
 	if (!IS_IRC_SERVER(server) || !server->connected)
 		cmd_param_error(CMDERR_NOT_CONNECTED);
 
-	handle = dcc_listen(net_sendbuffer_handle(server->handle),
-			    &own_ip, &port);
-	if (handle == NULL)
-		cmd_param_error(CMDERR_ERRNO);
+    dcc = dcc_chat_create(server, NULL, nick, "chat");
 
-	dcc = dcc_chat_create(server, NULL, nick, "chat");
-        dcc->handle = handle;
-	dcc->tagconn = g_input_add(dcc->handle, G_INPUT_READ,
-				   (GInputFunction) dcc_chat_listen, dcc);
+    if (passive == FALSE) {
 
-	/* send the chat request */
-	signal_emit("dcc request send", 1, dcc);
+        /* Standard DCC CHAT... let's listen for incoming connections */
+        handle = dcc_listen(net_sendbuffer_handle(server->handle),
+		    	    &own_ip, &port);
+	    if (handle == NULL)
+		    cmd_param_error(CMDERR_ERRNO);
 
-	dcc_ip2str(&own_ip, host);
-	irc_send_cmdv(server, "PRIVMSG %s :\001DCC CHAT CHAT %s %d\001",
-		      nick, host, port);
+        dcc->handle = handle;
+	    dcc->tagconn = g_input_add(dcc->handle, G_INPUT_READ,
+		    		   (GInputFunction) dcc_chat_listen, dcc);
+
+	    /* send the chat request */
+	    signal_emit("dcc request send", 1, dcc);
 
+	    dcc_ip2str(&own_ip, host);
+	    irc_send_cmdv(server, "PRIVMSG %s :\001DCC CHAT CHAT %s %d\001",
+		          nick, host, port);
+    } else {
+
+        /* Passive protocol... we want the other side to listen */
+	    /* send the chat request */
+
+        dcc->port = 0;
+        signal_emit("dcc request send", 1, dcc);
+
+        /* generate a random id - FF */
+    
+        srand(time(NULL));
+        p_id = rand() % 64;
+        dcc->pasv_id = p_id;
+    
+        /* 16974599 is the long format of 1.3.3.7, we use a fake IP since the other side
+        * shouldn't care of it: they will send the address for us to connect to in the reply
+        *
+        * - FF
+        */   
+    
+	    irc_send_cmdv(server, "PRIVMSG %s :\001DCC CHAT CHAT 16974599 0 %d\001",
+		          nick, p_id);
+    }
 	cmd_params_free(free_arg);
 }
 
@@ -542,14 +618,15 @@
 	char **params;
 	int paramcount;
         int autoallow = FALSE;
-
+	
         /* CHAT <unused> <address> <port> */
+		/* CHAT <unused> <address> 0 <id> (DCC CHAT passive protocol) - FF */
 	params = g_strsplit(data, " ", -1);
 	paramcount = strarray_length(params);
 
 	if (paramcount < 3) {
 		g_strfreev(params);
-                return;
+        return;
 	}
 
 	dcc = DCC_CHAT(dcc_find_request(DCC_CHAT_TYPE, nick, NULL));
@@ -562,21 +639,51 @@
 		} else {
 			/* we already have one dcc chat request
 			   from this nick, remove it. */
-                        dcc_destroy(DCC(dcc));
+            if (!dcc_is_passive(dcc))
+                dcc_destroy(DCC(dcc));
+            else if (paramcount == 4) {
+                if (dcc->pasv_id != atoi(params[3])) {
+                    /* IDs don't match! */
+                    dcc_destroy(DCC(dcc));
+                } else {
+
+                    /* IDs are ok! Update address and port and connect! */
+                    dcc->target = g_strdup(target);
+                    dcc->port = atoi(params[2]);
+                   	dcc_str2ip(params[1], &dcc->addr);
+                	net_ip2host(&dcc->addr, dcc->addrstr);
+                    
+                    dcc_chat_connect(dcc);
+
+                    g_strfreev(params);
+
+                    return;
+                }
+            }    
 		}
 	}
-
+	
 	dcc = dcc_chat_create(server, chat, nick, params[0]);
 	dcc->target = g_strdup(target);
 	dcc->port = atoi(params[2]);
+	
+	if (paramcount == 4 && atoi(params[2]) == 0) {
+		dcc->pasv_id = atoi(params[3]);
+	}
+	
 	dcc_str2ip(params[1], &dcc->addr);
 	net_ip2host(&dcc->addr, dcc->addrstr);
 
 	signal_emit("dcc request", 2, dcc, addr);
 
-	if (autoallow || DCC_CHAT_AUTOACCEPT(dcc, server, nick, addr))
-		dcc_chat_connect(dcc);
-
+	if (autoallow || DCC_CHAT_AUTOACCEPT(dcc, server, nick, addr)) {
+		if (paramcount == 4 && atoi(params[2]) == 0) {
+			/* Passive DCC... let's set up a listening socket and send reply back - FF */
+            dcc_chat_passive(dcc);
+		} else {
+			dcc_chat_connect(dcc);
+		}
+    }
 	g_strfreev(params);
 }
 
@@ -716,7 +823,8 @@
 	command_bind("action", NULL, (SIGNAL_FUNC) cmd_action);
 	command_bind("ctcp", NULL, (SIGNAL_FUNC) cmd_ctcp);
 	command_bind("dcc chat", NULL, (SIGNAL_FUNC) cmd_dcc_chat);
-	command_bind("mircdcc", NULL, (SIGNAL_FUNC) cmd_mircdcc);
+	command_set_options("dcc chat", "passive");
+    command_bind("mircdcc", NULL, (SIGNAL_FUNC) cmd_mircdcc);
 	command_bind("dcc close", NULL, (SIGNAL_FUNC) cmd_dcc_close);
 	command_bind("whois", NULL, (SIGNAL_FUNC) cmd_whois);
 	signal_add("dcc destroyed", (SIGNAL_FUNC) sig_dcc_destroyed);
diff -ruN irssi-0.8.9.orig/src/irc/dcc/dcc-get.c irssi-0.8.9/src/irc/dcc/dcc-get.c
--- irssi-0.8.9.orig/src/irc/dcc/dcc-get.c	2004-03-10 21:21:24.000000000 +0100
+++ irssi-0.8.9/src/irc/dcc/dcc-get.c	2004-03-10 21:23:29.000000000 +0100
@@ -24,10 +24,11 @@
 #include "network.h"
 #include "misc.h"
 #include "settings.h"
-
+#include "net-sendbuffer.h"
 #include "irc-servers.h"
 
 #include "dcc-get.h"
+#include "dcc-send.h"
 
 static int dcc_file_create_mode;
 
@@ -290,6 +291,53 @@
 	}
 }
 
+static void dcc_get_listen(GET_DCC_REC *dcc)
+{
+    GIOChannel *handle;
+    IPADDR addr;
+    int port;
+
+    /* accept connection */
+    handle = net_accept(dcc->handle, &addr, &port);
+    if (handle == NULL)
+        return;
+
+    net_disconnect(dcc->handle);
+    g_source_remove(dcc->tagconn);
+    dcc->tagconn = -1;
+
+    dcc->starttime = time(NULL);
+    dcc->handle = handle;
+    memcpy(&dcc->addr, &addr, sizeof(IPADDR));
+    net_ip2host(&dcc->addr, dcc->addrstr);
+    dcc->port = port;
+
+    dcc->tagconn = g_input_add(handle, G_INPUT_READ | G_INPUT_WRITE, 
+                            (GInputFunction) sig_dccget_connected, dcc);
+}
+
+void dcc_get_passive(GET_DCC_REC *dcc)
+{
+    GIOChannel *handle;
+    IPADDR own_ip;
+    int port;
+    char host[MAX_IP_LEN];
+        
+    handle = dcc_listen(net_sendbuffer_handle(dcc->server->handle),
+                        &own_ip, &port);
+    if (handle == NULL)
+        cmd_return_error(CMDERR_ERRNO);
+
+    dcc->handle = handle;
+    dcc->tagconn = g_input_add(dcc->handle, G_INPUT_READ,
+                    (GInputFunction) dcc_get_listen, dcc);
+
+    /* Let's send the reply to the other client! - FF */
+    dcc_ip2str(&own_ip, host);
+    irc_send_cmdv(dcc->server, "PRIVMSG %s :\001DCC SEND %s %s %d %"PRIuUOFF_T" %d\001",
+                dcc->nick, dcc->arg, host, port, dcc->size, dcc->pasv_id);
+}
+
 #define get_params_match(params, pos) \
 	((is_numeric(params[pos], '\0') || is_ipv6_address(params[pos])) && \
 	is_numeric(params[(pos)+1], '\0') && atol(params[(pos)+1]) < 65536 && \
@@ -332,14 +380,18 @@
 			      const char *target, CHAT_DCC_REC *chat)
 {
 	GET_DCC_REC *dcc;
-        IPADDR ip;
+    SEND_DCC_REC *temp_dcc;
+    IPADDR ip;
 	const char *address;
 	char **params, *fname;
 	int paramcount, fileparams;
 	int port, len, quoted = FALSE;
         uoff_t size;
+	int p_id = -1;
+	int passive = FALSE;
 
 	/* SEND <file name> <address> <port> <size> [...] */
+	/* SEND <file name> <address> 0 <size> <id> (DCC SEND passive protocol) - FF */
 	params = g_strsplit(data, " ", -1);
 	paramcount = strarray_length(params);
 
@@ -357,6 +409,12 @@
 	port = atoi(params[fileparams+1]);
 	size = str_to_uofft(params[fileparams+2]);
 
+	/* If this DCC uses passive protocol then store the id for later use. - FF */
+    if (paramcount == fileparams + 4) {
+        p_id = atoi(params[fileparams+3]); 
+        passive = TRUE;
+	}
+		
 	params[fileparams] = NULL;
         fname = g_strjoinv(" ", params);
 	g_strfreev(params);
@@ -368,15 +426,57 @@
 		g_memmove(fname, fname+1, len);
                 quoted = TRUE;
 	}
-
+    
+    if (passive == TRUE && port != 0) {   
+        /* This is NOT a DCC SEND request! This is a reply to our passive request.
+		 * We MUST check the IDs and then connect to the remote host.
+		 *
+		 * - FF
+		 */
+			
+		temp_dcc = DCC_SEND(dcc_find_request(DCC_SEND_TYPE, nick, fname));
+        if (temp_dcc != NULL && p_id == temp_dcc->pasv_id) {
+            
+            temp_dcc->target = g_strdup(target);
+            temp_dcc->port = port;
+            temp_dcc->size = size;
+            temp_dcc->file_quoted = quoted;
+            
+            memcpy(&temp_dcc->addr, &ip, sizeof(IPADDR));
+            if (temp_dcc->addr.family == AF_INET)
+                net_ip2host(&temp_dcc->addr, temp_dcc->addrstr);
+            else {
+                /* IPv6 */
+                strncpy(temp_dcc->addrstr, address, sizeof(temp_dcc->addrstr)-1);
+                temp_dcc->addrstr[sizeof(temp_dcc->addrstr)-1] = '\0';
+            }
+           
+			/* This new signal is added to let us invoke dcc_send_connect() which
+			 * is found in dcc-send.c. 
+			 *
+			 * - FF
+			 */
+            signal_emit("dcc reply send pasv", 1, temp_dcc);
+            g_free(fname);
+            return;
+        } else if (temp_dcc != NULL && p_id != temp_dcc->pasv_id) { 
+            /* IDs don't match... remove the old DCC SEND and return - FF*/
+            dcc_destroy(DCC(temp_dcc));
+            g_free(fname);
+            return;
+        }
+    }
+        
 	dcc = DCC_GET(dcc_find_request(DCC_GET_TYPE, nick, fname));
 	if (dcc != NULL) {
-		/* same DCC request offered again, remove the old one */
-		dcc_destroy(DCC(dcc));
+        dcc_destroy(DCC(dcc)); /* remove the old DCC */
 	}
 
 	dcc = dcc_get_create(server, chat, nick, fname);
 	dcc->target = g_strdup(target);
+
+    if (passive == TRUE && port == 0) dcc->pasv_id = p_id; /* Assign the ID to the DCC */
+    
 	memcpy(&dcc->addr, &ip, sizeof(ip));
 	if (dcc->addr.family == AF_INET)
 		net_ip2host(&dcc->addr, dcc->addrstr);
@@ -387,17 +487,17 @@
 	}
 	dcc->port = port;
 	dcc->size = size;
-        dcc->file_quoted = quoted;
-
+    dcc->file_quoted = quoted;
+    
 	signal_emit("dcc request", 2, dcc, addr);
 
-        g_free(fname);
+    g_free(fname);
 }
 
 /* handle receiving DCC - GET/RESUME. */
-void cmd_dcc_receive(const char *data, DCC_GET_FUNC accept_func)
+void cmd_dcc_receive(const char *data, DCC_GET_FUNC accept_func, DCC_GET_FUNC pasv_accept_func)
 {
-        GET_DCC_REC *dcc;
+    GET_DCC_REC *dcc;
 	GSList *tmp, *next;
 	char *nick, *fname;
 	void *free_arg;
@@ -411,8 +511,12 @@
 
 	if (*nick == '\0') {
 		dcc = DCC_GET(dcc_find_request_latest(DCC_GET_TYPE));
-		if (dcc != NULL)
-			accept_func(dcc);
+		if (dcc != NULL) {
+            if (!dcc_is_passive(dcc))
+			    accept_func(dcc);
+            else
+                pasv_accept_func(dcc);
+        }
 		cmd_params_free(free_arg);
 		return;
 	}
@@ -426,7 +530,10 @@
 		    (dcc_is_waiting_user(dcc) || dcc->from_dccserver) &&
 		    (*fname == '\0' || strcmp(dcc->arg, fname) == 0)) {
 			found = TRUE;
-			accept_func(dcc);
+			if (!dcc_is_passive(dcc))
+                accept_func(dcc);
+            else
+                pasv_accept_func(dcc);
 		}
 	}
 
@@ -439,7 +546,7 @@
 /* SYNTAX: DCC GET [<nick> [<file>]] */
 static void cmd_dcc_get(const char *data)
 {
-        cmd_dcc_receive(data, dcc_get_connect);
+        cmd_dcc_receive(data, dcc_get_connect, dcc_get_passive);
 }
 
 static void read_settings(void)
diff -ruN irssi-0.8.9.orig/src/irc/dcc/dcc-get.h irssi-0.8.9/src/irc/dcc/dcc-get.h
--- irssi-0.8.9.orig/src/irc/dcc/dcc-get.h	2004-03-10 21:21:24.000000000 +0100
+++ irssi-0.8.9/src/irc/dcc/dcc-get.h	2004-03-10 21:21:51.000000000 +0100
@@ -32,8 +32,9 @@
 typedef void (*DCC_GET_FUNC) (GET_DCC_REC *);
 
 /* handle receiving DCC - GET/RESUME. */
-void cmd_dcc_receive(const char *data, DCC_GET_FUNC accept_func);
+void cmd_dcc_receive(const char *data, DCC_GET_FUNC accept_func, DCC_GET_FUNC pasv_accept_func);
 
+void dcc_get_passive(GET_DCC_REC *dcc);
 void dcc_get_connect(GET_DCC_REC *dcc);
 char *dcc_get_download_path(const char *fname);
 
diff -ruN irssi-0.8.9.orig/src/irc/dcc/dcc-queue.c irssi-0.8.9/src/irc/dcc/dcc-queue.c
--- irssi-0.8.9.orig/src/irc/dcc/dcc-queue.c	2004-03-10 21:21:24.000000000 +0100
+++ irssi-0.8.9/src/irc/dcc/dcc-queue.c	2004-03-10 21:21:51.000000000 +0100
@@ -122,6 +122,30 @@
 	rec->servertag = g_strdup(servertag);
 	rec->nick = g_strdup(nick);
 	rec->file = g_strdup(fname);
+    rec->passive = FALSE;
+
+	qlist = (GSList **) &g_ptr_array_index(queuelist, queue);
+	if (mode == DCC_QUEUE_PREPEND)
+		*qlist = g_slist_insert(*qlist, rec, 1);
+	else
+		*qlist = g_slist_append(*qlist, rec);
+}
+
+/* Same as above but adds a passive DCC to the queue - FF */
+void dcc_queue_add_passive(int queue, int mode, const char *nick, const char *fname,
+		   const char *servertag, CHAT_DCC_REC *chat)
+{
+	DCC_QUEUE_REC *rec;
+	GSList **qlist;
+
+	g_assert(queue >= 0 && queue < queuelist->len);
+
+	rec = g_new0(DCC_QUEUE_REC, 1);
+	rec->chat = chat;
+	rec->servertag = g_strdup(servertag);
+	rec->nick = g_strdup(nick);
+	rec->file = g_strdup(fname);
+    rec->passive = TRUE;
 
 	qlist = (GSList **) &g_ptr_array_index(queuelist, queue);
 	if (mode == DCC_QUEUE_PREPEND)
diff -ruN irssi-0.8.9.orig/src/irc/dcc/dcc-queue.h irssi-0.8.9/src/irc/dcc/dcc-queue.h
--- irssi-0.8.9.orig/src/irc/dcc/dcc-queue.h	2004-03-10 21:21:24.000000000 +0100
+++ irssi-0.8.9/src/irc/dcc/dcc-queue.h	2004-03-10 21:21:51.000000000 +0100
@@ -14,6 +14,7 @@
 	char *servertag;
 	char *nick;
 	char *file;
+    int passive; /* for passive DCCs - FF */
 } DCC_QUEUE_REC;
 
 /* create a new queue. returns it's designation number (int) */
@@ -28,6 +29,8 @@
 /* adds nick/fname/servertag triplet into queue */
 void dcc_queue_add(int queue, int mode, const char *nick, const char *fname,
 		   const char *servertag, CHAT_DCC_REC *chat);
+void dcc_queue_add_passive(int queue, int mode, const char *nick, const char *fname,
+           const char *servertag, CHAT_DCC_REC *chat);
 
 int dcc_queue_remove_head(int queue);
 
diff -ruN irssi-0.8.9.orig/src/irc/dcc/dcc-rec.h irssi-0.8.9/src/irc/dcc/dcc-rec.h
--- irssi-0.8.9.orig/src/irc/dcc/dcc-rec.h	2004-03-10 21:21:24.000000000 +0100
+++ irssi-0.8.9/src/irc/dcc/dcc-rec.h	2004-03-10 21:21:51.000000000 +0100
@@ -20,6 +20,8 @@
 time_t starttime; /* transfer start time */
 uoff_t transfd; /* bytes transferred */
 
+int pasv_id; /* DCC Id for passive DCCs. <0 means a passive DCC, >=0 means a standard DCC - FF */ 
+
 unsigned int destroyed:1; /* We're about to destroy this DCC recond */
 
 GHashTable *module_data;
diff -ruN irssi-0.8.9.orig/src/irc/dcc/dcc-resume.c irssi-0.8.9/src/irc/dcc/dcc-resume.c
--- irssi-0.8.9.orig/src/irc/dcc/dcc-resume.c	2004-03-10 21:21:24.000000000 +0100
+++ irssi-0.8.9/src/irc/dcc/dcc-resume.c	2004-03-10 21:21:51.000000000 +0100
@@ -45,24 +45,27 @@
 }
 
 static int dcc_ctcp_resume_parse(int type, const char *data, const char *nick,
-				 FILE_DCC_REC **dcc, uoff_t *size)
+				 FILE_DCC_REC **dcc, uoff_t *size, int *pasv_id)
 {
 	char **params;
-	int paramcount;
+	int paramcount, fileparams;
         int port;
 
 	/* RESUME|ACCEPT <file name> <port> <size> */
+    /* RESUME|ACCEPT <file name> 0 <size> <id> (passive protocol) - FF */
 	params = g_strsplit(data, " ", -1);
 	paramcount = strarray_length(params);
-
+    
 	if (paramcount >= 3) {
-		port = atoi(params[paramcount-2]);
-		*size = str_to_uofft(params[paramcount-1]);
-
+		port = atoi(params[1]);
+		*size = str_to_uofft(params[2]);
+        *pasv_id = (port == 0) ? atoi(params[3]) : -1;
 		*dcc = dcc_resume_find(type, nick, port);
+        g_strfreev(params);
+        return ((*dcc)->pasv_id == *pasv_id); /* If the ID is different then the DCC cannot be resumed */
 	}
 	g_strfreev(params);
-	return paramcount >= 3;
+	return FALSE;
 }
 
 static int dcc_resume_file_check(FILE_DCC_REC *dcc, IRC_SERVER_REC *server,
@@ -91,15 +94,22 @@
 	FILE_DCC_REC *dcc;
         char *str;
         uoff_t size;
+    int pasv_id = -1;
 
-	if (!dcc_ctcp_resume_parse(DCC_SEND_TYPE, data, nick, &dcc, &size)) {
+	if (!dcc_ctcp_resume_parse(DCC_SEND_TYPE, data, nick, &dcc, &size, &pasv_id)) {
 		signal_emit("dcc error ctcp", 5, "RESUME", data,
 			    nick, addr, target);
 	} else if (dcc != NULL && dcc_resume_file_check(dcc, server, size)) {
-		str = g_strdup_printf(DCC_SEND(dcc)->file_quoted ?
-				      "DCC ACCEPT \"%s\" %d %"PRIuUOFF_T :
-				      "DCC ACCEPT %s %d %"PRIuUOFF_T,
-				      dcc->arg, dcc->port, dcc->transfd);
+        if (!dcc_is_passive(dcc))
+		    str = g_strdup_printf(DCC_SEND(dcc)->file_quoted ?
+			    	      "DCC ACCEPT \"%s\" %d %"PRIuUOFF_T :
+				          "DCC ACCEPT %s %d %"PRIuUOFF_T,
+				          dcc->arg, dcc->port, dcc->transfd);
+        else
+            str = g_strdup_printf(DCC_SEND(dcc)->file_quoted ?
+                          "DCC ACCEPT \"%s\" 0 %"PRIuUOFF_T" %d" :
+                          "DCC ACCEPT %s 0 %"PRIuUOFF_T" %d",
+                          dcc->arg, dcc->transfd, dcc->pasv_id);
 		dcc_ctcp_message(dcc->server, dcc->nick,
 				 dcc->chat, FALSE, str);
 		g_free(str);
@@ -113,13 +123,18 @@
 {
 	FILE_DCC_REC *dcc;
         uoff_t size;
+    int pasv_id;
 
-	if (!dcc_ctcp_resume_parse(DCC_GET_TYPE, data, nick, &dcc, &size) ||
+	if (!dcc_ctcp_resume_parse(DCC_GET_TYPE, data, nick, &dcc, &size, &pasv_id) ||
 	    (dcc != NULL && DCC_GET(dcc)->get_type != DCC_GET_RESUME)) {
 		signal_emit("dcc error ctcp", 5, "ACCEPT", data,
 			    nick, addr, target);
-	} else if (dcc != NULL && dcc_resume_file_check(dcc, server, size))
-		dcc_get_connect(DCC_GET(dcc));
+	} else if (dcc != NULL && dcc_resume_file_check(dcc, server, size)) {
+        if (!dcc_is_passive(dcc))
+	        dcc_get_connect(DCC_GET(dcc));
+        else
+            dcc_get_passive(DCC_GET(dcc));
+    }
 }
 
 /* Resume a DCC GET */
@@ -149,10 +164,16 @@
 		dcc->starttime = time(NULL);
 		dcc_reject(DCC(dcc), NULL);
 	} else {
-		str = g_strdup_printf(dcc->file_quoted ?
-				      "DCC RESUME \"%s\" %d %"PRIuUOFF_T :
-				      "DCC RESUME %s %d %"PRIuUOFF_T,
-				      dcc->arg, dcc->port, dcc->transfd);
+        if (!dcc_is_passive(dcc))
+		    str = g_strdup_printf(dcc->file_quoted ?
+			    	      "DCC RESUME \"%s\" %d %"PRIuUOFF_T :
+				          "DCC RESUME %s %d %"PRIuUOFF_T,
+				          dcc->arg, dcc->port, dcc->transfd);
+        else
+            str = g_strdup_printf(dcc->file_quoted ?
+                          "DCC RESUME \"%s\" 0 %"PRIuUOFF_T" %d" :
+                          "DCC RESUME %s 0 %"PRIuUOFF_T" %d",
+                          dcc->arg, dcc->transfd, dcc->pasv_id);
 		dcc_ctcp_message(dcc->server, dcc->nick,
 				 dcc->chat, FALSE, str);
 		g_free(str);
@@ -162,7 +183,7 @@
 /* SYNTAX: DCC RESUME [<nick> [<file>]] */
 static void cmd_dcc_resume(const char *data)
 {
-	cmd_dcc_receive(data, dcc_send_resume);
+	cmd_dcc_receive(data, dcc_send_resume, dcc_send_resume);
 }
 
 void dcc_resume_init(void)
diff -ruN irssi-0.8.9.orig/src/irc/dcc/dcc-send.c irssi-0.8.9/src/irc/dcc/dcc-send.c
--- irssi-0.8.9.orig/src/irc/dcc/dcc-send.c	2004-03-10 21:21:24.000000000 +0100
+++ irssi-0.8.9/src/irc/dcc/dcc-send.c	2004-03-10 21:21:51.000000000 +0100
@@ -39,7 +39,7 @@
 #endif
 
 static int dcc_send_one_file(int queue, const char *target, const char *fname,
-			     IRC_SERVER_REC *server, CHAT_DCC_REC *chat);
+			     IRC_SERVER_REC *server, CHAT_DCC_REC *chat, int passive);
 
 static void dcc_queue_send_next(int queue)
 {
@@ -58,7 +58,7 @@
 		} else {
 			send_started = dcc_send_one_file(queue, qrec->nick,
 							 qrec->file, server,
-							 qrec->chat);
+							 qrec->chat, qrec->passive);
 		}
                 dcc_queue_remove_head(queue);
 	}
@@ -70,7 +70,7 @@
 }
 
 static void dcc_send_add(const char *servertag, CHAT_DCC_REC *chat,
-			 const char *nick, char *fileargs, int add_mode)
+			 const char *nick, char *fileargs, int add_mode, int passive)
 {
 	struct stat st;
 	glob_t globbuf;
@@ -127,8 +127,12 @@
 			}
 		}
 
-		dcc_queue_add(queue, add_mode, nick,
-			      fname, servertag, chat);
+        if (passive == FALSE)
+		    dcc_queue_add(queue, add_mode, nick,
+			        fname, servertag, chat);
+        else
+            dcc_queue_add_passive(queue, add_mode, nick,
+                    fname, servertag, chat);
 		files++;
 	}
 
@@ -138,7 +142,7 @@
 	globfree(&globbuf);
 }
 
-/* DCC SEND [-append | -prepend | -flush | -rmtail | -rmhead]
+/* DCC SEND [-append | -prepend | -flush | -rmtail | -rmhead | -passive]
             <nick> <file> [<file> ...] */
 static void cmd_dcc_send(const char *data, IRC_SERVER_REC *server,
 			 WI_ITEM_REC *item)
@@ -149,6 +153,7 @@
 	CHAT_DCC_REC *chat;
 	GHashTable *optlist;
 	int queue, mode;
+    int passive = FALSE;
 
 	if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_OPTIONS |
 			    PARAM_FLAG_GETREST, "dcc send",
@@ -168,6 +173,8 @@
 	if (servertag == NULL && chat == NULL)
 		cmd_param_error(CMDERR_NOT_CONNECTED);
 
+    if (g_hash_table_lookup(optlist, "passive") != NULL) passive = TRUE;
+    
 	if (g_hash_table_lookup(optlist, "rmhead") != NULL) {
 		queue = dcc_queue_old(nick, servertag);
 		if (queue != -1)
@@ -190,8 +197,8 @@
 
 		if (*fileargs == '\0')
 			cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
-
-		dcc_send_add(servertag, chat, nick, fileargs, mode);
+        
+		dcc_send_add(servertag, chat, nick, fileargs, mode, passive);
 	}
 
 	cmd_params_free(free_arg);
@@ -310,6 +317,26 @@
 	signal_emit("dcc connected", 1, dcc);
 }
 
+/* input function: DCC SEND - connect to the receiver (passive protocol) */
+static void dcc_send_connect(SEND_DCC_REC *dcc)
+{
+    dcc->handle = dcc_connect_ip(&dcc->addr, dcc->port);
+
+    if (dcc->handle != NULL) {
+        dcc->starttime = time(NULL);
+        
+        dcc->tagread = g_input_add(dcc->handle, G_INPUT_READ,
+                        (GInputFunction) dcc_send_read_size, dcc);
+        dcc->tagwrite = g_input_add(dcc->handle, G_INPUT_WRITE,
+                        (GInputFunction) dcc_send_data, dcc);
+        signal_emit("dcc connected", 1, dcc);
+    } else {
+        /* error connecting */
+        signal_emit("dcc error connect", 1, dcc);
+        dcc_destroy(DCC(dcc));
+    }
+}
+
 static char *dcc_send_get_file(const char *fname)
 {
 	char *str, *path;
@@ -329,12 +356,12 @@
 }
 
 static int dcc_send_one_file(int queue, const char *target, const char *fname,
-			     IRC_SERVER_REC *server, CHAT_DCC_REC *chat)
+			     IRC_SERVER_REC *server, CHAT_DCC_REC *chat, int passive)
 {
 	struct stat st;
 	char *str;
 	char host[MAX_IP_LEN];
-	int hfile, port;
+	int hfile, port = 0;
         SEND_DCC_REC *dcc;
 	IPADDR own_ip;
         GIOChannel *handle;
@@ -360,15 +387,20 @@
 		return FALSE;
 	}
 
-        /* start listening */
-	handle = dcc_listen(chat != NULL ? chat->handle :
-			    net_sendbuffer_handle(server->handle),
-			    &own_ip, &port);
-	if (handle == NULL) {
-		close(hfile);
-		g_warning("dcc_listen() failed: %s", strerror(errno));
-		return FALSE;
-	}
+    /* start listening (only if passive == FALSE )*/
+    
+    if (passive == FALSE) {
+	    handle = dcc_listen(chat != NULL ? chat->handle :
+		    	    net_sendbuffer_handle(server->handle),
+			        &own_ip, &port);
+	    if (handle == NULL) {
+		    close(hfile);
+		    g_warning("dcc_listen() failed: %s", strerror(errno));
+		    return FALSE;
+	    }
+    } else {
+        handle = NULL;
+    }
 
 	fname = g_basename(fname);
 
@@ -391,18 +423,34 @@
 	dcc->fhandle = hfile;
 	dcc->queue = queue;
         dcc->file_quoted = strchr(fname, ' ') != NULL;
-	dcc->tagconn = g_input_add(handle, G_INPUT_READ,
+	if (passive == FALSE)
+        dcc->tagconn = g_input_add(handle, G_INPUT_READ,
 				   (GInputFunction) dcc_send_connected, dcc);
-
+    
+    /* Generate an ID for this send if using passive protocol - FF */
+    if (passive == TRUE) {
+        srand(time(NULL));
+        dcc->pasv_id = rand() % 64;
+    }
+    
 	/* send DCC request */
 	signal_emit("dcc request send", 1, dcc);
 
+    
 	dcc_ip2str(&own_ip, host);
-	str = g_strdup_printf(dcc->file_quoted ?
-			      "DCC SEND \"%s\" %s %d %"PRIuUOFF_T :
-			      "DCC SEND %s %s %d %"PRIuUOFF_T,
-			      dcc->arg, host, port, dcc->size);
-	dcc_ctcp_message(server, target, chat, FALSE, str);
+    if (passive == FALSE) {
+	    str = g_strdup_printf(dcc->file_quoted ?
+		    	      "DCC SEND \"%s\" %s %d %"PRIuUOFF_T :
+			          "DCC SEND %s %s %d %"PRIuUOFF_T,
+			          dcc->arg, host, port, dcc->size);
+    } else {
+        str = g_strdup_printf(dcc->file_quoted ?
+                      "DCC SEND \"%s\" 16974599 0 %"PRIuUOFF_T" %d" :
+                      "DCC SEND %s 16974599 0 %"PRIuUOFF_T" %d",
+                      dcc->arg, dcc->size, dcc->pasv_id);
+    }
+    dcc_ctcp_message(server, target, chat, FALSE, str);
+    
 	g_free(str);
 
 	return TRUE;
@@ -414,8 +462,9 @@
 	settings_add_str("dcc", "dcc_upload_path", "~");
 	settings_add_bool("dcc", "dcc_send_replace_space_with_underscore", FALSE);
 	signal_add("dcc destroyed", (SIGNAL_FUNC) sig_dcc_destroyed);
+    signal_add("dcc reply send pasv", (SIGNAL_FUNC) dcc_send_connect);
 	command_bind("dcc send", NULL, (SIGNAL_FUNC) cmd_dcc_send);
-	command_set_options("dcc send", "append flush prepend rmhead rmtail");
+	command_set_options("dcc send", "append flush prepend rmhead rmtail passive");
 
 	dcc_queue_init();
 }
@@ -426,5 +475,6 @@
 
         dcc_unregister_type("SEND");
 	signal_remove("dcc destroyed", (SIGNAL_FUNC) sig_dcc_destroyed);
-	command_unbind("dcc send", (SIGNAL_FUNC) cmd_dcc_send);
+	signal_remove("dcc reply send pasv", (SIGNAL_FUNC) dcc_send_connect);
+    command_unbind("dcc send", (SIGNAL_FUNC) cmd_dcc_send);
 }
diff -ruN irssi-0.8.9.orig/src/irc/dcc/dcc.c irssi-0.8.9/src/irc/dcc/dcc.c
--- irssi-0.8.9.orig/src/irc/dcc/dcc.c	2004-03-10 21:21:24.000000000 +0100
+++ irssi-0.8.9/src/irc/dcc/dcc.c	2004-03-10 21:21:51.000000000 +0100
@@ -92,7 +92,9 @@
 
 	dcc->servertag = server != NULL ? g_strdup(server->tag) :
 		(chat == NULL ? NULL : g_strdup(chat->servertag));
-
+	
+	dcc->pasv_id = -1; /* Not a passive DCC - FF */
+	
 	dcc_conns = g_slist_append(dcc_conns, dcc);
 	signal_emit("dcc created", 1, dcc);
 }
diff -ruN irssi-0.8.9.orig/src/irc/dcc/dcc.h irssi-0.8.9/src/irc/dcc/dcc.h
--- irssi-0.8.9.orig/src/irc/dcc/dcc.h	2004-03-10 21:21:24.000000000 +0100
+++ irssi-0.8.9/src/irc/dcc/dcc.h	2004-03-10 21:21:51.000000000 +0100
@@ -24,6 +24,10 @@
 #define dcc_is_waiting_user(dcc) \
         ((dcc)->handle == NULL)
 
+/* passive DCC - FF */
+#define	dcc_is_passive(dcc) \
+		((dcc)->pasv_id >= 0)
+		
 extern GSList *dcc_conns;
 
 void dcc_register_type(const char *type);

Attachment: signature.asc
Description: This is a digitally signed message part

Reply via email to