File Source: DCCSend.java
1 /*
2 * Copyright (c) 2006-2009 Chris Smith, Shane Mc Cormack, Gregory Holmes
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to deal
6 * in the Software without restriction, including without limitation the rights
7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 * copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 * SOFTWARE.
21 */
22
23 package com.dmdirc.addons.dcc;
24
25 import java.io.DataInputStream;
26 import java.io.DataOutputStream;
27 import java.io.File;
28 import java.io.FileInputStream;
29 import java.io.FileOutputStream;
30 import java.io.IOException;
31 import java.io.FileNotFoundException;
32 import java.util.ArrayList;
33 import java.util.List;
34
35 /**
36 * This class handles a DCC Send.
37 *
38 * @author Shane 'Dataforce' McCormack
39 */
40 public class DCCSend extends DCC {
41 /** List of active sends. */
/*
P/P * Method: com.dmdirc.addons.dcc.DCCSend__static_init
*
* Postconditions:
* SENDS == &new ArrayList(DCCSend__static_init#1)
* new ArrayList(DCCSend__static_init#1) num objects == 1
*/
42 private static final List<DCCSend> SENDS = new ArrayList<DCCSend>();
43
44 /** File Transfer Types. */
/*
P/P * Method: com.dmdirc.addons.dcc.DCCSend$TransferType__static_init
*
* Postconditions:
* $VALUES == &new DCCSend$TransferType[](DCCSend$TransferType__static_init#3)
* RECEIVE == &new DCCSend$TransferType(DCCSend$TransferType__static_init#2)
* $VALUES[1] == &new DCCSend$TransferType(DCCSend$TransferType__static_init#2)
* SEND == &new DCCSend$TransferType(DCCSend$TransferType__static_init#1)
* $VALUES[0] == &new DCCSend$TransferType(DCCSend$TransferType__static_init#1)
* new DCCSend$TransferType(DCCSend$TransferType__static_init#1) num objects == 1
* new DCCSend$TransferType(DCCSend$TransferType__static_init#2) num objects == 1
* new DCCSend$TransferType[](DCCSend$TransferType__static_init#3) num objects == 1
* $VALUES.length == 2
*/
45 public enum TransferType { SEND, RECEIVE; }
46 /** The File transfer type for this file. */
47 private TransferType transferType = TransferType.RECEIVE;
48 /** The handler for this DCCSend. */
49 private DCCSendInterface handler;
50 /** Used to send data out the socket. */
51 private DataOutputStream out;
52 /** Used to read data from the socket. */
53 private DataInputStream in;
54 /** File we are using. */
55 private File transferFile;
56 /** Used to write data to the file. */
57 private DataOutputStream fileOut;
58 /** Used to read data from the file. */
59 private DataInputStream fileIn;
60 /** Where are we starting from? */
61 private int startpos;
62 /** How big is this file? */
63 private long size = -1;
64 /** How much of this file have we read so far? */
65 private long readSize;
66 /** What is the name of the file? */
67 private String filename = "";
68 /** What is the token for this send? */
69 private String token = "";
70 /** Block Size. */
71 private final int blockSize;
72 /** Is this a turbo dcc? */
73 private boolean turbo = false;
74
75 /** Creates a new instance of DCCSend with a default block size. */
76 public DCCSend() {
/*
P/P * Method: void com.dmdirc.addons.dcc.DCCSend()
*
* Postconditions:
* this.address == 0
* this.listen == 0
* this.port == 0
* this.running == 0
* this.turbo == 0
* this.blockSize == 1_024
* this.filename == &""
* this.token == &""
* this.serverListeningSem == &new Semaphore(DCC#2)
* this.serverSocketSem == &new Semaphore(DCC#1)
* ...
*/
77 this(1024);
78 }
79
80 /**
81 * Creates a new instance of DCCSend.
82 *
83 * @param blockSize Block size to use
84 */
85 public DCCSend(final int blockSize) {
/*
P/P * Method: void com.dmdirc.addons.dcc.DCCSend(int)
*
* Postconditions:
* this.address == 0
* this.listen == 0
* this.port == 0
* this.running == 0
* this.turbo == 0
* this.blockSize == blockSize
* init'ed(this.blockSize)
* this.filename == &""
* this.token == &""
* this.serverListeningSem == &new Semaphore(DCC#2)
* ...
*/
86 super();
87 this.blockSize = blockSize;
88 synchronized (SENDS) {
89 SENDS.add(this);
90 }
91 }
92
93 /**
94 * Reset this send to be used again (eg a resend).
95 */
96 public void reset() {
/*
P/P * Method: void reset()
*
* Preconditions:
* init'ed(this.filename)
* init'ed(this.startpos)
* this.serverSocketSem != null
* init'ed(this.transferType)
* (soft) init'ed(this.fileIn)
* (soft) init'ed(this.serverSocket)
* (soft) init'ed(this.socket)
* (soft) this.serverListeningSem != null
*
* Postconditions:
* this.fileIn == One-of{old this.fileIn, &new DataInputStream(setFileName#2), null}
* init'ed(this.fileIn)
* this.filename == old this.filename
* init'ed(this.filename)
* possibly_updated(this.in)
* possibly_updated(this.out)
* this.readSize == One-of{old this.startpos, old this.readSize}
* this.serverSocket == null
* this.socket == null
* init'ed(this.startpos)
* ...
*/
97 close();
98 setFileName(filename);
99 setFileStart(startpos);
100 }
101
102 /**
103 * Get a copy of the list of active sends.
104 *
105 * @return A copy of the list of active sends.
106 */
107 public static List<DCCSend> getSends() {
/*
P/P * Method: List getSends()
*
* Postconditions:
* return_value == &new ArrayList(getSends#1)
* new ArrayList(getSends#1) num objects == 1
*/
108 synchronized (SENDS) {
109 return new ArrayList<DCCSend>(SENDS);
110 }
111 }
112
113 /**
114 * Called to remove this object from the sends list.
115 */
116 public void removeFromSends() {
/*
P/P * Method: void removeFromSends()
*/
117 synchronized (SENDS) {
118 SENDS.remove(this);
119 }
120 }
121
122 /**
123 * Set the filename of this file
124 *
125 * @param filename Filename
126 */
127 public void setFileName(final String filename) {
/*
P/P * Method: void setFileName(String)
*
* Preconditions:
* init'ed(this.transferType)
*
* Postconditions:
* this.fileIn == One-of{old this.fileIn, &new DataInputStream(setFileName#2), null}
* this.filename == filename
* init'ed(this.filename)
* this.transferFile == One-of{old this.transferFile, &new File(setFileName#1)}
* new DataInputStream(setFileName#2) num objects <= 1
* new File(setFileName#1) num objects <= 1
*/
128 this.filename = filename;
129 if (transferType == TransferType.SEND) {
130 transferFile = new File(filename);
131 try {
132 fileIn = new DataInputStream(new FileInputStream(transferFile.getAbsolutePath()));
133 } catch (FileNotFoundException e) {
134 fileIn = null;
135 } catch (SecurityException e) {
136 fileIn = null;
137 }
138 }
139 }
140
141 /**
142 * Get the filename of this file
143 *
144 * @return Filename
145 */
146 public String getFileName() {
/*
P/P * Method: String getFileName()
*
* Preconditions:
* init'ed(this.filename)
*
* Postconditions:
* return_value == this.filename
* init'ed(return_value)
*/
147 return filename;
148 }
149
150 /**
151 * Get the filename of this file, without the path
152 *
153 * @return Filename without path
154 */
155 public String getShortFileName() {
/*
P/P * Method: String getShortFileName()
*
* Preconditions:
* init'ed(this.filename)
*
* Postconditions:
* init'ed(return_value)
*/
156 return (new File(filename)).getName();
157 }
158
159 /**
160 * Set dcc Type.
161 *
162 * @param type Type of DCC Send this is.
163 */
164 public void setType(final TransferType type) {
/*
P/P * Method: void setType(DCCSend$TransferType)
*
* Postconditions:
* this.transferType == type
* init'ed(this.transferType)
*/
165 this.transferType = type;
166 }
167
168 /**
169 * Get dcc Type.
170 *
171 * @return Type of DCC Send this is.
172 */
173 public TransferType getType() {
/*
P/P * Method: DCCSend$TransferType getType()
*
* Preconditions:
* init'ed(this.transferType)
*
* Postconditions:
* return_value == this.transferType
* init'ed(return_value)
*/
174 return transferType;
175 }
176
177 /**
178 * Set turbo mode on/off.
179 * Turbo mode doesn't wait for ack packets. Only relevent when sending.
180 *
181 * @param turbo True for turbo dcc, else false
182 */
183 public void setTurbo(final boolean turbo) {
/*
P/P * Method: void setTurbo(bool)
*
* Postconditions:
* this.turbo == turbo
* init'ed(this.turbo)
*/
184 this.turbo = turbo;
185 }
186
187 /**
188 * Is turbo mode on/off.
189 * Turbo mode doesn't wait for ack packets. Only relevent when sending.
190 *
191 * @return True for turbo dcc, else false
192 */
193 public boolean isTurbo() {
/*
P/P * Method: bool isTurbo()
*
* Preconditions:
* init'ed(this.turbo)
*
* Postconditions:
* return_value == this.turbo
* init'ed(return_value)
*/
194 return turbo;
195 }
196
197 /**
198 * Set the Token for this send
199 *
200 * @param token Token for this send
201 */
202 public void setToken(final String token) {
/*
P/P * Method: void setToken(String)
*
* Postconditions:
* this.token == token
* init'ed(this.token)
*/
203 this.token = token;
204 }
205
206 /**
207 * Get the Token for this send
208 *
209 * @return Token for this send
210 */
211 public String getToken() {
/*
P/P * Method: String getToken()
*
* Preconditions:
* init'ed(this.token)
*
* Postconditions:
* return_value == this.token
* init'ed(return_value)
*/
212 return token;
213 }
214
215 /**
216 * Make a Token for this send.
217 * This token will be unique compared to all the other known sends
218 *
219 * @return The Token for this send.
220 */
221 public String makeToken() {
/*
P/P * Method: String makeToken()
*
* Preconditions:
* init'ed(this.filename)
*
* Postconditions:
* java.lang.Integer:toString(...)._tainted == 0
* return_value == &java.lang.Integer:toString(...)
* this.token == &java.lang.Integer:toString(...)
*/
222 String myToken = "";
223 boolean unique = true;
224 do {
225 myToken = Integer.toString(Math.abs((myToken+filename).hashCode()));
226 unique = (findByToken(myToken) == null);
227 } while (!unique);
228 setToken(myToken);
229 return myToken;
230 }
231
232 /**
233 * Find a send based on a given token.
234 *
235 * @param token Token to look for. (case sensitive)
236 * @return The first DCCSend that matches the given token.
237 * null if none match, or token is "" or null.
238 */
239 public static DCCSend findByToken(final String token) {
/*
P/P * Method: DCCSend findByToken(String)
*
* Presumptions:
* java.util.Iterator:next(...)@241 != null
* send.token@241 != null
*
* Postconditions:
* init'ed(return_value)
*
* Test Vectors:
* token: Addr_Set{null}, Inverse{null}
* java.lang.String:equals(...)@242: {0}, {1}
* java.lang.String:isEmpty(...)@240: {0}, {1}
* java.util.Iterator:hasNext(...)@241: {0}, {1}
*/
240 if (token == null || token.isEmpty()) { return null; }
241 for (DCCSend send : getSends()) {
242 if (send.getToken().equals(token)) {
243 return send;
244 }
245 }
246 return null;
247 }
248
249 /**
250 * Set the size of the file
251 *
252 * @param size File size
253 */
254 public void setFileSize(final long size) {
/*
P/P * Method: void setFileSize(long)
*
* Postconditions:
* this.size == size
* init'ed(this.size)
*/
255 this.size = size;
256 }
257
258 /**
259 * Get the expected size of the file
260 *
261 * @return The expected File size (-1 if unknown)
262 */
263 public long getFileSize() {
/*
P/P * Method: long getFileSize()
*
* Preconditions:
* init'ed(this.size)
*
* Postconditions:
* return_value == this.size
* init'ed(return_value)
*/
264 return size;
265 }
266
267 /**
268 * Set the starting position of the file
269 *
270 * @param startpos Starting position
271 * @return -1 if fileIn is null or if dcc receive, else the result of fileIn.skipBytes()
272 */
273 public int setFileStart(final int startpos) {
/*
P/P * Method: int setFileStart(int)
*
* Preconditions:
* init'ed(this.transferType)
* (soft) init'ed(this.fileIn)
*
* Postconditions:
* init'ed(return_value)
* this.readSize == One-of{startpos, old this.readSize}
* init'ed(this.startpos)
*
* Test Vectors:
* this.fileIn: Addr_Set{null}, Inverse{null}
*/
274 this.startpos = startpos;
275 if (transferType == TransferType.SEND && fileIn != null) {
276 try {
277 this.startpos = fileIn.skipBytes(startpos);
278 readSize = startpos;
279 return this.startpos;
280 } catch (IOException ioe) { }
281 }
282 return -1;
283 }
284
285 /**
286 * Get the starting position of the file
287 *
288 * @return starting position of file.
289 */
290 public int getFileStart() {
/*
P/P * Method: int getFileStart()
*
* Preconditions:
* init'ed(this.startpos)
*
* Postconditions:
* return_value == this.startpos
* init'ed(return_value)
*/
291 return this.startpos;
292 }
293
294 /**
295 * Change the handler for this DCC Send
296 *
297 * @param handler A class implementing DCCSendInterface
298 */
299 public void setHandler(final DCCSendInterface handler) {
/*
P/P * Method: void setHandler(DCCSendInterface)
*
* Postconditions:
* this.handler == handler
* init'ed(this.handler)
*/
300 this.handler = handler;
301 }
302
303 /**
304 * Called when the socket is first opened, before any data is handled.
305 */
306 @Override
307 protected void socketOpened() {
308 try {
/*
P/P * Method: void socketOpened()
*
* Preconditions:
* init'ed(this.handler)
* (soft) init'ed(this.in)
* (soft) init'ed(this.out)
* (soft) init'ed(this.filename)
* (soft) this.socket != null
* (soft) init'ed(this.startpos)
* (soft) init'ed(this.transferType)
*
* Postconditions:
* this.fileOut == One-of{old this.fileOut, &new DataOutputStream(socketOpened#2)}
* this.handler.timeStarted == old this.handler.timeStarted
* this.in in Addr_Set{null,&new DataInputStream(socketOpened#5)}
* this.out in Addr_Set{null,&new DataOutputStream(socketOpened#4)}
* this.transferFile == One-of{&new File(socketOpened#1), old this.transferFile}
* new DataInputStream(socketOpened#5) num objects <= 1
* new DataOutputStream(socketOpened#2) num objects <= 1
* new DataOutputStream(socketOpened#4) num objects <= 1
* new File(socketOpened#1) num objects <= 1
*
* Test Vectors:
* this.handler: Addr_Set{null}, Inverse{null}
*/
309 transferFile = new File(filename);
310 if (transferType == TransferType.RECEIVE) {
311 fileOut = new DataOutputStream(new FileOutputStream(transferFile.getAbsolutePath(), (startpos > 0)));
312 }
313 out = new DataOutputStream(socket.getOutputStream());
314 in = new DataInputStream(socket.getInputStream());
315 if (handler != null) {
316 handler.socketOpened(this);
317 }
318 } catch (IOException ioe) {
319 socketClosed();
320 }
321 }
322
323 /**
324 * Called when the socket is closed, before the thread terminates.
325 */
326 @Override
327 protected void socketClosed() {
328 // Try to close both, even if one fails.
/*
P/P * Method: void socketClosed()
*
* Preconditions:
* init'ed(this.in)
* init'ed(this.out)
* init'ed(this.handler)
*
* Postconditions:
* this.in == null
* this.out == null
*
* Test Vectors:
* this.in: Addr_Set{null}, Inverse{null}
* this.out: Addr_Set{null}, Inverse{null}
* this.handler: Addr_Set{null}, Inverse{null}
*/
329 if (out != null) { try { out.close(); } catch (IOException e) { } }
330 if (in != null) { try { in.close(); } catch (IOException e) { } }
331 out = null;
332 in = null;
333 if (handler != null) {
334 handler.socketClosed(this);
335 }
336 synchronized (SENDS) {
337 SENDS.remove(this);
338 }
339 }
340
341 /**
342 * Handle the socket.
343 *
344 * @return false when socket is closed, true will cause the method to be
345 * called again.
346 */
347 @Override
348 protected boolean handleSocket() {
/*
P/P * Method: bool handleSocket()
*
* Preconditions:
* init'ed(this.out)
* (soft) this.readSize in {-6_442_450_943..6_442_450_943}
* (soft) this.blockSize >= 0
* (soft) this.fileIn != null
* (soft) this.fileOut != null
* (soft) init'ed(this.handler)
* (soft) init'ed(this.in)
* (soft) init'ed(this.size)
* (soft) init'ed(this.transferType)
* (soft) init'ed(this.turbo)
*
* Postconditions:
* init'ed(return_value)
* possibly_updated(this.handler.transferCount)
* init'ed(this.readSize)
*
* Test Vectors:
* this.in: Inverse{null}, Addr_Set{null}
* this.out: Addr_Set{null}, Inverse{null}
*/
349 if (out == null || in == null) { return false; }
350 if (transferType == TransferType.RECEIVE) {
351 return handleReceive();
352 } else {
353 return handleSend();
354 }
355 }
356
357
358 /**
359 * Handle the socket as a RECEIVE.
360 *
361 * @return false when socket is closed (or should be closed), true will cause the method to be
362 * called again.
363 */
364 protected boolean handleReceive() {
365 try {
/*
P/P * Method: bool handleReceive()
*
* Preconditions:
* (soft) this.readSize in {-6_442_450_943..6_442_450_943}
* (soft) this.blockSize >= 0
* (soft) this.fileOut != null
* (soft) init'ed(this.handler)
* (soft) this.in != null
* (soft) this.out != null
* (soft) init'ed(this.size)
*
* Presumptions:
* this.readSize + java.io.DataInputStream:read(...)@367 in {-231..232-1}
*
* Postconditions:
* init'ed(return_value)
* this.handler.transferCount == old this.handler.transferCount
* this.readSize in {-6_442_450_943..6_442_450_943}
*
* Test Vectors:
* this.handler: Addr_Set{null}, Inverse{null}
* java.io.DataInputStream:read(...)@367: {0}, {1..232-1}, {-231..-1}
*/
366 final byte[] data = new byte[blockSize];
367 final int bytesRead = in.read(data);
368 readSize = readSize + bytesRead;
369
370 if (bytesRead > 0) {
371 if (handler != null) { handler.dataTransfered(this, bytesRead); }
372 fileOut.write(data, 0, bytesRead);
373 // Send ack
374 out.writeInt((int)readSize);
375 out.flush();
376 if (readSize == size) {
377 fileOut.close();
378 return false;
379 } else {
380 return true;
381 }
382 } else if (bytesRead < 0) {
383 fileOut.close();
384 return false;
385 }
386 } catch (IOException e) {
387 return false;
388 }
389 return false;
390 }
391
392 /**
393 * Handle the socket as a SEND.
394 *
395 * @return false when socket is closed (or should be closed), true will cause the method to be
396 * called again.
397 */
398 protected boolean handleSend() {
399 try {
/*
P/P * Method: bool handleSend()
*
* Preconditions:
* (soft) init'ed(this.readSize)
* (soft) this.blockSize >= 0
* (soft) this.fileIn != null
* (soft) init'ed(this.handler)
* (soft) this.in != null
* (soft) this.out != null
* (soft) init'ed(this.size)
* (soft) init'ed(this.turbo)
*
* Presumptions:
* this.readSize + java.io.DataInputStream:read(...)@401 in {-263..264-1}
*
* Postconditions:
* init'ed(return_value)
* this.handler.transferCount == old this.handler.transferCount
* init'ed(this.readSize)
*
* Test Vectors:
* this.handler: Addr_Set{null}, Inverse{null}
* this.turbo: {1}, {0}
* java.io.DataInputStream:read(...)@401: {0}, {1..232-1}, {-231..-1}
* java.io.DataInputStream:readInt(...)@429: {-231..0}, {1..232-1}
*/
400 final byte[] data = new byte[blockSize];
401 int bytesRead = fileIn.read(data);
402 readSize = readSize + bytesRead;
403
404 if (bytesRead > 0) {
405 if (handler != null) { handler.dataTransfered(this, bytesRead); }
406 out.write(data, 0, bytesRead);
407 out.flush();
408
409 // Wait for acknowlegement packet.
410 if (!turbo) {
411 int bytesRecieved;
412 do {
413 bytesRecieved = in.readInt();
414 } while ((readSize - bytesRecieved) > 0);
415 }
416
417 if (readSize == size) {
418 fileIn.close();
419
420 // Process all the ack packets that may have been sent.
421 // In true turbo dcc mode, none will have been sent and the socket
422 // will just close, in fast-dcc mode all the acks will be here,
423 // So keep reading acks untill the socket closes (IOException) or we
424 // have recieved all the acks.
425 if (turbo) {
426 int ack = 0;
427 do {
428 try {
429 ack = in.readInt();
430 } catch (IOException e) {
431 break;
432 }
433 } while (ack > 0 && (readSize - ack) > 0);
434 }
435
436 return false;
437 }
438
439 return true;
440 } else if (bytesRead < 0) {
441 fileIn.close();
442 return true;
443 }
444 } catch (IOException e) {
445 return false;
446 }
447 return false;
448 }
449 }
SofCheck Inspector Build Version : 2.17854
| DCCSend.java |
2009-Jun-25 01:54:24 |
| DCCSend.class |
2009-Sep-02 17:04:14 |
| DCCSend$TransferType.class |
2009-Sep-02 17:04:14 |