View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.net.ftp;
19  
20  import java.io.BufferedReader;
21  import java.io.BufferedWriter;
22  import java.io.IOException;
23  import java.io.InputStreamReader;
24  import java.io.OutputStreamWriter;
25  import java.io.Reader;
26  import java.net.Inet4Address;
27  import java.net.Inet6Address;
28  import java.net.InetAddress;
29  import java.net.SocketException;
30  import java.net.SocketTimeoutException;
31  import java.util.ArrayList;
32  
33  import org.apache.commons.net.MalformedServerReplyException;
34  import org.apache.commons.net.ProtocolCommandSupport;
35  import org.apache.commons.net.SocketClient;
36  import org.apache.commons.net.io.CRLFLineReader;
37  import org.apache.commons.net.util.NetConstants;
38  
39  /**
40   * FTP provides the basic the functionality necessary to implement your own FTP client. It extends org.apache.commons.net.SocketClient since extending
41   * TelnetClient was causing unwanted behavior (like connections that did not time out properly).
42   * <p>
43   * To derive the full benefits of the FTP class requires some knowledge of the FTP protocol defined in RFC 959. However, there is no reason why you should have
44   * to use the FTP class. The {@link org.apache.commons.net.ftp.FTPClient} class, derived from FTP, implements all the functionality required of an FTP client.
45   * The FTP class is made public to provide access to various FTP constants and to make it easier for adventurous programmers (or those with special needs) to
46   * interact with the FTP protocol and implement their own clients. A set of methods with names corresponding to the FTP command names are provided to facilitate
47   * this interaction.
48   * <p>
49   * You should keep in mind that the FTP server may choose to prematurely close a connection if the client has been idle for longer than a given time period
50   * (usually 900 seconds). The FTP class will detect a premature FTP server connection closing when it receives a
51   * {@link org.apache.commons.net.ftp.FTPReply#SERVICE_NOT_AVAILABLE FTPReply.SERVICE_NOT_AVAILABLE } response to a command. When that occurs, the FTP class
52   * method encountering that reply will throw an {@link org.apache.commons.net.ftp.FTPConnectionClosedException} . <code>FTPConectionClosedException</code> is a
53   * subclass of <code> IOException </code> and therefore need not be caught separately, but if you are going to catch it separately, its catch block must appear
54   * before the more general <code> IOException </code> catch block. When you encounter an {@link org.apache.commons.net.ftp.FTPConnectionClosedException} , you
55   * must disconnect the connection with {@link #disconnect disconnect() } to properly clean up the system resources used by FTP. Before disconnecting, you may
56   * check the last reply code and text with {@link #getReplyCode getReplyCode }, {@link #getReplyString getReplyString }, and {@link #getReplyStrings
57   * getReplyStrings}. You may avoid server disconnections while the client is idle by periodically sending NOOP commands to the server.
58   * <p>
59   * Rather than list it separately for each method, we mention here that every method communicating with the server and throwing an IOException can also throw a
60   * {@link org.apache.commons.net.MalformedServerReplyException} , which is a subclass of IOException. A MalformedServerReplyException will be thrown when the
61   * reply received from the server deviates enough from the protocol specification that it cannot be interpreted in a useful manner despite attempts to be as
62   * lenient as possible.
63   *
64   * @see FTPClient
65   * @see FTPConnectionClosedException
66   * @see org.apache.commons.net.MalformedServerReplyException
67   */
68  
69  public class FTP extends SocketClient {
70      /** The default FTP data port (20). */
71      public static final int DEFAULT_DATA_PORT = 20;
72      /** The default FTP control port (21). */
73      public static final int DEFAULT_PORT = 21;
74  
75      /**
76       * A constant used to indicate the file(s) being transferred should be treated as ASCII. This is the default file type. All constants ending in
77       * <code>FILE_TYPE</code> are used to indicate file types.
78       */
79      public static final int ASCII_FILE_TYPE = 0;
80  
81      /**
82       * A constant used to indicate the file(s) being transferred should be treated as EBCDIC. Note however that there are several EBCDIC formats. All
83       * constants ending in <code>FILE_TYPE</code> are used to indicate file types.
84       */
85      public static final int EBCDIC_FILE_TYPE = 1;
86  
87      /**
88       * A constant used to indicate the file(s) being transferred should be treated as a binary image, i.e., no translations should be performed. All constants
89       * ending in <code>FILE_TYPE</code> are used to indicate file types.
90       */
91      public static final int BINARY_FILE_TYPE = 2;
92  
93      /**
94       * A constant used to indicate the file(s) being transferred should be treated as a local type. All constants ending in <code>FILE_TYPE</code> are used to
95       * indicate file types.
96       */
97      public static final int LOCAL_FILE_TYPE = 3;
98  
99      /**
100      * A constant used for text files to indicate a non-print text format. This is the default format. All constants ending in <code>TEXT_FORMAT</code> are used
101      * to indicate text formatting for text transfers (both ASCII and EBCDIC).
102      */
103     public static final int NON_PRINT_TEXT_FORMAT = 4;
104 
105     /**
106      * A constant used to indicate a text file contains format vertical format control characters. All constants ending in <code>TEXT_FORMAT</code> are used to
107      * indicate text formatting for text transfers (both ASCII and EBCDIC).
108      */
109     public static final int TELNET_TEXT_FORMAT = 5;
110 
111     /**
112      * A constant used to indicate a text file contains ASA vertical format control characters. All constants ending in <code>TEXT_FORMAT</code> are used to
113      * indicate text formatting for text transfers (both ASCII and EBCDIC).
114      */
115     public static final int CARRIAGE_CONTROL_TEXT_FORMAT = 6;
116 
117     /**
118      * A constant used to indicate a file is to be treated as a continuous sequence of bytes. This is the default structure. All constants ending in
119      * <code>_STRUCTURE</code> are used to indicate file structure for file transfers.
120      */
121     public static final int FILE_STRUCTURE = 7;
122 
123     /**
124      * A constant used to indicate a file is to be treated as a sequence of records. All constants ending in <code>_STRUCTURE</code> are used to indicate file
125      * structure for file transfers.
126      */
127     public static final int RECORD_STRUCTURE = 8;
128 
129     /**
130      * A constant used to indicate a file is to be treated as a set of independent indexed pages. All constants ending in <code>_STRUCTURE</code> are used to
131      * indicate file structure for file transfers.
132      */
133     public static final int PAGE_STRUCTURE = 9;
134 
135     /**
136      * A constant used to indicate a file is to be transferred as a stream of bytes. This is the default transfer mode. All constants ending in
137      * <code>TRANSFER_MODE</code> are used to indicate file transfer modes.
138      */
139     public static final int STREAM_TRANSFER_MODE = 10;
140 
141     /**
142      * A constant used to indicate a file is to be transferred as a series of blocks. All constants ending in <code>TRANSFER_MODE</code> are used to indicate
143      * file transfer modes.
144      */
145     public static final int BLOCK_TRANSFER_MODE = 11;
146 
147     /**
148      * A constant used to indicate a file is to be transferred as FTP compressed data. All constants ending in <code>TRANSFER_MODE</code> are used to indicate
149      * file transfer modes.
150      */
151     public static final int COMPRESSED_TRANSFER_MODE = 12;
152 
153     // We have to ensure that the protocol communication is in ASCII,
154     // but we use ISO-8859-1 just in case 8-bit characters cross
155     // the wire.
156     /**
157      * The default character encoding used for communicating over an FTP control connection. The default encoding is an ASCII-compatible encoding. Some FTP
158      * servers expect other encodings. You can change the encoding used by an FTP instance with {@link #setControlEncoding setControlEncoding}.
159      */
160     public static final String DEFAULT_CONTROL_ENCODING = "ISO-8859-1";
161 
162     /** Length of the FTP reply code (3 alphanumerics) */
163     public static final int REPLY_CODE_LEN = 3;
164 
165     private static final String modes = "AEILNTCFRPSBC";
166     protected int _replyCode;
167     protected ArrayList<String> _replyLines;
168     protected boolean _newReplyString;
169     protected String _replyString;
170     protected String _controlEncoding;
171 
172     /**
173      * A ProtocolCommandSupport object used to manage the registering of ProtocolCommandListeners and the firing of ProtocolCommandEvents.
174      */
175     protected ProtocolCommandSupport _commandSupport_;
176 
177     /**
178      * This is used to signal whether a block of multiline responses beginning with xxx must be terminated by the same numeric code xxx See section 4.2 of RFC
179      * 959 for details.
180      */
181     protected boolean strictMultilineParsing;
182 
183     /**
184      * If this is true, then non-multiline replies must have the format: 3-digit code <space> <text> If false, then the 3-digit code does not have to be
185      * followed by space See section 4.2 of RFC 959 for details.
186      */
187     private boolean strictReplyParsing = true;
188 
189     /**
190      * Wraps SocketClient._input_ to facilitate the reading of text from the FTP control connection. Do not access the control connection via
191      * SocketClient._input_. This member starts with a null value, is initialized in {@link #_connectAction_}, and set to null in {@link #disconnect}.
192      */
193     protected BufferedReader _controlInput_;
194 
195     /**
196      * Wraps SocketClient._output_ to facilitate the writing of text to the FTP control connection. Do not access the control connection via
197      * SocketClient._output_. This member starts with a null value, is initialized in {@link #_connectAction_}, and set to null in {@link #disconnect}.
198      */
199     protected BufferedWriter _controlOutput_;
200 
201     /**
202      * The default FTP constructor. Sets the default port to <code>DEFAULT_PORT</code> and initializes internal data structures for saving FTP reply
203      * information.
204      */
205     public FTP() {
206         setDefaultPort(DEFAULT_PORT);
207         _replyLines = new ArrayList<>();
208         _newReplyString = false;
209         _replyString = null;
210         _controlEncoding = DEFAULT_CONTROL_ENCODING;
211         _commandSupport_ = new ProtocolCommandSupport(this);
212     }
213 
214     /**
215      * Get the reply, but don't pass it to command listeners. Used for keep-alive processing only.
216      *
217      * @since 3.0
218      * @throws IOException on error
219      */
220     protected void __getReplyNoReport() throws IOException {
221         getReply(false);
222     }
223 
224     /**
225      * Send a noop and get the reply without reporting to the command listener. Intended for use with keep-alive.
226      *
227      * @throws IOException on error
228      * @since 3.0
229      */
230     protected void __noop() throws IOException {
231         final String msg = buildMessage(FTPCmd.NOOP.getCommand(), null);
232         send(msg);
233         __getReplyNoReport(); // This may time out
234     }
235 
236     /**
237      * Initiates control connections and gets initial reply. Initializes {@link #_controlInput_} and {@link #_controlOutput_}.
238      */
239     @Override
240     protected void _connectAction_() throws IOException {
241         _connectAction_(null);
242     }
243 
244     /**
245      * Initiates control connections and gets initial reply. Initializes {@link #_controlInput_} and {@link #_controlOutput_}.
246      *
247      * @param socketIsReader the reader to reuse (if non-null)
248      * @throws IOException on error
249      * @since 3.4
250      */
251     protected void _connectAction_(final Reader socketIsReader) throws IOException {
252         super._connectAction_(); // sets up _input_ and _output_
253         if (socketIsReader == null) {
254             _controlInput_ = new CRLFLineReader(new InputStreamReader(_input_, getControlEncoding()));
255         } else {
256             _controlInput_ = new CRLFLineReader(socketIsReader);
257         }
258         _controlOutput_ = new BufferedWriter(new OutputStreamWriter(_output_, getControlEncoding()));
259         if (connectTimeout > 0) { // NET-385
260             final int original = _socket_.getSoTimeout();
261             _socket_.setSoTimeout(connectTimeout);
262             try {
263                 getReply();
264                 // If we received code 120, we have to fetch completion reply.
265                 if (FTPReply.isPositivePreliminary(_replyCode)) {
266                     getReply();
267                 }
268             } catch (final SocketTimeoutException e) {
269                 final IOException ioe = new IOException("Timed out waiting for initial connect reply");
270                 ioe.initCause(e);
271                 throw ioe;
272             } finally {
273                 _socket_.setSoTimeout(original);
274             }
275         } else {
276             getReply();
277             // If we received code 120, we have to fetch completion reply.
278             if (FTPReply.isPositivePreliminary(_replyCode)) {
279                 getReply();
280             }
281         }
282     }
283 
284     /**
285      * A convenience method to send the FTP ABOR command to the server, receive the reply, and return the reply code.
286      *
287      * @return The reply code received from the server.
288      * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason
289      *                                      causing the server to send FTP reply code 421. This exception may be caught either as an IOException or
290      *                                      independently as itself.
291      * @throws IOException                  If an I/O error occurs while either sending the command or receiving the server reply.
292      */
293     public int abor() throws IOException {
294         return sendCommand(FTPCmd.ABOR);
295     }
296 
297     /**
298      * A convenience method to send the FTP ACCT command to the server, receive the reply, and return the reply code.
299      *
300      * @param account The account name to access.
301      * @return The reply code received from the server.
302      * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason
303      *                                      causing the server to send FTP reply code 421. This exception may be caught either as an IOException or
304      *                                      independently as itself.
305      * @throws IOException                  If an I/O error occurs while either sending the command or receiving the server reply.
306      */
307     public int acct(final String account) throws IOException {
308         return sendCommand(FTPCmd.ACCT, account);
309     }
310 
311     /**
312      * A convenience method to send the FTP ALLO command to the server, receive the reply, and return the reply code.
313      *
314      * @param bytes The number of bytes to allocate.
315      * @return The reply code received from the server.
316      * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason
317      *                                      causing the server to send FTP reply code 421. This exception may be caught either as an IOException or
318      *                                      independently as itself.
319      * @throws IOException                  If an I/O error occurs while either sending the command or receiving the server reply.
320      */
321     public int allo(final int bytes) throws IOException {
322         return sendCommand(FTPCmd.ALLO, Integer.toString(bytes));
323     }
324 
325     /**
326      * A convenience method to send the FTP ALLO command to the server, receive the reply, and return the reply code.
327      *
328      * @param bytes      The number of bytes to allocate.
329      * @param recordSize The size of a record.
330      * @return The reply code received from the server.
331      * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason
332      *                                      causing the server to send FTP reply code 421. This exception may be caught either as an IOException or
333      *                                      independently as itself.
334      * @throws IOException                  If an I/O error occurs while either sending the command or receiving the server reply.
335      */
336     public int allo(final int bytes, final int recordSize) throws IOException {
337         return sendCommand(FTPCmd.ALLO, Integer.toString(bytes) + " R " + Integer.toString(recordSize));
338     }
339 
340     /**
341      * A convenience method to send the FTP ALLO command to the server, receive the reply, and return the reply code.
342      *
343      * @param bytes The number of bytes to allocate.
344      * @return The reply code received from the server.
345      * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason
346      *                                      causing the server to send FTP reply code 421. This exception may be caught either as an IOException or
347      *                                      independently as itself.
348      * @throws IOException                  If an I/O error occurs while either sending the command or receiving the server reply.
349      */
350     public int allo(final long bytes) throws IOException {
351         return sendCommand(FTPCmd.ALLO, Long.toString(bytes));
352     }
353 
354     /**
355      * A convenience method to send the FTP ALLO command to the server, receive the reply, and return the reply code.
356      *
357      * @param bytes      The number of bytes to allocate.
358      * @param recordSize The size of a record.
359      * @return The reply code received from the server.
360      * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason
361      *                                      causing the server to send FTP reply code 421. This exception may be caught either as an IOException or
362      *                                      independently as itself.
363      * @throws IOException                  If an I/O error occurs while either sending the command or receiving the server reply.
364      */
365     public int allo(final long bytes, final int recordSize) throws IOException {
366         return sendCommand(FTPCmd.ALLO, Long.toString(bytes) + " R " + Integer.toString(recordSize));
367     }
368 
369     /**
370      * A convenience method to send the FTP APPE command to the server, receive the reply, and return the reply code. Remember, it is up to you to manage the
371      * data connection. If you don't need this low level of access, use {@link org.apache.commons.net.ftp.FTPClient} , which will handle all low level details
372      * for you.
373      *
374      * @param pathname The pathname to use for the file when stored at the remote end of the transfer.
375      * @return The reply code received from the server.
376      * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason
377      *                                      causing the server to send FTP reply code 421. This exception may be caught either as an IOException or
378      *                                      independently as itself.
379      * @throws IOException                  If an I/O error occurs while either sending the command or receiving the server reply.
380      */
381     public int appe(final String pathname) throws IOException {
382         return sendCommand(FTPCmd.APPE, pathname);
383     }
384 
385     private String buildMessage(final String command, final String args) {
386         final StringBuilder __commandBuffer = new StringBuilder();
387 
388         __commandBuffer.append(command);
389 
390         if (args != null) {
391             __commandBuffer.append(' ');
392             __commandBuffer.append(args);
393         }
394         __commandBuffer.append(SocketClient.NETASCII_EOL);
395         return __commandBuffer.toString();
396     }
397 
398     /**
399      * A convenience method to send the FTP CDUP command to the server, receive the reply, and return the reply code.
400      *
401      * @return The reply code received from the server.
402      * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason
403      *                                      causing the server to send FTP reply code 421. This exception may be caught either as an IOException or
404      *                                      independently as itself.
405      * @throws IOException                  If an I/O error occurs while either sending the command or receiving the server reply.
406      */
407     public int cdup() throws IOException {
408         return sendCommand(FTPCmd.CDUP);
409     }
410 
411     /**
412      * A convenience method to send the FTP CWD command to the server, receive the reply, and return the reply code.
413      *
414      * @param directory The new working directory.
415      * @return The reply code received from the server.
416      * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason
417      *                                      causing the server to send FTP reply code 421. This exception may be caught either as an IOException or
418      *                                      independently as itself.
419      * @throws IOException                  If an I/O error occurs while either sending the command or receiving the server reply.
420      */
421     public int cwd(final String directory) throws IOException {
422         return sendCommand(FTPCmd.CWD, directory);
423     }
424 
425     /**
426      * A convenience method to send the FTP DELE command to the server, receive the reply, and return the reply code.
427      *
428      * @param pathname The pathname to delete.
429      * @return The reply code received from the server.
430      * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason
431      *                                      causing the server to send FTP reply code 421. This exception may be caught either as an IOException or
432      *                                      independently as itself.
433      * @throws IOException                  If an I/O error occurs while either sending the command or receiving the server reply.
434      */
435     public int dele(final String pathname) throws IOException {
436         return sendCommand(FTPCmd.DELE, pathname);
437     }
438 
439     /**
440      * Closes the control connection to the FTP server and sets to null some internal data so that the memory may be reclaimed by the garbage collector. The
441      * reply text and code information from the last command is voided so that the memory it used may be reclaimed. Also sets {@link #_controlInput_} and
442      * {@link #_controlOutput_} to null.
443      *
444      * @throws IOException If an error occurs while disconnecting.
445      */
446     @Override
447     public void disconnect() throws IOException {
448         super.disconnect();
449         _controlInput_ = null;
450         _controlOutput_ = null;
451         _newReplyString = false;
452         _replyString = null;
453     }
454 
455     /**
456      * A convenience method to send the FTP EPRT command to the server, receive the reply, and return the reply code.
457      *
458      * Examples:
459      * <ul>
460      * <li>EPRT |1|132.235.1.2|6275|</li>
461      * <li>EPRT |2|1080::8:800:200C:417A|5282|</li>
462      * </ul>
463      *
464      * @see "http://www.faqs.org/rfcs/rfc2428.html"
465      *
466      * @param host The host owning the port.
467      * @param port The new port.
468      * @return The reply code received from the server.
469      * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason
470      *                                      causing the server to send FTP reply code 421. This exception may be caught either as an IOException or
471      *                                      independently as itself.
472      * @throws IOException                  If an I/O error occurs while either sending the command or receiving the server reply.
473      * @since 2.2
474      */
475     public int eprt(final InetAddress host, final int port) throws IOException {
476         final int num;
477         final StringBuilder info = new StringBuilder();
478         String h;
479 
480         // If IPv6, trim the zone index
481         h = host.getHostAddress();
482         num = h.indexOf('%');
483         if (num > 0) {
484             h = h.substring(0, num);
485         }
486 
487         info.append("|");
488 
489         if (host instanceof Inet4Address) {
490             info.append("1");
491         } else if (host instanceof Inet6Address) {
492             info.append("2");
493         }
494         info.append("|");
495         info.append(h);
496         info.append("|");
497         info.append(port);
498         info.append("|");
499 
500         return sendCommand(FTPCmd.EPRT, info.toString());
501     }
502 
503     /**
504      * A convenience method to send the FTP EPSV command to the server, receive the reply, and return the reply code. Remember, it's up to you to interpret the
505      * reply string containing the host/port information.
506      *
507      * @return The reply code received from the server.
508      * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason
509      *                                      causing the server to send FTP reply code 421. This exception may be caught either as an IOException or
510      *                                      independently as itself.
511      * @throws IOException                  If an I/O error occurs while either sending the command or receiving the server reply.
512      * @since 2.2
513      */
514     public int epsv() throws IOException {
515         return sendCommand(FTPCmd.EPSV);
516     }
517 
518     /**
519      * A convenience method to send the FTP FEAT command to the server, receive the reply, and return the reply code.
520      *
521      * @return The reply code received by the server
522      * @throws IOException If an I/O error occurs while either sending the command or receiving the server reply.
523      * @since 2.2
524      */
525     public int feat() throws IOException {
526         return sendCommand(FTPCmd.FEAT);
527     }
528 
529     /**
530      * Provide command support to super-class
531      */
532     @Override
533     protected ProtocolCommandSupport getCommandSupport() {
534         return _commandSupport_;
535     }
536 
537     /**
538      * @return The character encoding used to communicate over the control connection.
539      */
540     public String getControlEncoding() {
541         return _controlEncoding;
542     }
543 
544     /**
545      * Fetches a reply from the FTP server and returns the integer reply code. After calling this method, the actual reply text can be accessed from either
546      * calling {@link #getReplyString getReplyString } or {@link #getReplyStrings getReplyStrings }. Only use this method if you are implementing your own FTP
547      * client or if you need to fetch a secondary response from the FTP server.
548      *
549      * @return The integer value of the reply code of the fetched FTP reply.
550      * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason
551      *                                      causing the server to send FTP reply code 421. This exception may be caught either as an IOException or
552      *                                      independently as itself.
553      * @throws IOException                  If an I/O error occurs while receiving the server reply.
554      */
555     public int getReply() throws IOException {
556         return getReply(true);
557     }
558 
559     private int getReply(final boolean reportReply) throws IOException {
560         final int length;
561 
562         _newReplyString = true;
563         _replyLines.clear();
564 
565         String line = _controlInput_.readLine();
566 
567         if (line == null) {
568             throw new FTPConnectionClosedException("Connection closed without indication.");
569         }
570 
571         // In case we run into an anomaly we don't want fatal index exceptions
572         // to be thrown.
573         length = line.length();
574         if (length < REPLY_CODE_LEN) {
575             throw new MalformedServerReplyException("Truncated server reply: " + line);
576         }
577 
578         String code;
579         try {
580             code = line.substring(0, REPLY_CODE_LEN);
581             _replyCode = Integer.parseInt(code);
582         } catch (final NumberFormatException e) {
583             throw new MalformedServerReplyException("Could not parse response code.\nServer Reply: " + line);
584         }
585 
586         _replyLines.add(line);
587 
588         // Check the server reply type
589         if (length > REPLY_CODE_LEN) {
590             final char sep = line.charAt(REPLY_CODE_LEN);
591             // Get extra lines if message continues.
592             if (sep == '-') {
593                 do {
594                     line = _controlInput_.readLine();
595 
596                     if (line == null) {
597                         throw new FTPConnectionClosedException("Connection closed without indication.");
598                     }
599 
600                     _replyLines.add(line);
601 
602                     // The length() check handles problems that could arise from readLine()
603                     // returning too soon after encountering a naked CR or some other
604                     // anomaly.
605                 } while (isStrictMultilineParsing() ? strictCheck(line, code) : lenientCheck(line));
606 
607             } else if (isStrictReplyParsing()) {
608                 if (length == REPLY_CODE_LEN + 1) { // expecting some text
609                     throw new MalformedServerReplyException("Truncated server reply: '" + line + "'");
610                 }
611                 if (sep != ' ') {
612                     throw new MalformedServerReplyException("Invalid server reply: '" + line + "'");
613                 }
614             }
615         } else if (isStrictReplyParsing()) {
616             throw new MalformedServerReplyException("Truncated server reply: '" + line + "'");
617         }
618 
619         if (reportReply) {
620             fireReplyReceived(_replyCode, getReplyString());
621         }
622 
623         if (_replyCode == FTPReply.SERVICE_NOT_AVAILABLE) {
624             throw new FTPConnectionClosedException("FTP response 421 received.  Server closed connection.");
625         }
626         return _replyCode;
627     }
628 
629     /**
630      * Returns the integer value of the reply code of the last FTP reply. You will usually only use this method after you connect to the FTP server to check
631      * that the connection was successful since <code> connect </code> is of type void.
632      *
633      * @return The integer value of the reply code of the last FTP reply.
634      */
635     public int getReplyCode() {
636         return _replyCode;
637     }
638 
639     /**
640      * Returns the entire text of the last FTP server response exactly as it was received, including all end of line markers in NETASCII format.
641      *
642      * @return The entire text from the last FTP response as a String.
643      */
644     public String getReplyString() {
645         final StringBuilder buffer;
646 
647         if (!_newReplyString) {
648             return _replyString;
649         }
650 
651         buffer = new StringBuilder(256);
652 
653         for (final String line : _replyLines) {
654             buffer.append(line);
655             buffer.append(SocketClient.NETASCII_EOL);
656         }
657 
658         _newReplyString = false;
659 
660         return _replyString = buffer.toString();
661     }
662 
663     /**
664      * Returns the nth line of text from the last FTP server response as a string. The end of line markers of each are stripped from the line.
665      *
666      * @param index The index of the line to return, 0-based.
667      *
668      * @return The lines of text from the last FTP response as an array.
669      */
670     String getReplyString(final int index) {
671         return _replyLines.get(index);
672     }
673 
674     /**
675      * Returns the lines of text from the last FTP server response as an array of strings, one entry per line. The end of line markers of each are stripped from
676      * each line.
677      *
678      * @return The lines of text from the last FTP response as an array.
679      */
680     public String[] getReplyStrings() {
681         return _replyLines.toArray(NetConstants.EMPTY_STRING_ARRAY);
682     }
683 
684     /**
685      * A convenience method to send the FTP HELP command to the server, receive the reply, and return the reply code.
686      *
687      * @return The reply code received from the server.
688      * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason
689      *                                      causing the server to send FTP reply code 421. This exception may be caught either as an IOException or
690      *                                      independently as itself.
691      * @throws IOException                  If an I/O error occurs while either sending the command or receiving the server reply.
692      */
693     public int help() throws IOException {
694         return sendCommand(FTPCmd.HELP);
695     }
696 
697     /**
698      * A convenience method to send the FTP HELP command to the server, receive the reply, and return the reply code.
699      *
700      * @param command The command name on which to request help.
701      * @return The reply code received from the server.
702      * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason
703      *                                      causing the server to send FTP reply code 421. This exception may be caught either as an IOException or
704      *                                      independently as itself.
705      * @throws IOException                  If an I/O error occurs while either sending the command or receiving the server reply.
706      */
707     public int help(final String command) throws IOException {
708         return sendCommand(FTPCmd.HELP, command);
709     }
710 
711     /**
712      * Return whether strict multiline parsing is enabled, as per RFC 959, section 4.2.
713      *
714      * @return True if strict, false if lenient
715      * @since 2.0
716      */
717     public boolean isStrictMultilineParsing() {
718         return strictMultilineParsing;
719     }
720 
721     /**
722      * Return whether strict non-multiline parsing is enabled, as per RFC 959, section 4.2.
723      * <p>
724      * The default is true, which requires the 3-digit code be followed by space and some text. <br>
725      * If false, only the 3-digit code is required (as was the case for versions up to 3.5) <br>
726      *
727      * @return True if strict (default), false if additional checks are not made
728      * @since 3.6
729      */
730     public boolean isStrictReplyParsing() {
731         return strictReplyParsing;
732     }
733 
734     // The strict check is too strong a condition because of non-conforming ftp
735     // servers like ftp.funet.fi which sent 226 as the last line of a
736     // 426 multi-line reply in response to ls /. We relax the condition to
737     // test that the line starts with a digit rather than starting with
738     // the code.
739     private boolean lenientCheck(final String line) {
740         return !(line.length() > REPLY_CODE_LEN && line.charAt(REPLY_CODE_LEN) != '-' && Character.isDigit(line.charAt(0)));
741     }
742 
743     /**
744      * A convenience method to send the FTP LIST command to the server, receive the reply, and return the reply code. Remember, it is up to you to manage the
745      * data connection. If you don't need this low level of access, use {@link org.apache.commons.net.ftp.FTPClient} , which will handle all low level details
746      * for you.
747      *
748      * @return The reply code received from the server.
749      * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason
750      *                                      causing the server to send FTP reply code 421. This exception may be caught either as an IOException or
751      *                                      independently as itself.
752      * @throws IOException                  If an I/O error occurs while either sending the command or receiving the server reply.
753      */
754     public int list() throws IOException {
755         return sendCommand(FTPCmd.LIST);
756     }
757 
758     /**
759      * A convenience method to send the FTP LIST command to the server, receive the reply, and return the reply code. Remember, it is up to you to manage the
760      * data connection. If you don't need this low level of access, use {@link org.apache.commons.net.ftp.FTPClient} , which will handle all low level details
761      * for you.
762      *
763      * @param pathname The pathname to list, may be {@code null} in which case the command is sent with no parameters
764      * @return The reply code received from the server.
765      * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason
766      *                                      causing the server to send FTP reply code 421. This exception may be caught either as an IOException or
767      *                                      independently as itself.
768      * @throws IOException                  If an I/O error occurs while either sending the command or receiving the server reply.
769      */
770     public int list(final String pathname) throws IOException {
771         return sendCommand(FTPCmd.LIST, pathname);
772     }
773 
774     /**
775      * Sends the MDTM command for the given file.
776      *
777      * @param file name of file
778      * @return the status
779      * @throws IOException on error
780      * @since 2.0
781      **/
782     public int mdtm(final String file) throws IOException {
783         return sendCommand(FTPCmd.MDTM, file);
784     }
785 
786     /**
787      * A convenience method to send the FTP MFMT command to the server, receive the reply, and return the reply code.
788      *
789      * @param pathname The pathname for which mtime is to be changed
790      * @param timeval  Timestamp in <code>yyyyMMDDhhmmss</code> format
791      * @return The reply code received from the server.
792      * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason
793      *                                      causing the server to send FTP reply code 421. This exception may be caught either as an IOException or
794      *                                      independently as itself.
795      * @throws IOException                  If an I/O error occurs while either sending the command or receiving the server reply.
796      * @since 2.2
797      * @see <a href="http://tools.ietf.org/html/draft-somers-ftp-mfxx-04">http://tools.ietf.org/html/draft-somers-ftp-mfxx-04</a>
798      **/
799     public int mfmt(final String pathname, final String timeval) throws IOException {
800         return sendCommand(FTPCmd.MFMT, timeval + " " + pathname);
801     }
802 
803     /**
804      * A convenience method to send the FTP MKD command to the server, receive the reply, and return the reply code.
805      *
806      * @param pathname The pathname of the new directory to create.
807      * @return The reply code received from the server.
808      * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason
809      *                                      causing the server to send FTP reply code 421. This exception may be caught either as an IOException or
810      *                                      independently as itself.
811      * @throws IOException                  If an I/O error occurs while either sending the command or receiving the server reply.
812      */
813     public int mkd(final String pathname) throws IOException {
814         return sendCommand(FTPCmd.MKD, pathname);
815     }
816 
817     /**
818      * A convenience method to send the FTP MLSD command to the server, receive the reply, and return the reply code. Remember, it is up to you to manage the
819      * data connection. If you don't need this low level of access, use {@link org.apache.commons.net.ftp.FTPClient} , which will handle all low level details
820      * for you.
821      *
822      * @return The reply code received from the server.
823      * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason
824      *                                      causing the server to send FTP reply code 421. This exception may be caught either as an IOException or
825      *                                      independently as itself.
826      * @throws IOException                  If an I/O error occurs while either sending the command or receiving the server reply.
827      * @since 3.0
828      */
829     public int mlsd() throws IOException {
830         return sendCommand(FTPCmd.MLSD);
831     }
832 
833     /**
834      * A convenience method to send the FTP MLSD command to the server, receive the reply, and return the reply code. Remember, it is up to you to manage the
835      * data connection. If you don't need this low level of access, use {@link org.apache.commons.net.ftp.FTPClient} , which will handle all low level details
836      * for you.
837      *
838      * @param path the path to report on
839      * @return The reply code received from the server, may be {@code null} in which case the command is sent with no parameters
840      * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason
841      *                                      causing the server to send FTP reply code 421. This exception may be caught either as an IOException or
842      *                                      independently as itself.
843      * @throws IOException                  If an I/O error occurs while either sending the command or receiving the server reply.
844      * @since 3.0
845      */
846     public int mlsd(final String path) throws IOException {
847         return sendCommand(FTPCmd.MLSD, path);
848     }
849 
850     /**
851      * A convenience method to send the FTP MLST command to the server, receive the reply, and return the reply code. Remember, it is up to you to manage the
852      * data connection. If you don't need this low level of access, use {@link org.apache.commons.net.ftp.FTPClient} , which will handle all low level details
853      * for you.
854      *
855      * @return The reply code received from the server.
856      * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason
857      *                                      causing the server to send FTP reply code 421. This exception may be caught either as an IOException or
858      *                                      independently as itself.
859      * @throws IOException                  If an I/O error occurs while either sending the command or receiving the server reply.
860      * @since 3.0
861      */
862     public int mlst() throws IOException {
863         return sendCommand(FTPCmd.MLST);
864     }
865 
866     /**
867      * A convenience method to send the FTP MLST command to the server, receive the reply, and return the reply code. Remember, it is up to you to manage the
868      * data connection. If you don't need this low level of access, use {@link org.apache.commons.net.ftp.FTPClient} , which will handle all low level details
869      * for you.
870      *
871      * @param path the path to report on
872      * @return The reply code received from the server, may be {@code null} in which case the command is sent with no parameters
873      * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason
874      *                                      causing the server to send FTP reply code 421. This exception may be caught either as an IOException or
875      *                                      independently as itself.
876      * @throws IOException                  If an I/O error occurs while either sending the command or receiving the server reply.
877      * @since 3.0
878      */
879     public int mlst(final String path) throws IOException {
880         return sendCommand(FTPCmd.MLST, path);
881     }
882 
883     /**
884      * A convenience method to send the FTP MODE command to the server, receive the reply, and return the reply code.
885      *
886      * @param mode The transfer mode to use (one of the <code>TRANSFER_MODE</code> constants).
887      * @return The reply code received from the server.
888      * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason
889      *                                      causing the server to send FTP reply code 421. This exception may be caught either as an IOException or
890      *                                      independently as itself.
891      * @throws IOException                  If an I/O error occurs while either sending the command or receiving the server reply.
892      */
893     public int mode(final int mode) throws IOException {
894         return sendCommand(FTPCmd.MODE, modes.substring(mode, mode + 1));
895     }
896 
897     /**
898      * A convenience method to send the FTP NLST command to the server, receive the reply, and return the reply code. Remember, it is up to you to manage the
899      * data connection. If you don't need this low level of access, use {@link org.apache.commons.net.ftp.FTPClient} , which will handle all low level details
900      * for you.
901      *
902      * @return The reply code received from the server.
903      * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason
904      *                                      causing the server to send FTP reply code 421. This exception may be caught either as an IOException or
905      *                                      independently as itself.
906      * @throws IOException                  If an I/O error occurs while either sending the command or receiving the server reply.
907      */
908     public int nlst() throws IOException {
909         return sendCommand(FTPCmd.NLST);
910     }
911 
912     /**
913      * A convenience method to send the FTP NLST command to the server, receive the reply, and return the reply code. Remember, it is up to you to manage the
914      * data connection. If you don't need this low level of access, use {@link org.apache.commons.net.ftp.FTPClient} , which will handle all low level details
915      * for you.
916      *
917      * @param pathname The pathname to list, may be {@code null} in which case the command is sent with no parameters
918      * @return The reply code received from the server.
919      * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason
920      *                                      causing the server to send FTP reply code 421. This exception may be caught either as an IOException or
921      *                                      independently as itself.
922      * @throws IOException                  If an I/O error occurs while either sending the command or receiving the server reply.
923      */
924     public int nlst(final String pathname) throws IOException {
925         return sendCommand(FTPCmd.NLST, pathname);
926     }
927 
928     /**
929      * A convenience method to send the FTP NOOP command to the server, receive the reply, and return the reply code.
930      *
931      * @return The reply code received from the server.
932      * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason
933      *                                      causing the server to send FTP reply code 421. This exception may be caught either as an IOException or
934      *                                      independently as itself.
935      * @throws IOException                  If an I/O error occurs while either sending the command or receiving the server reply.
936      */
937     public int noop() throws IOException {
938         return sendCommand(FTPCmd.NOOP);
939     }
940 
941     /**
942      * A convenience method to send the FTP PASS command to the server, receive the reply, and return the reply code.
943      *
944      * @param password The plain text password of the user being logged into.
945      * @return The reply code received from the server.
946      * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason
947      *                                      causing the server to send FTP reply code 421. This exception may be caught either as an IOException or
948      *                                      independently as itself.
949      * @throws IOException                  If an I/O error occurs while either sending the command or receiving the server reply.
950      */
951     public int pass(final String password) throws IOException {
952         return sendCommand(FTPCmd.PASS, password);
953     }
954 
955     /**
956      * A convenience method to send the FTP PASV command to the server, receive the reply, and return the reply code. Remember, it's up to you to interpret the
957      * reply string containing the host/port information.
958      *
959      * @return The reply code received from the server.
960      * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason
961      *                                      causing the server to send FTP reply code 421. This exception may be caught either as an IOException or
962      *                                      independently as itself.
963      * @throws IOException                  If an I/O error occurs while either sending the command or receiving the server reply.
964      */
965     public int pasv() throws IOException {
966         return sendCommand(FTPCmd.PASV);
967     }
968 
969     /**
970      * A convenience method to send the FTP PORT command to the server, receive the reply, and return the reply code.
971      *
972      * @param host The host owning the port.
973      * @param port The new port.
974      * @return The reply code received from the server.
975      * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason
976      *                                      causing the server to send FTP reply code 421. This exception may be caught either as an IOException or
977      *                                      independently as itself.
978      * @throws IOException                  If an I/O error occurs while either sending the command or receiving the server reply.
979      */
980     public int port(final InetAddress host, final int port) throws IOException {
981         int num;
982         final StringBuilder info = new StringBuilder(24);
983 
984         info.append(host.getHostAddress().replace('.', ','));
985         num = port >>> 8;
986         info.append(',');
987         info.append(num);
988         info.append(',');
989         num = port & 0xff;
990         info.append(num);
991 
992         return sendCommand(FTPCmd.PORT, info.toString());
993     }
994 
995     /**
996      * A convenience method to send the FTP PWD command to the server, receive the reply, and return the reply code.
997      *
998      * @return The reply code received from the server.
999      * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason
1000      *                                      causing the server to send FTP reply code 421. This exception may be caught either as an IOException or
1001      *                                      independently as itself.
1002      * @throws IOException                  If an I/O error occurs while either sending the command or receiving the server reply.
1003      */
1004     public int pwd() throws IOException {
1005         return sendCommand(FTPCmd.PWD);
1006     }
1007 
1008     /**
1009      * A convenience method to send the FTP QUIT command to the server, receive the reply, and return the reply code.
1010      *
1011      * @return The reply code received from the server.
1012      * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason
1013      *                                      causing the server to send FTP reply code 421. This exception may be caught either as an IOException or
1014      *                                      independently as itself.
1015      * @throws IOException                  If an I/O error occurs while either sending the command or receiving the server reply.
1016      */
1017     public int quit() throws IOException {
1018         return sendCommand(FTPCmd.QUIT);
1019     }
1020 
1021     /**
1022      * A convenience method to send the FTP REIN command to the server, receive the reply, and return the reply code.
1023      *
1024      * @return The reply code received from the server.
1025      * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason
1026      *                                      causing the server to send FTP reply code 421. This exception may be caught either as an IOException or
1027      *                                      independently as itself.
1028      * @throws IOException                  If an I/O error occurs while either sending the command or receiving the server reply.
1029      */
1030     public int rein() throws IOException {
1031         return sendCommand(FTPCmd.REIN);
1032     }
1033 
1034     /**
1035      * A convenience method to send the FTP REST command to the server, receive the reply, and return the reply code.
1036      *
1037      * @param marker The marker at which to restart a transfer.
1038      * @return The reply code received from the server.
1039      * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason
1040      *                                      causing the server to send FTP reply code 421. This exception may be caught either as an IOException or
1041      *                                      independently as itself.
1042      * @throws IOException                  If an I/O error occurs while either sending the command or receiving the server reply.
1043      */
1044     public int rest(final String marker) throws IOException {
1045         return sendCommand(FTPCmd.REST, marker);
1046     }
1047 
1048     /**
1049      * A convenience method to send the FTP RETR command to the server, receive the reply, and return the reply code. Remember, it is up to you to manage the
1050      * data connection. If you don't need this low level of access, use {@link org.apache.commons.net.ftp.FTPClient} , which will handle all low level details
1051      * for you.
1052      *
1053      * @param pathname The pathname of the file to retrieve.
1054      * @return The reply code received from the server.
1055      * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason
1056      *                                      causing the server to send FTP reply code 421. This exception may be caught either as an IOException or
1057      *                                      independently as itself.
1058      * @throws IOException                  If an I/O error occurs while either sending the command or receiving the server reply.
1059      */
1060     public int retr(final String pathname) throws IOException {
1061         return sendCommand(FTPCmd.RETR, pathname);
1062     }
1063 
1064     /**
1065      * A convenience method to send the FTP RMD command to the server, receive the reply, and return the reply code.
1066      *
1067      * @param pathname The pathname of the directory to remove.
1068      * @return The reply code received from the server.
1069      * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason
1070      *                                      causing the server to send FTP reply code 421. This exception may be caught either as an IOException or
1071      *                                      independently as itself.
1072      * @throws IOException                  If an I/O error occurs while either sending the command or receiving the server reply.
1073      */
1074     public int rmd(final String pathname) throws IOException {
1075         return sendCommand(FTPCmd.RMD, pathname);
1076     }
1077 
1078     /**
1079      * A convenience method to send the FTP RNFR command to the server, receive the reply, and return the reply code.
1080      *
1081      * @param pathname The pathname to rename from.
1082      * @return The reply code received from the server.
1083      * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason
1084      *                                      causing the server to send FTP reply code 421. This exception may be caught either as an IOException or
1085      *                                      independently as itself.
1086      * @throws IOException                  If an I/O error occurs while either sending the command or receiving the server reply.
1087      */
1088     public int rnfr(final String pathname) throws IOException {
1089         return sendCommand(FTPCmd.RNFR, pathname);
1090     }
1091 
1092     /**
1093      * A convenience method to send the FTP RNTO command to the server, receive the reply, and return the reply code.
1094      *
1095      * @param pathname The pathname to rename to
1096      * @return The reply code received from the server.
1097      * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason
1098      *                                      causing the server to send FTP reply code 421. This exception may be caught either as an IOException or
1099      *                                      independently as itself.
1100      * @throws IOException                  If an I/O error occurs while either sending the command or receiving the server reply.
1101      */
1102     public int rnto(final String pathname) throws IOException {
1103         return sendCommand(FTPCmd.RNTO, pathname);
1104     }
1105 
1106     private void send(final String message) throws IOException, FTPConnectionClosedException, SocketException {
1107         try {
1108             _controlOutput_.write(message);
1109             _controlOutput_.flush();
1110         } catch (final SocketException e) {
1111             if (!isConnected()) {
1112                 throw new FTPConnectionClosedException("Connection unexpectedly closed.");
1113             }
1114             throw e;
1115         }
1116     }
1117 
1118     /**
1119      * Sends an FTP command to the server, waits for a reply and returns the numerical response code. After invocation, for more detailed information, the
1120      * actual reply text can be accessed by calling {@link #getReplyString getReplyString } or {@link #getReplyStrings getReplyStrings }.
1121      *
1122      * @param command The FTPCmd enum corresponding to the FTP command to send.
1123      * @return The integer value of the FTP reply code returned by the server in response to the command.
1124      * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason
1125      *                                      causing the server to send FTP reply code 421. This exception may be caught either as an IOException or
1126      *                                      independently as itself.
1127      * @throws IOException                  If an I/O error occurs while either sending the command or receiving the server reply.
1128      * @since 3.3
1129      */
1130     public int sendCommand(final FTPCmd command) throws IOException {
1131         return sendCommand(command, null);
1132     }
1133 
1134     /**
1135      * Sends an FTP command to the server, waits for a reply and returns the numerical response code. After invocation, for more detailed information, the
1136      * actual reply text can be accessed by calling {@link #getReplyString getReplyString } or {@link #getReplyStrings getReplyStrings }.
1137      *
1138      * @param command The FTPCmd enum corresponding to the FTP command to send.
1139      * @param args    The arguments to the FTP command. If this parameter is set to null, then the command is sent with no argument.
1140      * @return The integer value of the FTP reply code returned by the server in response to the command.
1141      * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason
1142      *                                      causing the server to send FTP reply code 421. This exception may be caught either as an IOException or
1143      *                                      independently as itself.
1144      * @throws IOException                  If an I/O error occurs while either sending the command or receiving the server reply.
1145      * @since 3.3
1146      */
1147     public int sendCommand(final FTPCmd command, final String args) throws IOException {
1148         return sendCommand(command.getCommand(), args);
1149     }
1150 
1151     /**
1152      * Sends an FTP command with no arguments to the server, waits for a reply and returns the numerical response code. After invocation, for more detailed
1153      * information, the actual reply text can be accessed by calling {@link #getReplyString getReplyString } or {@link #getReplyStrings getReplyStrings }.
1154      *
1155      * @param command The FTPCommand constant corresponding to the FTP command to send.
1156      * @return The integer value of the FTP reply code returned by the server in response to the command.
1157      * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason
1158      *                                      causing the server to send FTP reply code 421. This exception may be caught either as an IOException or
1159      *                                      independently as itself.
1160      * @throws IOException                  If an I/O error occurs while either sending the command or receiving the server reply.
1161      */
1162     public int sendCommand(final int command) throws IOException {
1163         return sendCommand(command, null);
1164     }
1165 
1166     /**
1167      * Sends an FTP command to the server, waits for a reply and returns the numerical response code. After invocation, for more detailed information, the
1168      * actual reply text can be accessed by calling {@link #getReplyString getReplyString } or {@link #getReplyStrings getReplyStrings }.
1169      *
1170      * @param command The FTPCommand constant corresponding to the FTP command to send.
1171      * @param args    The arguments to the FTP command. If this parameter is set to null, then the command is sent with no argument.
1172      * @return The integer value of the FTP reply code returned by the server in response to the command.
1173      * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason
1174      *                                      causing the server to send FTP reply code 421. This exception may be caught either as an IOException or
1175      *                                      independently as itself.
1176      * @throws IOException                  If an I/O error occurs while either sending the command or receiving the server reply.
1177      * @deprecated (3.3) Use {@link #sendCommand(FTPCmd, String)} instead
1178      */
1179     @Deprecated
1180     public int sendCommand(final int command, final String args) throws IOException {
1181         return sendCommand(FTPCommand.getCommand(command), args);
1182     }
1183 
1184     /**
1185      * Sends an FTP command with no arguments to the server, waits for a reply and returns the numerical response code. After invocation, for more detailed
1186      * information, the actual reply text can be accessed by calling {@link #getReplyString getReplyString } or {@link #getReplyStrings getReplyStrings }.
1187      *
1188      * @param command The text representation of the FTP command to send.
1189      * @return The integer value of the FTP reply code returned by the server in response to the command.
1190      * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason
1191      *                                      causing the server to send FTP reply code 421. This exception may be caught either as an IOException or
1192      *                                      independently as itself.
1193      * @throws IOException                  If an I/O error occurs while either sending the command or receiving the server reply.
1194      */
1195     public int sendCommand(final String command) throws IOException {
1196         return sendCommand(command, null);
1197     }
1198 
1199     /**
1200      * Sends an FTP command to the server, waits for a reply and returns the numerical response code. After invocation, for more detailed information, the
1201      * actual reply text can be accessed by calling {@link #getReplyString getReplyString } or {@link #getReplyStrings getReplyStrings }.
1202      *
1203      * @param command The text representation of the FTP command to send.
1204      * @param args    The arguments to the FTP command. If this parameter is set to null, then the command is sent with no argument.
1205      * @return The integer value of the FTP reply code returned by the server in response to the command.
1206      * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason
1207      *                                      causing the server to send FTP reply code 421. This exception may be caught either as an IOException or
1208      *                                      independently as itself.
1209      * @throws IOException                  If an I/O error occurs while either sending the command or receiving the server reply.
1210      */
1211     public int sendCommand(final String command, final String args) throws IOException {
1212         if (_controlOutput_ == null) {
1213             throw new IOException("Connection is not open");
1214         }
1215 
1216         final String message = buildMessage(command, args);
1217 
1218         send(message);
1219 
1220         fireCommandSent(command, message);
1221 
1222         return getReply();
1223     }
1224 
1225     /**
1226      * Saves the character encoding to be used by the FTP control connection. Some FTP servers require that commands be issued in a non-ASCII encoding like
1227      * UTF-8 so that file names with multi-byte character representations (e.g, Big 8) can be specified.
1228      * <p>
1229      * Please note that this has to be set before the connection is established.
1230      *
1231      * @param encoding The new character encoding for the control connection.
1232      */
1233     public void setControlEncoding(final String encoding) {
1234         _controlEncoding = encoding;
1235     }
1236 
1237     /**
1238      * Set strict multiline parsing.
1239      *
1240      * @param strictMultilineParsing the setting
1241      * @since 2.0
1242      */
1243     public void setStrictMultilineParsing(final boolean strictMultilineParsing) {
1244         this.strictMultilineParsing = strictMultilineParsing;
1245     }
1246 
1247     /**
1248      * Set strict non-multiline parsing.
1249      * <p>
1250      * If true, it requires the 3-digit code be followed by space and some text. <br>
1251      * If false, only the 3-digit code is required (as was the case for versions up to 3.5)
1252      * <p>
1253      * <b>This should not be required by a well-behaved FTP server</b> <br>
1254      *
1255      * @param strictReplyParsing the setting
1256      * @since 3.6
1257      */
1258     public void setStrictReplyParsing(final boolean strictReplyParsing) {
1259         this.strictReplyParsing = strictReplyParsing;
1260     }
1261 
1262     /**
1263      * A convenience method to send the FTP SITE command to the server, receive the reply, and return the reply code.
1264      *
1265      * @param parameters The site parameters to send.
1266      * @return The reply code received from the server.
1267      * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason
1268      *                                      causing the server to send FTP reply code 421. This exception may be caught either as an IOException or
1269      *                                      independently as itself.
1270      * @throws IOException                  If an I/O error occurs while either sending the command or receiving the server reply.
1271      */
1272     public int site(final String parameters) throws IOException {
1273         return sendCommand(FTPCmd.SITE, parameters);
1274     }
1275 
1276     /**
1277      * A convenience method to send the FTP SIZE command to the server, receive the reply, and return the reply code.
1278      *
1279      * @param parameters The site parameters to send.
1280      * @return The reply code received from the server.
1281      * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason
1282      *                                      causing the server to send FTP reply code 421. This exception may be caught either as an IOException or
1283      *                                      independently as itself.
1284      * @throws IOException                  If an I/O error occurs while either sending the command or receiving the server reply.
1285      * @since 3.7
1286      */
1287     public int size(final String parameters) throws IOException {
1288         return sendCommand(FTPCmd.SIZE, parameters);
1289     }
1290 
1291     /**
1292      * A convenience method to send the FTP SMNT command to the server, receive the reply, and return the reply code.
1293      *
1294      * @param dir The directory name.
1295      * @return The reply code received from the server.
1296      * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason
1297      *                                      causing the server to send FTP reply code 421. This exception may be caught either as an IOException or
1298      *                                      independently as itself.
1299      * @throws IOException                  If an I/O error occurs while either sending the command or receiving the server reply.
1300      */
1301     public int smnt(final String dir) throws IOException {
1302         return sendCommand(FTPCmd.SMNT, dir);
1303     }
1304 
1305     /**
1306      * A convenience method to send the FTP STAT command to the server, receive the reply, and return the reply code.
1307      *
1308      * @return The reply code received from the server.
1309      * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason
1310      *                                      causing the server to send FTP reply code 421. This exception may be caught either as an IOException or
1311      *                                      independently as itself.
1312      * @throws IOException                  If an I/O error occurs while either sending the command or receiving the server reply.
1313      */
1314     public int stat() throws IOException {
1315         return sendCommand(FTPCmd.STAT);
1316     }
1317 
1318     /**
1319      * A convenience method to send the FTP STAT command to the server, receive the reply, and return the reply code.
1320      *
1321      * @param pathname A pathname to list.
1322      * @return The reply code received from the server.
1323      * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason
1324      *                                      causing the server to send FTP reply code 421. This exception may be caught either as an IOException or
1325      *                                      independently as itself.
1326      * @throws IOException                  If an I/O error occurs while either sending the command or receiving the server reply.
1327      */
1328     public int stat(final String pathname) throws IOException {
1329         return sendCommand(FTPCmd.STAT, pathname);
1330     }
1331 
1332     /**
1333      * A convenience method to send the FTP STOR command to the server, receive the reply, and return the reply code. Remember, it is up to you to manage the
1334      * data connection. If you don't need this low level of access, use {@link org.apache.commons.net.ftp.FTPClient} , which will handle all low level details
1335      * for you.
1336      *
1337      * @param pathname The pathname to use for the file when stored at the remote end of the transfer.
1338      * @return The reply code received from the server.
1339      * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason
1340      *                                      causing the server to send FTP reply code 421. This exception may be caught either as an IOException or
1341      *                                      independently as itself.
1342      * @throws IOException                  If an I/O error occurs while either sending the command or receiving the server reply.
1343      */
1344     public int stor(final String pathname) throws IOException {
1345         return sendCommand(FTPCmd.STOR, pathname);
1346     }
1347 
1348     /**
1349      * A convenience method to send the FTP STOU command to the server, receive the reply, and return the reply code. Remember, it is up to you to manage the
1350      * data connection. If you don't need this low level of access, use {@link org.apache.commons.net.ftp.FTPClient} , which will handle all low level details
1351      * for you.
1352      *
1353      * @return The reply code received from the server.
1354      * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason
1355      *                                      causing the server to send FTP reply code 421. This exception may be caught either as an IOException or
1356      *                                      independently as itself.
1357      * @throws IOException                  If an I/O error occurs while either sending the command or receiving the server reply.
1358      */
1359     public int stou() throws IOException {
1360         return sendCommand(FTPCmd.STOU);
1361     }
1362 
1363     /**
1364      * A convenience method to send the FTP STOU command to the server, receive the reply, and return the reply code. Remember, it is up to you to manage the
1365      * data connection. If you don't need this low level of access, use {@link org.apache.commons.net.ftp.FTPClient} , which will handle all low level details
1366      * for you.
1367      *
1368      * @param pathname The base pathname to use for the file when stored at the remote end of the transfer. Some FTP servers require this.
1369      * @return The reply code received from the server.
1370      * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason
1371      *                                      causing the server to send FTP reply code 421. This exception may be caught either as an IOException or
1372      *                                      independently as itself.
1373      * @throws IOException                  If an I/O error occurs while either sending the command or receiving the server reply.
1374      */
1375     public int stou(final String pathname) throws IOException {
1376         return sendCommand(FTPCmd.STOU, pathname);
1377     }
1378 
1379     // The RFC-compliant multiline termination check
1380     private boolean strictCheck(final String line, final String code) {
1381         return !(line.startsWith(code) && line.charAt(REPLY_CODE_LEN) == ' ');
1382     }
1383 
1384     /**
1385      * A convenience method to send the FTP STRU command to the server, receive the reply, and return the reply code.
1386      *
1387      * @param structure The structure of the file (one of the <code>_STRUCTURE</code> constants).
1388      * @return The reply code received from the server.
1389      * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason
1390      *                                      causing the server to send FTP reply code 421. This exception may be caught either as an IOException or
1391      *                                      independently as itself.
1392      * @throws IOException                  If an I/O error occurs while either sending the command or receiving the server reply.
1393      */
1394     public int stru(final int structure) throws IOException {
1395         return sendCommand(FTPCmd.STRU, modes.substring(structure, structure + 1));
1396     }
1397 
1398     /**
1399      * A convenience method to send the FTP SYST command to the server, receive the reply, and return the reply code.
1400      *
1401      * @return The reply code received from the server.
1402      * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason
1403      *                                      causing the server to send FTP reply code 421. This exception may be caught either as an IOException or
1404      *                                      independently as itself.
1405      * @throws IOException                  If an I/O error occurs while either sending the command or receiving the server reply.
1406      */
1407     public int syst() throws IOException {
1408         return sendCommand(FTPCmd.SYST);
1409     }
1410 
1411     /**
1412      * A convenience method to send the FTP TYPE command to the server, receive the reply, and return the reply code.
1413      *
1414      * @param fileType The type of the file (one of the <code>FILE_TYPE</code> constants).
1415      * @return The reply code received from the server.
1416      * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason
1417      *                                      causing the server to send FTP reply code 421. This exception may be caught either as an IOException or
1418      *                                      independently as itself.
1419      * @throws IOException                  If an I/O error occurs while either sending the command or receiving the server reply.
1420      */
1421     public int type(final int fileType) throws IOException {
1422         return sendCommand(FTPCmd.TYPE, modes.substring(fileType, fileType + 1));
1423     }
1424 
1425     /**
1426      * A convenience method to send the FTP TYPE command for text files to the server, receive the reply, and return the reply code.
1427      *
1428      * @param fileType         The type of the file (one of the <code>FILE_TYPE</code> constants).
1429      * @param formatOrByteSize The format of the file (one of the <code>_FORMAT</code> constants). In the case of <code>LOCAL_FILE_TYPE</code>, the byte size.
1430      * @return The reply code received from the server.
1431      * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason
1432      *                                      causing the server to send FTP reply code 421. This exception may be caught either as an IOException or
1433      *                                      independently as itself.
1434      * @throws IOException                  If an I/O error occurs while either sending the command or receiving the server reply.
1435      */
1436     public int type(final int fileType, final int formatOrByteSize) throws IOException {
1437         final StringBuilder arg = new StringBuilder();
1438 
1439         arg.append(modes.charAt(fileType));
1440         arg.append(' ');
1441         if (fileType == LOCAL_FILE_TYPE) {
1442             arg.append(formatOrByteSize);
1443         } else {
1444             arg.append(modes.charAt(formatOrByteSize));
1445         }
1446 
1447         return sendCommand(FTPCmd.TYPE, arg.toString());
1448     }
1449 
1450     /**
1451      * A convenience method to send the FTP USER command to the server, receive the reply, and return the reply code.
1452      *
1453      * @param user The user to login under.
1454      * @return The reply code received from the server.
1455      * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a result of the client being idle or some other reason
1456      *                                      causing the server to send FTP reply code 421. This exception may be caught either as an IOException or
1457      *                                      independently as itself.
1458      * @throws IOException                  If an I/O error occurs while either sending the command or receiving the server reply.
1459      */
1460     public int user(final String user) throws IOException {
1461         return sendCommand(FTPCmd.USER, user);
1462     }
1463 }