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.

DCCPlugin.java 28KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601
  1. /*
  2. * Copyright (c) 2006-2010 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.Main;
  24. import com.dmdirc.Server;
  25. import com.dmdirc.actions.ActionManager;
  26. import com.dmdirc.actions.CoreActionType;
  27. import com.dmdirc.actions.interfaces.ActionType;
  28. import com.dmdirc.addons.dcc.actions.DCCActions;
  29. import com.dmdirc.addons.dcc.io.DCC;
  30. import com.dmdirc.addons.dcc.io.DCCChat;
  31. import com.dmdirc.addons.dcc.io.DCCTransfer;
  32. import com.dmdirc.addons.dcc.kde.KFileChooser;
  33. import com.dmdirc.addons.ui_swing.SwingController;
  34. import com.dmdirc.commandparser.CommandManager;
  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.logger.ErrorLevel;
  44. import com.dmdirc.logger.Logger;
  45. import com.dmdirc.parser.interfaces.ClientInfo;
  46. import com.dmdirc.parser.interfaces.Parser;
  47. import com.dmdirc.plugins.Plugin;
  48. import com.dmdirc.plugins.PluginManager;
  49. import com.dmdirc.ui.WindowManager;
  50. import java.io.File;
  51. import java.io.IOException;
  52. import java.net.InetAddress;
  53. import java.net.UnknownHostException;
  54. import javax.swing.JFileChooser;
  55. import javax.swing.JOptionPane;
  56. /**
  57. * This plugin adds DCC to dmdirc.
  58. *
  59. * @author Shane 'Dataforce' McCormack
  60. */
  61. public final class DCCPlugin extends Plugin implements ActionListener {
  62. /** The DCCCommand we created. */
  63. private DCCCommand command;
  64. /** Our DCC Container window. */
  65. private PlaceholderContainer container;
  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. final Thread questionThread = new Thread(new Runnable() {
  85. /** {@inheritDoc} */
  86. @Override
  87. public void run() {
  88. int result = JOptionPane.showConfirmDialog(null, question, title, JOptionPane.YES_NO_OPTION);
  89. if (result == desiredAnswer) {
  90. handleProcessEvent(type, format, true, arguments);
  91. }
  92. }
  93. }, "QuestionThread: " + title);
  94. // Start the thread
  95. questionThread.start();
  96. }
  97. /**
  98. * Ask the location to save a file, then start the download.
  99. *
  100. * @param nickname Person this dcc is from.
  101. * @param send The DCCSend to save for.
  102. * @param parser The parser this send was received on
  103. * @param reverse Is this a reverse dcc?
  104. * @param sendFilename The name of the file which is being received
  105. * @param token Token used in reverse dcc.
  106. */
  107. public void saveFile(final String nickname, final DCCTransfer send, final Parser parser, final boolean reverse, final String sendFilename, final String token) {
  108. // New thread to ask the user where to save in to stop us locking the UI
  109. final Thread dccThread = new Thread(new Runnable() {
  110. /** {@inheritDoc} */
  111. @Override
  112. public void run() {
  113. final JFileChooser jc = KFileChooser.getFileChooser(DCCPlugin.this, IdentityManager.getGlobalConfig().getOption(getDomain(), "receive.savelocation"));
  114. jc.setDialogTitle("Save " + sendFilename + " As - DMDirc");
  115. jc.setFileSelectionMode(JFileChooser.FILES_ONLY);
  116. jc.setMultiSelectionEnabled(false);
  117. jc.setSelectedFile(new File(send.getFileName()));
  118. int result;
  119. if (IdentityManager.getGlobalConfig().getOptionBool(getDomain(), "receive.autoaccept")) {
  120. result = JFileChooser.APPROVE_OPTION;
  121. } else {
  122. result = jc.showSaveDialog(((SwingController) PluginManager
  123. .getPluginManager().getPluginInfoByName("ui_swing")
  124. .getPlugin()).getMainFrame());
  125. }
  126. if (result == JFileChooser.APPROVE_OPTION) {
  127. send.setFileName(jc.getSelectedFile().getPath());
  128. boolean resume = false;
  129. if (jc.getSelectedFile().exists()) {
  130. if (send.getFileSize() > -1 && send.getFileSize() <= jc.getSelectedFile().length()) {
  131. if (IdentityManager.getGlobalConfig().getOptionBool(getDomain(), "receive.autoaccept")) {
  132. return;
  133. } else {
  134. JOptionPane.showMessageDialog(
  135. ((SwingController) PluginManager
  136. .getPluginManager().getPluginInfoByName("ui_swing")
  137. .getPlugin()).getMainFrame(), "This file has already been completed, or is longer than the file you are receiving.\nPlease choose a different file.", "Problem with selected file", JOptionPane.ERROR_MESSAGE);
  138. saveFile(nickname, send, parser, reverse, sendFilename, token);
  139. return;
  140. }
  141. } else {
  142. if (IdentityManager.getGlobalConfig().getOptionBool(getDomain(), "receive.autoaccept")) {
  143. resume = true;
  144. } else {
  145. result = JOptionPane.showConfirmDialog(
  146. ((SwingController) PluginManager
  147. .getPluginManager().getPluginInfoByName("ui_swing")
  148. .getPlugin()).getMainFrame(), "This file exists already, do you want to resume an exisiting download?", "Resume Download?", JOptionPane.YES_NO_OPTION);
  149. resume = (result == JOptionPane.YES_OPTION);
  150. }
  151. }
  152. }
  153. if (reverse && !token.isEmpty()) {
  154. new TransferContainer(DCCPlugin.this, send, "*Receive: " + nickname, nickname, null);
  155. send.setToken(token);
  156. if (resume) {
  157. if (IdentityManager.getGlobalConfig().getOptionBool(getDomain(), "receive.reverse.sendtoken")) {
  158. parser.sendCTCP(nickname, "DCC", "RESUME " + sendFilename + " 0 " + jc.getSelectedFile().length() + " " + token);
  159. } else {
  160. parser.sendCTCP(nickname, "DCC", "RESUME " + sendFilename + " 0 " + jc.getSelectedFile().length());
  161. }
  162. } else {
  163. if (listen(send)) {
  164. parser.sendCTCP(nickname, "DCC", "SEND " + sendFilename + " " + DCC.ipToLong(getListenIP(parser)) + " " + send.getPort() + " " + send.getFileSize() + " " + token);
  165. } else {
  166. // Listen failed.
  167. }
  168. }
  169. } else {
  170. new TransferContainer(DCCPlugin.this, send, "Receive: " + nickname, nickname, null);
  171. if (resume) {
  172. parser.sendCTCP(nickname, "DCC", "RESUME " + sendFilename + " " + send.getPort() + " " + jc.getSelectedFile().length());
  173. } else {
  174. send.connect();
  175. }
  176. }
  177. }
  178. }
  179. }, "saveFileThread: " + sendFilename);
  180. // Start the thread
  181. dccThread.start();
  182. }
  183. /**
  184. * Process an event of the specified type.
  185. *
  186. * @param type The type of the event to process
  187. * @param format Format of messages that are about to be sent. (May be null)
  188. * @param arguments The arguments for the event
  189. */
  190. @Override
  191. public void processEvent(final ActionType type, final StringBuffer format, final Object... arguments) {
  192. handleProcessEvent(type, format, false, arguments);
  193. }
  194. /**
  195. * Make the given DCC start listening.
  196. * This will either call dcc.listen() or dcc.listen(startPort, endPort)
  197. * depending on config.
  198. *
  199. * @param dcc DCC to start listening.
  200. * @return True if Socket was opened.
  201. */
  202. protected boolean listen(final DCC dcc) {
  203. final boolean usePortRange = IdentityManager.getGlobalConfig().getOptionBool(getDomain(), "firewall.ports.usePortRange");
  204. final int startPort = IdentityManager.getGlobalConfig().getOptionInt(getDomain(), "firewall.ports.startPort");
  205. final int endPort = IdentityManager.getGlobalConfig().getOptionInt(getDomain(), "firewall.ports.endPort");
  206. try {
  207. if (usePortRange) {
  208. dcc.listen(startPort, endPort);
  209. } else {
  210. dcc.listen();
  211. }
  212. return true;
  213. } catch (IOException ioe) {
  214. return false;
  215. }
  216. }
  217. /**
  218. * Process an event of the specified type.
  219. *
  220. * @param type The type of the event to process
  221. * @param format Format of messages that are about to be sent. (May be null)
  222. * @param dontAsk Don't ask any questions, assume yes.
  223. * @param arguments The arguments for the event
  224. */
  225. public void handleProcessEvent(final ActionType type, final StringBuffer format, final boolean dontAsk, final Object... arguments) {
  226. if (IdentityManager.getGlobalConfig().getOptionBool(getDomain(), "receive.autoaccept") && !dontAsk) {
  227. handleProcessEvent(type, format, true, arguments);
  228. return;
  229. }
  230. if (type == CoreActionType.SERVER_CTCP) {
  231. final String ctcpType = (String) arguments[2];
  232. final String[] ctcpData = ((String) arguments[3]).split(" ");
  233. if (ctcpType.equalsIgnoreCase("DCC")) {
  234. if (ctcpData[0].equalsIgnoreCase("chat") && ctcpData.length > 3) {
  235. final String nickname = ((ClientInfo) arguments[1]).getNickname();
  236. if (dontAsk) {
  237. final DCCChat chat = new DCCChat();
  238. try {
  239. chat.setAddress(Long.parseLong(ctcpData[2]), Integer.parseInt(ctcpData[3]));
  240. } catch (NumberFormatException nfe) {
  241. return;
  242. }
  243. final String myNickname = ((Server) arguments[0]).getParser().getLocalClient().getNickname();
  244. final DCCFrameContainer<?> f = new ChatContainer(this, chat, "Chat: " + nickname, myNickname, nickname);
  245. f.addLine("DCCChatStarting", nickname, chat.getHost(), chat.getPort());
  246. chat.connect();
  247. } else {
  248. ActionManager.processEvent(DCCActions.DCC_CHAT_REQUEST, null, ((Server) arguments[0]), nickname);
  249. 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);
  250. return;
  251. }
  252. } else if (ctcpData[0].equalsIgnoreCase("send") && ctcpData.length > 3) {
  253. final String nickname = ((ClientInfo) arguments[1]).getNickname();
  254. final String filename;
  255. String tmpFilename;
  256. // Clients tend to put files with spaces in the name in "" so lets look for that.
  257. final StringBuilder filenameBits = new StringBuilder();
  258. int i;
  259. final boolean quoted = ctcpData[1].startsWith("\"");
  260. if (quoted) {
  261. for (i = 1; i < ctcpData.length; i++) {
  262. String bit = ctcpData[i];
  263. if (i == 1) {
  264. bit = bit.substring(1);
  265. }
  266. if (bit.endsWith("\"")) {
  267. filenameBits.append(" " + bit.substring(0, bit.length() - 1));
  268. break;
  269. } else {
  270. filenameBits.append(" " + bit);
  271. }
  272. }
  273. tmpFilename = filenameBits.toString().trim();
  274. } else {
  275. tmpFilename = ctcpData[1];
  276. i = 1;
  277. }
  278. // Try to remove path names if sent.
  279. // Change file separatorChar from other OSs first
  280. if (File.separatorChar == '/') {
  281. tmpFilename = tmpFilename.replace('\\', File.separatorChar);
  282. } else {
  283. tmpFilename = tmpFilename.replace('/', File.separatorChar);
  284. }
  285. // Then get just the name of the file.
  286. filename = (new File(tmpFilename)).getName();
  287. final String ip = ctcpData[++i];
  288. final String port = ctcpData[++i];
  289. long size;
  290. if (ctcpData.length + 1 > i) {
  291. try {
  292. size = Integer.parseInt(ctcpData[++i]);
  293. } catch (NumberFormatException nfe) {
  294. size = -1;
  295. }
  296. } else {
  297. size = -1;
  298. }
  299. final String token = (ctcpData.length - 1 > i && !ctcpData[i + 1].equals("T")) ? ctcpData[++i] : "";
  300. // Ignore incorrect ports, or non-numeric IP/Port
  301. try {
  302. int portInt = Integer.parseInt(port);
  303. if (portInt > 65535 || portInt < 0) {
  304. return;
  305. }
  306. Long.parseLong(ip);
  307. } catch (NumberFormatException nfe) {
  308. return;
  309. }
  310. DCCTransfer send = DCCTransfer.findByToken(token);
  311. if (send == null && !dontAsk) {
  312. if (!token.isEmpty() && !port.equals("0")) {
  313. // This is a reverse DCC Send that we no longer care about.
  314. return;
  315. } else {
  316. ActionManager.processEvent(DCCActions.DCC_SEND_REQUEST, null, ((Server) arguments[0]), nickname, filename);
  317. 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 Send Request", JOptionPane.YES_OPTION, type, format, arguments);
  318. return;
  319. }
  320. } else {
  321. final boolean newSend = send == null;
  322. if (newSend) {
  323. send = new DCCTransfer(IdentityManager.getGlobalConfig().getOptionInt(getDomain(), "send.blocksize"));
  324. send.setTurbo(IdentityManager.getGlobalConfig().getOptionBool(getDomain(), "send.forceturbo"));
  325. }
  326. try {
  327. send.setAddress(Long.parseLong(ip), Integer.parseInt(port));
  328. } catch (NumberFormatException nfe) {
  329. return;
  330. }
  331. if (newSend) {
  332. send.setFileName(filename);
  333. send.setFileSize(size);
  334. saveFile(nickname, send, ((Server) arguments[0]).getParser(), "0".equals(port), (quoted) ? "\"" + filename + "\"" : filename, token);
  335. } else {
  336. send.connect();
  337. }
  338. }
  339. } else if ((ctcpData[0].equalsIgnoreCase("resume") || ctcpData[0].equalsIgnoreCase("accept")) && ctcpData.length > 2) {
  340. final String filename;
  341. // Clients tend to put files with spaces in the name in "" so lets look for that.
  342. final StringBuilder filenameBits = new StringBuilder();
  343. int i;
  344. final boolean quoted = ctcpData[1].startsWith("\"");
  345. if (quoted) {
  346. for (i = 1; i < ctcpData.length; i++) {
  347. String bit = ctcpData[i];
  348. if (i == 1) {
  349. bit = bit.substring(1);
  350. }
  351. if (bit.endsWith("\"")) {
  352. filenameBits.append(" " + bit.substring(0, bit.length() - 1));
  353. break;
  354. } else {
  355. filenameBits.append(" " + bit);
  356. }
  357. }
  358. filename = filenameBits.toString().trim();
  359. } else {
  360. filename = ctcpData[1];
  361. i = 1;
  362. }
  363. try {
  364. final int port = Integer.parseInt(ctcpData[++i]);
  365. final int position = Integer.parseInt(ctcpData[++i]);
  366. final String token = (ctcpData.length - 1 > i) ? " " + ctcpData[++i] : "";
  367. // Now look for a dcc that matches.
  368. for (DCCTransfer send : DCCTransfer.getTransfers()) {
  369. if (send.getPort() == port && (new File(send.getFileName())).getName().equalsIgnoreCase(filename)) {
  370. if ((!token.isEmpty() && !send.getToken().isEmpty()) && (!token.equals(send.getToken()))) {
  371. continue;
  372. }
  373. final Parser parser = ((Server) arguments[0]).getParser();
  374. final String nickname = ((ClientInfo) arguments[1]).getNickname();
  375. if (ctcpData[0].equalsIgnoreCase("resume")) {
  376. parser.sendCTCP(nickname, "DCC", "ACCEPT " + ((quoted) ? "\"" + filename + "\"" : filename) + " " + port + " " + send.setFileStart(position) + token);
  377. } else {
  378. send.setFileStart(position);
  379. if (port == 0) {
  380. // Reverse dcc
  381. if (listen(send)) {
  382. if (send.getToken().isEmpty()) {
  383. parser.sendCTCP(nickname, "DCC", "SEND " + ((quoted) ? "\"" + filename + "\"" : filename) + " " + DCC.ipToLong(send.getHost()) + " " + send.getPort() + " " + send.getFileSize());
  384. } else {
  385. parser.sendCTCP(nickname, "DCC", "SEND " + ((quoted) ? "\"" + filename + "\"" : filename) + " " + DCC.ipToLong(send.getHost()) + " " + send.getPort() + " " + send.getFileSize() + " " + send.getToken());
  386. }
  387. } else {
  388. // Listen failed.
  389. }
  390. } else {
  391. send.connect();
  392. }
  393. }
  394. }
  395. }
  396. } catch (NumberFormatException nfe) {
  397. }
  398. }
  399. }
  400. }
  401. }
  402. /**
  403. * Retrieves the container for the placeholder.
  404. *
  405. * @since 0.6.4
  406. * @return This plugin's placeholder container
  407. */
  408. public synchronized PlaceholderContainer getContainer() {
  409. if (container == null) {
  410. createContainer();
  411. }
  412. return container;
  413. }
  414. /**
  415. * Removes the cached container.
  416. *
  417. * @since 0.6.4
  418. */
  419. public synchronized void removeContainer() {
  420. container = null;
  421. }
  422. /**
  423. * Create the container window.
  424. */
  425. protected void createContainer() {
  426. container = new PlaceholderContainer(this);
  427. WindowManager.addWindow(container);
  428. }
  429. /** {@inheritDoc} */
  430. @Override
  431. public void domainUpdated() {
  432. final Identity defaults = IdentityManager.getAddonIdentity();
  433. defaults.setOption(getDomain(), "receive.savelocation",
  434. Main.getConfigDir() + "downloads" + System.getProperty("file.separator"));
  435. }
  436. /**
  437. * Called when the plugin is loaded.
  438. */
  439. @Override
  440. public void onLoad() {
  441. final File dir = new File(IdentityManager.getGlobalConfig().getOption(getDomain(), "receive.savelocation"));
  442. if (dir.exists()) {
  443. if (!dir.isDirectory()) {
  444. Logger.userError(ErrorLevel.LOW, "Unable to create download dir (file exists instead)");
  445. }
  446. } else {
  447. try {
  448. dir.mkdirs();
  449. dir.createNewFile();
  450. } catch (IOException ex) {
  451. Logger.userError(ErrorLevel.LOW, "Unable to create download dir");
  452. }
  453. }
  454. command = new DCCCommand(this);
  455. CommandManager.registerCommand(command);
  456. ActionManager.registerActionTypes(DCCActions.values());
  457. ActionManager.addListener(this, CoreActionType.SERVER_CTCP);
  458. }
  459. /**
  460. * Called when this plugin is Unloaded.
  461. */
  462. @Override
  463. public synchronized void onUnload() {
  464. CommandManager.unregisterCommand(command);
  465. ActionManager.removeListener(this);
  466. if (container != null) {
  467. container.close();
  468. }
  469. }
  470. /**
  471. * Get the IP Address we should send as our listening IP.
  472. *
  473. * @return The IP Address we should send as our listening IP.
  474. */
  475. public String getListenIP() {
  476. return getListenIP(null);
  477. }
  478. /**
  479. * Get the IP Address we should send as our listening IP.
  480. *
  481. * @param parser Parser the IRC Parser where this dcc is initiated
  482. * @return The IP Address we should send as our listening IP.
  483. */
  484. public String getListenIP(final Parser parser) {
  485. final String configIP = IdentityManager.getGlobalConfig().getOption(getDomain(), "firewall.ip");
  486. if (!configIP.isEmpty()) {
  487. try {
  488. return InetAddress.getByName(configIP).getHostAddress();
  489. } catch (UnknownHostException ex) {
  490. //Fallthrough
  491. }
  492. }
  493. if (parser != null) {
  494. final String myHost = parser.getLocalClient().getHostname();
  495. if (!myHost.isEmpty()) {
  496. try {
  497. return InetAddress.getByName(myHost).getHostAddress();
  498. } catch (UnknownHostException e) { /* Will return default host below */ }
  499. }
  500. }
  501. try {
  502. return InetAddress.getLocalHost().getHostAddress();
  503. } catch (UnknownHostException e) {
  504. // This is almost certainly not what we want, but we can't work out
  505. // the right one.
  506. return "127.0.0.1";
  507. }
  508. }
  509. /** {@inheritDoc} */
  510. @Override
  511. public void showConfig(final PreferencesDialogModel manager) {
  512. final PreferencesCategory general = new PluginPreferencesCategory(getPluginInfo(), "DCC", "", "category-dcc");
  513. final PreferencesCategory firewall = new PluginPreferencesCategory(getPluginInfo(), "Firewall", "");
  514. final PreferencesCategory sending = new PluginPreferencesCategory(getPluginInfo(), "Sending", "");
  515. final PreferencesCategory receiving = new PluginPreferencesCategory(getPluginInfo(), "Receiving", "");
  516. manager.getCategory("Plugins").addSubCategory(general.setInlineAfter());
  517. general.addSubCategory(firewall.setInline());
  518. general.addSubCategory(sending.setInline());
  519. general.addSubCategory(receiving.setInline());
  520. firewall.addSetting(new PreferencesSetting(PreferencesType.TEXT,
  521. getDomain(), "firewall.ip", "Forced IP",
  522. "What IP should be sent as our IP (Blank = work it out)"));
  523. firewall.addSetting(new PreferencesSetting(PreferencesType.BOOLEAN,
  524. getDomain(), "firewall.ports.usePortRange", "Use Port Range",
  525. "Useful if you have a firewall that only forwards specific ports"));
  526. firewall.addSetting(new PreferencesSetting(PreferencesType.INTEGER,
  527. getDomain(), "firewall.ports.startPort", "Start Port",
  528. "Port to try to listen on first"));
  529. firewall.addSetting(new PreferencesSetting(PreferencesType.INTEGER,
  530. getDomain(), "firewall.ports.endPort", "End Port",
  531. "Port to try to listen on last"));
  532. receiving.addSetting(new PreferencesSetting(PreferencesType.DIRECTORY,
  533. getDomain(), "receive.savelocation", "Default save location",
  534. "Where the save as window defaults to?"));
  535. sending.addSetting(new PreferencesSetting(PreferencesType.BOOLEAN,
  536. getDomain(), "send.reverse", "Reverse DCC",
  537. "With reverse DCC, the sender connects rather than " +
  538. "listens like normal dcc"));
  539. sending.addSetting(new PreferencesSetting(PreferencesType.BOOLEAN,
  540. getDomain(), "send.forceturbo", "Use Turbo DCC",
  541. "Turbo DCC doesn't wait for ack packets. this is " +
  542. "faster but not always supported."));
  543. receiving.addSetting(new PreferencesSetting(PreferencesType.BOOLEAN,
  544. getDomain(), "receive.reverse.sendtoken",
  545. "Send token in reverse receive",
  546. "If you have problems with reverse dcc receive resume," +
  547. " try toggling this."));
  548. general.addSetting(new PreferencesSetting(PreferencesType.INTEGER,
  549. getDomain(), "send.blocksize", "Blocksize to use for DCC",
  550. "Change the block size for send/receive, this can " +
  551. "sometimes speed up transfers."));
  552. general.addSetting(new PreferencesSetting(PreferencesType.BOOLEAN,
  553. getDomain(), "general.percentageInTitle",
  554. "Show percentage of transfers in the window title",
  555. "Show the current percentage of transfers in the DCC window title"));
  556. }
  557. }