Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

DCCPlugin.java 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334
  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 com.dmdirc.commandparser.CommandManager;
  24. import com.dmdirc.plugins.Plugin;
  25. import com.dmdirc.ui.swing.JWrappingLabel;
  26. import com.dmdirc.ui.WindowManager;
  27. import com.dmdirc.ui.swing.components.TextFrame;
  28. import com.dmdirc.config.IdentityManager;
  29. import com.dmdirc.config.Identity;
  30. import com.dmdirc.logger.ErrorLevel;
  31. import com.dmdirc.logger.Logger;
  32. import com.dmdirc.parser.IRCParser;
  33. import com.dmdirc.parser.ClientInfo;
  34. import com.dmdirc.Server;
  35. import com.dmdirc.Main;
  36. import com.dmdirc.actions.ActionManager;
  37. import com.dmdirc.actions.interfaces.ActionType;
  38. import com.dmdirc.actions.CoreActionType;
  39. import com.dmdirc.interfaces.ActionListener;
  40. import java.io.File;
  41. import java.io.IOException;
  42. import java.util.List;
  43. import java.util.ArrayList;
  44. import java.util.Properties;
  45. import javax.swing.SwingConstants;
  46. import javax.swing.JOptionPane;
  47. import javax.swing.JFileChooser;
  48. import javax.swing.JFrame;
  49. /**
  50. * This plugin adds DCC to dmdirc
  51. *
  52. * @author Shane 'Dataforce' McCormack
  53. * @version $Id: DCCPlugin.java 969 2007-04-30 18:38:20Z ShaneMcC $
  54. */
  55. public final class DCCPlugin extends Plugin implements ActionListener {
  56. /** The DCCCommand we created */
  57. private DCCCommand command = null;
  58. /** Our DCC Container window. */
  59. private DCCFrame container;
  60. /** What domain do we store all settings in the global config under. */
  61. private static final String MY_DOMAIN = "plugin-DCC";
  62. /** Child Frames */
  63. private List<DCCFrame> childFrames = new ArrayList<DCCFrame>();
  64. // /** Pending Sends (This uses DCCFrame not DCCSend so we can check if the window was closed or not) */
  65. // private Map<String, DCCFrame> pendingSends = new HashMap<String, DCCFrame>();
  66. /**
  67. * Creates a new instance of the DCC Plugin.
  68. */
  69. public DCCPlugin() {
  70. super();
  71. }
  72. /**
  73. * Ask a question, if the answer is the answer required, then recall handleProcessEvent
  74. *
  75. * @param question Question to ask
  76. * @param title Title of question dialog
  77. * @param desiredAnswer Answer required
  78. * @param type Actiontype to pass back
  79. * @param format StringBuffer to pass back
  80. * @param arguments arguments to pass back
  81. */
  82. public void askQuestion(final String question, final String title, final int desiredAnswer, final ActionType type, final StringBuffer format, final Object... arguments) {
  83. // New thread to ask the question in to stop us locking the UI
  84. Thread questionThread = new Thread(new Runnable() {
  85. public void run() {
  86. int result = JOptionPane.showConfirmDialog((JFrame)Main.getUI().getMainWindow(), question, title, JOptionPane.YES_NO_OPTION);
  87. if (result == desiredAnswer) {
  88. handleProcessEvent(type, format, true, arguments);
  89. }
  90. }
  91. }, "QuestionThread: "+title);
  92. // Start the thread
  93. questionThread.start();
  94. }
  95. /**
  96. * Ask the location to save a file, then start the download.
  97. *
  98. * @param nickname Person this dcc is from.
  99. * @param send The DCCSend to save for.
  100. * @param parser The parser this send was received on
  101. * @param reverse Is this a reverse dcc?
  102. * @param token Token used in reverse dcc.
  103. */
  104. public void saveFile(final String nickname, final DCCSend send, final IRCParser parser, final boolean reverse, final String sendFilename, final String token) {
  105. // New thread to ask the user where to save in to stop us locking the UI
  106. Thread dccThread = new Thread(new Runnable() {
  107. public void run() {
  108. final JFileChooser jc = new JFileChooser(IdentityManager.getGlobalConfig().getOption(MY_DOMAIN, "recieve.savelocation"));
  109. jc.setDialogTitle("Save "+sendFilename+" As - DMDirc ");
  110. jc.setFileSelectionMode(jc.FILES_AND_DIRECTORIES);
  111. jc.setMultiSelectionEnabled(false);
  112. jc.setSelectedFile(new File(send.getFileName()));
  113. int result = jc.showSaveDialog((JFrame)Main.getUI().getMainWindow());
  114. if (result == JFileChooser.APPROVE_OPTION) {
  115. send.setFileName(jc.getSelectedFile().getPath());
  116. if (reverse && !token.isEmpty()) {
  117. new DCCSendWindow(DCCPlugin.this, send, "*Recieve: "+nickname, parser.getMyNickname(), nickname);
  118. send.listen();
  119. parser.sendCTCP(nickname, "DCC", "SEND "+sendFilename+" "+DCC.ipToLong(send.getHost())+" "+send.getPort()+" "+send.getFileSize()+" "+token);
  120. } else {
  121. new DCCSendWindow(DCCPlugin.this, send, "Recieve: "+nickname, parser.getMyNickname(), nickname);
  122. send.connect();
  123. }
  124. }
  125. }
  126. }, "saveFileThread: "+sendFilename);
  127. // Start the thread
  128. dccThread.start();
  129. }
  130. /**
  131. * Process an event of the specified type.
  132. *
  133. * @param type The type of the event to process
  134. * @param format Format of messages that are about to be sent. (May be null)
  135. * @param arguments The arguments for the event
  136. */
  137. @Override
  138. public void processEvent(final ActionType type, final StringBuffer format, final Object... arguments) {
  139. handleProcessEvent(type, format, false, arguments);
  140. }
  141. /**
  142. * Process an event of the specified type.
  143. *
  144. * @param type The type of the event to process
  145. * @param format Format of messages that are about to be sent. (May be null)
  146. * @param dontAsk Don't ask any questions, assume yes.
  147. * @param arguments The arguments for the event
  148. */
  149. public void handleProcessEvent(final ActionType type, final StringBuffer format, final boolean dontAsk, final Object... arguments) {
  150. if (type == CoreActionType.SERVER_CTCP) {
  151. final String ctcpType = (String)arguments[2];
  152. final String[] ctcpData = ((String)arguments[3]).split(" ");
  153. if (ctcpType.equalsIgnoreCase("DCC")) {
  154. if (ctcpData[0].equalsIgnoreCase("chat") && ctcpData.length > 3) {
  155. final String nickname = ((ClientInfo)arguments[1]).getNickname();
  156. if (!dontAsk) {
  157. askQuestion("User "+nickname+" on "+((Server)arguments[0]).toString()+" would like to start a DCC Chat with you.\n\nDo you want to continue?", "DCC Chat Request", JOptionPane.YES_OPTION, type, format, arguments);
  158. return;
  159. } else {
  160. DCCChat chat = new DCCChat();
  161. try {
  162. chat.setAddress(Long.parseLong(ctcpData[2]), Integer.parseInt(ctcpData[3]));
  163. } catch (NumberFormatException nfe) { return; }
  164. String myNickname = ((Server)arguments[0]).getParser().getMyNickname();
  165. new DCCChatWindow(this, chat, "Chat: "+nickname, myNickname, nickname);
  166. chat.connect();
  167. }
  168. } else if (ctcpData[0].equalsIgnoreCase("send") && ctcpData.length > 3) {
  169. final String nickname = ((ClientInfo)arguments[1]).getNickname();
  170. final String filename;
  171. // Clients tend to put files with spaces in the name in "" so lets look for that.
  172. final StringBuilder filenameBits = new StringBuilder();
  173. int i;
  174. boolean quoted = ctcpData[1].startsWith("\"");
  175. if (quoted) {
  176. for (i = 1; i < ctcpData.length; i++) {
  177. String bit = ctcpData[i];
  178. if (i == 1) { bit = bit.substring(1); }
  179. if (bit.endsWith("\"")) {
  180. filenameBits.append(" "+bit.substring(0, bit.length()-1));
  181. break;
  182. } else {
  183. filenameBits.append(" "+bit);
  184. }
  185. }
  186. filename = filenameBits.toString().trim();
  187. } else {
  188. filename = ctcpData[1];
  189. i = 1;
  190. }
  191. final String ip = ctcpData[++i];
  192. final String port = ctcpData[++i];
  193. long size;
  194. long startpos;
  195. if (ctcpData.length+1 > i) {
  196. try {
  197. size = Long.parseLong(ctcpData[++i]);
  198. } catch (NumberFormatException nfe) { size = -1; }
  199. } else { size = -1; }
  200. // Add support for resume later
  201. startpos = 0;
  202. if (!dontAsk) {
  203. askQuestion("User "+nickname+" on "+((Server)arguments[0]).toString()+" would like to send you a file over DCC.\n\nFile: "+filename+"\n\nDo you want to continue?", "DCC Chat Request", JOptionPane.YES_OPTION, type, format, arguments);
  204. return;
  205. } else {
  206. DCCSend send = new DCCSend();
  207. try {
  208. if (!port.equals("0")) {
  209. send.setAddress(Long.parseLong(ip), Integer.parseInt(port));
  210. }
  211. } catch (NumberFormatException nfe) { return; }
  212. send.setFileName(filename);
  213. send.setFileSize(size);
  214. send.setFileStart(startpos);
  215. saveFile(nickname, send, ((Server)arguments[0]).getParser(), port.equals("0"), (quoted) ? "\""+filename+"\"" : filename, (ctcpData.length-1 > i) ? ctcpData[++i] : "");
  216. }
  217. }
  218. }
  219. }
  220. }
  221. /**
  222. * Create the container window
  223. */
  224. protected void createContainer() {
  225. container = new DCCFrame(this, "DCCs"){};
  226. JWrappingLabel label = new JWrappingLabel("This is a placeholder window to group DCCs together.", SwingConstants.CENTER);
  227. label.setText(label.getText()+"\n\nClosing this window will close all the active DCCs");
  228. ((TextFrame)container.getFrame()).getContentPane().add(label);
  229. WindowManager.addWindow(container.getFrame());
  230. }
  231. /**
  232. * Add a window to the container window
  233. *
  234. * @param window Window to remove
  235. */
  236. protected void addWindow(final DCCFrame window) {
  237. if (window == container) { return; }
  238. if (container == null) { createContainer(); }
  239. WindowManager.addWindow(container.getFrame(), window.getFrame());
  240. childFrames.add(window);
  241. }
  242. /**
  243. * Remove a window from the container window
  244. *
  245. * @param window Window to remove
  246. */
  247. protected void delWindow(final DCCFrame window) {
  248. if (container == null) { return; }
  249. if (window == container) {
  250. container = null;
  251. for (DCCFrame win : childFrames) {
  252. if (win != window) {
  253. win.close();
  254. }
  255. }
  256. childFrames.clear();
  257. } else {
  258. childFrames.remove(window);
  259. if (childFrames.size() == 0) {
  260. container.close();
  261. container = null;
  262. }
  263. }
  264. }
  265. /**
  266. * Called when the plugin is loaded.
  267. */
  268. @Override
  269. public void onLoad() {
  270. Properties defaults = new Properties();
  271. defaults.setProperty(MY_DOMAIN + ".recieve.savelocation", Main.getConfigDir() + "downloads" + System.getProperty("file.separator"));
  272. defaults.setProperty("identity.name", "DCC Plugin Defaults");
  273. IdentityManager.addIdentity(new Identity(defaults));
  274. final File dir = new File(IdentityManager.getGlobalConfig().getOption(MY_DOMAIN, "recieve.savelocation"));
  275. if (!dir.exists()) {
  276. try {
  277. dir.mkdirs();
  278. dir.createNewFile();
  279. } catch (IOException ex) {
  280. Logger.userError(ErrorLevel.LOW, "Unable to create download dir");
  281. }
  282. } else {
  283. if (!dir.isDirectory()) {
  284. Logger.userError(ErrorLevel.LOW, "Unable to create download dir (file exists instead)");
  285. }
  286. }
  287. command = new DCCCommand(this);
  288. ActionManager.addListener(this, CoreActionType.SERVER_CTCP);
  289. }
  290. /**
  291. * Called when this plugin is Unloaded
  292. */
  293. @Override
  294. public void onUnload() {
  295. CommandManager.unregisterCommand(command);
  296. ActionManager.removeListener(this);
  297. if (container != null) {
  298. container.close();
  299. }
  300. }
  301. /**
  302. * Get SVN Version information.
  303. *
  304. * @return SVN Version String
  305. */
  306. public static String getSvnInfo() { return "$Id: DCCPlugin.java 969 2007-04-30 18:38:20Z ShaneMcC $"; }
  307. }