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.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796
  1. /*
  2. * Copyright (c) 2006-2013 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.Server;
  24. import com.dmdirc.actions.ActionManager;
  25. import com.dmdirc.actions.CoreActionType;
  26. import com.dmdirc.addons.dcc.actions.DCCActions;
  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.SwingController;
  32. import com.dmdirc.addons.ui_swing.SwingWindowFactory;
  33. import com.dmdirc.addons.ui_swing.components.frames.ComponentFrame;
  34. import com.dmdirc.config.ConfigManager;
  35. import com.dmdirc.config.Identity;
  36. import com.dmdirc.config.IdentityManager;
  37. import com.dmdirc.config.prefs.PluginPreferencesCategory;
  38. import com.dmdirc.config.prefs.PreferencesCategory;
  39. import com.dmdirc.config.prefs.PreferencesDialogModel;
  40. import com.dmdirc.config.prefs.PreferencesSetting;
  41. import com.dmdirc.config.prefs.PreferencesType;
  42. import com.dmdirc.interfaces.ActionListener;
  43. import com.dmdirc.interfaces.actions.ActionType;
  44. import com.dmdirc.logger.ErrorLevel;
  45. import com.dmdirc.logger.Logger;
  46. import com.dmdirc.parser.interfaces.ClientInfo;
  47. import com.dmdirc.parser.interfaces.Parser;
  48. import com.dmdirc.plugins.BasePlugin;
  49. import com.dmdirc.plugins.PluginInfo;
  50. import com.dmdirc.ui.WindowManager;
  51. import java.io.File;
  52. import java.io.IOException;
  53. import java.net.InetAddress;
  54. import java.net.UnknownHostException;
  55. import java.util.Arrays;
  56. import java.util.HashSet;
  57. import javax.swing.JFileChooser;
  58. import javax.swing.JOptionPane;
  59. /**
  60. * This plugin adds DCC to DMDirc.
  61. */
  62. public final class DCCPlugin extends BasePlugin implements ActionListener {
  63. /** Our DCC Container window. */
  64. private PlaceholderContainer container;
  65. /** This plugin's plugin info. */
  66. private final PluginInfo pluginInfo;
  67. /** Config manager to read settings from. */
  68. private final ConfigManager config;
  69. /** Parent swing controller. */
  70. private final SwingController controller;
  71. /** Parent identity manager. */
  72. private final IdentityManager identityManager;
  73. /**
  74. * Creates a new instance of this plugin.
  75. *
  76. * @param controller The controller to register UI implementations with
  77. * @param pluginInfo This plugin's plugin info
  78. * @param identityManager The Identity Manager that controls the current config
  79. */
  80. public DCCPlugin(final SwingController controller,
  81. final PluginInfo pluginInfo,
  82. final IdentityManager identityManager
  83. ) {
  84. super();
  85. this.identityManager = identityManager;
  86. this.controller = controller;
  87. config = controller.getGlobalConfig();
  88. this.pluginInfo = pluginInfo;
  89. registerCommand(new DCCCommand(controller.getMainFrame(), this),
  90. DCCCommand.INFO);
  91. final SwingWindowFactory factory = controller.getWindowFactory();
  92. factory.registerImplementation(new HashSet<String>(Arrays.asList(
  93. "com.dmdirc.addons.dcc.ui.PlaceholderPanel")),
  94. ComponentFrame.class);
  95. factory.registerImplementation(new HashSet<String>(Arrays.asList(
  96. "com.dmdirc.addons.dcc.ui.TransferPanel")),
  97. ComponentFrame.class);
  98. }
  99. /**
  100. * Ask a question, if the answer is the answer required, then recall
  101. * handleProcessEvent.
  102. *
  103. * @param question Question to ask
  104. * @param title Title of question dialog
  105. * @param desiredAnswer Answer required
  106. * @param type Actiontype to pass back
  107. * @param format StringBuffer to pass back
  108. * @param arguments arguments to pass back
  109. */
  110. public void askQuestion(final String question, final String title,
  111. final int desiredAnswer, final ActionType type,
  112. final StringBuffer format, final Object... arguments) {
  113. // New thread to ask the question in to stop us locking the UI
  114. new Thread(new Runnable() {
  115. /** {@inheritDoc} */
  116. @Override
  117. public void run() {
  118. final int result = JOptionPane.showConfirmDialog(null, question,
  119. title, JOptionPane.YES_NO_OPTION);
  120. if (result == desiredAnswer) {
  121. handleProcessEvent(type, format, true, arguments);
  122. }
  123. }
  124. }, "QuestionThread: " + title).start();
  125. }
  126. /**
  127. * Ask the location to save a file, then start the download.
  128. *
  129. * @param nickname Person this dcc is from.
  130. * @param send The DCCSend to save for.
  131. * @param parser The parser this send was received on
  132. * @param reverse Is this a reverse dcc?
  133. * @param token Token used in reverse dcc.
  134. */
  135. public void saveFile(final String nickname, final DCCTransfer send,
  136. final Parser parser, final boolean reverse, final String token) {
  137. // New thread to ask the user where to save in to stop us locking the UI
  138. new Thread(new Runnable() {
  139. /** {@inheritDoc} */
  140. @Override
  141. public void run() {
  142. final JFileChooser jc = KFileChooser.getFileChooser(config,
  143. DCCPlugin.this,
  144. config.getOption(getDomain(), "receive.savelocation"));
  145. int result;
  146. if (config.getOptionBool(getDomain(), "receive.autoaccept")) {
  147. result = JFileChooser.APPROVE_OPTION;
  148. } else {
  149. result = showFileChooser(send, jc);
  150. }
  151. if (result != JFileChooser.APPROVE_OPTION) {
  152. return;
  153. }
  154. send.setFileName(jc.getSelectedFile().getPath());
  155. if (!handleExists(send, jc, nickname, parser,reverse, token)) {
  156. return;
  157. }
  158. final boolean resume = handleResume(jc);
  159. if (reverse && !token.isEmpty()) {
  160. new TransferContainer(DCCPlugin.this, send, config,
  161. "*Receive: " + nickname, nickname, null);
  162. send.setToken(token);
  163. if (resume) {
  164. if (config.getOptionBool(getDomain(),
  165. "receive.reverse.sendtoken")) {
  166. parser.sendCTCP(nickname, "DCC", "RESUME "
  167. + send.getShortFileName() + " 0 "
  168. + jc.getSelectedFile().length() + " "
  169. + token);
  170. } else {
  171. parser.sendCTCP(nickname, "DCC", "RESUME "
  172. + send.getShortFileName() + " 0 "
  173. + jc.getSelectedFile().length());
  174. }
  175. } else {
  176. if (listen(send)) {
  177. parser.sendCTCP(nickname, "DCC", "SEND "
  178. + send.getShortFileName() + " "
  179. + DCC.ipToLong(getListenIP(parser))
  180. + " " + send.getPort() + " "
  181. + send.getFileSize() + " " + token);
  182. }
  183. }
  184. } else {
  185. new TransferContainer(DCCPlugin.this, send, config,
  186. "Receive: " + nickname, nickname, null);
  187. if (resume) {
  188. parser.sendCTCP(nickname, "DCC", "RESUME "
  189. + send.getShortFileName() + " "
  190. + send.getPort() + " "
  191. + jc.getSelectedFile().length());
  192. } else {
  193. send.connect();
  194. }
  195. }
  196. }
  197. }, "saveFileThread: " + send.getShortFileName()).start();
  198. }
  199. /**
  200. * Checks if the selected file exists and prompts the user as required.
  201. *
  202. * @param send DCC Transfer
  203. * @param jc File chooser
  204. * @param nickname Remote nickname
  205. * @param parser Parser
  206. * @param reverse Reverse DCC?
  207. * @param token DCC token
  208. *
  209. * @return true if the user wants to continue, false if they wish to abort
  210. */
  211. private boolean handleExists(final DCCTransfer send, final JFileChooser jc,
  212. final String nickname, final Parser parser, final boolean reverse,
  213. final String token) {
  214. if (jc.getSelectedFile().exists() && send.getFileSize() > -1
  215. && send.getFileSize() <= jc.getSelectedFile().length()) {
  216. if (config.getOptionBool(getDomain(), "receive.autoaccept")) {
  217. return false;
  218. } else {
  219. JOptionPane.showMessageDialog(controller.getMainFrame(),
  220. "This file has already "
  221. + "been completed, or is longer than the file you are "
  222. + "receiving.\nPlease choose a different file.",
  223. "Problem with selected file",
  224. JOptionPane.ERROR_MESSAGE);
  225. saveFile(nickname, send, parser, reverse, token);
  226. return false;
  227. }
  228. }
  229. return true;
  230. }
  231. /**
  232. * Prompts the user to resume a transfer if required.
  233. *
  234. * @param jc File chooser
  235. *
  236. * @return true if the user wants to continue the transfer false otherwise
  237. */
  238. private boolean handleResume(final JFileChooser jc) {
  239. if (jc.getSelectedFile().exists()) {
  240. if (config.getOptionBool(getDomain(), "receive.autoaccept")) {
  241. return true;
  242. } else {
  243. final int result = JOptionPane.showConfirmDialog(
  244. controller.getMainFrame(), "This file exists already"
  245. + ", do you want to resume an exisiting download?",
  246. "Resume Download?", JOptionPane.YES_NO_OPTION);
  247. return (result == JOptionPane.YES_OPTION);
  248. }
  249. }
  250. return false;
  251. }
  252. /**
  253. * Sets up and display a file chooser.
  254. *
  255. * @param send DCCTransfer object sending the file
  256. * @param jc File chooser
  257. *
  258. * @return the return state of the file chooser on popdown:
  259. * <ul>
  260. * <li>JFileChooser.CANCEL_OPTION
  261. * <li>JFileChooser.APPROVE_OPTION
  262. * <li>JFileChooser.ERROR_OPTION if an error occurs or the
  263. * dialog is dismissed
  264. * </ul>
  265. */
  266. private int showFileChooser(final DCCTransfer send, final JFileChooser jc) {
  267. jc.setDialogTitle("Save " + send.getShortFileName() + " As - DMDirc");
  268. jc.setFileSelectionMode(JFileChooser.FILES_ONLY);
  269. jc.setMultiSelectionEnabled(false);
  270. jc.setSelectedFile(new File(send.getFileName()));
  271. return jc.showSaveDialog(controller.getMainFrame());
  272. }
  273. /** {@inheritDoc} */
  274. @Override
  275. public void processEvent(final ActionType type, final StringBuffer format,
  276. final Object... arguments) {
  277. handleProcessEvent(type, format, false, arguments);
  278. }
  279. /**
  280. * Make the given DCC start listening.
  281. * This will either call dcc.listen() or dcc.listen(startPort, endPort)
  282. * depending on config.
  283. *
  284. * @param dcc DCC to start listening.
  285. * @return True if Socket was opened.
  286. */
  287. protected boolean listen(final DCC dcc) {
  288. final boolean usePortRange = config.getOptionBool(getDomain(),
  289. "firewall.ports.usePortRange");
  290. try {
  291. if (usePortRange) {
  292. final int startPort = config.getOptionInt(getDomain(),
  293. "firewall.ports.startPort");
  294. final int endPort = config.getOptionInt(getDomain(),
  295. "firewall.ports.endPort");
  296. dcc.listen(startPort, endPort);
  297. } else {
  298. dcc.listen();
  299. }
  300. return true;
  301. } catch (IOException ioe) {
  302. return false;
  303. }
  304. }
  305. /**
  306. * Process an event of the specified type.
  307. *
  308. * @param type The type of the event to process
  309. * @param format Format of messages that are about to be sent. (May be null)
  310. * @param dontAsk Don't ask any questions, assume yes.
  311. * @param arguments The arguments for the event
  312. */
  313. public void handleProcessEvent(final ActionType type,
  314. final StringBuffer format, final boolean dontAsk,
  315. final Object... arguments) {
  316. if (config.getOptionBool(getDomain(), "receive.autoaccept") && !dontAsk) {
  317. handleProcessEvent(type, format, true, arguments);
  318. return;
  319. }
  320. if (type == CoreActionType.SERVER_CTCP) {
  321. final String[] ctcpData = ((String) arguments[3]).split(" ");
  322. if ("DCC".equalsIgnoreCase((String) arguments[2])) {
  323. if ("chat".equalsIgnoreCase(ctcpData[0])
  324. && ctcpData.length > 3) {
  325. handleChat(type, format, dontAsk, ctcpData, arguments);
  326. } else if ("send".equalsIgnoreCase(ctcpData[0])
  327. && ctcpData.length > 3) {
  328. handleSend(type, format, dontAsk, ctcpData, arguments);
  329. } else if (("resume".equalsIgnoreCase(ctcpData[0])
  330. || "accept".equalsIgnoreCase(ctcpData[0]))
  331. && ctcpData.length > 2) {
  332. handleReceive(ctcpData, arguments);
  333. }
  334. }
  335. }
  336. }
  337. /**
  338. * Handles a DCC chat request.
  339. *
  340. * @param type The type of the event to process
  341. * @param format Format of messages that are about to be sent. (May be null)
  342. * @param dontAsk Don't ask any questions, assume yes.
  343. * @param ctcpData CTCP data bits
  344. * @param arguments The arguments for the event
  345. */
  346. private void handleChat(final ActionType type, final StringBuffer format,
  347. final boolean dontAsk, final String[] ctcpData,
  348. final Object... arguments) {
  349. final String nickname = ((ClientInfo) arguments[1]).getNickname();
  350. if (dontAsk) {
  351. final DCCChat chat = new DCCChat();
  352. try {
  353. chat.setAddress(Long.parseLong(ctcpData[2]),
  354. Integer.parseInt(ctcpData[3]));
  355. } catch (NumberFormatException nfe) {
  356. return;
  357. }
  358. final String myNickname = ((Server) arguments[0]).getParser()
  359. .getLocalClient().getNickname();
  360. final DCCFrameContainer f = new ChatContainer(this, chat, config,
  361. "Chat: " + nickname, myNickname, nickname);
  362. f.addLine("DCCChatStarting", nickname, chat.getHost(),
  363. chat.getPort());
  364. chat.connect();
  365. } else {
  366. ActionManager.getActionManager().triggerEvent(
  367. DCCActions.DCC_CHAT_REQUEST, null, arguments[0],
  368. nickname);
  369. askQuestion("User " + nickname + " on "
  370. + ((Server) arguments[0]).getName()
  371. + " would like to start a DCC Chat with you.\n\n"
  372. + "Do you want to continue?",
  373. "DCC Chat Request", JOptionPane.YES_OPTION,
  374. type, format, arguments);
  375. return;
  376. }
  377. }
  378. /**
  379. * Handles a DCC send request.
  380. *
  381. * @param type The type of the event to process
  382. * @param format Format of messages that are about to be sent. (May be null)
  383. * @param dontAsk Don't ask any questions, assume yes.
  384. * @param ctcpData CTCP data bits
  385. * @param arguments The arguments for the event
  386. */
  387. private void handleSend(final ActionType type, final StringBuffer format,
  388. final boolean dontAsk, final String[] ctcpData,
  389. final Object... arguments) {
  390. final String nickname = ((ClientInfo) arguments[1]).getNickname();
  391. final String filename;
  392. String tmpFilename;
  393. // Clients tend to put files with spaces in the name in ""
  394. final StringBuilder filenameBits = new StringBuilder();
  395. int i;
  396. final boolean quoted = ctcpData[1].startsWith("\"");
  397. if (quoted) {
  398. for (i = 1; i < ctcpData.length; i++) {
  399. String bit = ctcpData[i];
  400. if (i == 1) {
  401. bit = bit.substring(1);
  402. }
  403. if (bit.endsWith("\"")) {
  404. filenameBits.append(" ")
  405. .append(bit.substring(0, bit.length() - 1));
  406. break;
  407. } else {
  408. filenameBits.append(" ").append(bit);
  409. }
  410. }
  411. tmpFilename = filenameBits.toString().trim();
  412. } else {
  413. tmpFilename = ctcpData[1];
  414. i = 1;
  415. }
  416. // Try to remove path names if sent.
  417. // Change file separatorChar from other OSs first
  418. if (File.separatorChar == '/') {
  419. tmpFilename = tmpFilename.replace('\\', File.separatorChar);
  420. } else {
  421. tmpFilename = tmpFilename.replace('/', File.separatorChar);
  422. }
  423. // Then get just the name of the file.
  424. filename = new File(tmpFilename).getName();
  425. final String ip = ctcpData[++i];
  426. final String port = ctcpData[++i];
  427. long size;
  428. if (ctcpData.length + 1 > i) {
  429. try {
  430. size = Integer.parseInt(ctcpData[++i]);
  431. } catch (NumberFormatException nfe) {
  432. size = -1;
  433. }
  434. } else {
  435. size = -1;
  436. }
  437. final String token = (ctcpData.length - 1 > i
  438. && !ctcpData[i + 1].equals("T")) ? ctcpData[++i] : "";
  439. // Ignore incorrect ports, or non-numeric IP/Port
  440. try {
  441. final int portInt = Integer.parseInt(port);
  442. if (portInt > 65535 || portInt < 0) {
  443. return;
  444. }
  445. Long.parseLong(ip);
  446. } catch (NumberFormatException nfe) {
  447. return;
  448. }
  449. DCCTransfer send = DCCTransfer.findByToken(token);
  450. if (send == null && !dontAsk) {
  451. if (!token.isEmpty() && !port.equals("0")) {
  452. // This is a reverse DCC Send that we no longer care about.
  453. return;
  454. } else {
  455. ActionManager.getActionManager().triggerEvent(
  456. DCCActions.DCC_SEND_REQUEST, null,
  457. arguments[0], nickname, filename);
  458. askQuestion("User " + nickname + " on "
  459. + ((Server) arguments[0]).getName()
  460. + " would like to send you a file over DCC.\n\nFile: "
  461. + filename + "\n\nDo you want to continue?",
  462. "DCC Send Request", JOptionPane.YES_OPTION, type,
  463. format, arguments);
  464. return;
  465. }
  466. } else {
  467. final boolean newSend = send == null;
  468. if (newSend) {
  469. send = new DCCTransfer(config.getOptionInt(getDomain(),
  470. "send.blocksize"));
  471. send.setTurbo(config.getOptionBool(getDomain(), "send.forceturbo"));
  472. }
  473. try {
  474. send.setAddress(Long.parseLong(ip), Integer.parseInt(port));
  475. } catch (NumberFormatException nfe) {
  476. return;
  477. }
  478. if (newSend) {
  479. send.setFileName(filename);
  480. send.setFileSize(size);
  481. saveFile(nickname, send, ((Server) arguments[0]).getParser(),
  482. "0".equals(port), token);
  483. } else {
  484. send.connect();
  485. }
  486. }
  487. }
  488. /**
  489. * Handles a DCC chat request.
  490. *
  491. * @param ctcpData CTCP data bits
  492. * @param arguments The arguments for the event
  493. */
  494. private void handleReceive(final String[] ctcpData,
  495. final Object... arguments) {
  496. final String filename;
  497. // Clients tend to put files with spaces in the name in ""
  498. final StringBuilder filenameBits = new StringBuilder();
  499. int i;
  500. final boolean quoted = ctcpData[1].startsWith("\"");
  501. if (quoted) {
  502. for (i = 1; i < ctcpData.length; i++) {
  503. String bit = ctcpData[i];
  504. if (i == 1) {
  505. bit = bit.substring(1);
  506. }
  507. if (bit.endsWith("\"")) {
  508. filenameBits.append(" ")
  509. .append(bit.substring(0, bit.length() - 1));
  510. break;
  511. } else {
  512. filenameBits.append(" ").append(bit);
  513. }
  514. }
  515. filename = filenameBits.toString().trim();
  516. } else {
  517. filename = ctcpData[1];
  518. i = 1;
  519. }
  520. final int port;
  521. final int position;
  522. try {
  523. port = Integer.parseInt(ctcpData[++i]);
  524. position = Integer.parseInt(ctcpData[++i]);
  525. } catch (NumberFormatException nfe) {
  526. return;
  527. }
  528. final String token = (ctcpData.length - 1 > i) ? " "
  529. + ctcpData[++i] : "";
  530. // Now look for a dcc that matches.
  531. for (DCCTransfer send : DCCTransfer.getTransfers()) {
  532. if (send.getPort() == port && (new File(send.getFileName()))
  533. .getName().equalsIgnoreCase(filename)) {
  534. if ((!token.isEmpty() && !send.getToken().isEmpty())
  535. && (!token.equals(send.getToken()))) {
  536. continue;
  537. }
  538. final Parser parser = ((Server) arguments[0]).getParser();
  539. final String nick = ((ClientInfo) arguments[1]).getNickname();
  540. if (ctcpData[0].equalsIgnoreCase("resume")) {
  541. parser.sendCTCP(nick, "DCC", "ACCEPT "+ ((quoted) ? "\""
  542. + filename + "\"" : filename) + " " + port + " "
  543. + send.setFileStart(position) + token);
  544. } else {
  545. send.setFileStart(position);
  546. if (port == 0) {
  547. // Reverse dcc
  548. if (listen(send)) {
  549. if (send.getToken().isEmpty()) {
  550. parser.sendCTCP(nick, "DCC", "SEND "
  551. + ((quoted) ? "\"" + filename
  552. + "\"" : filename) + " "
  553. + DCC.ipToLong(send.getHost())
  554. + " " + send.getPort()
  555. + " " + send.getFileSize());
  556. } else {
  557. parser.sendCTCP(nick, "DCC", "SEND "
  558. + ((quoted) ? "\"" + filename
  559. + "\"" : filename)
  560. + " " + DCC.ipToLong(send.getHost())
  561. + " " + send.getPort()
  562. + " " + send.getFileSize() + " "
  563. + send.getToken());
  564. }
  565. }
  566. } else {
  567. send.connect();
  568. }
  569. }
  570. }
  571. }
  572. }
  573. /**
  574. * Retrieves the container for the placeholder.
  575. *
  576. * @since 0.6.4
  577. * @return This plugin's placeholder container
  578. */
  579. public synchronized PlaceholderContainer getContainer() {
  580. if (container == null) {
  581. createContainer();
  582. }
  583. return container;
  584. }
  585. /**
  586. * Removes the cached container.
  587. *
  588. * @since 0.6.4
  589. */
  590. public synchronized void removeContainer() {
  591. container = null;
  592. }
  593. /**
  594. * Create the container window.
  595. */
  596. protected void createContainer() {
  597. container = new PlaceholderContainer(this, config, controller);
  598. WindowManager.getWindowManager().addWindow(container);
  599. }
  600. /** {@inheritDoc} */
  601. @Override
  602. public void domainUpdated() {
  603. final Identity defaults = IdentityManager.getIdentityManager()
  604. .getGlobalAddonIdentity();
  605. defaults.setOption(getDomain(), "receive.savelocation",
  606. identityManager.getConfigDir() + "downloads"
  607. + System.getProperty("file.separator"));
  608. }
  609. /**
  610. * Called when the plugin is loaded.
  611. */
  612. @Override
  613. public void onLoad() {
  614. final File dir = new File(config.getOption(getDomain(),
  615. "receive.savelocation"));
  616. if (dir.exists()) {
  617. if (!dir.isDirectory()) {
  618. Logger.userError(ErrorLevel.LOW,
  619. "Unable to create download dir (file exists instead)");
  620. }
  621. } else {
  622. try {
  623. dir.mkdirs();
  624. dir.createNewFile();
  625. } catch (IOException ex) {
  626. Logger.userError(ErrorLevel.LOW,
  627. "Unable to create download dir");
  628. }
  629. }
  630. ActionManager.getActionManager().registerTypes(DCCActions.values());
  631. ActionManager.getActionManager().registerListener(this,
  632. CoreActionType.SERVER_CTCP);
  633. super.onLoad();
  634. }
  635. /**
  636. * Called when this plugin is Unloaded.
  637. */
  638. @Override
  639. public synchronized void onUnload() {
  640. ActionManager.getActionManager().unregisterListener(this);
  641. if (container != null) {
  642. container.close();
  643. }
  644. super.onUnload();
  645. }
  646. /**
  647. * Get the IP Address we should send as our listening IP.
  648. *
  649. * @return The IP Address we should send as our listening IP.
  650. */
  651. public String getListenIP() {
  652. return getListenIP(null);
  653. }
  654. /**
  655. * Get the IP Address we should send as our listening IP.
  656. *
  657. * @param parser Parser the IRC Parser where this dcc is initiated
  658. * @return The IP Address we should send as our listening IP.
  659. */
  660. public String getListenIP(final Parser parser) {
  661. final String configIP = config.getOption(getDomain(), "firewall.ip");
  662. if (!configIP.isEmpty()) {
  663. try {
  664. return InetAddress.getByName(configIP).getHostAddress();
  665. } catch (UnknownHostException ex) { //NOPMD - handled below
  666. //Continue below
  667. }
  668. }
  669. if (parser != null) {
  670. final String myHost = parser.getLocalClient().getHostname();
  671. if (!myHost.isEmpty()) {
  672. try {
  673. return InetAddress.getByName(myHost).getHostAddress();
  674. } catch (UnknownHostException e) { //NOPMD - handled below
  675. //Continue below
  676. }
  677. }
  678. }
  679. try {
  680. return InetAddress.getLocalHost().getHostAddress();
  681. } catch (UnknownHostException e) {
  682. // This is almost certainly not what we want, but we can't work out
  683. // the right one.
  684. return "127.0.0.1"; //NOPMD
  685. }
  686. }
  687. /** {@inheritDoc} */
  688. @Override
  689. public void showConfig(final PreferencesDialogModel manager) {
  690. final PreferencesCategory general = new PluginPreferencesCategory(
  691. pluginInfo, "DCC", "", "category-dcc");
  692. final PreferencesCategory firewall = new PluginPreferencesCategory(
  693. pluginInfo, "Firewall", "");
  694. final PreferencesCategory sending = new PluginPreferencesCategory(
  695. pluginInfo, "Sending", "");
  696. final PreferencesCategory receiving = new PluginPreferencesCategory(
  697. pluginInfo, "Receiving", "");
  698. manager.getCategory("Plugins").addSubCategory(general.setInlineAfter());
  699. general.addSubCategory(firewall.setInline());
  700. general.addSubCategory(sending.setInline());
  701. general.addSubCategory(receiving.setInline());
  702. firewall.addSetting(new PreferencesSetting(PreferencesType.TEXT,
  703. getDomain(), "firewall.ip", "Forced IP",
  704. "What IP should be sent as our IP (Blank = work it out)",
  705. manager.getConfigManager(), manager.getIdentity()));
  706. firewall.addSetting(new PreferencesSetting(PreferencesType.BOOLEAN,
  707. getDomain(), "firewall.ports.usePortRange", "Use Port Range",
  708. "Useful if you have a firewall that only forwards specific "
  709. + "ports", manager.getConfigManager(), manager.getIdentity()));
  710. firewall.addSetting(new PreferencesSetting(PreferencesType.INTEGER,
  711. getDomain(), "firewall.ports.startPort", "Start Port",
  712. "Port to try to listen on first", manager.getConfigManager(),
  713. manager.getIdentity()));
  714. firewall.addSetting(new PreferencesSetting(PreferencesType.INTEGER,
  715. getDomain(), "firewall.ports.endPort", "End Port",
  716. "Port to try to listen on last", manager.getConfigManager(),
  717. manager.getIdentity()));
  718. receiving.addSetting(new PreferencesSetting(PreferencesType.DIRECTORY,
  719. getDomain(), "receive.savelocation", "Default save location",
  720. "Where the save as window defaults to?",
  721. manager.getConfigManager(), manager.getIdentity()));
  722. sending.addSetting(new PreferencesSetting(PreferencesType.BOOLEAN,
  723. getDomain(), "send.reverse", "Reverse DCC",
  724. "With reverse DCC, the sender connects rather than "
  725. + "listens like normal dcc", manager.getConfigManager(),
  726. manager.getIdentity()));
  727. sending.addSetting(new PreferencesSetting(PreferencesType.BOOLEAN,
  728. getDomain(), "send.forceturbo", "Use Turbo DCC",
  729. "Turbo DCC doesn't wait for ack packets. this is "
  730. + "faster but not always supported.",
  731. manager.getConfigManager(), manager.getIdentity()));
  732. receiving.addSetting(new PreferencesSetting(PreferencesType.BOOLEAN,
  733. getDomain(), "receive.reverse.sendtoken",
  734. "Send token in reverse receive",
  735. "If you have problems with reverse dcc receive resume,"
  736. + " try toggling this.", manager.getConfigManager(),
  737. manager.getIdentity()));
  738. general.addSetting(new PreferencesSetting(PreferencesType.INTEGER,
  739. getDomain(), "send.blocksize", "Blocksize to use for DCC",
  740. "Change the block size for send/receive, this can "
  741. + "sometimes speed up transfers.", manager.getConfigManager(),
  742. manager.getIdentity()));
  743. general.addSetting(new PreferencesSetting(PreferencesType.BOOLEAN,
  744. getDomain(), "general.percentageInTitle",
  745. "Show percentage of transfers in the window title",
  746. "Show the current percentage of transfers in the DCC window "
  747. + "title", manager.getConfigManager(), manager.getIdentity()));
  748. }
  749. }