0N/A/*
3261N/A * Copyright (c) 2001, 2010, 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.io.*;
0N/Aimport java.net.*;
0N/A
0N/A/*
0N/A * @test
0N/A * @bug 4398880
0N/A * @summary FTP URL processing modified to conform to RFC 1738
0N/A */
0N/A
0N/Apublic class FtpURL {
0N/A /**
0N/A * A class that simulates, on a separate, an FTP server.
0N/A */
0N/A
0N/A private class FtpServer extends Thread {
0N/A private ServerSocket server;
0N/A private int port;
0N/A private boolean done = false;
0N/A private boolean portEnabled = true;
0N/A private boolean pasvEnabled = true;
0N/A private String username;
0N/A private String password;
0N/A private String cwd;
0N/A private String filename;
0N/A private String type;
0N/A private boolean list = false;
0N/A
0N/A /**
0N/A * This Inner class will handle ONE client at a time.
0N/A * That's where 99% of the protocol handling is done.
0N/A */
0N/A
0N/A private class FtpServerHandler {
0N/A BufferedReader in;
0N/A PrintWriter out;
0N/A Socket client;
0N/A private final int ERROR = 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 PASV = 9;
0N/A private final int PORT = 10;
0N/A private final int LIST = 11;
0N/A private final int REIN = 12;
0N/A private final int QUIT = 13;
0N/A private final int STOR = 14;
0N/A private final int NLST = 15;
0N/A private final int RNFR = 16;
0N/A private final int RNTO = 17;
0N/A String[] cmds = { "USER", "PASS", "CWD", "CDUP", "PWD", "TYPE",
0N/A "NOOP", "RETR", "PASV", "PORT", "LIST", "REIN",
0N/A "QUIT", "STOR", "NLST", "RNFR", "RNTO" };
0N/A private String arg = null;
0N/A private ServerSocket pasv = null;
0N/A private int data_port = 0;
0N/A private InetAddress data_addr = null;
0N/A
0N/A /**
0N/A * Parses a line to match it with one of the supported FTP commands.
0N/A * Returns the command number.
0N/A */
0N/A
0N/A private int parseCmd(String cmd) {
0N/A if (cmd == null || cmd.length() < 3)
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 if (cmd.length() > blank+1)
0N/A arg = cmd.substring(blank+1, cmd.length());
0N/A else
0N/A arg = null;
0N/A for (int i = 0; i < cmds.length; i++) {
0N/A if (s.equalsIgnoreCase(cmds[i]))
0N/A return i+1;
0N/A }
0N/A return ERROR;
0N/A }
0N/A
0N/A public FtpServerHandler(Socket cl) {
0N/A client = cl;
0N/A }
0N/A
0N/A protected boolean isPasvSet() {
0N/A if (pasv != null && !pasvEnabled) {
0N/A try {
0N/A pasv.close();
0N/A } catch (IOException ex) {
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 /**
0N/A * Open the data socket with the client. This can be the
0N/A * result of a "PASV" or "PORT" command.
0N/A */
0N/A
0N/A protected OutputStream getOutDataStream() {
0N/A try {
0N/A if (isPasvSet()) {
0N/A Socket s = pasv.accept();
0N/A return s.getOutputStream();
0N/A }
0N/A if (data_addr != null) {
0N/A Socket s = new Socket(data_addr, data_port);
0N/A data_addr = null;
0N/A data_port = 0;
0N/A return s.getOutputStream();
0N/A }
0N/A } catch (Exception e) {
0N/A e.printStackTrace();
0N/A }
0N/A return null;
0N/A }
0N/A
0N/A protected InputStream getInDataStream() {
0N/A try {
0N/A if (isPasvSet()) {
0N/A Socket s = pasv.accept();
0N/A return s.getInputStream();
0N/A }
0N/A if (data_addr != null) {
0N/A Socket s = new Socket(data_addr, data_port);
0N/A data_addr = null;
0N/A data_port = 0;
0N/A return s.getInputStream();
0N/A }
0N/A } catch (Exception e) {
0N/A e.printStackTrace();
0N/A }
0N/A return null;
0N/A }
0N/A
0N/A /**
0N/A * Handles the protocol exchange with the client.
0N/A */
0N/A
0N/A public void run() {
0N/A boolean done = false;
0N/A String str;
0N/A int res;
0N/A boolean logged = false;
0N/A boolean waitpass = false;
0N/A
0N/A try {
0N/A in = new BufferedReader(new InputStreamReader(client.getInputStream()));
0N/A out = new PrintWriter(client.getOutputStream(), true);
0N/A out.println("220 tatooine FTP server (SunOS 5.8) ready.");
0N/A } catch (Exception ex) {
0N/A return;
0N/A }
0N/A synchronized (FtpServer.this) {
0N/A while (!done) {
0N/A try {
0N/A str = in.readLine();
0N/A res = parseCmd(str);
0N/A if ((res > PASS && res != QUIT) && !logged) {
0N/A out.println("530 Not logged in.");
0N/A continue;
0N/A }
0N/A switch (res) {
0N/A case ERROR:
0N/A out.println("500 '" + str + "': command not understood.");
0N/A break;
0N/A case USER:
0N/A if (!logged && !waitpass) {
0N/A username = str.substring(5);
0N/A password = null;
0N/A cwd = null;
0N/A if ("user2".equals(username)) {
0N/A out.println("230 Guest login ok, access restrictions apply.");
0N/A logged = true;
0N/A } else {
0N/A out.println("331 Password required for " + arg);
0N/A waitpass = true;
0N/A }
0N/A } else {
0N/A out.println("503 Bad sequence of commands.");
0N/A }
0N/A break;
0N/A case PASS:
0N/A if (!logged && waitpass) {
0N/A out.println("230 Guest login ok, access restrictions apply.");
0N/A password = str.substring(5);
0N/A logged = true;
0N/A waitpass = false;
0N/A } else
0N/A out.println("503 Bad sequence of commands.");
0N/A break;
0N/A case QUIT:
0N/A out.println("221 Goodbye.");
0N/A out.flush();
0N/A out.close();
0N/A if (pasv != null)
0N/A pasv.close();
0N/A done = true;
0N/A break;
0N/A case TYPE:
0N/A out.println("200 Type set to " + arg + ".");
0N/A type = arg;
0N/A break;
0N/A case CWD:
0N/A out.println("250 CWD command successful.");
0N/A if (cwd == null)
0N/A cwd = str.substring(4);
0N/A else
0N/A cwd = cwd + "/" + str.substring(4);
0N/A break;
0N/A case CDUP:
0N/A out.println("250 CWD command successful.");
0N/A break;
0N/A case PWD:
0N/A out.println("257 \"" + cwd + "\" is current directory");
0N/A break;
0N/A case PASV:
0N/A if (!pasvEnabled) {
0N/A out.println("500 PASV is disabled, use PORT instead.");
0N/A continue;
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("227 Entering Passive Mode (127,0,0,1," +
0N/A (port >> 8) + "," + (port & 0xff) +")");
0N/A } catch (IOException ssex) {
0N/A out.println("425 Can't build data connection: Connection refused.");
0N/A }
0N/A break;
0N/A case PORT:
0N/A if (!portEnabled) {
0N/A out.println("500 PORT is disabled, use PASV instead");
0N/A continue;
0N/A }
0N/A StringBuffer host;
0N/A int i=0, j=4;
0N/A while (j>0) {
0N/A i = 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 '" + arg + "': command not understood.");
0N/A continue;
0N/A }
0N/A try {
0N/A host = new StringBuffer(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 = arg.substring(i+1);
0N/A i = ports.indexOf(',');
0N/A data_port = Integer.parseInt(ports.substring(0,i)) << 8;
0N/A data_port += (Integer.parseInt(ports.substring(i+1)));
0N/A data_addr = InetAddress.getByName(host.toString());
0N/A out.println("200 Command okay.");
0N/A } catch (Exception ex3) {
0N/A data_port = 0;
0N/A data_addr = null;
0N/A out.println("500 '" + arg + "': command not understood.");
0N/A }
0N/A break;
0N/A case RETR:
0N/A {
0N/A filename = str.substring(5);
0N/A OutputStream dout = getOutDataStream();
0N/A if (dout != null) {
0N/A out.println("200 Command okay.");
0N/A PrintWriter pout = new PrintWriter(new BufferedOutputStream(dout));
0N/A pout.println("Hello World!");
0N/A pout.flush();
0N/A pout.close();
0N/A list = false;
0N/A } else
0N/A out.println("425 Can't build data connection: Connection refused.");
0N/A }
0N/A break;
0N/A case NLST:
0N/A filename = arg;
0N/A case LIST:
0N/A {
0N/A OutputStream dout = getOutDataStream();
0N/A if (dout != null) {
0N/A out.println("200 Command okay.");
0N/A PrintWriter pout = new PrintWriter(new BufferedOutputStream(dout));
0N/A pout.println("total 130");
0N/A pout.println("drwxrwxrwt 7 sys sys 577 May 12 03:30 .");
0N/A pout.println("drwxr-xr-x 39 root root 1024 Mar 27 12:55 ..");
0N/A pout.println("drwxrwxr-x 2 root root 176 Apr 10 12:02 .X11-pipe");
0N/A pout.println("drwxrwxr-x 2 root root 176 Apr 10 12:02 .X11-unix");
0N/A pout.println("drwxrwxrwx 2 root root 179 Mar 30 15:09 .pcmcia");
0N/A pout.println("drwxrwxrwx 2 jladen staff 117 Mar 30 18:18 .removable");
0N/A pout.println("drwxrwxrwt 2 root root 327 Mar 30 15:08 .rpc_door");
0N/A pout.println("-rw-r--r-- 1 root other 21 May 5 16:59 hello2.txt");
0N/A pout.println("-rw-rw-r-- 1 root sys 5968 Mar 30 15:08 ps_data");
0N/A pout.flush();
0N/A pout.close();
0N/A list = true;
0N/A try {
0N/A FtpServer.this.wait ();
0N/A } catch (Exception e) {}
0N/A } else
0N/A out.println("425 Can't build data connection: Connection refused.");
0N/A }
0N/A break;
0N/A case STOR:
0N/A {
0N/A InputStream is = getInDataStream();
0N/A if (is != null) {
0N/A out.println("200 Command okay.");
0N/A BufferedInputStream din = new BufferedInputStream(is);
0N/A int val;
0N/A do {
0N/A val = din.read();
0N/A } while (val != -1);
0N/A din.close();
0N/A } else
0N/A out.println("425 Can't build data connection: Connection refused.");
0N/A }
0N/A break;
0N/A }
0N/A } catch (IOException ioe) {
0N/A ioe.printStackTrace();
0N/A try {
0N/A out.close();
0N/A } catch (Exception ex2) {
0N/A }
0N/A done = true;
0N/A }
0N/A }
0N/A }
0N/A }
0N/A }
0N/A
0N/A public FtpServer(int port) {
0N/A this.port = port;
0N/A try {
0N/A server = new ServerSocket(port);
0N/A } catch (IOException e) {
0N/A }
0N/A }
0N/A
0N/A public FtpServer() {
0N/A this(21);
0N/A }
0N/A
0N/A public int getPort() {
0N/A if (server != null)
0N/A return server.getLocalPort();
0N/A return 0;
0N/A }
0N/A
0N/A /**
0N/A * A way to tell the server that it can stop.
0N/A */
0N/A synchronized public void terminate() {
0N/A done = true;
0N/A }
0N/A
0N/A synchronized public void setPortEnabled(boolean ok) {
0N/A portEnabled = ok;
0N/A }
0N/A
0N/A synchronized public void setPasvEnabled(boolean ok) {
0N/A pasvEnabled = ok;
0N/A }
0N/A
0N/A String getUsername() {
0N/A return username;
0N/A }
0N/A
0N/A String getPassword() {
0N/A return password;
0N/A }
0N/A
0N/A String pwd() {
0N/A return cwd;
0N/A }
0N/A
0N/A String getFilename() {
0N/A return filename;
0N/A }
0N/A
0N/A String getType() {
0N/A return type;
0N/A }
0N/A
0N/A synchronized boolean getList() {
0N/A notify ();
0N/A return list;
0N/A }
0N/A
0N/A /*
0N/A * All we got to do here is create a ServerSocket and wait for connections.
0N/A * When a connection happens, we just have to create a thread that will
0N/A * handle it.
0N/A */
0N/A public void run() {
0N/A try {
0N/A Socket client;
0N/A for (int i=0; i<2; i++) {
0N/A client = server.accept();
0N/A (new FtpServerHandler(client)).run();
0N/A }
0N/A } catch(Exception e) {
2612N/A } finally {
2612N/A try { server.close(); } catch (IOException unused) {}
0N/A }
0N/A }
0N/A }
0N/A public static void main(String[] args) throws Exception {
0N/A FtpURL test = new FtpURL();
0N/A }
0N/A
0N/A public FtpURL() throws Exception {
2612N/A FtpServer server = new FtpServer(0);
0N/A BufferedReader in = null;
0N/A try {
0N/A server.start();
0N/A int port = server.getPort();
0N/A
0N/A // Now let's check the URL handler
0N/A
0N/A URL url = new URL("ftp://user:password@localhost:" + port + "/%2Fetc/motd;type=a");
0N/A URLConnection con = url.openConnection();
0N/A in = new BufferedReader(new InputStreamReader(con.getInputStream()));
0N/A String s;
0N/A do {
0N/A s = in.readLine();
0N/A } while (s != null);
0N/A if (!("user".equals(server.getUsername())))
0N/A throw new RuntimeException("Inccorect username received");
0N/A if (!("password".equals(server.getPassword())))
0N/A throw new RuntimeException("Inccorect password received");
0N/A if (!("/etc".equals(server.pwd())))
0N/A throw new RuntimeException("Inccorect directory received");
0N/A if (!("motd".equals(server.getFilename())))
0N/A throw new RuntimeException("Inccorect username received");
0N/A if (!("A".equals(server.getType())))
0N/A throw new RuntimeException("Incorrect type received");
0N/A
0N/A in.close();
0N/A // We're done!
0N/A
0N/A // Second URL test
0N/A port = server.getPort();
0N/A
0N/A // Now let's check the URL handler
0N/A
0N/A url = new URL("ftp://user2:@localhost:" + port + "/%2Fusr/bin;type=d");
0N/A con = url.openConnection();
0N/A in = new BufferedReader(new InputStreamReader(con.getInputStream()));
0N/A do {
0N/A s = in.readLine();
0N/A } while (s != null);
0N/A if (!server.getList())
0N/A throw new RuntimeException(";type=d didn't generate a NLST");
0N/A if (server.getPassword() != null)
0N/A throw new RuntimeException("password should be null!");
0N/A if (! "bin".equals(server.getFilename()))
0N/A throw new RuntimeException("Incorrect filename received");
0N/A if (! "/usr".equals(server.pwd()))
0N/A throw new RuntimeException("Incorrect pwd received");
0N/A // We're done!
0N/A
0N/A } catch (Exception e) {
0N/A throw new RuntimeException("FTP support error: " + e.getMessage());
2612N/A } finally {
2612N/A try { in.close(); } catch (IOException unused) {}
2612N/A server.terminate();
2612N/A server.server.close();
0N/A }
0N/A }
0N/A}