/* dcc-send-limiter.c : Limit the transmit speed of DCC sends For irssi 0.8+ compile: export IRSSI=~/cvs/irssi gcc dcc-send-limiter.c -o ~/.irssi/modules/libdcc_send_limiter.so -g -shared -I$IRSSI -I$IRSSI/src -I$IRSSI/src/core -I$IRSSI/src/irc/core -I$IRSSI/src/irc/dcc `glib-config --cflags` -O usage: /LOAD dcc_send_limiter Copyright (C) 2001 Timo Sirainen Modified 2001/07/04 by Martin Persson * updated to only keep track of the last 30 sec Modified 2001/07/01 by Martin Persson * added speed send checks * fixed crash when destroying dcc sends that didn't contain any module data * fixed crash when initiating dcc send 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 */ #define MODULE_NAME "irc/dcc/limiter" #define HAVE_CONFIG_H #include "common.h" #include "signals.h" #include "network.h" #include "settings.h" #include "irc.h" #include "dcc-send.h" typedef struct { int timeout_tag; unsigned long skip_bytes; unsigned long starttime; unsigned long max_speed; } MODULE_SEND_DCC_REC; static void dcc_send_data(SEND_DCC_REC *dcc); static void reset_dcc_send(SEND_DCC_REC *dcc) { MODULE_SEND_DCC_REC *mdcc; if (g_slist_find(dcc_conns, dcc) == NULL) { /* the DCC was closed during the wait */ return; } mdcc = MODULE_DATA(dcc); g_source_remove(mdcc->timeout_tag); mdcc->timeout_tag = -1; dcc->tagwrite = g_input_add(dcc->handle, G_INPUT_WRITE, (GInputFunction) dcc_send_data, dcc); } static int sent_too_much(SEND_DCC_REC *dcc, MODULE_SEND_DCC_REC *mdcc) { GTimeVal gtv; unsigned long timediff, curtime; unsigned long transfd, speed; /* 0 == unlimited speed */ if (mdcc->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 - mdcc->skip_bytes); timediff = curtime - mdcc->starttime + 1; speed = ((transfd * 1000) / timediff); /* reset speed counter every 30 seconds */ if (timediff >= 30000) { mdcc->starttime = curtime; mdcc->skip_bytes = dcc->transfd; } return (speed > (mdcc->max_speed * 1024)); } /* input function: DCC SEND - we're ready to send more data */ static void dcc_send_data(SEND_DCC_REC *dcc) { MODULE_SEND_DCC_REC *mdcc; char buffer[512]; int ret, max_speed; GTimeVal gtv; mdcc = MODULE_DATA(dcc); max_speed = settings_get_int("dcc_send_top_speed"); if (max_speed != mdcc->max_speed) { /* speed setting has changed, calculate speed from current position instead of from the start to eliminate speed boosts/slowdowns */ mdcc->max_speed = max_speed; mdcc->skip_bytes = dcc->transfd; g_get_current_time(>v); mdcc->starttime = (gtv.tv_sec * 1000) + (gtv.tv_usec / 1000); } if (sent_too_much(dcc, mdcc)) { /* disable calling this function for 1/10th of a second. */ g_source_remove(dcc->tagwrite); dcc->tagwrite = -1; mdcc->timeout_tag = g_timeout_add(100, (GSourceFunc) reset_dcc_send, dcc); return; } ret = read(dcc->fhandle, buffer, sizeof(buffer)); if (ret <= 0) { /* no need to call this function anymore.. in fact it just eats all the cpu.. */ dcc->waitforend = TRUE; g_source_remove(dcc->tagwrite); dcc->tagwrite = -1; return; } ret = net_transmit(dcc->handle, buffer, ret); if (ret > 0) dcc->transfd += ret; dcc->gotalldata = FALSE; lseek(dcc->fhandle, dcc->transfd, SEEK_SET); signal_emit("dcc transfer update", 1, dcc); } static void sig_dcc_connected(SEND_DCC_REC *dcc) { MODULE_SEND_DCC_REC *mdcc; GTimeVal gtv; if (!IS_DCC_SEND(dcc)) return; mdcc = g_new0(MODULE_SEND_DCC_REC, 1); MODULE_DATA_SET(dcc, mdcc); mdcc->timeout_tag = -1; mdcc->skip_bytes = 0; mdcc->max_speed = settings_get_int("dcc_send_top_speed"); /* get starttime in milliseconds */ g_get_current_time(>v); mdcc->starttime = (gtv.tv_sec * 1000) + (gtv.tv_usec / 1000); g_source_remove(dcc->tagwrite); dcc->tagwrite = g_input_add(dcc->handle, G_INPUT_WRITE, (GInputFunction) dcc_send_data, dcc); } static void sig_dcc_destroyed(SEND_DCC_REC *dcc) { MODULE_SEND_DCC_REC *mdcc; if (!IS_DCC_SEND(dcc)) return; mdcc = MODULE_DATA(dcc); if (mdcc != NULL) { if (mdcc->timeout_tag != -1) g_source_remove(mdcc->timeout_tag); g_free(mdcc); } } void dcc_send_limiter_init(void) { settings_add_int("dcc", "dcc_send_top_speed", 30); signal_add_last("dcc connected", (SIGNAL_FUNC) sig_dcc_connected); signal_add_first("dcc destroyed", (SIGNAL_FUNC) sig_dcc_destroyed); module_register("dcc_send_limiter", "core"); } void dcc_send_limiter_deinit(void) { signal_remove("dcc connected", (SIGNAL_FUNC) sig_dcc_connected); signal_remove("dcc destroyed", (SIGNAL_FUNC) sig_dcc_destroyed); }