This commit was manufactured by cvs2svn to create branch
[official-gcc.git] / libjava / gnu / java / net / protocol / ftp / FTPConnection.java
blob2c1237349f55cf66d29e55332facf1a3efd17b50
1 /* FTPConnection.java --
2 Copyright (C) 2003, 2004 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19 02111-1307 USA.
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
39 package gnu.java.net.protocol.ftp;
41 import gnu.java.net.CRLFInputStream;
42 import gnu.java.net.CRLFOutputStream;
43 import gnu.java.net.EmptyX509TrustManager;
44 import gnu.java.net.LineInputStream;
46 import java.io.BufferedInputStream;
47 import java.io.BufferedOutputStream;
48 import java.io.InputStream;
49 import java.io.IOException;
50 import java.io.OutputStream;
51 import java.net.BindException;
52 import java.net.InetAddress;
53 import java.net.InetSocketAddress;
54 import java.net.ProtocolException;
55 import java.net.Socket;
56 import java.net.UnknownHostException;
57 import java.security.GeneralSecurityException;
58 import java.util.ArrayList;
59 import java.util.List;
61 import javax.net.ssl.SSLContext;
62 import javax.net.ssl.SSLSocket;
63 import javax.net.ssl.SSLSocketFactory;
64 import javax.net.ssl.TrustManager;
66 /**
67 * An FTP client connection, or PI.
68 * This implements RFC 959, with the following exceptions:
69 * <ul>
70 * <li>STAT, HELP, SITE, SMNT, and ACCT commands are not supported.</li>
71 * <li>the TYPE command does not allow alternatives to the default bytesize
72 * (Non-print), and local bytesize is not supported.</li>
73 * </ul>
75 * @author Chris Burdess (dog@gnu.org)
77 public class FTPConnection
80 /**
81 * The default FTP transmission control port.
83 public static final int FTP_PORT = 21;
85 /**
86 * The FTP data port.
88 public static final int FTP_DATA_PORT = 20;
90 // -- FTP vocabulary --
91 protected static final String USER = "USER";
92 protected static final String PASS = "PASS";
93 protected static final String ACCT = "ACCT";
94 protected static final String CWD = "CWD";
95 protected static final String CDUP = "CDUP";
96 protected static final String SMNT = "SMNT";
97 protected static final String REIN = "REIN";
98 protected static final String QUIT = "QUIT";
100 protected static final String PORT = "PORT";
101 protected static final String PASV = "PASV";
102 protected static final String TYPE = "TYPE";
103 protected static final String STRU = "STRU";
104 protected static final String MODE = "MODE";
106 protected static final String RETR = "RETR";
107 protected static final String STOR = "STOR";
108 protected static final String STOU = "STOU";
109 protected static final String APPE = "APPE";
110 protected static final String ALLO = "ALLO";
111 protected static final String REST = "REST";
112 protected static final String RNFR = "RNFR";
113 protected static final String RNTO = "RNTO";
114 protected static final String ABOR = "ABOR";
115 protected static final String DELE = "DELE";
116 protected static final String RMD = "RMD";
117 protected static final String MKD = "MKD";
118 protected static final String PWD = "PWD";
119 protected static final String LIST = "LIST";
120 protected static final String NLST = "NLST";
121 protected static final String SITE = "SITE";
122 protected static final String SYST = "SYST";
123 protected static final String STAT = "STAT";
124 protected static final String HELP = "HELP";
125 protected static final String NOOP = "NOOP";
127 protected static final String AUTH = "AUTH";
128 protected static final String PBSZ = "PBSZ";
129 protected static final String PROT = "PROT";
130 protected static final String CCC = "CCC";
131 protected static final String TLS = "TLS";
133 public static final int TYPE_ASCII = 1;
134 public static final int TYPE_EBCDIC = 2;
135 public static final int TYPE_BINARY = 3;
137 public static final int STRUCTURE_FILE = 1;
138 public static final int STRUCTURE_RECORD = 2;
139 public static final int STRUCTURE_PAGE = 3;
141 public static final int MODE_STREAM = 1;
142 public static final int MODE_BLOCK = 2;
143 public static final int MODE_COMPRESSED = 3;
145 // -- Telnet constants --
146 private static final String US_ASCII = "US-ASCII";
149 * The socket used to communicate with the server.
151 protected Socket socket;
154 * The socket input stream.
156 protected LineInputStream in;
159 * The socket output stream.
161 protected CRLFOutputStream out;
164 * The timeout when attempting to connect a socket.
166 protected int connectionTimeout;
169 * The read timeout on sockets.
171 protected int timeout;
174 * If true, print debugging information.
176 protected boolean debug;
179 * The current data transfer process in use by this connection.
181 protected DTP dtp;
184 * The current representation type.
186 protected int representationType = TYPE_ASCII;
189 * The current file structure type.
191 protected int fileStructure = STRUCTURE_FILE;
194 * The current transfer mode.
196 protected int transferMode = MODE_STREAM;
199 * If true, use passive mode.
201 protected boolean passive = false;
204 * Creates a new connection to the server using the default port.
205 * @param hostname the hostname of the server to connect to
207 public FTPConnection(String hostname)
208 throws UnknownHostException, IOException
210 this(hostname, -1, 0, 0, false);
214 * Creates a new connection to the server.
215 * @param hostname the hostname of the server to connect to
216 * @param port the port to connect to(if &lt;=0, use default port)
218 public FTPConnection(String hostname, int port)
219 throws UnknownHostException, IOException
221 this(hostname, port, 0, 0, false);
225 * Creates a new connection to the server.
226 * @param hostname the hostname of the server to connect to
227 * @param port the port to connect to(if &lt;=0, use default port)
228 * @param connectionTimeout the connection timeout, in milliseconds
229 * @param timeout the I/O timeout, in milliseconds
230 * @param debug print debugging information
232 public FTPConnection(String hostname, int port,
233 int connectionTimeout, int timeout, boolean debug)
234 throws UnknownHostException, IOException
236 this.connectionTimeout = connectionTimeout;
237 this.timeout = timeout;
238 this.debug = debug;
239 if (port <= 0)
241 port = FTP_PORT;
244 // Set up socket
245 socket = new Socket();
246 InetSocketAddress address = new InetSocketAddress(hostname, port);
247 if (connectionTimeout > 0)
249 socket.connect(address, connectionTimeout);
251 else
253 socket.connect(address);
255 if (timeout > 0)
257 socket.setSoTimeout(timeout);
260 InputStream in = socket.getInputStream();
261 in = new BufferedInputStream(in);
262 in = new CRLFInputStream(in);
263 this.in = new LineInputStream(in);
264 OutputStream out = socket.getOutputStream();
265 out = new BufferedOutputStream(out);
266 this.out = new CRLFOutputStream(out);
268 // Read greeting
269 FTPResponse response = getResponse();
270 switch (response.getCode())
272 case 220: // hello
273 break;
274 default:
275 throw new FTPException(response);
280 * Authenticate using the specified username and password.
281 * If the username suffices for the server, the password will not be used
282 * and may be null.
283 * @param username the username
284 * @param password the optional password
285 * @return true on success, false otherwise
287 public boolean authenticate(String username, String password)
288 throws IOException
290 String cmd = USER + ' ' + username;
291 send(cmd);
292 FTPResponse response = getResponse();
293 switch (response.getCode())
295 case 230: // User logged in
296 return true;
297 case 331: // User name okay, need password
298 break;
299 case 332: // Need account for login
300 case 530: // No such user
301 return false;
302 default:
303 throw new FTPException(response);
305 cmd = PASS + ' ' + password;
306 send(cmd);
307 response = getResponse();
308 switch (response.getCode())
310 case 230: // User logged in
311 case 202: // Superfluous
312 return true;
313 case 332: // Need account for login
314 case 530: // Bad password
315 return false;
316 default:
317 throw new FTPException(response);
322 * Negotiates TLS over the current connection.
323 * See IETF draft-murray-auth-ftp-ssl-15.txt for details.
324 * @param confidential whether to provide confidentiality for the
325 * connection
327 public boolean starttls(boolean confidential)
328 throws IOException
330 return starttls(confidential, new EmptyX509TrustManager());
334 * Negotiates TLS over the current connection.
335 * See IETF draft-murray-auth-ftp-ssl-15.txt for details.
336 * @param confidential whether to provide confidentiality for the
337 * connection
338 * @param tm the trust manager used to validate the server certificate.
340 public boolean starttls(boolean confidential, TrustManager tm)
341 throws IOException
345 // Use SSLSocketFactory to negotiate a TLS session and wrap the
346 // current socket.
347 SSLContext context = SSLContext.getInstance("TLS");
348 // We don't require strong validation of the server certificate
349 TrustManager[] trust = new TrustManager[] { tm };
350 context.init(null, trust, null);
351 SSLSocketFactory factory = context.getSocketFactory();
353 send(AUTH + ' ' + TLS);
354 FTPResponse response = getResponse();
355 switch (response.getCode())
357 case 500:
358 case 502:
359 case 504:
360 case 534:
361 case 431:
362 return false;
363 case 234:
364 break;
365 default:
366 throw new FTPException(response);
369 String hostname = socket.getInetAddress().getHostName();
370 int port = socket.getPort();
371 SSLSocket ss =
372 (SSLSocket) factory.createSocket(socket, hostname, port, true);
373 String[] protocols = { "TLSv1", "SSLv3" };
374 ss.setEnabledProtocols(protocols);
375 ss.setUseClientMode(true);
376 ss.startHandshake();
378 // PBSZ:PROT sequence
379 send(PBSZ + ' ' + Integer.MAX_VALUE);
380 response = getResponse();
381 switch (response.getCode())
383 case 501: // syntax error
384 case 503: // not authenticated
385 return false;
386 case 200:
387 break;
388 default:
389 throw new FTPException(response);
391 send(PROT + ' ' +(confidential ? 'P' : 'C'));
392 response = getResponse();
393 switch (response.getCode())
395 case 503: // not authenticated
396 case 504: // invalid level
397 case 536: // level not supported
398 return false;
399 case 200:
400 break;
401 default:
402 throw new FTPException(response);
405 if (confidential)
407 // Set up streams
408 InputStream in = ss.getInputStream();
409 in = new BufferedInputStream(in);
410 in = new CRLFInputStream(in);
411 this.in = new LineInputStream(in);
412 OutputStream out = ss.getOutputStream();
413 out = new BufferedOutputStream(out);
414 this.out = new CRLFOutputStream(out);
416 return true;
418 catch (GeneralSecurityException e)
420 return false;
425 * Changes directory to the specified path.
426 * @param path an absolute or relative pathname
427 * @return true on success, false if the specified path does not exist
429 public boolean changeWorkingDirectory(String path)
430 throws IOException
432 String cmd = CWD + ' ' + path;
433 send(cmd);
434 FTPResponse response = getResponse();
435 switch (response.getCode())
437 case 250:
438 return true;
439 case 550:
440 return false;
441 default:
442 throw new FTPException(response);
447 * Changes directory to the parent of the current working directory.
448 * @return true on success, false otherwise
450 public boolean changeToParentDirectory()
451 throws IOException
453 send(CDUP);
454 FTPResponse response = getResponse();
455 switch (response.getCode())
457 case 250:
458 return true;
459 case 550:
460 return false;
461 default:
462 throw new FTPException(response);
467 * Terminates an authenticated login.
468 * If file transfer is in progress, it remains active for result response
469 * only.
471 public void reinitialize()
472 throws IOException
474 send(REIN);
475 FTPResponse response = getResponse();
476 switch (response.getCode())
478 case 220:
479 if (dtp != null)
481 dtp.complete();
482 dtp = null;
484 break;
485 default:
486 throw new FTPException(response);
491 * Terminates the control connection.
492 * The file transfer connection remains open for result response only.
493 * This connection is invalid and no further commands may be issued.
495 public void logout()
496 throws IOException
498 send(QUIT);
501 getResponse(); // not required
503 catch (IOException e)
506 if (dtp != null)
508 dtp.complete();
509 dtp = null;
513 socket.close();
515 catch (IOException e)
521 * Initialise the data transfer process.
523 protected void initialiseDTP()
524 throws IOException
526 if (dtp != null)
528 dtp.complete();
529 dtp = null;
532 InetAddress localhost = socket.getLocalAddress();
533 if (passive)
535 send(PASV);
536 FTPResponse response = getResponse();
537 switch (response.getCode())
539 case 227:
540 String message = response.getMessage();
543 int start = message.indexOf(',');
544 char c = message.charAt(start - 1);
545 while (c >= 0x30 && c <= 0x39)
547 c = message.charAt((--start) - 1);
549 int mid1 = start;
550 for (int i = 0; i < 4; i++)
552 mid1 = message.indexOf(',', mid1 + 1);
554 int mid2 = message.indexOf(',', mid1 + 1);
555 if (mid1 == -1 || mid2 < mid1)
557 throw new ProtocolException("Malformed 227: " +
558 message);
560 int end = mid2;
561 c = message.charAt(end + 1);
562 while (c >= 0x30 && c <= 0x39)
564 c = message.charAt((++end) + 1);
567 String address =
568 message.substring(start, mid1).replace(',', '.');
569 int port_hi =
570 Integer.parseInt(message.substring(mid1 + 1, mid2));
571 int port_lo =
572 Integer.parseInt(message.substring(mid2 + 1, end + 1));
573 int port = (port_hi << 8) | port_lo;
575 /*System.out.println("Entering passive mode: " + address +
576 ":" + port);*/
577 dtp = new PassiveModeDTP(address, port, localhost,
578 connectionTimeout, timeout);
579 break;
581 catch (ArrayIndexOutOfBoundsException e)
583 throw new ProtocolException(e.getMessage() + ": " +
584 message);
586 catch (NumberFormatException e)
588 throw new ProtocolException(e.getMessage() + ": " +
589 message);
591 default:
592 throw new FTPException(response);
595 else
597 // Get the local port
598 int port = socket.getLocalPort() + 1;
599 int tries = 0;
600 // Bind the active mode DTP
601 while (dtp == null)
605 dtp = new ActiveModeDTP(localhost, port,
606 connectionTimeout, timeout);
607 /*System.out.println("Listening on: " + port);*/
609 catch (BindException e)
611 port++;
612 tries++;
613 if (tries > 9)
615 throw e;
620 // Send PORT command
621 StringBuffer buf = new StringBuffer(PORT);
622 buf.append(' ');
623 // Construct the address/port string form
624 byte[] address = localhost.getAddress();
625 for (int i = 0; i < address.length; i++)
627 int a =(int) address[i];
628 if (a < 0)
630 a += 0x100;
632 buf.append(a);
633 buf.append(',');
635 int port_hi =(port & 0xff00) >> 8;
636 int port_lo =(port & 0x00ff);
637 buf.append(port_hi);
638 buf.append(',');
639 buf.append(port_lo);
640 send(buf.toString());
641 // Get response
642 FTPResponse response = getResponse();
643 switch (response.getCode())
645 case 200: // OK
646 break;
647 default:
648 dtp.abort();
649 dtp = null;
650 throw new FTPException(response);
653 dtp.setTransferMode(transferMode);
657 * Set passive mode.
658 * @param flag true if we should use passive mode, false otherwise
660 public void setPassive(boolean flag)
661 throws IOException
663 if (passive != flag)
665 passive = flag;
666 initialiseDTP();
671 * Returns the current representation type of the transfer data.
672 * @return TYPE_ASCII, TYPE_EBCDIC, or TYPE_BINARY
674 public int getRepresentationType()
676 return representationType;
680 * Sets the desired representation type of the transfer data.
681 * @param type TYPE_ASCII, TYPE_EBCDIC, or TYPE_BINARY
683 public void setRepresentationType(int type)
684 throws IOException
686 StringBuffer buf = new StringBuffer(TYPE);
687 buf.append(' ');
688 switch (type)
690 case TYPE_ASCII:
691 buf.append('A');
692 break;
693 case TYPE_EBCDIC:
694 buf.append('E');
695 break;
696 case TYPE_BINARY:
697 buf.append('I');
698 break;
699 default:
700 throw new IllegalArgumentException(Integer.toString(type));
702 //buf.append(' ');
703 //buf.append('N');
704 send(buf.toString());
705 FTPResponse response = getResponse();
706 switch (response.getCode())
708 case 200:
709 representationType = type;
710 break;
711 default:
712 throw new FTPException(response);
717 * Returns the current file structure type.
718 * @return STRUCTURE_FILE, STRUCTURE_RECORD, or STRUCTURE_PAGE
720 public int getFileStructure()
722 return fileStructure;
726 * Sets the desired file structure type.
727 * @param structure STRUCTURE_FILE, STRUCTURE_RECORD, or STRUCTURE_PAGE
729 public void setFileStructure(int structure)
730 throws IOException
732 StringBuffer buf = new StringBuffer(STRU);
733 buf.append(' ');
734 switch (structure)
736 case STRUCTURE_FILE:
737 buf.append('F');
738 break;
739 case STRUCTURE_RECORD:
740 buf.append('R');
741 break;
742 case STRUCTURE_PAGE:
743 buf.append('P');
744 break;
745 default:
746 throw new IllegalArgumentException(Integer.toString(structure));
748 send(buf.toString());
749 FTPResponse response = getResponse();
750 switch (response.getCode())
752 case 200:
753 fileStructure = structure;
754 break;
755 default:
756 throw new FTPException(response);
761 * Returns the current transfer mode.
762 * @return MODE_STREAM, MODE_BLOCK, or MODE_COMPRESSED
764 public int getTransferMode()
766 return transferMode;
770 * Sets the desired transfer mode.
771 * @param mode MODE_STREAM, MODE_BLOCK, or MODE_COMPRESSED
773 public void setTransferMode(int mode)
774 throws IOException
776 StringBuffer buf = new StringBuffer(MODE);
777 buf.append(' ');
778 switch (mode)
780 case MODE_STREAM:
781 buf.append('S');
782 break;
783 case MODE_BLOCK:
784 buf.append('B');
785 break;
786 case MODE_COMPRESSED:
787 buf.append('C');
788 break;
789 default:
790 throw new IllegalArgumentException(Integer.toString(mode));
792 send(buf.toString());
793 FTPResponse response = getResponse();
794 switch (response.getCode())
796 case 200:
797 transferMode = mode;
798 if (dtp != null)
800 dtp.setTransferMode(mode);
802 break;
803 default:
804 throw new FTPException(response);
809 * Retrieves the specified file.
810 * @param filename the filename of the file to retrieve
811 * @return an InputStream containing the file content
813 public InputStream retrieve(String filename)
814 throws IOException
816 if (dtp == null || transferMode == MODE_STREAM)
818 initialiseDTP();
821 int size = -1;
822 String cmd = SIZE + ' ' + filename;
823 send(cmd);
824 FTPResponse response = getResponse();
825 switch (response.getCode())
827 case 213:
828 size = Integer.parseInt(response.getMessage());
829 break;
830 case 550: // File not found
831 default:
832 throw new FTPException(response);
835 String cmd = RETR + ' ' + filename;
836 send(cmd);
837 FTPResponse response = getResponse();
838 switch (response.getCode())
840 case 125: // Data connection already open; transfer starting
841 case 150: // File status okay; about to open data connection
842 return dtp.getInputStream();
843 default:
844 throw new FTPException(response);
849 * Returns a stream for uploading a file.
850 * If a file with the same filename already exists on the server, it will
851 * be overwritten.
852 * @param filename the name of the file to save the content as
853 * @return an OutputStream to write the file data to
855 public OutputStream store(String filename)
856 throws IOException
858 if (dtp == null || transferMode == MODE_STREAM)
860 initialiseDTP();
862 String cmd = STOR + ' ' + filename;
863 send(cmd);
864 FTPResponse response = getResponse();
865 switch (response.getCode())
867 case 125: // Data connection already open; transfer starting
868 case 150: // File status okay; about to open data connection
869 return dtp.getOutputStream();
870 default:
871 throw new FTPException(response);
876 * Returns a stream for uploading a file.
877 * If a file with the same filename already exists on the server, the
878 * content specified will be appended to the existing file.
879 * @param filename the name of the file to save the content as
880 * @return an OutputStream to write the file data to
882 public OutputStream append(String filename)
883 throws IOException
885 if (dtp == null || transferMode == MODE_STREAM)
887 initialiseDTP();
889 String cmd = APPE + ' ' + filename;
890 send(cmd);
891 FTPResponse response = getResponse();
892 switch (response.getCode())
894 case 125: // Data connection already open; transfer starting
895 case 150: // File status okay; about to open data connection
896 return dtp.getOutputStream();
897 default:
898 throw new FTPException(response);
903 * This command may be required by some servers to reserve sufficient
904 * storage to accommodate the new file to be transferred.
905 * It should be immediately followed by a <code>store</code> or
906 * <code>append</code>.
907 * @param size the number of bytes of storage to allocate
909 public void allocate(long size)
910 throws IOException
912 String cmd = ALLO + ' ' + size;
913 send(cmd);
914 FTPResponse response = getResponse();
915 switch (response.getCode())
917 case 200: // OK
918 case 202: // Superfluous
919 break;
920 default:
921 throw new FTPException(response);
926 * Renames a file.
927 * @param oldName the current name of the file
928 * @param newName the new name
929 * @return true if successful, false otherwise
931 public boolean rename(String oldName, String newName)
932 throws IOException
934 String cmd = RNFR + ' ' + oldName;
935 send(cmd);
936 FTPResponse response = getResponse();
937 switch (response.getCode())
939 case 450: // File unavailable
940 case 550: // File not found
941 return false;
942 case 350: // Pending
943 break;
944 default:
945 throw new FTPException(response);
947 cmd = RNTO + ' ' + newName;
948 send(cmd);
949 response = getResponse();
950 switch (response.getCode())
952 case 250: // OK
953 return true;
954 case 450:
955 case 550:
956 return false;
957 default:
958 throw new FTPException(response);
963 * Aborts the transfer in progress.
964 * @return true if a transfer was in progress, false otherwise
966 public boolean abort()
967 throws IOException
969 send(ABOR);
970 FTPResponse response = getResponse();
971 // Abort client DTP
972 if (dtp != null)
974 dtp.abort();
976 switch (response.getCode())
978 case 226: // successful abort
979 return false;
980 case 426: // interrupted
981 response = getResponse();
982 if (response.getCode() == 226)
984 return true;
986 // Otherwise fall through to throw exception
987 default:
988 throw new FTPException(response);
993 * Causes the file specified to be deleted at the server site.
994 * @param filename the file to delete
996 public boolean delete(String filename)
997 throws IOException
999 String cmd = DELE + ' ' + filename;
1000 send(cmd);
1001 FTPResponse response = getResponse();
1002 switch (response.getCode())
1004 case 250: // OK
1005 return true;
1006 case 450: // File unavailable
1007 case 550: // File not found
1008 return false;
1009 default:
1010 throw new FTPException(response);
1015 * Causes the directory specified to be deleted.
1016 * This may be an absolute or relative pathname.
1017 * @param pathname the directory to delete
1019 public boolean removeDirectory(String pathname)
1020 throws IOException
1022 String cmd = RMD + ' ' + pathname;
1023 send(cmd);
1024 FTPResponse response = getResponse();
1025 switch (response.getCode())
1027 case 250: // OK
1028 return true;
1029 case 550: // File not found
1030 return false;
1031 default:
1032 throw new FTPException(response);
1037 * Causes the directory specified to be created at the server site.
1038 * This may be an absolute or relative pathname.
1039 * @param pathname the directory to create
1041 public boolean makeDirectory(String pathname)
1042 throws IOException
1044 String cmd = MKD + ' ' + pathname;
1045 send(cmd);
1046 FTPResponse response = getResponse();
1047 switch (response.getCode())
1049 case 257: // Directory created
1050 return true;
1051 case 550: // File not found
1052 return false;
1053 default:
1054 throw new FTPException(response);
1059 * Returns the current working directory.
1061 public String getWorkingDirectory()
1062 throws IOException
1064 send(PWD);
1065 FTPResponse response = getResponse();
1066 switch (response.getCode())
1068 case 257:
1069 String message = response.getMessage();
1070 if (message.charAt(0) == '"')
1072 int end = message.indexOf('"', 1);
1073 if (end == -1)
1075 throw new ProtocolException(message);
1077 return message.substring(1, end);
1079 else
1081 int end = message.indexOf(' ');
1082 if (end == -1)
1084 return message;
1086 else
1088 return message.substring(0, end);
1091 default:
1092 throw new FTPException(response);
1097 * Returns a listing of information about the specified pathname.
1098 * If the pathname specifies a directory or other group of files, the
1099 * server should transfer a list of files in the specified directory.
1100 * If the pathname specifies a file then the server should send current
1101 * information on the file. A null argument implies the user's
1102 * current working or default directory.
1103 * @param pathname the context pathname, or null
1105 public InputStream list(String pathname)
1106 throws IOException
1108 if (dtp == null || transferMode == MODE_STREAM)
1110 initialiseDTP();
1112 if (pathname == null)
1114 send(LIST);
1116 else
1118 String cmd = LIST + ' ' + pathname;
1119 send(cmd);
1121 FTPResponse response = getResponse();
1122 switch (response.getCode())
1124 case 125: // Data connection already open; transfer starting
1125 case 150: // File status okay; about to open data connection
1126 return dtp.getInputStream();
1127 default:
1128 throw new FTPException(response);
1133 * Returns a directory listing. The pathname should specify a
1134 * directory or other system-specific file group descriptor; a null
1135 * argument implies the user's current working or default directory.
1136 * @param pathname the directory pathname, or null
1137 * @return a list of filenames(strings)
1139 public List nameList(String pathname)
1140 throws IOException
1142 if (dtp == null || transferMode == MODE_STREAM)
1144 initialiseDTP();
1146 if (pathname == null)
1148 send(NLST);
1150 else
1152 String cmd = NLST + ' ' + pathname;
1153 send(cmd);
1155 FTPResponse response = getResponse();
1156 switch (response.getCode())
1158 case 125: // Data connection already open; transfer starting
1159 case 150: // File status okay; about to open data connection
1160 InputStream in = dtp.getInputStream();
1161 in = new BufferedInputStream(in);
1162 in = new CRLFInputStream(in); // TODO ensure that TYPE is correct
1163 LineInputStream li = new LineInputStream(in);
1164 List ret = new ArrayList();
1165 for (String line = li.readLine();
1166 line != null;
1167 line = li.readLine())
1169 ret.add(line);
1171 li.close();
1172 return ret;
1173 default:
1174 throw new FTPException(response);
1179 * Returns the type of operating system at the server.
1181 public String system()
1182 throws IOException
1184 send(SYST);
1185 FTPResponse response = getResponse();
1186 switch (response.getCode())
1188 case 215:
1189 String message = response.getMessage();
1190 int end = message.indexOf(' ');
1191 if (end == -1)
1193 return message;
1195 else
1197 return message.substring(0, end);
1199 default:
1200 throw new FTPException(response);
1205 * Does nothing.
1206 * This method can be used to ensure that the connection does not time
1207 * out.
1209 public void noop()
1210 throws IOException
1212 send(NOOP);
1213 FTPResponse response = getResponse();
1214 switch (response.getCode())
1216 case 200:
1217 break;
1218 default:
1219 throw new FTPException(response);
1223 // -- I/O --
1226 * Sends the specified command line to the server.
1227 * The CRLF sequence is automatically appended.
1228 * @param cmd the command line to send
1230 protected void send(String cmd)
1231 throws IOException
1233 byte[] data = cmd.getBytes(US_ASCII);
1234 out.write(data);
1235 out.writeln();
1236 out.flush();
1240 * Reads the next response from the server.
1241 * If the server sends the "transfer complete" code, this is handled here,
1242 * and the next response is passed to the caller.
1244 protected FTPResponse getResponse()
1245 throws IOException
1247 FTPResponse response = readResponse();
1248 if (response.getCode() == 226)
1250 if (dtp != null)
1252 dtp.transferComplete();
1254 response = readResponse();
1256 return response;
1260 * Reads and parses the next response from the server.
1262 protected FTPResponse readResponse()
1263 throws IOException
1265 String line = in.readLine();
1266 if (line == null)
1268 throw new ProtocolException( "EOF");
1270 if (line.length() < 4)
1272 throw new ProtocolException(line);
1274 int code = parseCode(line);
1275 if (code == -1)
1277 throw new ProtocolException(line);
1279 char c = line.charAt(3);
1280 if (c == ' ')
1282 return new FTPResponse(code, line.substring(4));
1284 else if (c == '-')
1286 StringBuffer buf = new StringBuffer(line.substring(4));
1287 buf.append('\n');
1288 while(true)
1290 line = in.readLine();
1291 if (line == null)
1293 throw new ProtocolException("EOF");
1295 if (line.length() >= 4 &&
1296 line.charAt(3) == ' ' &&
1297 parseCode(line) == code)
1299 return new FTPResponse(code, line.substring(4),
1300 buf.toString());
1302 else
1304 buf.append(line);
1305 buf.append('\n');
1309 else
1311 throw new ProtocolException(line);
1316 * Parses the 3-digit numeric code at the beginning of the given line.
1317 * Returns -1 on failure.
1319 static final int parseCode(String line)
1321 char[] c = { line.charAt(0), line.charAt(1), line.charAt(2) };
1322 int ret = 0;
1323 for (int i = 0; i < 3; i++)
1325 int digit =((int) c[i]) - 0x30;
1326 if (digit < 0 || digit > 9)
1328 return -1;
1330 // Computing integer powers is way too expensive in Java!
1331 switch (i)
1333 case 0:
1334 ret +=(100 * digit);
1335 break;
1336 case 1:
1337 ret +=(10 * digit);
1338 break;
1339 case 2:
1340 ret += digit;
1341 break;
1344 return ret;