diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/irc/dcc/dcc-chat.c | 157 | ||||
-rw-r--r-- | src/irc/dcc/dcc-get.c | 135 | ||||
-rw-r--r-- | src/irc/dcc/dcc-get.h | 4 | ||||
-rw-r--r-- | src/irc/dcc/dcc-queue.c | 25 | ||||
-rw-r--r-- | src/irc/dcc/dcc-queue.h | 4 | ||||
-rw-r--r-- | src/irc/dcc/dcc-rec.h | 2 | ||||
-rw-r--r-- | src/irc/dcc/dcc-resume.c | 65 | ||||
-rw-r--r-- | src/irc/dcc/dcc-send.c | 116 | ||||
-rw-r--r-- | src/irc/dcc/dcc.c | 4 | ||||
-rw-r--r-- | src/irc/dcc/dcc.h | 4 |
10 files changed, 415 insertions, 101 deletions
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); |