diff -urNp -x '*.orig' irssi-1.2.2.org/src/irc/dcc/dcc-rec.h irssi-1.2.2/src/irc/dcc/dcc-rec.h --- irssi-1.2.2.org/src/irc/dcc/dcc-rec.h 2019-08-29 15:48:43.000000000 +0200 +++ irssi-1.2.2/src/irc/dcc/dcc-rec.h 2021-05-04 21:22:16.382408355 +0200 @@ -22,6 +22,12 @@ uoff_t transfd; /* bytes transferred */ int pasv_id; /* DCC Id for passive DCCs. <0 means a passive DCC, >=0 means a standard DCC */ +int timeout_tag; + +unsigned long skip_bytes; +unsigned long limit_starttime; +unsigned long max_speed; + unsigned int destroyed:1; /* We're about to destroy this DCC recond */ GHashTable *module_data; diff -urNp -x '*.orig' irssi-1.2.2.org/src/irc/dcc/dcc-send.c irssi-1.2.2/src/irc/dcc/dcc-send.c --- irssi-1.2.2.org/src/irc/dcc/dcc-send.c 2019-08-29 15:48:46.000000000 +0200 +++ irssi-1.2.2/src/irc/dcc/dcc-send.c 2021-05-04 21:22:16.382408355 +0200 @@ -250,17 +250,87 @@ static void sig_dcc_destroyed(SEND_DCC_R { if (!IS_DCC_SEND(dcc)) return; + if (dcc->timeout_tag != -1) + g_source_remove(dcc->timeout_tag); + if (dcc->fhandle != -1) close(dcc->fhandle); dcc_queue_send_next(dcc->queue); } +static int sent_too_much(SEND_DCC_REC *dcc) +{ + GTimeVal gtv; + gulong timediff, curtime; + gulong transfd, speed; + + /* 0 == unlimited speed */ + if (dcc->max_speed == 0) return 0; + + /* get time difference in milliseconds */ + g_get_current_time(>v); + curtime = (gtv.tv_sec * 1000) + (gtv.tv_usec / 1000); + + transfd = (dcc->transfd - dcc->skip_bytes); + timediff = curtime - dcc->limit_starttime + 1; + speed = ((transfd * 1000) / timediff); + + /* reset speed counter every 30 seconds */ + if (timediff >= 30000) { + dcc->limit_starttime = curtime; + dcc->skip_bytes = dcc->transfd; + } + + return (speed > (dcc->max_speed * 1024)); +} + +static void dcc_send_data(SEND_DCC_REC *dcc); + +static void reset_dcc_send(SEND_DCC_REC *dcc) +{ + if (g_slist_find(dcc_conns, dcc) == NULL) { + /* the DCC was closed during the wait */ + return; + } + + if (dcc->timeout_tag != -1) + g_source_remove(dcc->timeout_tag); + dcc->timeout_tag = -1; + + dcc->tagwrite = g_input_add(dcc->handle, G_INPUT_WRITE, + (GInputFunction) dcc_send_data, dcc); + + return; +} + /* input function: DCC SEND - we're ready to send more data */ static void dcc_send_data(SEND_DCC_REC *dcc) { char buffer[512]; - int ret; + int ret, max_speed; + GTimeVal gtv; + + max_speed = settings_get_int("dcc_send_top_speed"); + if (max_speed != dcc->max_speed) { + /* speed setting has changed, calculate speed from current position + instead of from the start to eliminate speed boosts/slowdowns */ + + dcc->max_speed = max_speed; + dcc->skip_bytes = dcc->transfd; + + g_get_current_time(>v); + dcc->limit_starttime = (gtv.tv_sec * 1000) + (gtv.tv_usec / 1000); + } + + if (sent_too_much(dcc)) { + /* disable calling this function for 1/10th of a second. */ + g_source_remove(dcc->tagwrite); + dcc->tagwrite = -1; + dcc->timeout_tag = g_timeout_add(100, (GSourceFunc) + reset_dcc_send, dcc); + return; + } ret = read(dcc->fhandle, buffer, sizeof(buffer)); if (ret <= 0) { @@ -316,6 +386,7 @@ static void dcc_send_connected(SEND_DCC_ GIOChannel *handle; IPADDR addr; int port; + GTimeVal gtv; /* accept connection */ handle = net_accept(dcc->handle, &addr, &port); @@ -330,6 +401,13 @@ static void dcc_send_connected(SEND_DCC_ g_source_remove(dcc->tagconn); dcc->tagconn = -1; + dcc->skip_bytes = 0; + dcc->max_speed = settings_get_int("dcc_send_top_speed"); + + /* get starttime in milliseconds */ + g_get_current_time(>v); + dcc->limit_starttime = (gtv.tv_sec * 1000) + (gtv.tv_usec / 1000); + dcc->starttime = time(NULL); dcc->handle = handle; memcpy(&dcc->addr, &addr, sizeof(IPADDR)); @@ -434,6 +512,7 @@ static int dcc_send_one_file(int queue, dcc->size = st.st_size; dcc->fhandle = hfile; dcc->queue = queue; + dcc->timeout_tag = -1; dcc->file_quoted = strchr(fname, ' ') != NULL; if (!passive) { dcc->tagconn = g_input_add(handle, G_INPUT_READ, @@ -473,6 +552,7 @@ void dcc_send_init(void) dcc_register_type("SEND"); settings_add_str("dcc", "dcc_upload_path", "~"); settings_add_bool("dcc", "dcc_send_replace_space_with_underscore", FALSE); + settings_add_int("dcc", "dcc_send_top_speed", 0); 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);