0N/A/*
2362N/A * Copyright (c) 2006, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
0N/A * published by the Free Software Foundation.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/Aimport java.net.*;
0N/Aimport java.io.*;
0N/Aimport java.util.regex.*;
0N/Aimport java.security.*;
0N/Aimport javax.net.ssl.*;
0N/A
0N/A/*
0N/A * This class handles one client connection. It will interpret and act on the
0N/A * commands (like USER, GET, PUT etc...) sent through the socket passed to
0N/A * the constructor.
0N/A *
0N/A * To function it needs to be provided 2 handlers, one for the filesystem
0N/A * and one for authentication.
0N/A * @see FileSystemHandler
0N/A * @see AuthHandler
0N/A * @see #setHandlers(FtpFileSystemHandler,FtpAuthHandler)
0N/A */
0N/A
0N/Apublic class FtpCommandHandler extends Thread {
0N/A private FtpServer parent = null;
0N/A private Socket cmd = null;
0N/A private Socket oldCmd = null;
0N/A private InetAddress clientAddr = null;
0N/A private ServerSocket pasv = null;
0N/A
0N/A private BufferedReader in = null;
0N/A
0N/A private PrintStream out = null;
0N/A
0N/A private FtpFileSystemHandler fsh = null;
0N/A private FtpAuthHandler auth = null;
0N/A
0N/A private boolean done = false;
0N/A
0N/A private String username = null;
0N/A private String password = null;
0N/A private String account = null;
0N/A private boolean logged = false;
0N/A private boolean epsvAll = false;
0N/A private int dataPort = 0;
0N/A private InetAddress dataAddress = null;
0N/A private boolean pasvEnabled = true;
0N/A private boolean portEnabled = true;
0N/A private boolean extendedEnabled = true;
0N/A private boolean binary = true;
0N/A private String renameFrom = null;
0N/A private long restart = 0;
0N/A private boolean useCrypto = false;
0N/A private boolean useDataCrypto = false;
0N/A private SSLSocketFactory sslFact = null;
0N/A
0N/A private final int ERROR = -1;
0N/A private final int QUIT = 0;
0N/A private final int USER = 1;
0N/A private final int PASS = 2;
0N/A private final int CWD = 3;
0N/A private final int CDUP = 4;
0N/A private final int PWD = 5;
0N/A private final int TYPE = 6;
0N/A private final int NOOP = 7;
0N/A private final int RETR = 8;
0N/A private final int PORT = 9;
0N/A private final int PASV = 10;
0N/A private final int EPSV = 11;
0N/A private final int EPRT = 12;
0N/A private final int SYST = 13;
0N/A private final int STOR = 14;
0N/A private final int STOU = 15;
0N/A private final int LIST = 16;
0N/A private final int NLST = 17;
0N/A private final int RNFR = 18;
0N/A private final int RNTO = 19;
0N/A private final int DELE = 20;
0N/A private final int REST = 21;
0N/A private final int AUTH = 22;
0N/A private final int FEAT = 23;
0N/A private final int CCC = 24;
0N/A private final int PROT = 25;
0N/A private final int PBSZ = 26;
0N/A
0N/A private String[] commands =
0N/A { "QUIT", "USER", "PASS", "CWD", "CDUP", "PWD", "TYPE", "NOOP", "RETR",
0N/A "PORT", "PASV", "EPSV", "EPRT", "SYST", "STOR", "STOU", "LIST", "NLST",
0N/A "RNFR", "RNTO", "DELE", "REST", "AUTH", "FEAT", "CCC", "PROT", "PBSZ"
0N/A };
0N/A
0N/A private boolean isPasvSet() {
0N/A if (pasv != null && !pasvEnabled) {
0N/A try {
0N/A pasv.close();
0N/A } catch ( IOException e) {
0N/A
0N/A }
0N/A pasv = null;
0N/A }
0N/A if (pasvEnabled && pasv != null)
0N/A return true;
0N/A return false;
0N/A }
0N/A
0N/A private OutputStream getOutDataStream() throws IOException {
0N/A if (isPasvSet()) {
0N/A Socket s = pasv.accept();
0N/A if (useCrypto && useDataCrypto) {
0N/A SSLSocket ssl = (SSLSocket) sslFact.createSocket(s, clientAddr.getHostName(), s.getPort(), true);
0N/A ssl.setUseClientMode(false);
0N/A s = ssl;
0N/A }
0N/A return s.getOutputStream();
0N/A }
0N/A if (dataAddress != null) {
0N/A Socket s;
0N/A if (useCrypto) {
0N/A s = sslFact.createSocket(dataAddress, dataPort);
0N/A } else
0N/A s = new Socket(dataAddress, dataPort);
0N/A dataAddress = null;
0N/A dataPort = 0;
0N/A return s.getOutputStream();
0N/A }
0N/A return null;
0N/A }
0N/A
0N/A private InputStream getInDataStream() throws IOException {
0N/A if (isPasvSet()) {
0N/A Socket s = pasv.accept();
0N/A if (useCrypto && useDataCrypto) {
0N/A SSLSocket ssl = (SSLSocket) sslFact.createSocket(s, clientAddr.getHostName(), s.getPort(), true);
0N/A ssl.setUseClientMode(false);
0N/A s = ssl;
0N/A }
0N/A return s.getInputStream();
0N/A }
0N/A if (dataAddress != null) {
0N/A Socket s;
0N/A if (useCrypto) {
0N/A s = sslFact.createSocket(dataAddress, dataPort);
0N/A } else
0N/A s = new Socket(dataAddress, dataPort);
0N/A dataAddress = null;
0N/A dataPort = 0;
0N/A return s.getInputStream();
0N/A }
0N/A return null;
0N/A }
0N/A
0N/A private void parsePort(String port_arg) throws IOException {
0N/A if (epsvAll) {
0N/A out.println("501 PORT not allowed after EPSV ALL.");
0N/A return;
0N/A }
0N/A if (!portEnabled) {
0N/A out.println("500 PORT command is disabled, please use PASV.");
0N/A return;
0N/A }
0N/A StringBuffer host;
0N/A int i = 0, j = 4;
0N/A while (j > 0) {
0N/A i = port_arg.indexOf(',', i + 1);
0N/A if (i < 0)
0N/A break;
0N/A j--;
0N/A }
0N/A if (j != 0) {
0N/A out.println("500 '" + port_arg + "': command not understood.");
0N/A return;
0N/A }
0N/A try {
0N/A host = new StringBuffer(port_arg.substring(0, i));
0N/A for (j = 0; j < host.length(); j++)
0N/A if (host.charAt(j) == ',')
0N/A host.setCharAt(j, '.');
0N/A String ports = port_arg.substring(i + 1);
0N/A i = ports.indexOf(',');
0N/A dataPort = Integer.parseInt(ports.substring(0, i)) << 8;
0N/A dataPort += (Integer.parseInt(ports.substring(i + 1)));
0N/A dataAddress = InetAddress.getByName(host.toString());
0N/A out.println("200 Command okay.");
0N/A } catch (Exception ex3) {
0N/A dataPort = 0;
0N/A dataAddress = null;
0N/A out.println("500 '" + port_arg + "': command not understood.");
0N/A }
0N/A }
0N/A
0N/A private void parseEprt(String arg) {
0N/A if (epsvAll) {
0N/A out.println("501 PORT not allowed after EPSV ALL");
0N/A return;
0N/A }
0N/A if (!extendedEnabled || !portEnabled) {
0N/A out.println("500 EPRT is disabled, use PASV instead");
0N/A return;
0N/A }
0N/A Pattern p = Pattern.compile("\\|(\\d)\\|(.*)\\|(\\d+)\\|");
0N/A Matcher m = p.matcher(arg);
0N/A if (!m.find()) {
0N/A out.println("500 '" + arg + "': command not understood.");
0N/A return;
0N/A }
0N/A try {
0N/A dataAddress = InetAddress.getByName(m.group(2));
0N/A } catch (UnknownHostException e) {
0N/A out.println("500 " + arg + ": invalid address.");
0N/A dataAddress = null;
0N/A return;
0N/A }
0N/A dataPort = Integer.parseInt(m.group(3));
0N/A out.println("200 Command okay.");
0N/A }
0N/A
0N/A private void doPasv() {
0N/A if (!pasvEnabled) {
0N/A out.println("500 PASV is disabled, use PORT.");
0N/A return;
0N/A }
0N/A try {
0N/A if (pasv == null)
0N/A pasv = new ServerSocket(0);
0N/A int port = pasv.getLocalPort();
0N/A InetAddress rAddress = cmd.getLocalAddress();
0N/A if (rAddress instanceof Inet6Address) {
0N/A out.println("500 PASV illegal over IPv6 addresses, use EPSV.");
0N/A return;
0N/A }
0N/A byte[] a = rAddress.getAddress();
0N/A out.println("227 Entering Passive Mode " + a[0] + "," + a[1] + "," + a[2] + "," + a[3] + "," +
0N/A (port >> 8) + "," + (port & 0xff) );
0N/A } catch (IOException e) {
0N/A out.println("425 can't build data connection: Connection refused.");
0N/A }
0N/A }
0N/A
0N/A private void doEpsv(String arg) {
0N/A if (!extendedEnabled || !pasvEnabled) {
0N/A out.println("500 EPSV disabled, use PORT or PASV.");
0N/A return;
0N/A }
0N/A if ("all".equalsIgnoreCase(arg)) {
0N/A out.println("200 EPSV ALL Command successful.");
0N/A epsvAll = true;
0N/A return;
0N/A }
0N/A try {
0N/A if (pasv == null)
0N/A pasv = new ServerSocket(0);
0N/A int port = pasv.getLocalPort();
0N/A out.println("229 Entering Extended Passive Mode (|||" + port + "|)");
0N/A } catch (IOException e) {
0N/A out.println("500 Can't create data connection.");
0N/A }
0N/A }
0N/A
0N/A private void doRetr(String arg) {
0N/A try {
0N/A OutputStream dOut = getOutDataStream();
0N/A if (dOut != null) {
0N/A InputStream dIn = fsh.getFile(arg);
0N/A if (dIn == null) {
0N/A out.println("550 File not found.");
0N/A dOut.close();
0N/A return;
0N/A }
0N/A out.println("150 Opening " + (binary ? "BINARY " : "ASCII ") + " data connection for file " + arg +
0N/A "(" + fsh.getFileSize(arg) + " bytes).");
0N/A if (binary) {
0N/A byte[] buf = new byte[2048];
0N/A dOut = new BufferedOutputStream(dOut);
0N/A int count;
0N/A if (restart > 0) {
0N/A dIn.skip(restart);
0N/A restart = 0;
0N/A }
0N/A do {
0N/A count = dIn.read(buf);
0N/A if (count > 0)
0N/A dOut.write(buf, 0, count);
0N/A } while (count >= 0);
0N/A dOut.close();
0N/A dIn.close();
0N/A out.println("226 Transfer complete.");
0N/A }
0N/A }
0N/A } catch (IOException e) {
0N/A
0N/A }
0N/A }
0N/A
0N/A private void doStor(String arg, boolean unique) {
0N/A try {
0N/A InputStream dIn = getInDataStream();
0N/A if (dIn != null) {
0N/A OutputStream dOut = fsh.putFile(arg);
0N/A if (dOut == null) {
0N/A out.println("500 Can't create file " + arg);
0N/A dIn.close();
0N/A return;
0N/A }
0N/A out.println("150 Opening " + (binary ? "BINARY " : "ASCII ") + " data connection for file " + arg);
0N/A if (binary) {
0N/A byte[] buf = new byte[2048];
0N/A dOut = new BufferedOutputStream(dOut);
0N/A int count;
0N/A do {
0N/A count = dIn.read(buf);
0N/A if (count > 0)
0N/A dOut.write(buf, 0, count);
0N/A } while (count >= 0);
0N/A dOut.close();
0N/A dIn.close();
0N/A out.println("226 Transfer complete.");
0N/A }
0N/A }
0N/A } catch (IOException e) {
0N/A
0N/A }
0N/A }
0N/A
0N/A private void doList() {
0N/A try {
0N/A OutputStream dOut = getOutDataStream();
0N/A if (dOut != null) {
0N/A InputStream dIn = fsh.listCurrentDir();
0N/A if (dIn == null) {
0N/A out.println("550 File not found.");
0N/A dOut.close();
0N/A return;
0N/A }
0N/A out.println("150 Opening ASCII data connection for file list");
0N/A byte[] buf = new byte[2048];
0N/A dOut = new BufferedOutputStream(dOut);
0N/A int count;
0N/A do {
0N/A count = dIn.read(buf);
0N/A if (count > 0)
0N/A dOut.write(buf, 0, count);
0N/A } while (count >= 0);
0N/A dOut.close();
0N/A dIn.close();
0N/A out.println("226 Transfer complete.");
0N/A }
0N/A } catch (IOException e) {
0N/A
0N/A }
0N/A }
0N/A
0N/A private boolean useTLS() {
0N/A if (sslFact == null) {
0N/A sslFact = (SSLSocketFactory) SSLSocketFactory.getDefault();
0N/A }
0N/A if (sslFact == null)
0N/A return false;
0N/A return true;
0N/A }
0N/A
0N/A private void stopTLS() {
0N/A if (useCrypto) {
0N/A SSLSocket ssl = (SSLSocket) cmd;
0N/A try {
0N/A ssl.close();
0N/A } catch (IOException e) {
0N/A // nada
0N/A }
0N/A cmd = oldCmd;
0N/A oldCmd = null;
0N/A try {
0N/A in = new BufferedReader(new InputStreamReader(cmd.getInputStream()));
0N/A out = new PrintStream(cmd.getOutputStream(), true, "ISO8859_1");
0N/A } catch (Exception ex) {
0N/A
0N/A }
0N/A }
0N/A }
0N/A
0N/A public void setHandlers(FtpFileSystemHandler f, FtpAuthHandler a) {
0N/A fsh = f;
0N/A auth = a;
0N/A }
0N/A
0N/A public FtpCommandHandler(Socket cl, FtpServer p) {
0N/A parent = p;
0N/A cmd = cl;
0N/A clientAddr = cl.getInetAddress();
0N/A }
0N/A
0N/A public void terminate() {
0N/A done = true;
0N/A }
0N/A
0N/A private int parseCmd(StringBuffer cmd) {
0N/A
0N/A if (cmd == null || cmd.length() < 3) // Shortest command is 3 char long
0N/A return ERROR;
0N/A int blank = cmd.indexOf(" ");
0N/A if (blank < 0)
0N/A blank = cmd.length();
0N/A if (blank < 3)
0N/A return ERROR;
0N/A String s = cmd.substring(0,blank);
0N/A cmd.delete(0, blank + 1);
0N/A System.out.println("parse: cmd = " + s + " arg = " +cmd.toString());
0N/A for (int i = 0; i < commands.length; i++)
0N/A if (s.equalsIgnoreCase(commands[i]))
0N/A return i;
0N/A // Unknown command
0N/A return ERROR;
0N/A }
0N/A
0N/A private boolean checkLogged() {
0N/A if (!logged) {
0N/A out.println("530 Not logged in.");
0N/A return false;
0N/A }
0N/A return true;
0N/A }
0N/A
0N/A public void run() {
0N/A try {
0N/A // cmd.setSoTimeout(2000);
0N/A in = new BufferedReader(new InputStreamReader(cmd.getInputStream()));
0N/A out = new PrintStream(cmd.getOutputStream(), true, "ISO8859_1");
0N/A out.println("220 Java FTP test server (j2se 6.0) ready.");
0N/A out.flush();
0N/A if (auth.authType() == 0) // No auth needed
0N/A logged = true;
0N/A } catch (IOException e) {
0N/A e.printStackTrace();
0N/A return;
0N/A }
0N/A
0N/A String str;
0N/A StringBuffer buf;
0N/A int res;
0N/A while (!done) {
0N/A try {
0N/A str = in.readLine();
0N/A System.out.println("line: " + str);
0N/A buf = new StringBuffer(str);
0N/A res = parseCmd(buf);
0N/A switch (res) {
0N/A case ERROR:
0N/A out.println("500 '" + str +"': command not understood.");
0N/A break;
0N/A case QUIT:
0N/A out.println("221 Goodbye.");
0N/A done = true;
0N/A break;
0N/A case USER:
0N/A logged = false;
0N/A username = buf.toString();
0N/A if (auth.authType() > 1)
0N/A out.println("331 User name okay, need password.");
0N/A else {
0N/A if (auth.authenticate(username, null)) {
0N/A out.println("230 User logged in, proceed.");
0N/A logged = true;
0N/A } else {
0N/A out.println("331 User name okay, need password.");
0N/A }
0N/A }
0N/A break;
0N/A case PASS:
0N/A if (logged || (username == null)) {
0N/A out.println("503 Login with USER first.");
0N/A break;
0N/A }
0N/A password = buf.toString();
0N/A if (auth.authType() == 3) {
0N/A out.println("332 Need account for login.");
0N/A break;
0N/A }
0N/A if (auth.authenticate(username, password)) {
0N/A logged = true;
0N/A out.println("230 User " + username + " logged in.");
0N/A break;
0N/A }
0N/A out.println("530 Login incorrect.");
0N/A username = null;
0N/A break;
0N/A case CWD:
0N/A if (checkLogged()) {
0N/A String path = buf.toString();
0N/A if (fsh.cd(path)) {
0N/A out.println("250 CWD command successful.");
0N/A } else {
0N/A out.println("550 " + path + ": no such file or directory.");
0N/A }
0N/A }
0N/A break;
0N/A case CDUP:
0N/A if (checkLogged()) {
0N/A if (fsh.cdUp())
0N/A out.println("250 CWD command successful.");
0N/A else
0N/A out.println("550 invalid path.");
0N/A }
0N/A break;
0N/A case PWD:
0N/A if (checkLogged()) {
0N/A String s = fsh.pwd();
0N/A out.println("257 \"" + s + "\" is current directory");
0N/A }
0N/A break;
0N/A case NOOP:
0N/A if (checkLogged()) {
0N/A out.println("200 NOOP command successful.");
0N/A }
0N/A break;
0N/A case PORT:
0N/A if (checkLogged()) {
0N/A parsePort(buf.toString());
0N/A }
0N/A break;
0N/A case EPRT:
0N/A if (checkLogged()) {
0N/A parseEprt(buf.toString());
0N/A }
0N/A break;
0N/A case PASV:
0N/A if (checkLogged())
0N/A doPasv();
0N/A break;
0N/A case EPSV:
0N/A if (checkLogged())
0N/A doEpsv(buf.toString());
0N/A break;
0N/A case RETR:
0N/A if (checkLogged()) {
0N/A doRetr(buf.toString());
0N/A }
0N/A break;
0N/A case SYST:
0N/A if (checkLogged()) {
0N/A out.println("215 UNIX Type: L8 Version: Java 6.0");
0N/A }
0N/A break;
0N/A case TYPE:
0N/A if (checkLogged()) {
0N/A String arg = buf.toString();
0N/A if (arg.length() != 1 || "AIE".indexOf(arg.charAt(0)) < 0) {
0N/A out.println("500 'TYPE " + arg + "' command not understood.");
0N/A continue;
0N/A }
0N/A out.println("200 Type set to " + buf.toString() + ".");
0N/A if (arg.charAt(0) == 'I')
0N/A binary = true;
0N/A else
0N/A binary = false;
0N/A }
0N/A break;
0N/A case STOR:
0N/A case STOU:
0N/A // TODO: separate STOR and STOU (Store Unique)
0N/A if (checkLogged()) {
0N/A doStor(buf.toString(), false);
0N/A }
0N/A break;
0N/A case LIST:
0N/A if (checkLogged()) {
0N/A doList();
0N/A }
0N/A break;
0N/A case NLST:
0N/A // TODO: implememt
0N/A break;
0N/A case DELE:
0N/A if (checkLogged()) {
0N/A String arg = buf.toString();
0N/A if (fsh.removeFile(arg)) {
0N/A out.println("250 file " + arg + " deleted.");
0N/A break;
0N/A }
0N/A out.println("550 " + arg + ": no such file or directory.");
0N/A }
0N/A break;
0N/A case RNFR:
0N/A if (checkLogged()) {
0N/A if (renameFrom != null) {
0N/A out.println("503 Bad sequence of commands.");
0N/A break;
0N/A }
0N/A renameFrom = buf.toString();
0N/A if (fsh.fileExists(renameFrom)) {
0N/A out.println("350 File or directory exists, ready for destination name.");
0N/A } else {
0N/A out.println("550 " + renameFrom + ": no such file or directory");
0N/A renameFrom = null;
0N/A }
0N/A }
0N/A break;
0N/A case RNTO:
0N/A if (checkLogged()) {
0N/A if (renameFrom == null) {
0N/A out.println("503 Bad sequence of commands.");
0N/A break;
0N/A }
0N/A if (fsh.rename(renameFrom, buf.toString())) {
0N/A out.println("250 Rename successful");
0N/A } else {
0N/A out.println("550 Rename ");
0N/A }
0N/A renameFrom = null;
0N/A }
0N/A break;
0N/A case REST:
0N/A if (checkLogged()) {
0N/A String arg = buf.toString();
0N/A restart = Long.parseLong(arg);
0N/A if (restart > 0)
0N/A out.println("350 Restarting at " + restart + ". Send STORE or RETRIEVE to initiate transfer");
0N/A else
0N/A out.println("501 Syntax error in command of arguments.");
0N/A }
0N/A break;
0N/A case FEAT:
0N/A out.println("211-Features:");
0N/A out.println(" REST STREAM");
0N/A out.println(" PBSZ");
0N/A out.println(" AUTH TLS");
0N/A out.println(" PROT P");
0N/A out.println(" CCC");
0N/A out.println("211 End");
0N/A break;
0N/A case AUTH:
0N/A if ("TLS".equalsIgnoreCase(buf.toString()) && useTLS()) {
0N/A out.println("234 TLS Authentication OK.");
0N/A out.flush();
0N/A SSLSocket ssl;
0N/A String[] suites = sslFact.getSupportedCipherSuites();
0N/A try {
0N/A ssl = (SSLSocket) sslFact.createSocket(cmd, cmd.getInetAddress().getHostName(), cmd.getPort(), false);
0N/A ssl.setUseClientMode(false);
0N/A ssl.setEnabledCipherSuites(suites);
0N/A ssl.startHandshake();
0N/A } catch (IOException ioe) {
0N/A ioe.printStackTrace();
0N/A out.println("550 Unable to create secure channel.");
0N/A break;
0N/A }
0N/A oldCmd = cmd;
0N/A cmd = ssl;
0N/A out = new PrintStream(cmd.getOutputStream(), true, "ISO8859_1");
0N/A in = new BufferedReader(new InputStreamReader(cmd.getInputStream()));
0N/A System.out.println("Secure socket created!");
0N/A useCrypto = true;
0N/A break;
0N/A }
0N/A out.println("501 Unknown or unsupported AUTH type");
0N/A break;
0N/A case CCC:
0N/A out.println("200 Command OK.");
0N/A stopTLS();
0N/A break;
0N/A case PROT:
0N/A String arg = buf.toString();
0N/A if ("C".equalsIgnoreCase(arg)) {
0N/A // PROT C : Clear protection level
0N/A // No protection on data channel;
0N/A useDataCrypto = false;
0N/A out.println("200 Command OK.");
0N/A break;
0N/A }
0N/A if ("P".equalsIgnoreCase(arg)) {
0N/A // PROT P : Private protection level
0N/A // Data channel is integrity and confidentiality protected
0N/A useDataCrypto = true;
0N/A out.println("200 Command OK.");
0N/A break;
0N/A }
0N/A out.println("537 Requested PROT level not supported by security mechanism.");
0N/A break;
0N/A case PBSZ:
0N/A // TODO: finish
0N/A out.println("200 Command OK.");
0N/A break;
0N/A
0N/A }
0N/A
0N/A } catch (InterruptedIOException ie) {
0N/A // loop
0N/A } catch (IOException e) {
0N/A e.printStackTrace();
0N/A return;
0N/A }
0N/A }
0N/A try {
0N/A in.close();
0N/A out.close();
0N/A cmd.close();
0N/A } catch (IOException e) {
0N/A }
0N/A parent.removeClient(this);
0N/A }
0N/A}