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