blob: 3f1cc675f2c666771a8ea92ab271162697fe27c9 [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 4255280
30 * @summary URL.getContent() loses first six bytes for ftp URLs
31 */
32
33public class FtpGetContent {
34 static int filesize = 2048;
35
36 /**
37 * A class that simulates, on a separate, an FTP server.
38 */
39
40 private class FtpServer extends Thread {
41 private ServerSocket server;
42 private int port;
43 private boolean done = false;
44 private boolean portEnabled = true;
45 private boolean pasvEnabled = true;
46 private String username;
47 private String password;
48 private String cwd;
49 private String filename;
50 private String type;
51 private boolean list = false;
52
53 /**
54 * This Inner class will handle ONE client at a time.
55 * That's where 99% of the protocol handling is done.
56 */
57
58 private class FtpServerHandler extends Thread {
59 BufferedReader in;
60 PrintWriter out;
61 Socket client;
62 private final int ERROR = 0;
63 private final int USER = 1;
64 private final int PASS = 2;
65 private final int CWD = 3;
66 private final int CDUP = 4;
67 private final int PWD = 5;
68 private final int TYPE = 6;
69 private final int NOOP = 7;
70 private final int RETR = 8;
71 private final int PASV = 9;
72 private final int PORT = 10;
73 private final int LIST = 11;
74 private final int REIN = 12;
75 private final int QUIT = 13;
76 private final int STOR = 14;
77 private final int NLST = 15;
78 private final int RNFR = 16;
79 private final int RNTO = 17;
80 String[] cmds = { "USER", "PASS", "CWD", "CDUP", "PWD", "TYPE",
81 "NOOP", "RETR", "PASV", "PORT", "LIST", "REIN",
82 "QUIT", "STOR", "NLST", "RNFR", "RNTO" };
83 private String arg = null;
84 private ServerSocket pasv = null;
85 private int data_port = 0;
86 private InetAddress data_addr = null;
87
88 /**
89 * Parses a line to match it with one of the supported FTP commands.
90 * Returns the command number.
91 */
92
93 private int parseCmd(String cmd) {
94 if (cmd == null || cmd.length() < 3)
95 return ERROR;
96 int blank = cmd.indexOf(' ');
97 if (blank < 0)
98 blank = cmd.length();
99 if (blank < 3)
100 return ERROR;
101 String s = cmd.substring(0, blank);
102 if (cmd.length() > blank+1)
103 arg = cmd.substring(blank+1, cmd.length());
104 else
105 arg = null;
106 for (int i = 0; i < cmds.length; i++) {
107 if (s.equalsIgnoreCase(cmds[i]))
108 return i+1;
109 }
110 return ERROR;
111 }
112
113 public FtpServerHandler(Socket cl) {
114 client = cl;
115 }
116
117 protected boolean isPasvSet() {
118 if (pasv != null && !pasvEnabled) {
119 try {
120 pasv.close();
121 } catch (IOException ex) {
122 }
123 pasv = null;
124 }
125 if (pasvEnabled && pasv != null)
126 return true;
127 return false;
128 }
129
130 /**
131 * Open the data socket with the client. This can be the
132 * result of a "PASV" or "PORT" command.
133 */
134
135 protected OutputStream getOutDataStream() {
136 try {
137 if (isPasvSet()) {
138 Socket s = pasv.accept();
139 return s.getOutputStream();
140 }
141 if (data_addr != null) {
142 Socket s = new Socket(data_addr, data_port);
143 data_addr = null;
144 data_port = 0;
145 return s.getOutputStream();
146 }
147 } catch (Exception e) {
148 e.printStackTrace();
149 }
150 return null;
151 }
152
153 protected InputStream getInDataStream() {
154 try {
155 if (isPasvSet()) {
156 Socket s = pasv.accept();
157 return s.getInputStream();
158 }
159 if (data_addr != null) {
160 Socket s = new Socket(data_addr, data_port);
161 data_addr = null;
162 data_port = 0;
163 return s.getInputStream();
164 }
165 } catch (Exception e) {
166 e.printStackTrace();
167 }
168 return null;
169 }
170
171 /**
172 * Handles the protocol exchange with the client.
173 */
174
175 public void run() {
176 boolean done = false;
177 String str;
178 int res;
179 boolean logged = false;
180 boolean waitpass = false;
181
182 try {
183 in = new BufferedReader(new InputStreamReader(client.getInputStream()));
184 out = new PrintWriter(client.getOutputStream(), true);
185 out.println("220 tatooine FTP server (SunOS 5.8) ready.");
186 } catch (Exception ex) {
187 return;
188 }
189 while (!done) {
190 try {
191 str = in.readLine();
192 res = parseCmd(str);
193 if ((res > PASS && res != QUIT) && !logged) {
194 out.println("530 Not logged in.");
195 continue;
196 }
197 switch (res) {
198 case ERROR:
199 out.println("500 '" + str + "': command not understood.");
200 break;
201 case USER:
202 if (!logged && !waitpass) {
203 username = str.substring(5);
204 password = null;
205 cwd = null;
206 if ("user2".equals(username)) {
207 out.println("230 Guest login ok, access restrictions apply.");
208 logged = true;
209 } else {
210 out.println("331 Password required for " + arg);
211 waitpass = true;
212 }
213 } else {
214 out.println("503 Bad sequence of commands.");
215 }
216 break;
217 case PASS:
218 if (!logged && waitpass) {
219 out.println("230 Guest login ok, access restrictions apply.");
220 password = str.substring(5);
221 logged = true;
222 waitpass = false;
223 } else
224 out.println("503 Bad sequence of commands.");
225 break;
226 case QUIT:
227 out.println("221 Goodbye.");
228 out.flush();
229 out.close();
230 if (pasv != null)
231 pasv.close();
232 done = true;
233 break;
234 case TYPE:
235 out.println("200 Type set to " + arg + ".");
236 type = arg;
237 break;
238 case CWD:
239 out.println("250 CWD command successful.");
240 if (cwd == null)
241 cwd = str.substring(4);
242 else
243 cwd = cwd + "/" + str.substring(4);
244 break;
245 case CDUP:
246 out.println("250 CWD command successful.");
247 break;
248 case PWD:
249 out.println("257 \"" + cwd + "\" is current directory");
250 break;
251 case PASV:
252 if (!pasvEnabled) {
253 out.println("500 PASV is disabled, use PORT instead.");
254 continue;
255 }
256 try {
257 if (pasv == null)
258 pasv = new ServerSocket(0);
259 int port = pasv.getLocalPort();
260 out.println("227 Entering Passive Mode (127,0,0,1," +
261 (port >> 8) + "," + (port & 0xff) +")");
262 } catch (IOException ssex) {
263 out.println("425 Can't build data connection: Connection refused.");
264 }
265 break;
266 case PORT:
267 if (!portEnabled) {
268 out.println("500 PORT is disabled, use PASV instead");
269 continue;
270 }
271 StringBuffer host;
272 int i=0, j=4;
273 while (j>0) {
274 i = arg.indexOf(',', i+1);
275 if (i < 0)
276 break;
277 j--;
278 }
279 if (j != 0) {
280 out.println("500 '" + arg + "': command not understood.");
281 continue;
282 }
283 try {
284 host = new StringBuffer(arg.substring(0,i));
285 for (j=0; j < host.length(); j++)
286 if (host.charAt(j) == ',')
287 host.setCharAt(j, '.');
288 String ports = arg.substring(i+1);
289 i = ports.indexOf(',');
290 data_port = Integer.parseInt(ports.substring(0,i)) << 8;
291 data_port += (Integer.parseInt(ports.substring(i+1)));
292 data_addr = InetAddress.getByName(host.toString());
293 out.println("200 Command okay.");
294 } catch (Exception ex3) {
295 data_port = 0;
296 data_addr = null;
297 out.println("500 '" + arg + "': command not understood.");
298 }
299 break;
300 case RETR:
301 {
302 filename = str.substring(5);
303 OutputStream dout = getOutDataStream();
304 if (dout != null) {
305 out.println("200 Command okay.");
306 BufferedOutputStream pout = new BufferedOutputStream(dout);
307 for (int x = 0; x < filesize ; x++)
308 pout.write(0);
309 pout.flush();
310 pout.close();
311 list = false;
312 } else
313 out.println("425 Can't build data connection: Connection refused.");
314 }
315 break;
316 case NLST:
317 filename = arg;
318 case LIST:
319 {
320 OutputStream dout = getOutDataStream();
321 if (dout != null) {
322 out.println("200 Command okay.");
323 PrintWriter pout = new PrintWriter(new BufferedOutputStream(dout));
324 pout.println("total 130");
325 pout.println("drwxrwxrwt 7 sys sys 577 May 12 03:30 .");
326 pout.println("drwxr-xr-x 39 root root 1024 Mar 27 12:55 ..");
327 pout.println("drwxrwxr-x 2 root root 176 Apr 10 12:02 .X11-pipe");
328 pout.println("drwxrwxr-x 2 root root 176 Apr 10 12:02 .X11-unix");
329 pout.println("drwxrwxrwx 2 root root 179 Mar 30 15:09 .pcmcia");
330 pout.println("drwxrwxrwx 2 jladen staff 117 Mar 30 18:18 .removable");
331 pout.println("drwxrwxrwt 2 root root 327 Mar 30 15:08 .rpc_door");
332 pout.println("-rw-r--r-- 1 root other 21 May 5 16:59 hello2.txt");
333 pout.println("-rw-rw-r-- 1 root sys 5968 Mar 30 15:08 ps_data");
334 pout.flush();
335 pout.close();
336 list = true;
337 } else
338 out.println("425 Can't build data connection: Connection refused.");
339 }
340 break;
341 case STOR:
342 {
343 InputStream is = getInDataStream();
344 if (is != null) {
345 out.println("200 Command okay.");
346 BufferedInputStream din = new BufferedInputStream(is);
347 int val;
348 do {
349 val = din.read();
350 } while (val != -1);
351 din.close();
352 } else
353 out.println("425 Can't build data connection: Connection refused.");
354 }
355 break;
356 }
357 } catch (IOException ioe) {
358 ioe.printStackTrace();
359 try {
360 out.close();
361 } catch (Exception ex2) {
362 }
363 done = true;
364 }
365 }
366 }
367 }
368
369 public FtpServer(int port) {
370 this.port = port;
371 try {
372 server = new ServerSocket(port);
373 } catch (IOException e) {
374 }
375 }
376
377 public FtpServer() {
378 this(21);
379 }
380
381 public int getPort() {
382 if (server != null)
383 return server.getLocalPort();
384 return 0;
385 }
386
387 /**
388 * A way to tell the server that it can stop.
389 */
390 synchronized public void terminate() {
391 done = true;
392 }
393
394 synchronized public void setPortEnabled(boolean ok) {
395 portEnabled = ok;
396 }
397
398 synchronized public void setPasvEnabled(boolean ok) {
399 pasvEnabled = ok;
400 }
401
402 String getUsername() {
403 return username;
404 }
405
406 String getPassword() {
407 return password;
408 }
409
410 String pwd() {
411 return cwd;
412 }
413
414 String getFilename() {
415 return filename;
416 }
417
418 String getType() {
419 return type;
420 }
421
422 boolean getList() {
423 return list;
424 }
425
426 /*
427 * All we got to do here is create a ServerSocket and wait for connections.
428 * When a connection happens, we just have to create a thread that will
429 * handle it.
430 */
431 public void run() {
432 try {
433 Socket client;
434 while (!done) {
435 client = server.accept();
436 (new FtpServerHandler(client)).start();
437 }
438 server.close();
439 } catch(Exception e) {
440 }
441 }
442 }
443 public static void main(String[] args) throws Exception {
444 FtpGetContent test = new FtpGetContent();
445 }
446
447 public FtpGetContent() throws Exception {
448 FtpServer server = null;
449 try {
450 server = new FtpServer(0);
451 server.start();
452 int port = server.getPort();
453
454 // Now let's check the URL handler
455
456 URL url = new URL("ftp://localhost:" + port + "/pub/BigFile");
457 InputStream stream = (InputStream)url.getContent();
458 byte[] buffer = new byte[1024];
459 int totalBytes = 0;
460 int bytesRead = stream.read(buffer);
461 while (bytesRead != -1) {
462 totalBytes += bytesRead;
463 bytesRead = stream.read(buffer);
464 }
465 stream.close();
466 server.terminate();
467 server.interrupt();
468 if (totalBytes != filesize)
469 throw new RuntimeException("wrong file size!");
470 } catch (IOException e) {
471 try {
472 server.terminate();
473 server.interrupt();
474 } catch (Exception e2) {
475 }
476 throw new RuntimeException(e.getMessage());
477 }
478 }
479
480}