summaryrefslogtreecommitdiff
path: root/src/irc/dcc
diff options
context:
space:
mode:
Diffstat (limited to 'src/irc/dcc')
-rw-r--r--src/irc/dcc/Makefile.am6
-rw-r--r--src/irc/dcc/dcc-chat.c4
-rw-r--r--src/irc/dcc/dcc-get.c40
-rw-r--r--src/irc/dcc/dcc-get.h1
-rw-r--r--src/irc/dcc/dcc-server.c416
-rw-r--r--src/irc/dcc/dcc-server.h30
-rw-r--r--src/irc/dcc/dcc.c10
7 files changed, 488 insertions, 19 deletions
diff --git a/src/irc/dcc/Makefile.am b/src/irc/dcc/Makefile.am
index 85ccdac7..d2b1cf02 100644
--- a/src/irc/dcc/Makefile.am
+++ b/src/irc/dcc/Makefile.am
@@ -13,7 +13,8 @@ libirc_dcc_a_SOURCES = \
dcc-send.c \
dcc-resume.c \
dcc-autoget.c \
- dcc-queue.c
+ dcc-queue.c \
+ dcc-server.c
noinst_HEADERS = \
dcc-rec.h \
@@ -24,4 +25,5 @@ noinst_HEADERS = \
dcc-get.h \
dcc-send.h \
dcc-queue.h \
- module.h
+ module.h \
+ dcc-server.h
diff --git a/src/irc/dcc/dcc-chat.c b/src/irc/dcc/dcc-chat.c
index d7ae68df..3d97ae0c 100644
--- a/src/irc/dcc/dcc-chat.c
+++ b/src/irc/dcc/dcc-chat.c
@@ -55,7 +55,7 @@ static char *dcc_chat_get_new_id(const char *nick)
}
}
-static CHAT_DCC_REC *dcc_chat_create(IRC_SERVER_REC *server,
+CHAT_DCC_REC *dcc_chat_create(IRC_SERVER_REC *server,
CHAT_DCC_REC *chat,
const char *nick, const char *arg)
{
@@ -285,7 +285,7 @@ static void cmd_ctcp(const char *data, SERVER_REC *server)
}
/* 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 --git a/src/irc/dcc/dcc-get.c b/src/irc/dcc/dcc-get.c
index 9340645d..aa0c6390 100644
--- a/src/irc/dcc/dcc-get.c
+++ b/src/irc/dcc/dcc-get.c
@@ -31,7 +31,7 @@
static int dcc_file_create_mode;
-static GET_DCC_REC *dcc_get_create(IRC_SERVER_REC *server, CHAT_DCC_REC *chat,
+GET_DCC_REC *dcc_get_create(IRC_SERVER_REC *server, CHAT_DCC_REC *chat,
const char *nick, const char *arg)
{
GET_DCC_REC *dcc;
@@ -172,21 +172,23 @@ static void sig_dccget_receive(GET_DCC_REC *dcc)
}
/* 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;
+ char *fname, *tempfname, *str;
int ret, ret_errno, temphandle, old_umask;
- if (net_geterror(dcc->handle) != 0) {
- /* error connecting */
- signal_emit("dcc error connect", 1, dcc);
- dcc_destroy(DCC(dcc));
- return;
- }
+ if (!dcc->from_dccserver) {
+ if (net_geterror(dcc->handle) != 0) {
+ /* error connecting */
+ signal_emit("dcc error connect", 1, dcc);
+ dcc_destroy(DCC(dcc));
+ return;
+ }
- g_source_remove(dcc->tagconn);
- dcc->tagconn = -1;
+ g_source_remove(dcc->tagconn);
+ dcc->tagconn = -1;
+ }
g_free_not_null(dcc->file);
dcc->file = dcc_get_download_path(dcc->arg);
@@ -253,6 +255,12 @@ static void sig_dccget_connected(GET_DCC_REC *dcc)
dcc->tagread = g_input_add(dcc->handle, G_INPUT_READ,
(GInputFunction) sig_dccget_receive, dcc);
signal_emit("dcc connected", 1, dcc);
+
+ if (dcc->from_dccserver) {
+ str = g_strdup_printf("121 %s %d\n",
+ dcc->server ? dcc->server->nick : "??", 0);
+ net_transmit(dcc->handle, str, strlen(str));
+ }
}
void dcc_get_connect(GET_DCC_REC *dcc)
@@ -262,7 +270,13 @@ void dcc_get_connect(GET_DCC_REC *dcc)
DCC_GET_RENAME : DCC_GET_OVERWRITE;
}
+ if (dcc->from_dccserver) {
+ sig_dccget_connected(dcc);
+ return;
+ }
+
dcc->handle = dcc_connect_ip(&dcc->addr, dcc->port);
+
if (dcc->handle != NULL) {
dcc->tagconn =
g_input_add(dcc->handle,
@@ -408,8 +422,8 @@ void cmd_dcc_receive(const char *data, DCC_GET_FUNC accept_func)
GET_DCC_REC *dcc = tmp->data;
next = tmp->next;
- if (IS_DCC_GET(dcc) && dcc_is_waiting_user(dcc) &&
- g_strcasecmp(dcc->nick, nick) == 0 &&
+ if (IS_DCC_GET(dcc) && g_strcasecmp(dcc->nick, nick) == 0 &&
+ (dcc_is_waiting_user(dcc) || dcc->from_dccserver) &&
(*fname == '\0' || strcmp(dcc->arg, fname) == 0)) {
found = TRUE;
accept_func(dcc);
diff --git a/src/irc/dcc/dcc-get.h b/src/irc/dcc/dcc-get.h
index 65f5b682..321a8b4e 100644
--- a/src/irc/dcc/dcc-get.h
+++ b/src/irc/dcc/dcc-get.h
@@ -24,6 +24,7 @@ typedef struct {
char *file; /* file name we're really moving, arg is just the reference */
unsigned int file_quoted:1; /* file name was received quoted ("file name") */
+ unsigned int from_dccserver:1; /* get is using dccserver method */
} GET_DCC_REC;
#define DCC_GET_TYPE module_get_uniq_id_str("DCC", "GET")
diff --git a/src/irc/dcc/dcc-server.c b/src/irc/dcc/dcc-server.c
new file mode 100644
index 00000000..0a073a9d
--- /dev/null
+++ b/src/irc/dcc/dcc-server.c
@@ -0,0 +1,416 @@
+/*
+ dcc-server.c : irssi
+
+ Copyright (C) 2003 Mark Trumbull
+
+ 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 "module.h"
+#include "signals.h"
+#include "commands.h"
+#include "network.h"
+#include "net-sendbuffer.h"
+#include "line-split.h"
+#include "misc.h"
+
+#include "irc-servers.h"
+
+#include "dcc-chat.h"
+#include "dcc-get.h"
+#include "dcc-server.h"
+
+void sig_dccget_connected(GET_DCC_REC *dcc);
+GET_DCC_REC *dcc_get_create(IRC_SERVER_REC *server, CHAT_DCC_REC *chat,
+ const char *nick, const char *arg);
+
+void dcc_chat_input(CHAT_DCC_REC *dcc);
+CHAT_DCC_REC *dcc_chat_create(IRC_SERVER_REC *server, CHAT_DCC_REC *chat,
+ const char *nick, const char *arg);
+
+static void sig_dcc_destroyed(SERVER_DCC_REC *dcc)
+{
+ if (!IS_DCC_SERVER(dcc))
+ return;
+
+ if (dcc->sendbuf != NULL)
+ net_sendbuffer_destroy(dcc->sendbuf, FALSE);
+ line_split_free(dcc->readbuf);
+}
+
+/* Start listening for incoming connections */
+static GIOChannel *dcc_listen_port(GIOChannel *iface, IPADDR *ip, int port)
+{
+ if (net_getsockname(iface, ip, NULL) == -1)
+ return NULL;
+
+ if (IPADDR_IS_V6(ip))
+ return net_listen(NULL, &port);
+ else
+ return net_listen(&ip4_any, &port);
+}
+
+/* input function: DCC SERVER received some data.. */
+static void dcc_server_input(SERVER_DCC_REC *dcc)
+{
+ char tmpbuf[512], *str;
+ int recvlen, ret;
+
+ g_return_if_fail(IS_DCC_SERVER(dcc));
+
+ do {
+ recvlen = net_receive(dcc->handle, tmpbuf, sizeof(tmpbuf));
+
+ ret = line_split(tmpbuf, recvlen, &str, &dcc->readbuf);
+ if (ret == -1) {
+ /* connection lost */
+ dcc_close(DCC(dcc));
+ break;
+ }
+
+ if (ret > 0) {
+ dcc->transfd += ret;
+ signal_emit("dcc server message", 2, dcc, str);
+ }
+
+ if (dcc->connection_established) {
+ /* We set handle to NULL first because the new (chat/get) is using the same */
+ /* handle and we don't want dcc_close to disconnect it.*/
+ dcc->handle = NULL;
+ dcc_close(DCC(dcc));
+ break;
+ }
+ } while (ret > 0);
+}
+
+static void dcc_server_update_flags(SERVER_DCC_REC *dcc, const char *flags)
+{
+ g_return_if_fail(dcc != NULL);
+ g_return_if_fail(IS_DCC_SERVER(dcc));
+
+ if (*flags == '+' || *flags == '-') {
+ const char *ptr = flags + 1;
+ unsigned int value = (*flags == '+') ? 1 : 0;
+
+ while (*ptr) {
+ if (*ptr == 's' || *ptr == 'S') { dcc->accept_send = value; }
+ else if (*ptr == 'c' || *ptr == 'C') { dcc->accept_chat = value; }
+ else if (*ptr == 'f' || *ptr == 'F') { dcc->accept_fserve = value; }
+ ptr++;
+ }
+ }
+}
+
+/* Initialize DCC record */
+static void dcc_init_server_rec(SERVER_DCC_REC *dcc, IRC_SERVER_REC *server)
+{
+ g_return_if_fail(dcc != NULL);
+ g_return_if_fail(server != NULL);
+ g_return_if_fail(IS_DCC_SERVER(dcc));
+
+ MODULE_DATA_INIT(dcc);
+ dcc->created = time(NULL);
+ dcc->chat = NULL;
+ dcc->arg = NULL;
+ dcc->nick = NULL;
+ dcc->tagconn = dcc->tagread = dcc->tagwrite = -1;
+ dcc->server = server;
+ dcc->mynick = g_strdup(server != NULL ? server->nick : "??");
+ dcc->servertag = server != NULL ? g_strdup(server->tag) : NULL;
+
+ dcc_conns = g_slist_append(dcc_conns, dcc);
+ signal_emit("dcc created", 1, dcc);
+}
+
+static SERVER_DCC_REC *dcc_server_create(IRC_SERVER_REC *server, const char *flags)
+{
+ SERVER_DCC_REC *dcc;
+
+ dcc = g_new0(SERVER_DCC_REC, 1);
+ dcc->orig_type = dcc->type = DCC_SERVER_TYPE;
+ dcc_server_update_flags(dcc, flags);
+
+ dcc_init_server_rec(dcc, server);
+ return dcc;
+}
+
+static SERVER_DCC_REC *dcc_server_clone(SERVER_DCC_REC *dcc)
+{
+ SERVER_DCC_REC *newdcc;
+
+ g_return_val_if_fail(IS_DCC_SERVER(dcc), NULL);
+
+ newdcc = g_new0(SERVER_DCC_REC, 1);
+ newdcc->orig_type = newdcc->type = DCC_SERVER_TYPE;
+ newdcc->accept_send = dcc->accept_send;
+ newdcc->accept_chat = dcc->accept_chat;
+ newdcc->accept_fserve = dcc->accept_fserve;
+
+ dcc_init_server_rec(newdcc, dcc->server);
+ return newdcc;
+}
+
+/* input function: DCC SERVER - someone tried to connect to our socket */
+static void dcc_server_listen(SERVER_DCC_REC *dcc)
+{
+ SERVER_DCC_REC *newdcc;
+ IPADDR ip;
+ GIOChannel *handle;
+ int port;
+
+ g_return_if_fail(IS_DCC_SERVER(dcc));
+
+ /* accept connection */
+ handle = net_accept(dcc->handle, &ip, &port);
+ if (handle == NULL)
+ return;
+
+ /* Create a new DCC SERVER to handle this connection */
+ newdcc = dcc_server_clone(dcc);
+
+ newdcc->starttime = time(NULL);
+ newdcc->handle = handle;
+ newdcc->sendbuf = net_sendbuffer_create(handle, 0);
+ memcpy(&newdcc->addr, &ip, sizeof(IPADDR));
+ net_ip2host(&newdcc->addr, newdcc->addrstr);
+ newdcc->port = port;
+ newdcc->tagread = g_input_add(handle, G_INPUT_READ,
+ (GInputFunction) dcc_server_input, newdcc);
+
+ signal_emit("dcc connected", 1, newdcc);
+}
+
+/* DCC SERVER: text received */
+static void dcc_server_msg(SERVER_DCC_REC *dcc, const char *msg)
+{
+ g_return_if_fail(IS_DCC_SERVER(dcc));
+ g_return_if_fail(msg != NULL);
+
+ /* Check for CHAT protocol */
+ if (g_strncasecmp(msg, "100 ", 4) == 0) {
+ msg += 4;
+ /* Check if this server is accepting chat requests.*/
+ if (dcc->accept_chat) {
+ /* Connect and start DCC Chat */
+ char *str;
+ CHAT_DCC_REC *dccchat = dcc_chat_create(dcc->server, NULL, msg, "chat");
+
+ dccchat->starttime = time(NULL);
+ dccchat->handle = dcc->handle;
+ dccchat->sendbuf = net_sendbuffer_create(dccchat->handle, 0);
+ memcpy(&dccchat->addr, &dcc->addr, sizeof(IPADDR));
+ net_ip2host(&dccchat->addr, dccchat->addrstr);
+ dccchat->port = dcc->port;
+ dccchat->tagread = g_input_add(dccchat->handle, G_INPUT_READ,
+ (GInputFunction) dcc_chat_input, dccchat);
+
+ dcc->connection_established = 1;
+ signal_emit("dcc connected", 1, dccchat);
+
+ str = g_strdup_printf("101 %s\n",
+ (dccchat->server) ? dccchat->server->nick : "??");
+ net_sendbuffer_send(dccchat->sendbuf, str, strlen(str));
+ g_free(str);
+ }
+ }
+
+ /* Check for FSERVE protocol */
+ if (g_strncasecmp(msg, "110 ", 4) == 0) {
+ msg += 4;
+ /* Check if this server is accepting fserve requests.*/
+ if (dcc->accept_fserve) {
+ /* TODO - Connect and start DCC Fserve */
+ }
+ }
+
+ /* Check for SEND protocol */
+ if (g_strncasecmp(msg, "120 ", 4) == 0) {
+ msg += 4;
+ /* Check if this server is accepting send requests.*/
+ if (dcc->accept_send) {
+ /* Connect and start DCC Send */
+ GET_DCC_REC *dccget;
+ char **params, *fname, *nick;
+ int paramcount, len, quoted = FALSE;
+ uoff_t size;
+
+ /* 120 clientnickname filesize filename */
+ params = g_strsplit(msg, " ", -1);
+ paramcount = strarray_length(params);
+
+ if (paramcount < 3) {
+ g_strfreev(params);
+ signal_stop();
+ return;
+ }
+
+ nick = params[0];
+ size = str_to_uofft(params[1]);
+ fname = g_strjoinv(" ", &params[2]);
+
+ len = strlen(fname);
+ if (len > 1 && *fname == '"' && fname[len-1] == '"') {
+ /* "file name" - MIRC sends filenames with spaces like this */
+ fname[len-1] = '\0';
+ g_memmove(fname, fname+1, len);
+ quoted = TRUE;
+ }
+
+ dccget = dcc_get_create(dcc->server, NULL, nick, fname);
+ dccget->handle = dcc->handle;
+ dccget->target = g_strdup(dcc->server ? dcc->server->nick : "??");
+ memcpy(&dccget->addr, &dcc->addr, sizeof(dcc->addr));
+ if (dccget->addr.family == AF_INET) {
+ net_ip2host(&dccget->addr, dccget->addrstr);
+ } else {
+ /* with IPv6, show it to us as it was sent */
+ memcpy(dccget->addrstr, dcc->addrstr, sizeof(dccget->addrstr));
+ }
+ dccget->port = dcc->port;
+ dccget->size = size;
+ dccget->file_quoted = quoted;
+ dccget->from_dccserver = 1;
+
+ dcc->connection_established = 1;
+ signal_emit("dcc request", 2, dccget, dccget->addrstr);
+
+ g_strfreev(params);
+ g_free(fname);
+ }
+ }
+
+ signal_stop();
+}
+
+SERVER_DCC_REC *dcc_server_find_port(const char *port_str)
+{
+ GSList *tmp;
+ unsigned int port = 0;
+
+ g_return_val_if_fail(port_str != NULL, NULL);
+
+ port = atoi(port_str);
+
+ for (tmp = dcc_conns; tmp != NULL; tmp = tmp->next) {
+ SERVER_DCC_REC *dcc = tmp->data;
+
+ if (IS_DCC_SERVER(dcc) && dcc->port == port)
+ return dcc;
+ }
+
+ return NULL;
+}
+
+/* SYNTAX: DCC SERVER [+|-scf] [port] */
+static void cmd_dcc_server(const char *data, IRC_SERVER_REC *server)
+{
+ void *free_arg;
+ GIOChannel *handle;
+ SERVER_DCC_REC *dcc;
+ IPADDR own_ip;
+ char *flags, *port;
+
+ g_return_if_fail(data != NULL);
+
+ if (!cmd_get_params(data, &free_arg, 2, &flags, &port))
+ return;
+
+ dcc = dcc_server_find_port(port);
+ if (dcc != NULL) {
+ /* Server is already running, update it */
+ dcc_server_update_flags(dcc, flags);
+ cmd_params_free(free_arg);
+ return;
+ }
+
+ /* start listening */
+ if (!IS_IRC_SERVER(server) || !server->connected) {
+ cmd_param_error(CMDERR_NOT_CONNECTED);
+ }
+
+ handle = dcc_listen_port(net_sendbuffer_handle(server->handle),
+ &own_ip, atoi(port));
+
+ if (handle == NULL) {
+ cmd_param_error(CMDERR_ERRNO);
+ }
+
+ dcc = dcc_server_create(server, flags);
+ dcc->handle = handle;
+ dcc->port = atoi(port);
+ dcc->tagconn = g_input_add(dcc->handle, G_INPUT_READ,
+ (GInputFunction) dcc_server_listen, dcc);
+
+ signal_emit("dcc server started", 1, dcc);
+
+ cmd_params_free(free_arg);
+}
+
+/* DCC CLOSE SERVER <port> */
+static void cmd_dcc_close(char *data, SERVER_REC *server)
+{
+ GSList *tmp, *next;
+ char *port_str;
+ void *free_arg;
+ int found, port;
+
+ g_return_if_fail(data != NULL);
+
+ if (g_strncasecmp(data, "SERVER ", 7) != 0 ||
+ !cmd_get_params(data, &free_arg, 2, NULL, &port_str)) {
+ return;
+ }
+
+ if (*port_str == '\0') {
+ cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
+ }
+
+ port = atoi(port_str);
+ found = FALSE;
+ for (tmp = dcc_conns; tmp != NULL; tmp = next) {
+ SERVER_DCC_REC *dcc = tmp->data;
+
+ next = tmp->next;
+ if (IS_DCC_SERVER(dcc) && dcc->port == port) {
+ found = TRUE;
+ dcc_close(DCC(dcc));
+ }
+ }
+
+ if (found) {
+ signal_stop();
+ }
+
+ cmd_params_free(free_arg);
+}
+
+void dcc_server_init(void)
+{
+ dcc_register_type("SERVER");
+ command_bind("dcc server", NULL, (SIGNAL_FUNC) cmd_dcc_server);
+ command_bind("dcc close", NULL, (SIGNAL_FUNC) cmd_dcc_close);
+ signal_add("dcc destroyed", (SIGNAL_FUNC) sig_dcc_destroyed);
+ signal_add_first("dcc server message", (SIGNAL_FUNC) dcc_server_msg);
+}
+
+void dcc_server_deinit(void)
+{
+ dcc_unregister_type("SERVER");
+ command_unbind("dcc server", (SIGNAL_FUNC) cmd_dcc_server);
+ command_unbind("dcc close", (SIGNAL_FUNC) cmd_dcc_close);
+ signal_remove("dcc destroyed", (SIGNAL_FUNC) sig_dcc_destroyed);
+ signal_remove("dcc server message", (SIGNAL_FUNC) dcc_server_msg);
+}
+
diff --git a/src/irc/dcc/dcc-server.h b/src/irc/dcc/dcc-server.h
new file mode 100644
index 00000000..4f6f248e
--- /dev/null
+++ b/src/irc/dcc/dcc-server.h
@@ -0,0 +1,30 @@
+#ifndef __DCC_SERVER_H
+#define __DCC_SERVER_H
+
+#include "dcc.h"
+
+#define DCC_SERVER(dcc) \
+ MODULE_CHECK_CAST_MODULE(dcc, SERVER_DCC_REC, type, "DCC", "SERVER")
+
+#define IS_DCC_SERVER(dcc) \
+ (DCC_SERVER(dcc) ? TRUE : FALSE)
+
+struct SERVER_DCC_REC {
+#include "dcc-rec.h"
+ LINEBUF_REC *readbuf;
+ NET_SENDBUF_REC *sendbuf;
+
+ unsigned int accept_send:1; /* Accept SEND connections */
+ unsigned int accept_chat:1; /* Accept CHAT connections */
+ unsigned int accept_fserve:1; /* Accept FSERVE connections */
+ unsigned int connection_established:1; /* We have made a connection */
+};
+
+#define DCC_SERVER_TYPE module_get_uniq_id_str("DCC", "SERVER")
+
+typedef struct SERVER_DCC_REC SERVER_DCC_REC;
+
+void dcc_server_init(void);
+void dcc_server_deinit(void);
+
+#endif
diff --git a/src/irc/dcc/dcc.c b/src/irc/dcc/dcc.c
index 8bbca251..2fd6317f 100644
--- a/src/irc/dcc/dcc.c
+++ b/src/irc/dcc/dcc.c
@@ -34,6 +34,7 @@
#include "dcc-chat.h"
#include "dcc-get.h"
#include "dcc-send.h"
+#include "dcc-server.h"
void dcc_resume_init(void);
void dcc_resume_deinit(void);
@@ -431,11 +432,13 @@ static int dcc_timeout_func(void)
DCC_REC *dcc = tmp->data;
next = tmp->next;
- if (dcc->tagread == -1 && now > dcc->created) {
+ if (dcc->tagread == -1 && now > dcc->created && !IS_DCC_SERVER(dcc)) {
/* Timed out - don't send DCC REJECT CTCP so CTCP
flooders won't affect us and it really doesn't
matter that much anyway if the other side doen't
- get it.. */
+ get it..
+
+ We don't want dcc servers to time out. */
dcc_close(dcc);
}
}
@@ -540,6 +543,7 @@ void irc_dcc_init(void)
dcc_send_init();
dcc_resume_init();
dcc_autoget_init();
+ dcc_server_init();
module_register("dcc", "irc");
}
@@ -554,6 +558,7 @@ void irc_dcc_deinit(void)
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);
@@ -569,3 +574,4 @@ void irc_dcc_deinit(void)
g_source_remove(dcc_timeouttag);
}
+