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.

DCCCommand.java 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344
  1. /*
  2. * Copyright (c) 2006-2015 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.addons.dcc.events.DccChatRequestSentEvent;
  26. import com.dmdirc.addons.dcc.events.DccSendRequestEvent;
  27. import com.dmdirc.addons.dcc.io.DCC;
  28. import com.dmdirc.addons.dcc.io.DCCChat;
  29. import com.dmdirc.addons.dcc.io.DCCTransfer;
  30. import com.dmdirc.addons.dcc.kde.KFileChooser;
  31. import com.dmdirc.addons.ui_swing.UIUtilities;
  32. import com.dmdirc.addons.ui_swing.injection.MainWindow;
  33. import com.dmdirc.commandparser.BaseCommandInfo;
  34. import com.dmdirc.commandparser.CommandArguments;
  35. import com.dmdirc.commandparser.CommandInfo;
  36. import com.dmdirc.commandparser.CommandType;
  37. import com.dmdirc.commandparser.commands.Command;
  38. import com.dmdirc.commandparser.commands.IntelligentCommand;
  39. import com.dmdirc.commandparser.commands.context.CommandContext;
  40. import com.dmdirc.commandparser.commands.context.ServerCommandContext;
  41. import com.dmdirc.interfaces.CommandController;
  42. import com.dmdirc.interfaces.Connection;
  43. import com.dmdirc.parser.interfaces.Parser;
  44. import com.dmdirc.ui.WindowManager;
  45. import com.dmdirc.ui.messages.BackBufferFactory;
  46. import com.dmdirc.ui.input.AdditionalTabTargets;
  47. import com.dmdirc.ui.input.TabCompleterFactory;
  48. import com.dmdirc.ui.input.TabCompletionType;
  49. import com.dmdirc.ui.messages.sink.MessageSinkManager;
  50. import com.dmdirc.util.URLBuilder;
  51. import java.awt.Window;
  52. import java.io.File;
  53. import javax.annotation.Nonnull;
  54. import javax.inject.Inject;
  55. import javax.swing.JFileChooser;
  56. import javax.swing.JOptionPane;
  57. /**
  58. * This command allows starting dcc chats/file transfers.
  59. */
  60. public class DCCCommand extends Command implements IntelligentCommand {
  61. /** A command info object for this command. */
  62. public static final CommandInfo INFO = new BaseCommandInfo("dcc",
  63. "dcc <SEND|CHAT> <target> [params] - starts a DCC",
  64. CommandType.TYPE_SERVER);
  65. /** My Plugin. */
  66. private final DCCManager myPlugin;
  67. /** Main window used as the parent for dialogs. */
  68. private final Window mainWindow;
  69. /** Window management. */
  70. private final WindowManager windowManager;
  71. /** The sink manager to use to dispatch messages. */
  72. private final MessageSinkManager messageSinkManager;
  73. /** The factory to use for tab completers. */
  74. private final TabCompleterFactory tabCompleterFactory;
  75. /** The URL builder to use when finding icons. */
  76. private final URLBuilder urlBuilder;
  77. /** The bus to dispatch events on. */
  78. private final DMDircMBassador eventBus;
  79. private final BackBufferFactory backBufferFactory;
  80. /**
  81. * Creates a new instance of DCCCommand.
  82. */
  83. @Inject
  84. public DCCCommand(
  85. final CommandController controller,
  86. @MainWindow final Window mainWindow,
  87. final DCCManager plugin,
  88. final MessageSinkManager messageSinkManager,
  89. final WindowManager windowManager,
  90. final TabCompleterFactory tabCompleterFactory,
  91. final URLBuilder urlBuilder,
  92. final DMDircMBassador eventBus,
  93. final BackBufferFactory backBufferFactory) {
  94. super(controller);
  95. this.mainWindow = mainWindow;
  96. myPlugin = plugin;
  97. this.messageSinkManager = messageSinkManager;
  98. this.windowManager = windowManager;
  99. this.tabCompleterFactory = tabCompleterFactory;
  100. this.urlBuilder = urlBuilder;
  101. this.eventBus = eventBus;
  102. this.backBufferFactory = backBufferFactory;
  103. }
  104. @Override
  105. public void execute(@Nonnull final FrameContainer origin,
  106. final CommandArguments args, final CommandContext context) {
  107. if (args.getArguments().length > 1) {
  108. final String target = args.getArguments()[1];
  109. final Connection connection = ((ServerCommandContext) context).getConnection();
  110. final Parser parser = connection.getParser().get();
  111. final String myNickname = parser.getLocalClient().getNickname();
  112. if (parser.isValidChannelName(target)
  113. || parser.getStringConverter().equalsIgnoreCase(target,
  114. myNickname)) {
  115. new Thread(() -> {
  116. if (parser.getStringConverter().equalsIgnoreCase(target,
  117. myNickname)) {
  118. JOptionPane.showMessageDialog(null,
  119. "You can't DCC yourself.", "DCC Error",
  120. JOptionPane.ERROR_MESSAGE);
  121. } else {
  122. JOptionPane.showMessageDialog(null,
  123. "You can't DCC a channel.", "DCC Error",
  124. JOptionPane.ERROR_MESSAGE);
  125. }
  126. }, "DCC-Error-Message").start();
  127. return;
  128. }
  129. final String type = args.getArguments()[0];
  130. if ("chat".equalsIgnoreCase(type)) {
  131. startChat(parser, connection, origin, myNickname, target, true);
  132. } else if ("send".equalsIgnoreCase(type)) {
  133. sendFile(target, origin, connection, true,
  134. args.getArgumentsAsString(2));
  135. } else {
  136. sendLine(origin, args.isSilent(), FORMAT_ERROR,
  137. "Unknown DCC Type: '" + type + "'");
  138. }
  139. } else {
  140. showUsage(origin, true, INFO.getName(), INFO.getHelp());
  141. }
  142. }
  143. /**
  144. * Starts a DCC Chat.
  145. *
  146. * @param parser Parser from which command originated
  147. * @param connection Server from which command originated
  148. * @param origin Frame container from which command originated
  149. * @param myNickname My current nickname
  150. * @param target Target of the command
  151. * @param isSilent Is this a silent command
  152. */
  153. private void startChat(final Parser parser, final Connection connection,
  154. final FrameContainer origin, final String myNickname,
  155. final String target, final boolean isSilent) {
  156. final DCCChat chat = new DCCChat();
  157. if (myPlugin.listen(chat)) {
  158. final ChatContainer window = new ChatContainer(
  159. myPlugin.getContainer(),
  160. chat,
  161. origin.getConfigManager(),
  162. backBufferFactory,
  163. getController(),
  164. "*Chat: " + target,
  165. myNickname,
  166. target,
  167. tabCompleterFactory,
  168. messageSinkManager,
  169. urlBuilder,
  170. eventBus);
  171. windowManager.addWindow(myPlugin.getContainer(), window);
  172. parser.sendCTCP(target, "DCC", "CHAT chat " + DCC.ipToLong(
  173. myPlugin.getListenIP(parser)) + " " + chat.getPort());
  174. eventBus.publish(new DccChatRequestSentEvent(connection, target));
  175. sendLine(origin, isSilent, "DCCChatStarting", target, chat.getHost(), chat.getPort());
  176. window.addLine("DCCChatStarting", target, chat.getHost(), chat.getPort());
  177. } else {
  178. sendLine(origin, isSilent, "DCCChatError",
  179. "Unable to start chat with " + target
  180. + " - unable to create listen socket");
  181. }
  182. }
  183. /**
  184. * Ask for the file to send, then start the send.
  185. *
  186. * @param target Person this dcc is to.
  187. * @param origin The InputWindow this command was issued on
  188. * @param connection The server instance that this command is being executed on
  189. * @param isSilent Whether this command is silenced or not
  190. * @param filename The file to send
  191. *
  192. * @since 0.6.3m1
  193. */
  194. public void sendFile(final String target, final FrameContainer origin,
  195. final Connection connection, final boolean isSilent, final String filename) {
  196. // New thread to ask the user what file to send
  197. final File givenFile = new File(filename);
  198. final File selectedFile = UIUtilities.invokeAndWait(() -> {
  199. final JFileChooser jc = givenFile.exists()
  200. ? KFileChooser.getFileChooser(origin.getConfigManager(),
  201. myPlugin, givenFile)
  202. : KFileChooser.getFileChooser(origin.getConfigManager(),
  203. myPlugin);
  204. final int result = showFileChooser(givenFile, target, jc);
  205. if (result != JFileChooser.APPROVE_OPTION
  206. || !handleInvalidItems(jc)) {
  207. return null;
  208. }
  209. return jc.getSelectedFile();
  210. });
  211. if (selectedFile == null) {
  212. return;
  213. }
  214. new Thread(() -> {
  215. final DCCTransfer send = new DCCTransfer(origin
  216. .getConfigManager().getOptionInt(myPlugin.getDomain(),
  217. "send.blocksize"));
  218. send.setTurbo(origin.getConfigManager().getOptionBool(
  219. myPlugin.getDomain(), "send.forceturbo"));
  220. send.setType(DCCTransfer.TransferType.SEND);
  221. eventBus.publish(new DccSendRequestEvent(connection, target, selectedFile.
  222. getAbsolutePath()));
  223. sendLine(origin, isSilent, FORMAT_OUTPUT,
  224. "Starting DCC Send with: " + target);
  225. send.setFileName(selectedFile.getAbsolutePath());
  226. send.setFileSize(selectedFile.length());
  227. if (origin.getConfigManager().getOptionBool(
  228. myPlugin.getDomain(), "send.reverse")) {
  229. final Parser parser = connection.getParser().get();
  230. final TransferContainer container = new TransferContainer(myPlugin, send,
  231. origin.getConfigManager(), backBufferFactory, "Send: " + target,
  232. target, connection, urlBuilder, eventBus);
  233. windowManager.addWindow(myPlugin.getContainer(), container);
  234. parser.sendCTCP(target, "DCC", "SEND \""
  235. + selectedFile.getName() + "\" "
  236. + DCC.ipToLong(myPlugin.getListenIP(parser))
  237. + " 0 " + send.getFileSize() + " "
  238. + send.makeToken()
  239. + (send.isTurbo() ? " T" : ""));
  240. } else {
  241. final Parser parser = connection.getParser().get();
  242. if (myPlugin.listen(send)) {
  243. final TransferContainer container = new TransferContainer(myPlugin, send,
  244. origin.getConfigManager(), backBufferFactory, "*Send: "
  245. + target, target, connection, urlBuilder, eventBus);
  246. windowManager.addWindow(myPlugin.getContainer(), container);
  247. parser.sendCTCP(target, "DCC", "SEND \""
  248. + selectedFile.getName() + "\" "
  249. + DCC.ipToLong(myPlugin.getListenIP(parser))
  250. + " " + send.getPort() + " " + send.getFileSize()
  251. + (send.isTurbo() ? " T" : ""));
  252. } else {
  253. sendLine(origin, isSilent, "DCCSendError",
  254. "Unable to start dcc send with " + target
  255. + " - unable to create listen socket");
  256. }
  257. }
  258. }, "openFileThread").start();
  259. }
  260. /**
  261. * Checks for invalid items.
  262. *
  263. * @param jc File chooser to check
  264. *
  265. * @return true iif the selection was valid
  266. */
  267. private boolean handleInvalidItems(final JFileChooser jc) {
  268. if (jc.getSelectedFile().length() == 0) {
  269. JOptionPane.showMessageDialog(null,
  270. "You can't send empty files over DCC.", "DCC Error",
  271. JOptionPane.ERROR_MESSAGE);
  272. return false;
  273. } else if (!jc.getSelectedFile().exists()) {
  274. JOptionPane.showMessageDialog(null, "Invalid file specified",
  275. "DCC Error", JOptionPane.ERROR_MESSAGE);
  276. return false;
  277. }
  278. return true;
  279. }
  280. /**
  281. * Sets up and display a file chooser.
  282. *
  283. * @param givenFile File to display
  284. * @param target DCC target
  285. * @param jc File chooser
  286. *
  287. * @return the return state of the file chooser on popdown:
  288. * <ul>
  289. * <li>JFileChooser.CANCEL_OPTION
  290. * <li>JFileChooser.APPROVE_OPTION
  291. * <li>JFileChooser.ERROR_OPTION if an error occurs or the dialog is dismissed
  292. * </ul>
  293. */
  294. private int showFileChooser(final File givenFile, final String target,
  295. final JFileChooser jc) {
  296. if (givenFile.exists() && givenFile.isFile()) {
  297. jc.setSelectedFile(givenFile);
  298. return JFileChooser.APPROVE_OPTION;
  299. } else {
  300. jc.setDialogTitle("Send file to " + target + " - DMDirc ");
  301. jc.setFileSelectionMode(JFileChooser.FILES_ONLY);
  302. jc.setMultiSelectionEnabled(false);
  303. return jc.showOpenDialog(mainWindow);
  304. }
  305. }
  306. @Override
  307. public AdditionalTabTargets getSuggestions(final int arg,
  308. final IntelligentCommandContext context) {
  309. final AdditionalTabTargets res = new AdditionalTabTargets();
  310. if (arg == 0) {
  311. res.add("SEND");
  312. res.add("CHAT");
  313. res.excludeAll();
  314. } else if (arg == 1) {
  315. res.exclude(TabCompletionType.COMMAND);
  316. res.exclude(TabCompletionType.CHANNEL);
  317. } else {
  318. res.excludeAll();
  319. }
  320. return res;
  321. }
  322. }