You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

DCCSend.java 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447
  1. /*
  2. * Copyright (c) 2006-2008 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. package com.dmdirc.addons.dcc;
  23. import java.io.DataInputStream;
  24. import java.io.DataOutputStream;
  25. import java.io.File;
  26. import java.io.FileInputStream;
  27. import java.io.FileOutputStream;
  28. import java.io.IOException;
  29. import java.io.FileNotFoundException;
  30. import java.util.ArrayList;
  31. import java.util.List;
  32. /**
  33. * This class handles a DCC Send.
  34. *
  35. * @author Shane 'Dataforce' McCormack
  36. */
  37. public class DCCSend extends DCC {
  38. /** List of active sends. */
  39. private static List<DCCSend> sends = new ArrayList<DCCSend>();
  40. /** File Transfer Types. */
  41. public enum TransferType { SEND, RECEIVE; }
  42. /** The File transfer type for this file. */
  43. private TransferType transferType = TransferType.RECEIVE;
  44. /** The handler for this DCCSend. */
  45. private DCCSendInterface handler;
  46. /** Used to send data out the socket. */
  47. private DataOutputStream out;
  48. /** Used to read data from the socket. */
  49. private DataInputStream in;
  50. /** File we are using. */
  51. private File transferFile;
  52. /** Used to write data to the file. */
  53. private DataOutputStream fileOut;
  54. /** Used to read data from the file. */
  55. private DataInputStream fileIn;
  56. /** Where are we starting from? */
  57. private int startpos;
  58. /** How big is this file? */
  59. private long size = -1;
  60. /** How much of this file have we read so far? */
  61. private long readSize;
  62. /** What is the name of the file? */
  63. private String filename = "";
  64. /** What is the token for this send? */
  65. private String token = "";
  66. /** Block Size. */
  67. private final int blockSize;
  68. /** Is this a turbo dcc? */
  69. private boolean turbo = false;
  70. /** Creates a new instance of DCCSend with a default block size. */
  71. public DCCSend() {
  72. this(1024);
  73. }
  74. /**
  75. * Creates a new instance of DCCSend.
  76. *
  77. * @param blockSize Block size to use
  78. */
  79. public DCCSend(final int blockSize) {
  80. super();
  81. this.blockSize = blockSize;
  82. synchronized (sends) {
  83. sends.add(this);
  84. }
  85. }
  86. /**
  87. * Reset this send to be used again (eg a resend).
  88. */
  89. public void reset() {
  90. close();
  91. setFileName(filename);
  92. setFileStart(startpos);
  93. }
  94. /**
  95. * Get a copy of the list of active sends.
  96. *
  97. * @return A copy of the list of active sends.
  98. */
  99. public static List<DCCSend> getSends() {
  100. synchronized (sends) {
  101. return new ArrayList<DCCSend>(sends);
  102. }
  103. }
  104. /**
  105. * Called to remove this object from the sends list.
  106. */
  107. public void removeFromSends() {
  108. synchronized (sends) {
  109. sends.remove(this);
  110. }
  111. }
  112. /**
  113. * Set the filename of this file
  114. *
  115. * @param filename Filename
  116. */
  117. public void setFileName(final String filename) {
  118. this.filename = filename;
  119. if (transferType == TransferType.SEND) {
  120. transferFile = new File(filename);
  121. try {
  122. fileIn = new DataInputStream(new FileInputStream(transferFile.getAbsolutePath()));
  123. } catch (FileNotFoundException e) {
  124. fileIn = null;
  125. } catch (SecurityException e) {
  126. fileIn = null;
  127. }
  128. }
  129. }
  130. /**
  131. * Get the filename of this file
  132. *
  133. * @return Filename
  134. */
  135. public String getFileName() {
  136. return filename;
  137. }
  138. /**
  139. * Get the filename of this file, without the path
  140. *
  141. * @return Filename without path
  142. */
  143. public String getShortFileName() {
  144. return (new File(filename)).getName();
  145. }
  146. /**
  147. * Set dcc Type.
  148. *
  149. * @param type Type of DCC Send this is.
  150. */
  151. public void setType(final TransferType type) {
  152. this.transferType = type;
  153. }
  154. /**
  155. * Get dcc Type.
  156. *
  157. * @return Type of DCC Send this is.
  158. */
  159. public TransferType getType() {
  160. return transferType;
  161. }
  162. /**
  163. * Set turbo mode on/off.
  164. * Turbo mode doesn't wait for ack packets. Only relevent when sending.
  165. *
  166. * @param turbo True for turbo dcc, else false
  167. */
  168. public void setTurbo(final boolean turbo) {
  169. this.turbo = turbo;
  170. }
  171. /**
  172. * Is turbo mode on/off.
  173. * Turbo mode doesn't wait for ack packets. Only relevent when sending.
  174. *
  175. * @return True for turbo dcc, else false
  176. */
  177. public boolean isTurbo() {
  178. return turbo;
  179. }
  180. /**
  181. * Set the Token for this send
  182. *
  183. * @param token Token for this send
  184. */
  185. public void setToken(final String token) {
  186. this.token = token;
  187. }
  188. /**
  189. * Get the Token for this send
  190. *
  191. * @return Token for this send
  192. */
  193. public String getToken() {
  194. return token;
  195. }
  196. /**
  197. * Make a Token for this send.
  198. * This token will be unique compared to all the other known sends
  199. *
  200. * @return The Token for this send.
  201. */
  202. public String makeToken() {
  203. String token = "";
  204. boolean unique = true;
  205. do {
  206. token = Integer.toString(Math.abs((token+filename).hashCode()));
  207. unique = (findByToken(token) == null);
  208. } while (!unique);
  209. setToken(token);
  210. return token;
  211. }
  212. /**
  213. * Find a send based on a given token.
  214. *
  215. * @param token Token to look for. (case sensitive)
  216. * @return The first DCCSend that matches the given token.
  217. * null if none match, or token is "" or null.
  218. */
  219. public static DCCSend findByToken(final String token) {
  220. if (token == null || token.isEmpty()) { return null; }
  221. for (DCCSend send : getSends()) {
  222. if (send.getToken().equals(token)) {
  223. return send;
  224. }
  225. }
  226. return null;
  227. }
  228. /**
  229. * Set the size of the file
  230. *
  231. * @param size File size
  232. */
  233. public void setFileSize(final long size) {
  234. this.size = size;
  235. }
  236. /**
  237. * Get the expected size of the file
  238. *
  239. * @return The expected File size (-1 if unknown)
  240. */
  241. public long getFileSize() {
  242. return size;
  243. }
  244. /**
  245. * Set the starting position of the file
  246. *
  247. * @param startpos Starting position
  248. * @return -1 if fileIn is null or if dcc receive, else the result of fileIn.skipBytes()
  249. */
  250. public int setFileStart(final int startpos) {
  251. this.startpos = startpos;
  252. if (transferType == TransferType.SEND && fileIn != null) {
  253. try {
  254. this.startpos = fileIn.skipBytes(startpos);
  255. readSize = startpos;
  256. return this.startpos;
  257. } catch (IOException ioe) { }
  258. }
  259. return -1;
  260. }
  261. /**
  262. * Get the starting position of the file
  263. *
  264. * @return starting position of file.
  265. */
  266. public int getFileStart() {
  267. return this.startpos;
  268. }
  269. /**
  270. * Change the handler for this DCC Send
  271. *
  272. * @param handler A class implementing DCCSendInterface
  273. */
  274. public void setHandler(final DCCSendInterface handler) {
  275. this.handler = handler;
  276. }
  277. /**
  278. * Called when the socket is first opened, before any data is handled.
  279. */
  280. @Override
  281. protected void socketOpened() {
  282. try {
  283. transferFile = new File(filename);
  284. if (transferType == TransferType.RECEIVE) {
  285. fileOut = new DataOutputStream(new FileOutputStream(transferFile.getAbsolutePath(), (startpos > 0)));
  286. }
  287. out = new DataOutputStream(socket.getOutputStream());
  288. in = new DataInputStream(socket.getInputStream());
  289. if (handler != null) {
  290. handler.socketOpened(this);
  291. }
  292. } catch (IOException ioe) { }
  293. }
  294. /**
  295. * Called when the socket is closed, before the thread terminates.
  296. */
  297. @Override
  298. protected void socketClosed() {
  299. // Try to close both, even if one fails.
  300. if (out != null) { try { out.close(); } catch (IOException e) { } }
  301. if (in != null) { try { in.close(); } catch (IOException e) { } }
  302. out = null;
  303. in = null;
  304. if (handler != null) {
  305. handler.socketClosed(this);
  306. }
  307. synchronized (sends) {
  308. sends.remove(this);
  309. }
  310. }
  311. /**
  312. * Handle the socket.
  313. *
  314. * @return false when socket is closed, true will cause the method to be
  315. * called again.
  316. */
  317. @Override
  318. protected boolean handleSocket() {
  319. if (out == null || in == null) { return false; }
  320. if (transferType == TransferType.RECEIVE) {
  321. return handleReceive();
  322. } else {
  323. return handleSend();
  324. }
  325. }
  326. /**
  327. * Handle the socket as a RECEIVE.
  328. *
  329. * @return false when socket is closed (or should be closed), true will cause the method to be
  330. * called again.
  331. */
  332. protected boolean handleReceive() {
  333. try {
  334. final byte[] data = new byte[blockSize];
  335. final int bytesRead = in.read(data);
  336. readSize = readSize + bytesRead;
  337. if (bytesRead > 0) {
  338. if (handler != null) { handler.dataTransfered(this, bytesRead); }
  339. fileOut.write(data, 0, bytesRead);
  340. // Send ack
  341. out.writeInt((int)readSize);
  342. out.flush();
  343. if (readSize == size) {
  344. fileOut.close();
  345. return false;
  346. } else {
  347. return true;
  348. }
  349. } else if (bytesRead < 0) {
  350. fileOut.close();
  351. return false;
  352. }
  353. } catch (IOException e) {
  354. return false;
  355. }
  356. return false;
  357. }
  358. /**
  359. * Handle the socket as a SEND.
  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 handleSend() {
  365. try {
  366. final byte[] data = new byte[blockSize];
  367. int bytesRead = fileIn.read(data);
  368. readSize = readSize + bytesRead;
  369. if (bytesRead > 0) {
  370. if (handler != null) { handler.dataTransfered(this, bytesRead); }
  371. out.write(data, 0, bytesRead);
  372. out.flush();
  373. // Wait for acknowlegement packet.
  374. if (!turbo) {
  375. int bytesRecieved;
  376. do {
  377. bytesRecieved = in.readInt();
  378. } while ((readSize - bytesRecieved) > 0);
  379. }
  380. if (readSize == size) {
  381. fileIn.close();
  382. // Process all the ack packets that may have been sent.
  383. // In true turbo dcc mode, none will have been sent and the socket
  384. // will just close, in fast-dcc mode all the acks will be here,
  385. // So keep reading acks untill the socket closes (IOException) or we
  386. // have recieved all the acks.
  387. if (turbo) {
  388. int ack = 0;
  389. do {
  390. try {
  391. ack = in.readInt();
  392. } catch (IOException e) {
  393. break;
  394. }
  395. } while (ack > 0 && (readSize - ack) > 0);
  396. }
  397. return false;
  398. }
  399. return true;
  400. } else if (bytesRead < 0) {
  401. fileIn.close();
  402. return true;
  403. }
  404. } catch (IOException e) {
  405. return false;
  406. }
  407. return false;
  408. }
  409. }