1 /*
2  dcc-send-limiter.c : Limit the transmit speed of DCC sends
3 
4  For irssi 0.8+
5 
6  compile:
7    export IRSSI=~/cvs/irssi
8    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
9 
10  usage:
11    /LOAD dcc_send_limiter
12 
13     Copyright (C) 2001 Timo Sirainen
14 
15     Modified 2002/12/31 by Piotr Krukowiecki (Happy New Year! ;))
16     	* fixed unnecesary lag in sending data when send is resume
17     	* sends that were started before the module was loaded
18     	  now are being limited as well
19 
20     Modified 2001/07/04 by Martin Persson
21     	* updated to only keep track of the last 30 sec
22 
23     Modified 2001/07/01 by Martin Persson
24     	* added speed send checks
25     	* fixed crash when destroying dcc sends
26     	  that didn't contain any module data
27     	* fixed crash when initiating dcc send
28 
29 
30     This program is free software; you can redistribute it and/or modify
31     it under the terms of the GNU General Public License as published by
32     the Free Software Foundation; either version 2 of the License, or
33     (at your option) any later version.
34 
35     This program is distributed in the hope that it will be useful,
36     but WITHOUT ANY WARRANTY; without even the implied warranty of
37     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
38     GNU General Public License for more details.
39 
40     You should have received a copy of the GNU General Public License
41     along with this program; if not, write to the Free Software
42     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
43 */
44 
45 #define MODULE_NAME "irc/dcc/limiter"
46 #define HAVE_CONFIG_H
47 
48 #include "common.h"
49 #include "signals.h"
50 #include "network.h"
51 #include "settings.h"
52 
53 #include "irc.h"
54 #include "dcc-send.h"
55 
56 typedef struct {
57 	int timeout_tag;
58 
59 	unsigned long skip_bytes;
60 	unsigned long starttime;
61 	unsigned long max_speed;
62 } MODULE_SEND_DCC_REC;
63 
64 static void dcc_send_data(SEND_DCC_REC *dcc);
65 
reset_dcc_send(SEND_DCC_REC * dcc)66 static void reset_dcc_send(SEND_DCC_REC *dcc)
67 {
68 	MODULE_SEND_DCC_REC *mdcc;
69 
70 	if (g_slist_find(dcc_conns, dcc) == NULL) {
71 		/* the DCC was closed during the wait */
72 		return;
73 	}
74 
75 	mdcc = MODULE_DATA(dcc);
76 	g_source_remove(mdcc->timeout_tag);
77 	mdcc->timeout_tag = -1;
78 
79 	dcc->tagwrite = g_input_add(dcc->handle, G_INPUT_WRITE,
80 				    (GInputFunction) dcc_send_data, dcc);
81 }
82 
sent_too_much(SEND_DCC_REC * dcc,MODULE_SEND_DCC_REC * mdcc)83 static int sent_too_much(SEND_DCC_REC *dcc, MODULE_SEND_DCC_REC *mdcc)
84 {
85 	GTimeVal gtv;
86 	unsigned long timediff, curtime;
87 	unsigned long transfd, speed;
88 
89 	/* 0 == unlimited speed */
90 	if (mdcc->max_speed == 0) return 0;
91 
92 	/* get time difference in milliseconds */
93 	g_get_current_time(&gtv);
94 	curtime = (gtv.tv_sec * 1000) + (gtv.tv_usec / 1000);
95 
96 	transfd = (dcc->transfd - mdcc->skip_bytes);
97 	timediff = curtime - mdcc->starttime + 1;
98 	speed = ((transfd * 1000) / timediff);
99 
100 	/* reset speed counter every 30 seconds */
101 	if (timediff >= 30000) {
102 		mdcc->starttime = curtime;
103 		mdcc->skip_bytes = dcc->transfd;
104 	}
105 
106 	return (speed > (mdcc->max_speed * 1024));
107 }
108 
109 /* input function: DCC SEND - we're ready to send more data */
dcc_send_data(SEND_DCC_REC * dcc)110 static void dcc_send_data(SEND_DCC_REC *dcc)
111 {
112 	MODULE_SEND_DCC_REC *mdcc;
113 	char buffer[512];
114 	int ret, max_speed;
115 	GTimeVal gtv;
116 
117 	mdcc = MODULE_DATA(dcc);
118 
119 	max_speed = settings_get_int("dcc_send_top_speed");
120 	if (max_speed != mdcc->max_speed) {
121 		/* speed setting has changed, calculate speed from current position
122 		   instead of from the start to eliminate speed boosts/slowdowns */
123 
124 		mdcc->max_speed = max_speed;
125 		mdcc->skip_bytes = dcc->transfd;
126 
127 		g_get_current_time(&gtv);
128 		mdcc->starttime = (gtv.tv_sec * 1000) + (gtv.tv_usec / 1000);
129 	}
130 
131 	if (sent_too_much(dcc, mdcc)) {
132 		/* disable calling this function for 1/10th of a second. */
133 		g_source_remove(dcc->tagwrite);
134 		dcc->tagwrite = -1;
135 		mdcc->timeout_tag =
136 			g_timeout_add(100, (GSourceFunc) reset_dcc_send, dcc);
137 		return;
138 	}
139 
140 	ret = read(dcc->fhandle, buffer, sizeof(buffer));
141 	if (ret <= 0) {
142 		/* no need to call this function anymore..
143 		   in fact it just eats all the cpu.. */
144 		dcc->waitforend = TRUE;
145 		g_source_remove(dcc->tagwrite);
146 		dcc->tagwrite = -1;
147 		return;
148 	}
149 
150 	ret = net_transmit(dcc->handle, buffer, ret);
151 	if (ret > 0) dcc->transfd += ret;
152 	dcc->gotalldata = FALSE;
153 
154 	lseek(dcc->fhandle, dcc->transfd, SEEK_SET);
155 
156 	signal_emit("dcc transfer update", 1, dcc);
157 }
158 
sig_dcc_connected(SEND_DCC_REC * dcc)159 static void sig_dcc_connected(SEND_DCC_REC *dcc)
160 {
161 	MODULE_SEND_DCC_REC *mdcc;
162 	GTimeVal gtv;
163 
164 	if (!IS_DCC_SEND(dcc))
165 		return;
166 
167 	mdcc = g_new0(MODULE_SEND_DCC_REC, 1);
168 	MODULE_DATA_SET(dcc, mdcc);
169 	mdcc->timeout_tag = -1;
170 	mdcc->skip_bytes = dcc->transfd; /* now it works correct with dcc resume - doesn't wait 30s with sending data */
171 	mdcc->max_speed = settings_get_int("dcc_send_top_speed");
172 
173 	/* get starttime in milliseconds */
174 	g_get_current_time(&gtv);
175 	mdcc->starttime = (gtv.tv_sec * 1000) + (gtv.tv_usec / 1000);
176 
177 	g_source_remove(dcc->tagwrite);
178 	dcc->tagwrite = g_input_add(dcc->handle, G_INPUT_WRITE,
179 					(GInputFunction) dcc_send_data, dcc);
180 }
181 
sig_dcc_destroyed(SEND_DCC_REC * dcc)182 static void sig_dcc_destroyed(SEND_DCC_REC *dcc)
183 {
184 	MODULE_SEND_DCC_REC *mdcc;
185 
186 	if (!IS_DCC_SEND(dcc))
187 		return;
188 
189 	mdcc = MODULE_DATA(dcc);
190 	if (mdcc != NULL) {
191 		if (mdcc->timeout_tag != -1)
192 			g_source_remove(mdcc->timeout_tag);
193 
194 		g_free(mdcc);
195 	}
196 }
197 
dcc_send_limiter_init(void)198 void dcc_send_limiter_init(void)
199 {
200 	GSList *tmp;
201 	settings_add_int("dcc", "dcc_send_top_speed", 30);
202 
203 	signal_add_last("dcc connected", (SIGNAL_FUNC) sig_dcc_connected);
204 	signal_add_first("dcc destroyed", (SIGNAL_FUNC) sig_dcc_destroyed);
205 
206 	/* Limit already existing sends */
207 	for (tmp = dcc_conns; tmp != NULL; tmp = tmp->next) {
208 		SEND_DCC_REC *dcc = tmp->data;
209 		if (!IS_DCC_SEND(dcc)) continue;
210 		if (!dcc_is_connected(dcc)) continue;
211 
212 		sig_dcc_connected(dcc);
213 	}
214 
215         module_register("dcc_send_limiter", "core");
216 }
217 
dcc_send_limiter_deinit(void)218 void dcc_send_limiter_deinit(void)
219 {
220 	signal_remove("dcc connected", (SIGNAL_FUNC) sig_dcc_connected);
221 	signal_remove("dcc destroyed", (SIGNAL_FUNC) sig_dcc_destroyed);
222 }
223