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 == &amp;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, &amp;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, &amp;new Thread(connect#2)}
        		  *    possibly_updated(dcc.out)
        		  *    init'ed(dcc.port)
        		  *    dcc.serverSocket == One-of{old dcc.serverSocket, &amp;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 == &amp;new FrameContainer$IconChanger(FrameContainer#2)
        		  *    this.config != null
        		  *    this.icon == x2
        		  *    init'ed(this.icon)
        		  *    this.listeners == &amp;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, &amp;new DCCPlugin$3(createContainer#1)}
        		  *    this.container != null
        		  *    new DCCPlugin$3(createContainer#1) num objects <= 1
        		  *    new DCCPlugin$3(createContainer#1).changer == &amp;new FrameContainer$IconChanger(FrameContainer#2)
        		  *    new DCCPlugin$3(createContainer#1).config != null
        		  *    new DCCPlugin$3(createContainer#1).icon == &amp;"dcc"
        		  *    new DCCPlugin$3(createContainer#1).listeners == &amp;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, &amp;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 == &amp;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