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 15KB

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