blob: 90e2a5a5db9411d4c84c3ef1705e1f6ca98953df [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
20 * CA 95054 USA or visit www.sun.com if you need additional information or
21 * have any questions.
22 */
23
24import java.io.*;
25import java.net.*;
26
27/*
28 * @test
29 * @bug 4398880
30 * @summary FTP URL processing modified to conform to RFC 1738
31 */
32
33public class FtpURL {
34 /**
35 * A class that simulates, on a separate, an FTP server.
36 */
37
38 private class FtpServer extends Thread {
39 private ServerSocket server;
40 private int port;
41 private boolean done = false;
42 private boolean portEnabled = true;
43 private boolean pasvEnabled = true;
44 private String username;
45 private String password;
46 private String cwd;
47 private String filename;
48 private String type;
49 private boolean list = false;
50
51 /**
52 * This Inner class will handle ONE client at a time.
53 * That's where 99% of the protocol handling is done.
54 */
55
56 private class FtpServerHandler {
57 BufferedReader in;
58 PrintWriter out;
59 Socket client;
60 private final int ERROR = 0;
61 private final int USER = 1;
62 private final int PASS = 2;
63 private final int CWD = 3;
64 private final int CDUP = 4;
65 private final int PWD = 5;
66 private final int TYPE = 6;
67 private final int NOOP = 7;
68 private final int RETR = 8;
69 private final int PASV = 9;
70 private final int PORT = 10;
71 private final int LIST = 11;
72 private final int REIN = 12;
73 private final int QUIT = 13;
74 private final int STOR = 14;
75 private final int NLST = 15;
76 private final int RNFR = 16;
77 private final int RNTO = 17;
78 String[] cmds = { "USER", "PASS", "CWD", "CDUP", "PWD", "TYPE",
79 "NOOP", "RETR", "PASV", "PORT", "LIST", "REIN",
80 "QUIT", "STOR", "NLST", "RNFR", "RNTO" };
81 private String arg = null;
82 private ServerSocket pasv = null;
83 private int data_port = 0;
84 private InetAddress data_addr = null;
85
86 /**
87 * Parses a line to match it with one of the supported FTP commands.
88 * Returns the command number.
89 */
90
91 private int parseCmd(String cmd) {
92 if (cmd == null || cmd.length() < 3)
93 return ERROR;
94 int blank = cmd.indexOf(' ');
95 if (blank < 0)
96 blank = cmd.length();
97 if (blank < 3)
98 return ERROR;
99 String s = cmd.substring(0, blank);
100 if (cmd.length() > blank+1)
101 arg = cmd.substring(blank+1, cmd.length());
102 else
103 arg = null;
104 for (int i = 0; i < cmds.length; i++) {
105 if (s.equalsIgnoreCase(cmds[i]))
106 return i+1;
107 }
108 return ERROR;
109 }
110
111 public FtpServerHandler(Socket cl) {
112 client = cl;
113 }
114
115 protected boolean isPasvSet() {
116 if (pasv != null && !pasvEnabled) {
117 try {
118 pasv.close();
119 } catch (IOException ex) {
120 }
121 pasv = null;
122 }
123 if (pasvEnabled && pasv != null)
124 return true;
125 return false;
126 }
127
128 /**
129 * Open the data socket with the client. This can be the
130 * result of a "PASV" or "PORT" command.
131 */
132
133 protected OutputStream getOutDataStream() {
134 try {
135 if (isPasvSet()) {
136 Socket s = pasv.accept();
137 return s.getOutputStream();
138 }
139 if (data_addr != null) {
140 Socket s = new Socket(data_addr, data_port);
141 data_addr = null;
142 data_port = 0;
143 return s.getOutputStream();
144 }
145 } catch (Exception e) {
146 e.printStackTrace();
147 }
148 return null;
149 }
150
151 protected InputStream getInDataStream() {
152 try {
153 if (isPasvSet()) {
154 Socket s = pasv.accept();
155 return s.getInputStream();
156 }
157 if (data_addr != null) {
158 Socket s = new Socket(data_addr, data_port);
159 data_addr = null;
160 data_port = 0;
161 return s.getInputStream();
162 }
163 } catch (Exception e) {
164 e.printStackTrace();
165 }
166 return null;
167 }
168
169 /**
170 * Handles the protocol exchange with the client.
171 */
172
173 public void run() {
174 boolean done = false;
175 String str;
176 int res;
177 boolean logged = false;
178 boolean waitpass = false;
179
180 try {
181 in = new BufferedReader(new InputStreamReader(client.getInputStream()));
182 out = new PrintWriter(client.getOutputStream(), true);
183 out.println("220 tatooine FTP server (SunOS 5.8) ready.");
184 } catch (Exception ex) {
185 return;
186 }
187 synchronized (FtpServer.this) {
188 while (!done) {
189 try {
190 str = in.readLine();
191 res = parseCmd(str);
192 if ((res > PASS && res != QUIT) && !logged) {
193 out.println("530 Not logged in.");
194 continue;
195 }
196 switch (res) {
197 case ERROR:
198 out.println("500 '" + str + "': command not understood.");
199 break;
200 case USER:
201 if (!logged && !waitpass) {
202 username = str.substring(5);
203 password = null;
204 cwd = null;
205 if ("user2".equals(username)) {
206 out.println("230 Guest login ok, access restrictions apply.");
207 logged = true;
208 } else {
209 out.println("331 Password required for " + arg);
210 waitpass = true;
211 }
212 } else {
213 out.println("503 Bad sequence of commands.");
214 }
215 break;
216 case PASS:
217 if (!logged && waitpass) {
218 out.println("230 Guest login ok, access restrictions apply.");
219 password = str.substring(5);
220 logged = true;
221 waitpass = false;
222 } else
223 out.println("503 Bad sequence of commands.");
224 break;
225 case QUIT:
226 out.println("221 Goodbye.");
227 out.flush();
228 out.close();
229 if (pasv != null)
230 pasv.close();
231 done = true;
232 break;
233 case TYPE:
234 out.println("200 Type set to " + arg + ".");
235 type = arg;
236 break;
237 case CWD:
238 out.println("250 CWD command successful.");
239 if (cwd == null)
240 cwd = str.substring(4);
241 else
242 cwd = cwd + "/" + str.substring(4);
243 break;
244 case CDUP:
245 out.println("250 CWD command successful.");
246 break;
247 case PWD:
248 out.println("257 \"" + cwd + "\" is current directory");
249 break;
250 case PASV:
251 if (!pasvEnabled) {
252 out.println("500 PASV is disabled, use PORT instead.");
253 continue;
254 }
255 try {
256 if (pasv == null)
257 pasv = new ServerSocket(0);
258 int port = pasv.getLocalPort();
259 out.println("227 Entering Passive Mode (127,0,0,1," +
260 (port >> 8) + "," + (port & 0xff) +")");
261 } catch (IOException ssex) {
262 out.println("425 Can't build data connection: Connection refused.");
263 }
264 break;
265 case PORT:
266 if (!portEnabled) {
267 out.println("500 PORT is disabled, use PASV instead");
268 continue;
269 }
270 StringBuffer host;
271 int i=0, j=4;
272 while (j>0) {
273 i = arg.indexOf(',', i+1);
274 if (i < 0)
275 break;
276 j--;
277 }
278 if (j != 0) {
279 out.println("500 '" + arg + "': command not understood.");
280 continue;
281 }
282 try {
283 host = new StringBuffer(arg.substring(0,i));
284 for (j=0; j < host.length(); j++)
285 if (host.charAt(j) == ',')
286 host.setCharAt(j, '.');
287 String ports = arg.substring(i+1);
288 i = ports.indexOf(',');
289 data_port = Integer.parseInt(ports.substring(0,i)) << 8;
290 data_port += (Integer.parseInt(ports.substring(i+1)));
291 data_addr = InetAddress.getByName(host.toString());
292 out.println("200 Command okay.");
293 } catch (Exception ex3) {
294 data_port = 0;
295 data_addr = null;
296 out.println("500 '" + arg + "': command not understood.");
297 }
298 break;
299 case RETR:
300 {
301 filename = str.substring(5);
302 OutputStream dout = getOutDataStream();
303 if (dout != null) {
304 out.println("200 Command okay.");
305 PrintWriter pout = new PrintWriter(new BufferedOutputStream(dout));
306 pout.println("Hello World!");
307 pout.flush();
308 pout.close();
309 list = false;
310 } else
311 out.println("425 Can't build data connection: Connection refused.");
312 }
313 break;
314 case NLST:
315 filename = arg;
316 case LIST:
317 {
318 OutputStream dout = getOutDataStream();
319 if (dout != null) {
320 out.println("200 Command okay.");
321 PrintWriter pout = new PrintWriter(new BufferedOutputStream(dout));
322 pout.println("total 130");
323 pout.println("drwxrwxrwt 7 sys sys 577 May 12 03:30 .");
324 pout.println("drwxr-xr-x 39 root root 1024 Mar 27 12:55 ..");
325 pout.println("drwxrwxr-x 2 root root 176 Apr 10 12:02 .X11-pipe");
326 pout.println("drwxrwxr-x 2 root root 176 Apr 10 12:02 .X11-unix");
327 pout.println("drwxrwxrwx 2 root root 179 Mar 30 15:09 .pcmcia");
328 pout.println("drwxrwxrwx 2 jladen staff 117 Mar 30 18:18 .removable");
329 pout.println("drwxrwxrwt 2 root root 327 Mar 30 15:08 .rpc_door");
330 pout.println("-rw-r--r-- 1 root other 21 May 5 16:59 hello2.txt");
331 pout.println("-rw-rw-r-- 1 root sys 5968 Mar 30 15:08 ps_data");
332 pout.flush();
333 pout.close();
334 list = true;
335 try {
336 FtpServer.this.wait ();
337 } catch (Exception e) {}
338 } else
339 out.println("425 Can't build data connection: Connection refused.");
340 }
341 break;
342 case STOR:
343 {
344 InputStream is = getInDataStream();
345 if (is != null) {
346 out.println("200 Command okay.");
347 BufferedInputStream din = new BufferedInputStream(is);
348 int val;
349 do {
350 val = din.read();
351 } while (val != -1);
352 din.close();
353 } else
354 out.println("425 Can't build data connection: Connection refused.");
355 }
356 break;
357 }
358 } catch (IOException ioe) {
359 ioe.printStackTrace();
360 try {
361 out.close();
362 } catch (Exception ex2) {
363 }
364 done = true;
365 }
366 }
367 }
368 }
369 }
370
371 public FtpServer(int port) {
372 this.port = port;
373 try {
374 server = new ServerSocket(port);
375 } catch (IOException e) {
376 }
377 }
378
379 public FtpServer() {
380 this(21);
381 }
382
383 public int getPort() {
384 if (server != null)
385 return server.getLocalPort();
386 return 0;
387 }
388
389 /**
390 * A way to tell the server that it can stop.
391 */
392 synchronized public void terminate() {
393 done = true;
394 }
395
396 synchronized public void setPortEnabled(boolean ok) {
397 portEnabled = ok;
398 }
399
400 synchronized public void setPasvEnabled(boolean ok) {
401 pasvEnabled = ok;
402 }
403
404 String getUsername() {
405 return username;
406 }
407
408 String getPassword() {
409 return password;
410 }
411
412 String pwd() {
413 return cwd;
414 }
415
416 String getFilename() {
417 return filename;
418 }
419
420 String getType() {
421 return type;
422 }
423
424 synchronized boolean getList() {
425 notify ();
426 return list;
427 }
428
429 /*
430 * All we got to do here is create a ServerSocket and wait for connections.
431 * When a connection happens, we just have to create a thread that will
432 * handle it.
433 */
434 public void run() {
435 try {
436 Socket client;
437 for (int i=0; i<2; i++) {
438 client = server.accept();
439 (new FtpServerHandler(client)).run();
440 }
441 server.close();
442 } catch(Exception e) {
443 }
444 }
445 }
446 public static void main(String[] args) throws Exception {
447 FtpURL test = new FtpURL();
448 }
449
450 public FtpURL() throws Exception {
451 FtpServer server = null;
452 BufferedReader in = null;
453 try {
454 server = new FtpServer(0);
455 server.start();
456 int port = server.getPort();
457
458 // Now let's check the URL handler
459
460 URL url = new URL("ftp://user:password@localhost:" + port + "/%2Fetc/motd;type=a");
461 URLConnection con = url.openConnection();
462 in = new BufferedReader(new InputStreamReader(con.getInputStream()));
463 String s;
464 do {
465 s = in.readLine();
466 } while (s != null);
467 if (!("user".equals(server.getUsername())))
468 throw new RuntimeException("Inccorect username received");
469 if (!("password".equals(server.getPassword())))
470 throw new RuntimeException("Inccorect password received");
471 if (!("/etc".equals(server.pwd())))
472 throw new RuntimeException("Inccorect directory received");
473 if (!("motd".equals(server.getFilename())))
474 throw new RuntimeException("Inccorect username received");
475 if (!("A".equals(server.getType())))
476 throw new RuntimeException("Incorrect type received");
477
478 in.close();
479 // We're done!
480
481 // Second URL test
482 port = server.getPort();
483
484 // Now let's check the URL handler
485
486 url = new URL("ftp://user2:@localhost:" + port + "/%2Fusr/bin;type=d");
487 con = url.openConnection();
488 in = new BufferedReader(new InputStreamReader(con.getInputStream()));
489 do {
490 s = in.readLine();
491 } while (s != null);
492 if (!server.getList())
493 throw new RuntimeException(";type=d didn't generate a NLST");
494 if (server.getPassword() != null)
495 throw new RuntimeException("password should be null!");
496 if (! "bin".equals(server.getFilename()))
497 throw new RuntimeException("Incorrect filename received");
498 if (! "/usr".equals(server.pwd()))
499 throw new RuntimeException("Incorrect pwd received");
500 in.close();
501 // We're done!
502
503 } catch (Exception e) {
504 try {
505 in.close();
506 server.terminate();
507 server.interrupt();
508 } catch(Exception ex) {
509 }
510 throw new RuntimeException("FTP support error: " + e.getMessage());
511 }
512 }
513}