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)
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
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
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
;
67 * An FTP client connection, or PI.
68 * This implements RFC 959, with the following exceptions:
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>
75 * @author Chris Burdess (dog@gnu.org)
77 public class FTPConnection
81 * The default FTP transmission control port.
83 public static final int FTP_PORT
= 21;
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.
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 <=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 <=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
;
245 socket
= new Socket();
246 InetSocketAddress address
= new InetSocketAddress(hostname
, port
);
247 if (connectionTimeout
> 0)
249 socket
.connect(address
, connectionTimeout
);
253 socket
.connect(address
);
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
);
269 FTPResponse response
= getResponse();
270 switch (response
.getCode())
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
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
)
290 String cmd
= USER
+ ' ' + username
;
292 FTPResponse response
= getResponse();
293 switch (response
.getCode())
295 case 230: // User logged in
297 case 331: // User name okay, need password
299 case 332: // Need account for login
300 case 530: // No such user
303 throw new FTPException(response
);
305 cmd
= PASS
+ ' ' + password
;
307 response
= getResponse();
308 switch (response
.getCode())
310 case 230: // User logged in
311 case 202: // Superfluous
313 case 332: // Need account for login
314 case 530: // Bad password
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
327 public boolean starttls(boolean confidential
)
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
338 * @param tm the trust manager used to validate the server certificate.
340 public boolean starttls(boolean confidential
, TrustManager tm
)
345 // Use SSLSocketFactory to negotiate a TLS session and wrap the
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())
366 throw new FTPException(response
);
369 String hostname
= socket
.getInetAddress().getHostName();
370 int port
= socket
.getPort();
372 (SSLSocket
) factory
.createSocket(socket
, hostname
, port
, true);
373 String
[] protocols
= { "TLSv1", "SSLv3" };
374 ss
.setEnabledProtocols(protocols
);
375 ss
.setUseClientMode(true);
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
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
402 throw new FTPException(response
);
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
);
418 catch (GeneralSecurityException e
)
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
)
432 String cmd
= CWD
+ ' ' + path
;
434 FTPResponse response
= getResponse();
435 switch (response
.getCode())
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()
454 FTPResponse response
= getResponse();
455 switch (response
.getCode())
462 throw new FTPException(response
);
467 * Terminates an authenticated login.
468 * If file transfer is in progress, it remains active for result response
471 public void reinitialize()
475 FTPResponse response
= getResponse();
476 switch (response
.getCode())
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.
501 getResponse(); // not required
503 catch (IOException e
)
515 catch (IOException e
)
521 * Initialise the data transfer process.
523 protected void initialiseDTP()
532 InetAddress localhost
= socket
.getLocalAddress();
536 FTPResponse response
= getResponse();
537 switch (response
.getCode())
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);
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: " +
561 c
= message
.charAt(end
+ 1);
562 while (c
>= 0x30 && c
<= 0x39)
564 c
= message
.charAt((++end
) + 1);
568 message
.substring(start
, mid1
).replace(',', '.');
570 Integer
.parseInt(message
.substring(mid1
+ 1, mid2
));
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 +
577 dtp
= new PassiveModeDTP(address
, port
, localhost
,
578 connectionTimeout
, timeout
);
581 catch (ArrayIndexOutOfBoundsException e
)
583 throw new ProtocolException(e
.getMessage() + ": " +
586 catch (NumberFormatException e
)
588 throw new ProtocolException(e
.getMessage() + ": " +
592 throw new FTPException(response
);
597 // Get the local port
598 int port
= socket
.getLocalPort() + 1;
600 // Bind the active mode DTP
605 dtp
= new ActiveModeDTP(localhost
, port
,
606 connectionTimeout
, timeout
);
607 /*System.out.println("Listening on: " + port);*/
609 catch (BindException e
)
621 StringBuffer buf
= new StringBuffer(PORT
);
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
];
635 int port_hi
=(port
& 0xff00) >> 8;
636 int port_lo
=(port
& 0x00ff);
640 send(buf
.toString());
642 FTPResponse response
= getResponse();
643 switch (response
.getCode())
650 throw new FTPException(response
);
653 dtp
.setTransferMode(transferMode
);
658 * @param flag true if we should use passive mode, false otherwise
660 public void setPassive(boolean flag
)
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
)
686 StringBuffer buf
= new StringBuffer(TYPE
);
700 throw new IllegalArgumentException(Integer
.toString(type
));
704 send(buf
.toString());
705 FTPResponse response
= getResponse();
706 switch (response
.getCode())
709 representationType
= type
;
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
)
732 StringBuffer buf
= new StringBuffer(STRU
);
739 case STRUCTURE_RECORD
:
746 throw new IllegalArgumentException(Integer
.toString(structure
));
748 send(buf
.toString());
749 FTPResponse response
= getResponse();
750 switch (response
.getCode())
753 fileStructure
= structure
;
756 throw new FTPException(response
);
761 * Returns the current transfer mode.
762 * @return MODE_STREAM, MODE_BLOCK, or MODE_COMPRESSED
764 public int getTransferMode()
770 * Sets the desired transfer mode.
771 * @param mode MODE_STREAM, MODE_BLOCK, or MODE_COMPRESSED
773 public void setTransferMode(int mode
)
776 StringBuffer buf
= new StringBuffer(MODE
);
786 case MODE_COMPRESSED
:
790 throw new IllegalArgumentException(Integer
.toString(mode
));
792 send(buf
.toString());
793 FTPResponse response
= getResponse();
794 switch (response
.getCode())
800 dtp
.setTransferMode(mode
);
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
)
816 if (dtp
== null || transferMode
== MODE_STREAM
)
822 String cmd = SIZE + ' ' + filename;
824 FTPResponse response = getResponse();
825 switch (response.getCode())
828 size = Integer.parseInt(response.getMessage());
830 case 550: // File not found
832 throw new FTPException(response);
835 String cmd
= RETR
+ ' ' + filename
;
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();
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
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
)
858 if (dtp
== null || transferMode
== MODE_STREAM
)
862 String cmd
= STOR
+ ' ' + filename
;
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();
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
)
885 if (dtp
== null || transferMode
== MODE_STREAM
)
889 String cmd
= APPE
+ ' ' + filename
;
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();
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
)
912 String cmd
= ALLO
+ ' ' + size
;
914 FTPResponse response
= getResponse();
915 switch (response
.getCode())
918 case 202: // Superfluous
921 throw new FTPException(response
);
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
)
934 String cmd
= RNFR
+ ' ' + oldName
;
936 FTPResponse response
= getResponse();
937 switch (response
.getCode())
939 case 450: // File unavailable
940 case 550: // File not found
945 throw new FTPException(response
);
947 cmd
= RNTO
+ ' ' + newName
;
949 response
= getResponse();
950 switch (response
.getCode())
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()
970 FTPResponse response
= getResponse();
976 switch (response
.getCode())
978 case 226: // successful abort
980 case 426: // interrupted
981 response
= getResponse();
982 if (response
.getCode() == 226)
986 // Otherwise fall through to throw exception
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
)
999 String cmd
= DELE
+ ' ' + filename
;
1001 FTPResponse response
= getResponse();
1002 switch (response
.getCode())
1006 case 450: // File unavailable
1007 case 550: // File not found
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
)
1022 String cmd
= RMD
+ ' ' + pathname
;
1024 FTPResponse response
= getResponse();
1025 switch (response
.getCode())
1029 case 550: // File not found
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
)
1044 String cmd
= MKD
+ ' ' + pathname
;
1046 FTPResponse response
= getResponse();
1047 switch (response
.getCode())
1049 case 257: // Directory created
1051 case 550: // File not found
1054 throw new FTPException(response
);
1059 * Returns the current working directory.
1061 public String
getWorkingDirectory()
1065 FTPResponse response
= getResponse();
1066 switch (response
.getCode())
1069 String message
= response
.getMessage();
1070 if (message
.charAt(0) == '"')
1072 int end
= message
.indexOf('"', 1);
1075 throw new ProtocolException(message
);
1077 return message
.substring(1, end
);
1081 int end
= message
.indexOf(' ');
1088 return message
.substring(0, end
);
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
)
1108 if (dtp
== null || transferMode
== MODE_STREAM
)
1112 if (pathname
== null)
1118 String cmd
= LIST
+ ' ' + pathname
;
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();
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
)
1142 if (dtp
== null || transferMode
== MODE_STREAM
)
1146 if (pathname
== null)
1152 String cmd
= NLST
+ ' ' + pathname
;
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();
1167 line
= li
.readLine())
1174 throw new FTPException(response
);
1179 * Returns the type of operating system at the server.
1181 public String
system()
1185 FTPResponse response
= getResponse();
1186 switch (response
.getCode())
1189 String message
= response
.getMessage();
1190 int end
= message
.indexOf(' ');
1197 return message
.substring(0, end
);
1200 throw new FTPException(response
);
1206 * This method can be used to ensure that the connection does not time
1213 FTPResponse response
= getResponse();
1214 switch (response
.getCode())
1219 throw new FTPException(response
);
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
)
1233 byte[] data
= cmd
.getBytes(US_ASCII
);
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()
1247 FTPResponse response
= readResponse();
1248 if (response
.getCode() == 226)
1252 dtp
.transferComplete();
1254 response
= readResponse();
1260 * Reads and parses the next response from the server.
1262 protected FTPResponse
readResponse()
1265 String line
= in
.readLine();
1268 throw new ProtocolException( "EOF");
1270 if (line
.length() < 4)
1272 throw new ProtocolException(line
);
1274 int code
= parseCode(line
);
1277 throw new ProtocolException(line
);
1279 char c
= line
.charAt(3);
1282 return new FTPResponse(code
, line
.substring(4));
1286 StringBuffer buf
= new StringBuffer(line
.substring(4));
1290 line
= in
.readLine();
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),
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) };
1323 for (int i
= 0; i
< 3; i
++)
1325 int digit
=((int) c
[i
]) - 0x30;
1326 if (digit
< 0 || digit
> 9)
1330 // Computing integer powers is way too expensive in Java!
1334 ret
+=(100 * digit
);