forked from TheChef1337/PircBot2
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathDccFileTransfer.java
More file actions
507 lines (424 loc) · 15.7 KB
/
Copy pathDccFileTransfer.java
File metadata and controls
507 lines (424 loc) · 15.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
/*
Copyright Paul James Mutton, 2001-2009, http://www.jibble.org/
This file is part of PircBot.
This software is dual-licensed, allowing you to choose between the GNU
General Public License (GPL) and the www.jibble.org Commercial License.
Since the GPL may be too restrictive for use in a proprietary application,
a commercial license is also provided. Full license information can be
found at http://www.jibble.org/licenses/
*/
package PircBot;
import java.net.*;
import java.io.*;
/**
* This class is used to administer a DCC file transfer.
*
* @since 1.2.0
* @author Paul James Mutton,
* <a href="http://www.jibble.org/">http://www.jibble.org/</a>
* @version 1.5.0 (Build time: Mon Dec 14 20:07:17 2009)
*/
public class DccFileTransfer {
/**
* The default buffer size to use when sending and receiving files.
*/
public static final int BUFFER_SIZE = 1024;
/**
* Constructor used for receiving files.
*/
DccFileTransfer(PircBot bot, DccManager manager, String nick, String login, String hostname, String type, String filename, long address, int port, long size) {
_bot = bot;
_manager = manager;
_nick = nick;
_login = login;
_hostname = hostname;
_type = type;
_file = new File(filename);
_address = address;
_port = port;
_size = size;
_received = false;
_incoming = true;
}
/**
* Constructor used for sending files.
*/
DccFileTransfer(PircBot bot, DccManager manager, File file, String nick, int timeout) {
_bot = bot;
_manager = manager;
_nick = nick;
_file = file;
_size = file.length();
_timeout = timeout;
_received = true;
_incoming = false;
}
/**
* Receives a DccFileTransfer and writes it to the specified file.
* Resuming allows a partial download to be continue from the end of
* the current file contents.
*
* @param file The file to write to.
* @param resume True if you wish to try and resume the download instead
* of overwriting an existing file.
*
*/
public synchronized void receive(File file, boolean resume) {
if (!_received) {
_received = true;
_file = file;
if (_type.equals("SEND") && resume) {
_progress = file.length();
if (_progress == 0) {
doReceive(file, false);
}
else {
_bot.sendCTCPCommand(_nick, "DCC RESUME file.ext " + _port + " " + _progress);
_manager.addAwaitingResume(this);
}
}
else {
_progress = file.length();
doReceive(file, resume);
}
}
}
/**
* Receive the file in a new thread.
*/
void doReceive(final File file, final boolean resume) {
new Thread() {
@Override
public void run() {
BufferedOutputStream foutput = null;
Exception exception = null;
try {
// Convert the integer address to a proper IP address.
int[] ip = _bot.longToIp(_address);
String ipStr = ip[0] + "." + ip[1] + "." + ip[2] + "." + ip[3];
// Connect the socket and set a timeout.
_socket = new Socket(ipStr, _port);
_socket.setSoTimeout(30*1000);
_startTime = System.currentTimeMillis();
// No longer possible to resume this transfer once it's underway.
_manager.removeAwaitingResume(DccFileTransfer.this);
BufferedInputStream input = new BufferedInputStream(_socket.getInputStream());
BufferedOutputStream output = new BufferedOutputStream(_socket.getOutputStream());
// Following line fixed for jdk 1.1 compatibility.
foutput = new BufferedOutputStream(new FileOutputStream(file.getCanonicalPath(), resume));
byte[] inBuffer = new byte[BUFFER_SIZE];
byte[] outBuffer = new byte[4];
int bytesRead = 0;
while ((bytesRead = input.read(inBuffer, 0, inBuffer.length)) != -1) {
foutput.write(inBuffer, 0, bytesRead);
_progress += bytesRead;
// Send back an acknowledgement of how many bytes we have got so far.
outBuffer[0] = (byte) ((_progress >> 24) & 0xff);
outBuffer[1] = (byte) ((_progress >> 16) & 0xff);
outBuffer[2] = (byte) ((_progress >> 8) & 0xff);
outBuffer[3] = (byte) ((_progress >> 0) & 0xff);
output.write(outBuffer);
output.flush();
delay();
}
foutput.flush();
}
catch (Exception e) {
exception = e;
}
finally {
try {
foutput.close();
_socket.close();
}
catch (Exception anye) {
// Do nothing.
}
}
_bot.onFileTransferFinished(DccFileTransfer.this, exception);
}
}.start();
}
/**
* Method to send the file inside a new thread.
*/
void doSend(final boolean allowResume) {
new Thread() {
@Override
public void run() {
BufferedInputStream finput = null;
Exception exception = null;
try {
ServerSocket ss = null;
int[] ports = _bot.getDccPorts();
if (ports == null) {
// Use any free port.
ss = new ServerSocket(0);
}
else {
for (int i = 0; i < ports.length; i++) {
try {
ss = new ServerSocket(ports[i]);
// Found a port number we could use.
break;
}
catch (Exception e) {
// Do nothing; go round and try another port.
}
}
if (ss == null) {
// No ports could be used.
throw new IOException("All ports returned by getDccPorts() are in use.");
}
}
ss.setSoTimeout(_timeout);
_port = ss.getLocalPort();
InetAddress inetAddress = _bot.getDccInetAddress();
if (inetAddress == null) {
inetAddress = _bot.getInetAddress();
}
byte[] ip = inetAddress.getAddress();
long ipNum = _bot.ipToLong(ip);
// Rename the filename so it has no whitespace in it when we send it.
// .... I really should do this a bit more nicely at some point ....
String safeFilename = _file.getName().replace(' ', '_');
safeFilename = safeFilename.replace('\t', '_');
if (allowResume) {
_manager.addAwaitingResume(DccFileTransfer.this);
}
// Send the message to the user, telling them where to connect to in order to get the file.
_bot.sendCTCPCommand(_nick, "DCC SEND " + safeFilename + " " + ipNum + " " + _port + " " + _file.length());
// The client may now connect to us and download the file.
_socket = ss.accept();
_socket.setSoTimeout(30000);
_startTime = System.currentTimeMillis();
// No longer possible to resume this transfer once it's underway.
if (allowResume) {
_manager.removeAwaitingResume(DccFileTransfer.this);
}
// Might as well close the server socket now; it's finished with.
ss.close();
BufferedOutputStream output = new BufferedOutputStream(_socket.getOutputStream());
BufferedInputStream input = new BufferedInputStream(_socket.getInputStream());
finput = new BufferedInputStream(new FileInputStream(_file));
// Check for resuming.
if (_progress > 0) {
long bytesSkipped = 0;
while (bytesSkipped < _progress) {
bytesSkipped += finput.skip(_progress - bytesSkipped);
}
}
byte[] outBuffer = new byte[BUFFER_SIZE];
byte[] inBuffer = new byte[4];
int bytesRead = 0;
while ((bytesRead = finput.read(outBuffer, 0, outBuffer.length)) != -1) {
output.write(outBuffer, 0, bytesRead);
output.flush();
input.read(inBuffer, 0, inBuffer.length);
_progress += bytesRead;
delay();
}
}
catch (Exception e) {
exception = e;
}
finally {
try {
finput.close();
_socket.close();
}
catch (Exception e) {
// Do nothing.
}
}
_bot.onFileTransferFinished(DccFileTransfer.this, exception);
}
}.start();
}
/**
* Package mutator for setting the progress of the file transfer.
*/
void setProgress(long progress) {
_progress = progress;
}
/**
* Delay between packets.
*/
private void delay() {
if (_packetDelay > 0) {
try {
Thread.sleep(_packetDelay);
}
catch (InterruptedException e) {
// Do nothing.
}
}
}
/**
* Returns the nick of the other user taking part in this file transfer.
*
* @return the nick of the other user.
*
*/
public String getNick() {
return _nick;
}
/**
* Returns the login of the file sender.
*
* @return the login of the file sender. null if we are sending.
*
*/
public String getLogin() {
return _login;
}
/**
* Returns the hostname of the file sender.
*
* @return the hostname of the file sender. null if we are sending.
*
*/
public String getHostname() {
return _hostname;
}
/**
* Returns the suggested file to be used for this transfer.
*
* @return the suggested file to be used.
*
*/
public File getFile() {
return _file;
}
/**
* Returns the port number to be used when making the connection.
*
* @return the port number.
*
*/
public int getPort() {
return _port;
}
/**
* Returns true if the file transfer is incoming (somebody is sending
* the file to us).
*
* @return true if the file transfer is incoming.
*
*/
public boolean isIncoming() {
return _incoming;
}
/**
* Returns true if the file transfer is outgoing (we are sending the
* file to someone).
*
* @return true if the file transfer is outgoing.
*
*/
public boolean isOutgoing() {
return !isIncoming();
}
/**
* Sets the delay time between sending or receiving each packet.
* Default is 0.
* This is useful for throttling the speed of file transfers to maintain
* a good quality of service for other things on the machine or network.
*
* @param millis The number of milliseconds to wait between packets.
*
*/
public void setPacketDelay(long millis) {
_packetDelay = millis;
}
/**
* returns the delay time between each packet that is send or received.
*
* @return the delay between each packet.
*
*/
public long getPacketDelay() {
return _packetDelay;
}
/**
* Returns the size (in bytes) of the file being transfered.
*
* @return the size of the file. Returns -1 if the sender did not
* specify this value.
*/
public long getSize() {
return _size;
}
/**
* Returns the progress (in bytes) of the current file transfer.
* When resuming, this represents the total number of bytes in the
* file, which may be greater than the amount of bytes resumed in
* just this transfer.
*
* @return the progress of the transfer.
*/
public long getProgress() {
return _progress;
}
/**
* Returns the progress of the file transfer as a percentage.
* Note that this should never be negative, but could become
* greater than 100% if you attempt to resume a larger file
* onto a partially downloaded file that was smaller.
*
* @return the progress of the transfer as a percentage.
*/
public double getProgressPercentage() {
return 100 * (getProgress() / (double) getSize());
}
/**
* Stops the DCC file transfer by closing the connection.
*/
public void close() {
try {
_socket.close();
}
catch (Exception e) {
// Let the DCC manager worry about anything that may go wrong.
}
}
/**
* Returns the rate of data transfer in bytes per second.
* This value is an estimate based on the number of bytes
* transfered since the connection was established.
*
* @return data transfer rate in bytes per second.
*/
public long getTransferRate() {
long time = (System.currentTimeMillis() - _startTime) / 1000;
if (time <= 0) {
return 0;
}
return getProgress() / time;
}
/**
* Returns the address of the sender as a long.
*
* @return the address of the sender as a long.
*/
public long getNumericalAddress() {
return _address;
}
private PircBot _bot;
private DccManager _manager;
private String _nick;
private String _login = null;
private String _hostname = null;
private String _type;
private long _address;
private int _port;
private long _size;
private boolean _received;
private Socket _socket = null;
private long _progress = 0;
private File _file = null;
private int _timeout = 0;
private boolean _incoming;
private long _packetDelay = 0;
private long _startTime = 0;
}