summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTimo Sirainen <cras@irssi.org>2004-03-23 22:06:41 +0000
committercras <cras@dbcabf3a-b0e7-0310-adc4-f8d773084564>2004-03-23 22:06:41 +0000
commitaf4bcb70f2634b8917166e948e9d631ad3b5cccd (patch)
tree28cfa9891aedd97ac1f73c1a2501cccba643bd20
parent2a12dfb9d00a6196e071c1e9a965e6e11aa46471 (diff)
downloadirssi-af4bcb70f2634b8917166e948e9d631ad3b5cccd.zip
Passive DCC support by Francesco Fracassi (francesco.f at openssl.it)
git-svn-id: http://svn.irssi.org/repos/irssi/trunk@3236 dbcabf3a-b0e7-0310-adc4-f8d773084564
-rw-r--r--docs/help/in/dcc.in16
-rw-r--r--src/irc/dcc/dcc-chat.c157
-rw-r--r--src/irc/dcc/dcc-get.c135
-rw-r--r--src/irc/dcc/dcc-get.h4
-rw-r--r--src/irc/dcc/dcc-queue.c25
-rw-r--r--src/irc/dcc/dcc-queue.h4
-rw-r--r--src/irc/dcc/dcc-rec.h2
-rw-r--r--src/irc/dcc/dcc-resume.c65
-rw-r--r--src/irc/dcc/dcc-send.c116
-rw-r--r--src/irc/dcc/dcc.c4
-rw-r--r--src/irc/dcc/dcc.h4
11 files changed, 424 insertions, 108 deletions
diff --git a/docs/help/in/dcc.in b/docs/help/in/dcc.in
index 48e7c0ab..5711a0bf 100644
--- a/docs/help/in/dcc.in
+++ b/docs/help/in/dcc.in
@@ -1,27 +1,29 @@
@SYNTAX:dcc@
-This is a command to handle different DCC-connections. DCC is mainly
-used for more reliable and faster chatting and for sending and receiving
-files.
-
/DCC LIST
- 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 --git a/src/irc/dcc/dcc-chat.c b/src/irc/dcc/dcc-chat.c
index 3d97ae0c..c1eeafca 100644
--- a/src/irc/dcc/dcc-chat.c
+++ b/src/irc/dcc/dcc-chat.c
@@ -388,35 +388,77 @@ static void dcc_chat_connect(CHAT_DCC_REC *dcc)
}
}
-/* 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! */
+ 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;
+ GIOChannel *handle;
+ GHashTable *optlist;
+ 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 (*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);
+ if (!dcc_is_passive(dcc)) {
+ /* found from dcc chat requests,
+ we're the connecting side */
+ dcc_chat_connect(dcc);
+ } else {
+ /* We are accepting a passive DCC CHAT. */
+ dcc_chat_passive(dcc);
+ }
return;
}
@@ -424,30 +466,50 @@ static void cmd_dcc_chat(const char *data, IRC_SERVER_REC *server)
dcc->server == server) {
/* sending request again even while old request is
still waiting, remove it. */
- dcc_destroy(DCC(dcc));
+ 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->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);
+ if (g_hash_table_lookup(optlist, "passive") == NULL) {
+ /* 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 */
+ 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 */
+ irc_send_cmdv(server,
+ "PRIVMSG %s :\001DCC CHAT CHAT 16974599 0 %d\001",
+ nick, p_id);
+ }
cmd_params_free(free_arg);
}
@@ -541,16 +603,18 @@ static void ctcp_msg_dcc_chat(IRC_SERVER_REC *server, const char *data,
CHAT_DCC_REC *dcc;
char **params;
int paramcount;
- int autoallow = FALSE;
-
+ int passive, autoallow = FALSE;
+
/* CHAT <unused> <address> <port> */
+ /* CHAT <unused> <address> 0 <id> (DCC CHAT passive protocol) */
params = g_strsplit(data, " ", -1);
paramcount = strarray_length(params);
if (paramcount < 3) {
g_strfreev(params);
- return;
+ return;
}
+ passive = paramcount == 4 && strcmp(params[2], "0") == 0;
dcc = DCC_CHAT(dcc_find_request(DCC_CHAT_TYPE, nick, NULL));
if (dcc != NULL) {
@@ -559,24 +623,50 @@ static void ctcp_msg_dcc_chat(IRC_SERVER_REC *server, const char *data,
dcc chat from us .. allow it. */
dcc_destroy(DCC(dcc));
autoallow = TRUE;
- } else {
+ } else if (!dcc_is_passive(dcc)) {
/* we already have one dcc chat request
from this nick, remove it. */
- dcc_destroy(DCC(dcc));
+ dcc_destroy(DCC(dcc));
+ } else if (passive) {
+ 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 (passive)
+ 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 (passive) {
+ /* Passive DCC... let's set up a listening socket
+ and send reply back */
+ dcc_chat_passive(dcc);
+ } else {
+ dcc_chat_connect(dcc);
+ }
+ }
g_strfreev(params);
}
@@ -716,6 +806,7 @@ void dcc_chat_init(void)
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_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);
diff --git a/src/irc/dcc/dcc-get.c b/src/irc/dcc/dcc-get.c
index aa0c6390..e99d3cf6 100644
--- a/src/irc/dcc/dcc-get.c
+++ b/src/irc/dcc/dcc-get.c
@@ -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,54 @@ void dcc_get_connect(GET_DCC_REC *dcc)
}
}
+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! */
+ 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 +381,18 @@ static void ctcp_msg_dcc_send(IRC_SERVER_REC *server, const char *data,
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) */
params = g_strsplit(data, " ", -1);
paramcount = strarray_length(params);
@@ -357,6 +410,12 @@ static void ctcp_msg_dcc_send(IRC_SERVER_REC *server, const char *data,
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. */
+ if (paramcount == fileparams + 4) {
+ p_id = atoi(params[fileparams+3]);
+ passive = TRUE;
+ }
+
params[fileparams] = NULL;
fname = g_strjoinv(" ", params);
g_strfreev(params);
@@ -368,36 +427,73 @@ static void ctcp_msg_dcc_send(IRC_SERVER_REC *server, const char *data,
g_memmove(fname, fname+1, len);
quoted = TRUE;
}
+
+ if (passive && 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. */
+
+ 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 {
+ /* with IPv6, show it to us as it was sent */
+ strocpy(temp_dcc->addrstr, address,
+ sizeof(temp_dcc->addrstr));
+ }
- 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));
+ /* This new signal is added to let us invoke
+ dcc_send_connect() which is found in dcc-send.c */
+ 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 */
+ dcc_destroy(DCC(temp_dcc));
+ g_free(fname);
+ return;
+ }
}
+ dcc = DCC_GET(dcc_find_request(DCC_GET_TYPE, nick, fname));
+ if (dcc != NULL)
+ dcc_destroy(DCC(dcc)); /* remove the old DCC */
+
dcc = dcc_get_create(server, chat, nick, fname);
dcc->target = g_strdup(target);
+
+ if (passive && 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);
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';
+ strocpy(dcc->addrstr, address, sizeof(dcc->addrstr));
}
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 +507,12 @@ void cmd_dcc_receive(const char *data, DCC_GET_FUNC accept_func)
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 +526,10 @@ void cmd_dcc_receive(const char *data, DCC_GET_FUNC accept_func)
(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 +542,7 @@ void cmd_dcc_receive(const char *data, DCC_GET_FUNC accept_func)
/* 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 --git a/src/irc/dcc/dcc-get.h b/src/irc/dcc/dcc-get.h
index 321a8b4e..7ac7b3c1 100644
--- a/src/irc/dcc/dcc-get.h
+++ b/src/irc/dcc/dcc-get.h
@@ -32,8 +32,10 @@ typedef struct {
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 --git a/src/irc/dcc/dcc-queue.c b/src/irc/dcc/dcc-queue.c
index 677bc07b..9f982ed9 100644
--- a/src/irc/dcc/dcc-queue.c
+++ b/src/irc/dcc/dcc-queue.c
@@ -122,6 +122,31 @@ void dcc_queue_add(int queue, int mode, const char *nick, const char *fname,
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 */
+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 --git a/src/irc/dcc/dcc-queue.h b/src/irc/dcc/dcc-queue.h
index 85bd2083..d13fae66 100644
--- a/src/irc/dcc/dcc-queue.h
+++ b/src/irc/dcc/dcc-queue.h
@@ -14,6 +14,7 @@ typedef struct {
char *servertag;
char *nick;
char *file;
+ int passive; /* for passive DCCs */
} DCC_QUEUE_REC;
/* create a new queue. returns it's designation number (int) */
@@ -28,6 +29,9 @@ int dcc_queue_old(const char *nick, const char *servertag);
/* 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 --git a/src/irc/dcc/dcc-rec.h b/src/irc/dcc/dcc-rec.h
index 9fdd9582..afb029d3 100644
--- a/src/irc/dcc/dcc-rec.h
+++ b/src/irc/dcc/dcc-rec.h
@@ -20,6 +20,8 @@ int tagconn, tagread, tagwrite;
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 */
+
unsigned int destroyed:1; /* We're about to destroy this DCC recond */
GHashTable *module_data;
diff --git a/src/irc/dcc/dcc-resume.c b/src/irc/dcc/dcc-resume.c
index 3cae3fa5..19167ee3 100644
--- a/src/irc/dcc/dcc-resume.c
+++ b/src/irc/dcc/dcc-resume.c
@@ -45,24 +45,29 @@ static FILE_DCC_REC *dcc_resume_find(int type, const char *nick, int port)
}
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 port;
/* RESUME|ACCEPT <file name> <port> <size> */
+ /* RESUME|ACCEPT <file name> 0 <size> <id> (passive protocol) */
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);
+
+ /* If the ID is different then the DCC cannot be resumed */
+ return ((*dcc)->pasv_id == *pasv_id);
}
g_strfreev(params);
- return paramcount >= 3;
+ return FALSE;
}
static int dcc_resume_file_check(FILE_DCC_REC *dcc, IRC_SERVER_REC *server,
@@ -90,16 +95,24 @@ static void ctcp_msg_dcc_resume(IRC_SERVER_REC *server, const char *data,
{
FILE_DCC_REC *dcc;
char *str;
- uoff_t size;
+ 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 +126,18 @@ static void ctcp_msg_dcc_accept(IRC_SERVER_REC *server, const char *data,
{
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 */
@@ -128,7 +146,7 @@ static void dcc_send_resume(GET_DCC_REC *dcc)
off_t pos;
char *str;
- g_return_if_fail(dcc != NULL);
+ g_return_if_fail(dcc != NULL);
dcc->file = dcc_get_download_path(dcc->arg);
dcc->fhandle = open(dcc->file, O_WRONLY);
@@ -149,10 +167,17 @@ static void dcc_send_resume(GET_DCC_REC *dcc)
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 +187,7 @@ static void dcc_send_resume(GET_DCC_REC *dcc)
/* 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 --git a/src/irc/dcc/dcc-send.c b/src/irc/dcc/dcc-send.c
index 93a85fa1..cb90fca8 100644
--- a/src/irc/dcc/dcc-send.c
+++ b/src/irc/dcc/dcc-send.c
@@ -39,7 +39,8 @@
#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 +59,8 @@ static void dcc_queue_send_next(int queue)
} 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 +72,8 @@ static void dcc_queue_send_next(int queue)
}
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 +130,12 @@ static void dcc_send_add(const char *servertag, CHAT_DCC_REC *chat,
}
}
- dcc_queue_add(queue, add_mode, nick,
- fname, servertag, chat);
+ if (!passive)
+ 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 +145,7 @@ static void dcc_send_add(const char *servertag, CHAT_DCC_REC *chat,
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)
@@ -148,7 +155,7 @@ static void cmd_dcc_send(const char *data, IRC_SERVER_REC *server,
void *free_arg;
CHAT_DCC_REC *chat;
GHashTable *optlist;
- int queue, mode;
+ int queue, mode, passive;
if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_OPTIONS |
PARAM_FLAG_GETREST, "dcc send",
@@ -168,6 +175,8 @@ static void cmd_dcc_send(const char *data, IRC_SERVER_REC *server,
if (servertag == NULL && chat == NULL)
cmd_param_error(CMDERR_NOT_CONNECTED);
+ passive = g_hash_table_lookup(optlist, "passive") != NULL;
+
if (g_hash_table_lookup(optlist, "rmhead") != NULL) {
queue = dcc_queue_old(nick, servertag);
if (queue != -1)
@@ -190,8 +199,8 @@ static void cmd_dcc_send(const char *data, IRC_SERVER_REC *server,
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 +319,28 @@ static void dcc_send_connected(SEND_DCC_REC *dcc)
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,22 +360,23 @@ static char *dcc_send_get_file(const char *fname)
}
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;
+ GIOChannel *handle;
if (dcc_find_request(DCC_SEND_TYPE, target, fname)) {
- signal_emit("dcc error send exists", 2, target, fname);
+ signal_emit("dcc error send exists", 2, target, fname);
return FALSE;
}
- str = dcc_send_get_file(fname);
+ str = dcc_send_get_file(fname);
hfile = open(str, O_RDONLY);
g_free(str);
@@ -360,14 +392,19 @@ static int dcc_send_one_file(int queue, const char *target, const char *fname,
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,20 +428,37 @@ static int dcc_send_one_file(int queue, const char *target, const char *fname,
dcc->fhandle = hfile;
dcc->queue = queue;
dcc->file_quoted = strchr(fname, ' ') != NULL;
- dcc->tagconn = g_input_add(handle, G_INPUT_READ,
- (GInputFunction) dcc_send_connected, dcc);
+ if (!passive) {
+ dcc->tagconn = g_input_add(handle, G_INPUT_READ,
+ (GInputFunction) dcc_send_connected,
+ dcc);
+ }
+ /* Generate an ID for this send if using passive protocol */
+ if (passive) {
+ 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);
+ 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);
+ g_free(str);
return TRUE;
}
@@ -414,8 +468,9 @@ void dcc_send_init(void)
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 +481,6 @@ void dcc_send_deinit(void)
dcc_unregister_type("SEND");
signal_remove("dcc destroyed", (SIGNAL_FUNC) sig_dcc_destroyed);
+ signal_remove("dcc reply send pasv", (SIGNAL_FUNC) dcc_send_connect);
command_unbind("dcc send", (SIGNAL_FUNC) cmd_dcc_send);
}
diff --git a/src/irc/dcc/dcc.c b/src/irc/dcc/dcc.c
index b66db5f1..ddf31b4f 100644
--- a/src/irc/dcc/dcc.c
+++ b/src/irc/dcc/dcc.c
@@ -92,7 +92,9 @@ void dcc_init_rec(DCC_REC *dcc, IRC_SERVER_REC *server, CHAT_DCC_REC *chat,
dcc->servertag = server != NULL ? g_strdup(server->tag) :
(chat == NULL ? NULL : g_strdup(chat->servertag));
-
+
+ dcc->pasv_id = -1; /* Not a passive DCC */
+
dcc_conns = g_slist_append(dcc_conns, dcc);
signal_emit("dcc created", 1, dcc);
}
diff --git a/src/irc/dcc/dcc.h b/src/irc/dcc/dcc.h
index 5a4c9e23..10639207 100644
--- a/src/irc/dcc/dcc.h
+++ b/src/irc/dcc/dcc.h
@@ -24,6 +24,10 @@ typedef struct {
#define dcc_is_waiting_user(dcc) \
((dcc)->handle == NULL)
+/* passive DCC */
+#define dcc_is_passive(dcc) \
+ ((dcc)->pasv_id >= 0)
+
extern GSList *dcc_conns;
void dcc_register_type(const char *type);