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.

TransferContainer.java 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372
  1. /*
  2. * Copyright (c) 2006-2014 DMDirc Developers
  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 com.dmdirc.DMDircMBassador;
  24. import com.dmdirc.FrameContainer;
  25. import com.dmdirc.ServerState;
  26. import com.dmdirc.addons.dcc.events.DccSendDatatransferedEvent;
  27. import com.dmdirc.addons.dcc.events.DccSendSocketclosedEvent;
  28. import com.dmdirc.addons.dcc.events.DccSendSocketopenedEvent;
  29. import com.dmdirc.addons.dcc.io.DCC;
  30. import com.dmdirc.addons.dcc.io.DCCTransfer;
  31. import com.dmdirc.interfaces.Connection;
  32. import com.dmdirc.interfaces.config.AggregateConfigProvider;
  33. import com.dmdirc.parser.interfaces.Parser;
  34. import com.dmdirc.parser.interfaces.callbacks.SocketCloseListener;
  35. import com.dmdirc.ui.messages.ColourManagerFactory;
  36. import com.dmdirc.util.URLBuilder;
  37. import java.awt.Desktop;
  38. import java.io.File;
  39. import java.util.Arrays;
  40. import java.util.Date;
  41. import javax.swing.JOptionPane;
  42. /**
  43. * This class links DCC Send objects to a window.
  44. */
  45. public class TransferContainer extends FrameContainer implements
  46. DCCTransferHandler, SocketCloseListener {
  47. /** The dcc plugin that owns this frame */
  48. protected final DCCManager plugin;
  49. /** Config manager. */
  50. private final AggregateConfigProvider config;
  51. /** The Window we're using. */
  52. private boolean windowClosing = false;
  53. /** The DCCSend object we are a window for */
  54. private final DCCTransfer dcc;
  55. /** Other Nickname */
  56. private final String otherNickname;
  57. /** Total data transferred */
  58. private volatile long transferCount = 0;
  59. /** Time Started */
  60. private long timeStarted = 0;
  61. /** Plugin that this send belongs to. */
  62. private final DCCManager myPlugin;
  63. /** IRC Parser that caused this send */
  64. private Parser parser = null;
  65. /** Connection the send was initiated on. */
  66. private Connection connection = null;
  67. /** Show open button. */
  68. private final boolean showOpen = Desktop.isDesktopSupported()
  69. && Desktop.getDesktop().isSupported(Desktop.Action.OPEN);
  70. /** Event bus to post events on. */
  71. private final DMDircMBassador eventBus;
  72. /**
  73. * Creates a new instance of DCCTransferWindow with a given DCCTransfer object.
  74. *
  75. * @param plugin the DCC Plugin responsible for this window
  76. * @param dcc The DCCTransfer object this window wraps around
  77. * @param config Config manager
  78. * @param title The title of this window
  79. * @param targetNick Nickname of target
  80. * @param connection The connection that the send was that initiated on
  81. * @param urlBuilder The URL builder to use when finding icons.
  82. * @param eventBus The bus to dispatch events on.
  83. */
  84. public TransferContainer(final DCCManager plugin, final DCCTransfer dcc,
  85. final AggregateConfigProvider config,
  86. final ColourManagerFactory colourManagerFactory, final String title,
  87. final String targetNick, final Connection connection,
  88. final URLBuilder urlBuilder, final DMDircMBassador eventBus) {
  89. super(plugin.getContainer(), dcc.getType() == DCCTransfer.TransferType.SEND
  90. ? "dcc-send-inactive" : "dcc-receive-inactive",
  91. title, title, config, colourManagerFactory, urlBuilder, eventBus,
  92. Arrays.asList("com.dmdirc.addons.dcc.ui.TransferPanel"));
  93. this.plugin = plugin;
  94. this.dcc = dcc;
  95. this.connection = connection;
  96. this.config = config;
  97. parser = connection == null ? null : connection.getParser();
  98. myPlugin = plugin;
  99. if (parser != null) {
  100. parser.getCallbackManager().addNonCriticalCallback(
  101. SocketCloseListener.class, this);
  102. }
  103. dcc.addHandler(this);
  104. otherNickname = targetNick;
  105. this.eventBus = eventBus;
  106. }
  107. @Override
  108. public void onSocketClosed(final Parser parser, final Date date) {
  109. // Remove our reference to the parser (and its reference to us)
  110. this.parser.getCallbackManager().delAllCallback(this);
  111. this.parser = null;
  112. }
  113. /**
  114. * Get the DCCSend Object associated with this window
  115. *
  116. * @return The DCCSend Object associated with this window
  117. */
  118. public DCCTransfer getDCC() {
  119. return dcc;
  120. }
  121. /**
  122. * Retrieves the nickname of the other party involved in this transfer.
  123. *
  124. * @return The other party's nickname
  125. *
  126. * @since 0.6.4
  127. */
  128. public String getOtherNickname() {
  129. return otherNickname;
  130. }
  131. /**
  132. * Called when data is sent/received
  133. *
  134. * @param dcc The DCCSend that this message is from
  135. * @param bytes The number of new bytes that were transferred
  136. */
  137. @Override
  138. public void dataTransferred(final DCCTransfer dcc, final int bytes) {
  139. final double percent;
  140. synchronized (this) {
  141. transferCount += bytes;
  142. percent = getPercent();
  143. }
  144. final boolean percentageInTitle = config.getOptionBool(
  145. plugin.getDomain(), "general.percentageInTitle");
  146. if (percentageInTitle) {
  147. final StringBuilder title = new StringBuilder();
  148. if (dcc.isListenSocket()) {
  149. title.append('*');
  150. }
  151. title.append(dcc.getType() == DCCTransfer.TransferType.SEND
  152. ? "Sending: " : "Receiving: ");
  153. title.append(otherNickname);
  154. title.append(" (")
  155. .append(String.format("%.0f", Math.floor(percent)))
  156. .append("%)");
  157. setName(title.toString());
  158. setTitle(title.toString());
  159. }
  160. eventBus.publish(new DccSendDatatransferedEvent(this, bytes));
  161. }
  162. /**
  163. * Retrieves the current percentage progress of this transfer.
  164. *
  165. * @since 0.6.4
  166. * @return The percentage of this transfer that has been completed
  167. */
  168. public double getPercent() {
  169. return (100.00 / dcc.getFileSize()) * (transferCount
  170. + dcc.getFileStart());
  171. }
  172. /**
  173. * Retrieves the current transfer speed of this transfer.
  174. *
  175. * @since 0.6.4
  176. * @return The speed of this transfer in Bytes/Sec
  177. */
  178. public double getBytesPerSecond() {
  179. final long time = getElapsedTime();
  180. synchronized (this) {
  181. return time > 0 ? ((double) transferCount / time) : transferCount;
  182. }
  183. }
  184. /**
  185. * Retrieves the estimated time remaining for this transfer.
  186. *
  187. * @since 0.6.4
  188. * @return The number of seconds estimated for this transfer to complete
  189. */
  190. public double getRemainingTime() {
  191. final double bytesPerSecond = getBytesPerSecond();
  192. final long remainingBytes;
  193. synchronized (this) {
  194. remainingBytes = dcc.getFileSize() - dcc.getFileStart()
  195. - transferCount;
  196. }
  197. return bytesPerSecond > 0 ? (remainingBytes / bytesPerSecond) : 1;
  198. }
  199. /**
  200. * Retrieves the timestamp at which this transfer started.
  201. *
  202. * @since 0.6.4
  203. * @return The timestamp (milliseconds since 01/01/1970) at which this transfer started.
  204. */
  205. public long getStartTime() {
  206. return timeStarted;
  207. }
  208. /**
  209. * Retrieves the number of seconds that this transfer has been running for.
  210. *
  211. * @since 0.6.4
  212. * @return The number of seconds elapsed since this transfer started
  213. */
  214. public long getElapsedTime() {
  215. return (System.currentTimeMillis() - timeStarted) / 1000;
  216. }
  217. /**
  218. * Determines whether this transfer is complete or not.
  219. *
  220. * @since 0.6.4
  221. * @return True if the transfer is complete, false otherwise
  222. */
  223. public boolean isComplete() {
  224. return transferCount == dcc.getFileSize() - dcc.getFileStart();
  225. }
  226. /**
  227. * Determines whether the "Open" button should be displayed for this transfer.
  228. *
  229. * @since 0.6.4
  230. * @return True if the open button should be displayed, false otherwise
  231. */
  232. public boolean shouldShowOpenButton() {
  233. return showOpen && dcc.getType() == DCCTransfer.TransferType.RECEIVE;
  234. }
  235. /**
  236. * Called when the socket is closed
  237. *
  238. * @param dcc The DCCSend that this message is from
  239. */
  240. @Override
  241. public void socketClosed(final DCCTransfer dcc) {
  242. eventBus.publish(new DccSendSocketclosedEvent(this));
  243. if (!windowClosing) {
  244. synchronized (this) {
  245. if (transferCount == dcc.getFileSize() - dcc.getFileStart()) {
  246. setIcon(dcc.getType() == DCCTransfer.TransferType.SEND
  247. ? "dcc-send-done" : "dcc-receive-done");
  248. } else {
  249. setIcon(dcc.getType() == DCCTransfer.TransferType.SEND
  250. ? "dcc-send-failed" : "dcc-receive-failed");
  251. }
  252. }
  253. }
  254. }
  255. /**
  256. * Called when the socket is opened
  257. *
  258. * @param dcc The DCCSend that this message is from
  259. */
  260. @Override
  261. public void socketOpened(final DCCTransfer dcc) {
  262. eventBus.publish(new DccSendSocketopenedEvent(this));
  263. timeStarted = System.currentTimeMillis();
  264. setIcon(dcc.getType() == DCCTransfer.TransferType.SEND
  265. ? "dcc-send-active" : "dcc-receive-active");
  266. }
  267. /**
  268. * Attempts to resend the transfer.
  269. *
  270. * @since 0.6.4
  271. * @return True if the transfer could be resent, false otherwise
  272. */
  273. public boolean resend() {
  274. synchronized (this) {
  275. transferCount = 0;
  276. }
  277. dcc.reset();
  278. if (connection != null && connection.getState() == ServerState.CONNECTED) {
  279. final String myNickname = connection.getParser().getLocalClient()
  280. .getNickname();
  281. // Check again in case we have changed nickname to the same nickname
  282. //that this send is for.
  283. if (connection.getParser().getStringConverter().equalsIgnoreCase(
  284. otherNickname, myNickname)) {
  285. final Thread errorThread = new Thread(new Runnable() {
  286. @Override
  287. public void run() {
  288. JOptionPane.showMessageDialog(null,
  289. "You can't DCC yourself.", "DCC Error",
  290. JOptionPane.ERROR_MESSAGE);
  291. }
  292. }, "DCC-Error-Message");
  293. errorThread.start();
  294. } else {
  295. if (config.getOptionBool(plugin.getDomain(), "send.reverse")) {
  296. parser.sendCTCP(otherNickname, "DCC", "SEND \"" + new File(dcc.getFileName()).
  297. getName() + "\" "
  298. + DCC.ipToLong(myPlugin.getListenIP(parser))
  299. + " 0 " + dcc.getFileSize() + " " + dcc.makeToken()
  300. + ((dcc.isTurbo()) ? " T" : ""));
  301. } else if (plugin.listen(dcc)) {
  302. parser.sendCTCP(otherNickname, "DCC", "SEND \""
  303. + new File(dcc.getFileName()).getName() + "\" "
  304. + DCC.ipToLong(myPlugin.getListenIP(parser)) + " "
  305. + dcc.getPort() + " " + dcc.getFileSize()
  306. + ((dcc.isTurbo()) ? " T" : ""));
  307. }
  308. }
  309. return true;
  310. }
  311. return false;
  312. }
  313. /**
  314. * Closes this container (and it's associated frame).
  315. */
  316. @Override
  317. public void close() {
  318. windowClosing = true;
  319. super.close();
  320. dcc.removeFromTransfers();
  321. }
  322. public void addSocketCloseCallback(final SocketCloseListener listener) {
  323. if (connection != null && connection.getParser() != null) {
  324. connection.getParser().getCallbackManager()
  325. .addNonCriticalCallback(SocketCloseListener.class,
  326. listener);
  327. }
  328. }
  329. @Override
  330. public Connection getConnection() {
  331. return null;
  332. }
  333. }