001    /*
002     * ftp4j - A pure Java FTP client library
003     * 
004     * Copyright (C) 2008 Carlo Pelliccia (www.sauronsoftware.it)
005     * 
006     * This program is free software: you can redistribute it and/or modify
007     * it under the terms of the GNU General Public License as published by
008     * the Free Software Foundation, either version 3 of the License, or
009     * (at your option) any later version.
010     *
011     * This program is distributed in the hope that it will be useful,
012     * but WITHOUT ANY WARRANTY; without even the implied warranty of
013     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
014     * GNU General Public License for more details.
015     *
016     * You should have received a copy of the GNU General Public License
017     * along with this program.  If not, see <http://www.gnu.org/licenses/>.
018     */
019    package it.sauronsoftware.ftp4j;
020    
021    import it.sauronsoftware.ftp4j.connectors.DirectConnector;
022    import it.sauronsoftware.ftp4j.extrecognizers.DefaultTextualExtensionRecognizer;
023    import it.sauronsoftware.ftp4j.extrecognizers.ParametricTextualExtensionRecognizer;
024    import it.sauronsoftware.ftp4j.listparsers.DOSListParser;
025    import it.sauronsoftware.ftp4j.listparsers.EPLFListParser;
026    import it.sauronsoftware.ftp4j.listparsers.NetWareListParser;
027    import it.sauronsoftware.ftp4j.listparsers.UnixListParser;
028    
029    import java.io.File;
030    import java.io.FileInputStream;
031    import java.io.FileNotFoundException;
032    import java.io.FileOutputStream;
033    import java.io.IOException;
034    import java.io.InputStream;
035    import java.io.InputStreamReader;
036    import java.io.OutputStream;
037    import java.io.OutputStreamWriter;
038    import java.io.Reader;
039    import java.io.Writer;
040    import java.net.InetAddress;
041    import java.text.DateFormat;
042    import java.text.ParseException;
043    import java.text.SimpleDateFormat;
044    import java.util.ArrayList;
045    import java.util.Date;
046    import java.util.Iterator;
047    import java.util.StringTokenizer;
048    import java.util.regex.Matcher;
049    import java.util.regex.Pattern;
050    
051    /**
052     * This class implements a FTP client.
053     * 
054     * You can use an instance of this class to connect to a remote FTP site and do
055     * FTP operations like directory listing, file upload and download, resume a
056     * broken upload/download and so on.
057     * 
058     * The common flow is: create the object, connect to a remote FTP site with the
059     * connect() method, authenticate with login(), do anything you need with the
060     * contents of the remote site, quit the site with disconnect().
061     * 
062     * A FTPClient object can handle a connection per time. Once you have used and
063     * disconnected a FTPClient object you can use it again to connect another FTP
064     * server.
065     * 
066     * @author Carlo Pelliccia
067     * @version 1.3
068     */
069    public class FTPClient {
070    
071            /**
072             * The constant for the AUTO file transfer type. It lets the client pick
073             * between textual and binary types, depending on the extension of the file
074             * exchanged through a textual extension recognizer.
075             */
076            public static final int TYPE_AUTO = 0;
077    
078            /**
079             * The constant for the TEXTUAL file transfer type. It means that the data
080             * sent or received is treated as textual information. This implies charset
081             * conversion during the transfer.
082             */
083            public static final int TYPE_TEXTUAL = 1;
084    
085            /**
086             * The constant for the BINARY file transfer type. It means that the data
087             * sent or received is treated as a binary stream. The data is taken "as
088             * is", without any charset conversion.
089             */
090            public static final int TYPE_BINARY = 2;
091    
092            /**
093             * The DateFormat object used to parse the reply to a MDTM command.
094             */
095            private static final DateFormat MDTM_DATE_FORMAT = new SimpleDateFormat(
096                            "yyyyMMddHHmmss");
097    
098            /**
099             * The RegExp Pattern object used to parse the reply to a PASV command.
100             */
101            private static final Pattern PASV_PATTERN = Pattern
102                            .compile("\\d{1,3},\\d{1,3},\\d{1,3},\\d{1,3},\\d{1,3},\\d{1,3}");
103    
104            /**
105             * The RegExp Pattern object used to parse the reply to a PWD command.
106             */
107            private static final Pattern PWD_PATTERN = Pattern.compile("\"/.*\"");
108    
109            /**
110             * The connector used to connect the remote host.
111             */
112            private FTPConnector connector = new DirectConnector();
113    
114            /**
115             * The FTPCommunicationListener objects registered on the client.
116             */
117            private ArrayList communicationListeners = new ArrayList();
118    
119            /**
120             * The FTPListParser objects registered on the client.
121             */
122            private ArrayList listParsers = new ArrayList();
123    
124            /**
125             * The textual extension recognizer used by the client.
126             */
127            private FTPTextualExtensionRecognizer textualExtensionRecognizer = DefaultTextualExtensionRecognizer
128                            .getInstance();
129    
130            /**
131             * The FTPListParser used successfully during previous connection-scope list
132             * operations.
133             */
134            private FTPListParser parser = null;
135    
136            /**
137             * If the client is connected, it reports the remote host name or address.
138             */
139            private String host = null;
140    
141            /**
142             * If the client is connected, it reports the remote port number.
143             */
144            private int port = 0;
145    
146            /**
147             * If the client is authenticated, it reports the authentication username.
148             */
149            private String username;
150    
151            /**
152             * If the client is authenticated, it reports the authentication password.
153             */
154            private String password;
155    
156            /**
157             * The flag reporting the connection status.
158             */
159            private boolean connected = false;
160    
161            /**
162             * The flag reporting the authentication status.
163             */
164            private boolean authenticated = false;
165    
166            /**
167             * The flag for the passive FTP data transfer mode. Default value is true,
168             * cause it's usually the preferred FTP operating mode.
169             */
170            private boolean passive = true;
171    
172            /**
173             * The type of the data transfer contents (auto, textual, binary). The value
174             * should be one of TYPE_AUTO, TYPE_TEXTUAL and TYPE_BINARY constants.
175             * Default value is TYPE_AUTO.
176             */
177            private int type = TYPE_AUTO;
178    
179            /**
180             * The name of the charset used to establish textual communications. If not
181             * null the client will use always the given charset. If null the client
182             * tries to auto-detect the server charset. If this attempt fails the client
183             * will use the machine current charset.
184             */
185            private String charset = null;
186    
187            /**
188             * A flag used to mark whether the connected server supports UTF-8 pathnames
189             * encoding.
190             */
191            private boolean utf8Supported = false;
192    
193            /**
194             * This flag reports if there's any ongoing abortable data transfer
195             * operation. Its value should be accessed only under the eye of the
196             * abortLock synchronization object.
197             */
198            private boolean ongoingDataTransfer = false;
199    
200            /**
201             * The InputStream used for data transfer operations.
202             */
203            private InputStream dataTransferInputStream = null;
204    
205            /**
206             * The OutputStream used for data transfer operations.
207             */
208            private OutputStream dataTransferOutputStream = null;
209    
210            /**
211             * This flag turns to true when any data transfer stream is closed due to an
212             * abort suggestion.
213             */
214            private boolean aborted = false;
215    
216            /**
217             * Lock object used for synchronization.
218             */
219            private Object lock = new Object();
220    
221            /**
222             * Lock object used for synchronization in abort operations.
223             */
224            private Object abortLock = new Object();
225    
226            /**
227             * The communication channel established with the server.
228             */
229            private FTPCommunicationChannel communication = null;
230    
231            /**
232             * Builds and initializes the client.
233             */
234            public FTPClient() {
235                    // The built-in parsers.
236                    addListParser(new UnixListParser());
237                    addListParser(new DOSListParser());
238                    addListParser(new EPLFListParser());
239                    addListParser(new NetWareListParser());
240            }
241    
242            /**
243             * This method returns the connector used to connect the remote host.
244             * 
245             * @return The connector used to connect the remote host.
246             */
247            public FTPConnector getConnector() {
248                    return connector;
249            }
250    
251            /**
252             * This method sets the connector used to connect the remote host.
253             * 
254             * Default one is a
255             * it.sauronsoftware.ftp4j.connectors.direct.DirectConnector instance.
256             * 
257             * @param connector
258             *            The connector used to connect the remote host.
259             * @see DirectConnector
260             */
261            public void setConnector(FTPConnector connector) {
262                    this.connector = connector;
263            }
264    
265            /**
266             * This method enables/disables the use of the passive mode.
267             * 
268             * @param passive
269             *            If true the passive mode is enabled.
270             */
271            public void setPassive(boolean passive) {
272                    synchronized (lock) {
273                            this.passive = passive;
274                    }
275            }
276    
277            /**
278             * This methods sets how to treat the contents during a file transfer.
279             * 
280             * The type supplied should be one of TYPE_AUTO, TYPE_TEXTUAL or TYPE_BINARY
281             * constants. Default value is TYPE_AUTO.
282             * 
283             * TYPE_TEXTUAL means that the data sent or received is treated as textual
284             * information. This implies charset conversion during the transfer.
285             * 
286             * TYPE_BINARY means that the data sent or received is treated as a binary
287             * stream. The data is taken "as is", without any charset conversion.
288             * 
289             * TYPE_AUTO lets the client pick between textual and binary types,
290             * depending on the extension of the file exchanged, using a
291             * FTPTextualExtensionRecognizer instance, which could be set through the
292             * setTextualExtensionRecognizer method. The default recognizer is an
293             * instance of {@link DefaultTextualExtensionRecognizer}.
294             * 
295             * @param type
296             *            The type.
297             * @throws IllegalArgumentException
298             *             If the supplied type is not valid.
299             * @see FTPClient#setTextualExtensionRecognizer(FTPTextualExtensionRecognizer)
300             * @see DefaultTextualExtensionRecognizer
301             */
302            public void setType(int type) throws IllegalArgumentException {
303                    if (type != TYPE_AUTO && type != TYPE_BINARY && type != TYPE_TEXTUAL) {
304                            throw new IllegalArgumentException("Invalid type");
305                    }
306                    synchronized (lock) {
307                            this.type = type;
308                    }
309            }
310    
311            /**
312             * This method returns the value suggesting how the client encode and decode
313             * the contents during a data transfer.
314             * 
315             * @return The type as a numeric value. The value could be compared to the
316             *         constants TYPE_AUTO, TYPE_BINARY and TYPE_TEXTUAL.
317             */
318            public int getType() {
319                    synchronized (lock) {
320                            return type;
321                    }
322            }
323    
324            /**
325             * Returns the name of the charset used to establish textual communications.
326             * If not null the client will use always the given charset. If null the
327             * client tries to auto-detect the server charset. If this attempt fails the
328             * client will use the machine current charset.
329             * 
330             * @return The name of the charset used to establish textual communications.
331             * @since 1.1
332             */
333            public String getCharset() {
334                    synchronized (lock) {
335                            return charset;
336                    }
337            }
338    
339            /**
340             * Sets the name of the charset used to establish textual communications. If
341             * not null the client will use always the given charset. If null the client
342             * tries to auto-detect the server charset. If this attempt fails the client
343             * will use the machine current charset.
344             * 
345             * @param charset
346             *            The name of the charset used to establish textual
347             *            communications.
348             * @since 1.1
349             */
350            public void setCharset(String charset) {
351                    synchronized (lock) {
352                            this.charset = charset;
353                            if (connected) {
354                                    try {
355                                            communication.changeCharset(pickCharset());
356                                    } catch (IOException e) {
357                                            e.printStackTrace();
358                                    }
359                            }
360                    }
361            }
362    
363            /**
364             * This method returns the textual extension recognizer used by the client.
365             * 
366             * Default one is {@link DefaultTextualExtensionRecognizer}.
367             * 
368             * @return The textual extension recognizer used by the client.
369             * @see DefaultTextualExtensionRecognizer
370             */
371            public FTPTextualExtensionRecognizer getTextualExtensionRecognizer() {
372                    synchronized (lock) {
373                            return textualExtensionRecognizer;
374                    }
375            }
376    
377            /**
378             * This method sets the textual extension recognizer used by the client.
379             * 
380             * The default one is {@link DefaultTextualExtensionRecognizer}.
381             * 
382             * You can plug your own by implementing the
383             * {@link FTPTextualExtensionRecognizer} interface. For your convenience the
384             * ftp4j gives you another FTPTextualExtensionRecognizer implementation,
385             * which is {@link ParametricTextualExtensionRecognizer}.
386             * 
387             * @param textualExtensionRecognizer
388             *            The textual extension recognizer used by the client.
389             * @see DefaultTextualExtensionRecognizer
390             * @see ParametricTextualExtensionRecognizer
391             */
392            public void setTextualExtensionRecognizer(
393                            FTPTextualExtensionRecognizer textualExtensionRecognizer) {
394                    synchronized (lock) {
395                            this.textualExtensionRecognizer = textualExtensionRecognizer;
396                    }
397            }
398    
399            /**
400             * This method tests if this client is authenticated.
401             * 
402             * @return true if this client is authenticated, false otherwise.
403             */
404            public boolean isAuthenticated() {
405                    synchronized (lock) {
406                            return authenticated;
407                    }
408            }
409    
410            /**
411             * This method tests if this client is connected to a remote FTP server.
412             * 
413             * @return true if this client is connected to a remote FTP server, false
414             *         otherwise.
415             */
416            public boolean isConnected() {
417                    synchronized (lock) {
418                            return connected;
419                    }
420            }
421    
422            /**
423             * This method tests if this client works in passive FTP mode.
424             * 
425             * @return true if this client is configured to work in passive FTP mode.
426             */
427            public boolean isPassive() {
428                    synchronized (lock) {
429                            return passive;
430                    }
431            }
432    
433            /**
434             * If the client is connected, it reports the remote host name or address.
435             * 
436             * @return The remote host name or address.
437             */
438            public String getHost() {
439                    synchronized (lock) {
440                            return host;
441                    }
442            }
443    
444            /**
445             * If the client is connected, it reports the remote port number.
446             * 
447             * @return The remote port number.
448             */
449            public int getPort() {
450                    synchronized (lock) {
451                            return port;
452                    }
453            }
454    
455            /**
456             * If the client is authenticated, it reports the authentication password.
457             * 
458             * @return The authentication password.
459             */
460            public String getPassword() {
461                    synchronized (lock) {
462                            return password;
463                    }
464            }
465    
466            /**
467             * If the client is authenticated, it reports the authentication username.
468             * 
469             * @return The authentication username.
470             */
471            public String getUsername() {
472                    synchronized (lock) {
473                            return username;
474                    }
475            }
476    
477            /**
478             * This method adds a FTPCommunicationListener to the object.
479             * 
480             * @param listener
481             *            The listener.
482             */
483            public void addCommunicationListener(FTPCommunicationListener listener) {
484                    synchronized (lock) {
485                            communicationListeners.add(listener);
486                            if (communication != null) {
487                                    communication.addCommunicationListener(listener);
488                            }
489                    }
490            }
491    
492            /**
493             * This method removes a FTPCommunicationListener previously added to the
494             * object.
495             * 
496             * @param listener
497             *            The listener to be removed.
498             */
499            public void removeCommunicationListener(FTPCommunicationListener listener) {
500                    synchronized (lock) {
501                            communicationListeners.remove(listener);
502                            if (communication != null) {
503                                    communication.removeCommunicationListener(listener);
504                            }
505                    }
506            }
507    
508            /**
509             * This method returns a list with all the {@link FTPCommunicationListener}
510             * used by the client.
511             * 
512             * @return A list with all the FTPCommunicationListener used by the client.
513             */
514            public FTPCommunicationListener[] getCommunicationListeners() {
515                    synchronized (lock) {
516                            int size = communicationListeners.size();
517                            FTPCommunicationListener[] ret = new FTPCommunicationListener[size];
518                            for (int i = 0; i < size; i++) {
519                                    ret[i] = (FTPCommunicationListener) communicationListeners
520                                                    .get(i);
521                            }
522                            return ret;
523                    }
524            }
525    
526            /**
527             * This method adds a {@link FTPListParser} to the object.
528             * 
529             * @param listParser
530             *            The list parser.
531             */
532            public void addListParser(FTPListParser listParser) {
533                    synchronized (lock) {
534                            listParsers.add(listParser);
535                    }
536            }
537    
538            /**
539             * This method removes a {@link FTPListParser} previously added to the
540             * object.
541             * 
542             * @param listParser
543             *            The list parser to be removed.
544             */
545            public void removeListParser(FTPListParser listParser) {
546                    synchronized (lock) {
547                            listParsers.remove(listParser);
548                    }
549            }
550    
551            /**
552             * This method returns a list with all the {@link FTPListParser} used by the
553             * client.
554             * 
555             * @return A list with all the FTPListParsers used by the client.
556             */
557            public FTPListParser[] getListParsers() {
558                    synchronized (lock) {
559                            int size = listParsers.size();
560                            FTPListParser[] ret = new FTPListParser[size];
561                            for (int i = 0; i < size; i++) {
562                                    ret[i] = (FTPListParser) listParsers.get(i);
563                            }
564                            return ret;
565                    }
566            }
567    
568            /**
569             * This method connects the client to the remote FTP host, using the default
570             * port value 21.
571             * 
572             * @param host
573             *            The hostname of the remote server.
574             * @return The server welcome message, one line per array element.
575             * @throws IllegalStateException
576             *             If the client is already connected to a remote host.
577             * @throws IOException
578             *             If an I/O occurs.
579             * @throws FTPIllegalReplyException
580             *             If the server replies in an illegal way.
581             * @throws FTPException
582             *             If the server refuses the connection.
583             */
584            public String[] connect(String host) throws IllegalStateException,
585                            IOException, FTPIllegalReplyException, FTPException {
586                    return connect(host, 21);
587            }
588    
589            /**
590             * This method connects the client to the remote FTP host.
591             * 
592             * @param host
593             *            The host name or address of the remote server.
594             * @param port
595             *            The port listened by the remote server.
596             * @return The server welcome message, one line per array element.
597             * @throws IllegalStateException
598             *             If the client is already connected to a remote host.
599             * @throws IOException
600             *             If an I/O occurs.
601             * @throws FTPIllegalReplyException
602             *             If the server replies in an illegal way.
603             * @throws FTPException
604             *             If the server refuses the connection.
605             */
606            public String[] connect(String host, int port)
607                            throws IllegalStateException, IOException,
608                            FTPIllegalReplyException, FTPException {
609                    synchronized (lock) {
610                            // Is this client already connected to any host?
611                            if (connected) {
612                                    throw new IllegalStateException("Client already connected to "
613                                                    + host + " on port " + port);
614                            }
615                            // Ok, it's connection time. Let's try!
616                            FTPConnection connection = null;
617                            try {
618                                    // Open the connection.
619                                    connection = connector.connectForCommunicationChannel(host,
620                                                    port);
621                                    // Open the communication channel.
622                                    communication = new FTPCommunicationChannel(connection,
623                                                    pickCharset());
624                                    for (Iterator i = communicationListeners.iterator(); i
625                                                    .hasNext();) {
626                                            communication
627                                                            .addCommunicationListener((FTPCommunicationListener) i
628                                                                            .next());
629                                    }
630                                    // Welcome message.
631                                    FTPReply wm = communication.readFTPReply();
632                                    // Does this reply mean "ok"?
633                                    if (!wm.isSuccessCode()) {
634                                            // Mmmmm... it seems no!
635                                            throw new FTPException(wm);
636                                    }
637                                    // Flag this object as connected to the remote host.
638                                    connected = true;
639                                    authenticated = false;
640                                    parser = null;
641                                    this.host = host;
642                                    this.port = port;
643                                    this.username = null;
644                                    this.password = null;
645                                    this.utf8Supported = false;
646                                    return wm.getMessages();
647                            } catch (IOException e) {
648                                    // D'oh!
649                                    throw e;
650                            } finally {
651                                    // If connection has failed...
652                                    if (!connected) {
653                                            if (connection != null) {
654                                                    // Close the connection, 'cause it should be open.
655                                                    try {
656                                                            connection.close();
657                                                    } catch (Throwable t) {
658                                                            ;
659                                                    }
660                                            }
661                                    }
662                            }
663                    }
664            }
665    
666            /**
667             * This method disconnects from the remote server, optionally performing the
668             * QUIT procedure.
669             * 
670             * @param sendQuitCommand
671             *            If true the QUIT procedure with the server will be performed,
672             *            otherwise the connection is abruptly closed by the client
673             *            without sending any advice to the server.
674             * @throws IllegalStateException
675             *             If the client is not connected to a remote host.
676             * @throws IOException
677             *             If an I/O occurs (can be thrown only if sendQuitCommand is
678             *             true).
679             * @throws FTPIllegalReplyException
680             *             If the server replies in an illegal way (can be thrown only
681             *             if sendQuitCommand is true).
682             * @throws FTPException
683             *             If the server refuses the QUIT command (can be thrown only if
684             *             sendQuitCommand is true).
685             */
686            public void disconnect(boolean sendQuitCommand)
687                            throws IllegalStateException, IOException,
688                            FTPIllegalReplyException, FTPException {
689                    synchronized (lock) {
690                            // Is this client connected?
691                            if (!connected) {
692                                    throw new IllegalStateException("Client not connected");
693                            }
694                            if (sendQuitCommand) {
695                                    // Call the QUIT command.
696                                    communication.sendFTPCommand("QUIT");
697                                    FTPReply r = communication.readFTPReply();
698                                    if (!r.isSuccessCode()) {
699                                            throw new FTPException(r);
700                                    }
701                            }
702                            // Close the communication.
703                            communication.close();
704                            communication = null;
705                            // Reset the connection flag.
706                            connected = false;
707                    }
708            }
709    
710            /**
711             * This method causes the communication channel to be abruptly closed. Use
712             * it carefully, since this one is not thread-safe. It is given as an
713             * "emergency brake" to close the control connection when it is blocked. A
714             * thread-safe solution for the same purpose is a call to disconnect(false).
715             * 
716             * @see FTPClient#disconnect(boolean)
717             */
718            public void abruptlyCloseCommunication() {
719                    // Close the communication.
720                    communication.close();
721                    communication = null;
722                    // Reset the connection flag.
723                    connected = false;
724            }
725    
726            /**
727             * This method authenticates the user against the server.
728             * 
729             * @param username
730             *            The username.
731             * @param password
732             *            The password (if none set it to null).
733             * @throws IllegalStateException
734             *             If the client is not connected. Call the connect() method
735             *             before!
736             * @throws IOException
737             *             If an I/O error occurs.
738             * @throws FTPIllegalReplyException
739             *             If the server replies in an illegal way.
740             * @throws FTPException
741             *             If login fails.
742             */
743            public void login(String username, String password)
744                            throws IllegalStateException, IOException,
745                            FTPIllegalReplyException, FTPException {
746                    login(username, password, null);
747            }
748    
749            /**
750             * This method authenticates the user against the server.
751             * 
752             * @param username
753             *            The username.
754             * @param password
755             *            The password (if none set it to null).
756             * @param account
757             *            The account (if none set it to null). Be careful: some servers
758             *            don't implement this feature.
759             * @throws IllegalStateException
760             *             If the client is not connected. Call the connect() method
761             *             before!
762             * @throws IOException
763             *             If an I/O error occurs.
764             * @throws FTPIllegalReplyException
765             *             If the server replies in an illegal way.
766             * @throws FTPException
767             *             If login fails.
768             */
769            public void login(String username, String password, String account)
770                            throws IllegalStateException, IOException,
771                            FTPIllegalReplyException, FTPException {
772                    synchronized (lock) {
773                            // Is this client connected?
774                            if (!connected) {
775                                    throw new IllegalStateException("Client not connected");
776                            }
777                            // Reset the authentication flag.
778                            authenticated = false;
779                            // Usefull flags.
780                            boolean passwordRequired;
781                            boolean accountRequired;
782                            // Send the user and read the reply.
783                            communication.sendFTPCommand("USER " + username);
784                            FTPReply r = communication.readFTPReply();
785                            switch (r.getCode()) {
786                            case 230:
787                                    // Password and account aren't required.
788                                    passwordRequired = false;
789                                    accountRequired = false;
790                                    break;
791                            case 331:
792                                    // Password is required.
793                                    passwordRequired = true;
794                                    // Account... maybe! More information later...
795                                    accountRequired = false;
796                                    break;
797                            case 332:
798                                    // Password is not required, but account is required.
799                                    passwordRequired = false;
800                                    accountRequired = true;
801                            default:
802                                    // User validation failed.
803                                    throw new FTPException(r);
804                            }
805                            // Password.
806                            if (passwordRequired) {
807                                    if (password == null) {
808                                            throw new FTPException(331);
809                                    }
810                                    // Send the password.
811                                    communication.sendFTPCommand("PASS " + password);
812                                    r = communication.readFTPReply();
813                                    switch (r.getCode()) {
814                                    case 230:
815                                            // Account is not required.
816                                            accountRequired = false;
817                                            break;
818                                    case 332:
819                                            // Account is required.
820                                            accountRequired = true;
821                                            break;
822                                    default:
823                                            // Authentication failed.
824                                            throw new FTPException(r);
825                                    }
826                            }
827                            // Account.
828                            if (accountRequired) {
829                                    if (account == null) {
830                                            throw new FTPException(332);
831                                    }
832                                    // Send the account.
833                                    communication.sendFTPCommand("ACCT " + account);
834                                    r = communication.readFTPReply();
835                                    switch (r.getCode()) {
836                                    case 230:
837                                            // Well done!
838                                            break;
839                                    default:
840                                            // Something goes wrong.
841                                            throw new FTPException(r);
842                                    }
843                            }
844                            // Well, if this point is reached the client could consider itself
845                            // as authenticated.
846                            this.authenticated = true;
847                            this.username = username;
848                            this.password = password;
849                    }
850                    // Post-login operations.
851                    postLoginOperations();
852            }
853    
854            /**
855             * Performs some post-login operations, such trying to detect server support
856             * for utf8.
857             * 
858             * @throws IllegalStateException
859             *             If the client is not connected. Call the connect() method
860             *             before!
861             * @throws IOException
862             *             If an I/O error occurs.
863             * @throws FTPIllegalReplyException
864             *             If the server replies in an illegal way.
865             * @throws FTPException
866             *             If login fails.
867             */
868            private void postLoginOperations() throws IllegalStateException,
869                            IOException, FTPIllegalReplyException, FTPException {
870                    synchronized (lock) {
871                            utf8Supported = false;
872                            communication.sendFTPCommand("FEAT");
873                            FTPReply r = communication.readFTPReply();
874                            if (r.getCode() == 211) {
875                                    String[] lines = r.getMessages();
876                                    for (int i = 1; i < lines.length - 1; i++) {
877                                            String feat = lines[i].trim();
878                                            // UTF8 supported?
879                                            if ("UTF8".equalsIgnoreCase(feat)) {
880                                                    utf8Supported = true;
881                                                    communication.changeCharset("UTF-8");
882                                            }
883                                    }
884                            }
885                    }
886            }
887    
888            /**
889             * This method performs a logout operation for the current user, leaving the
890             * connection open, thus it can be used to start a new user session. Be
891             * careful with this: some FTP servers don't implement this feature, even
892             * though it is a standard FTP one.
893             * 
894             * @throws IllegalStateException
895             *             If the client is not connected or not authenticated.
896             * @throws IOException
897             *             If an I/O error occurs.
898             * @throws FTPIllegalReplyException
899             *             If the server replies in an illegal way.
900             * @throws FTPException
901             *             If the operation fails.
902             */
903            public void logout() throws IllegalStateException, IOException,
904                            FTPIllegalReplyException, FTPException {
905                    synchronized (lock) {
906                            // Is this client connected?
907                            if (!connected) {
908                                    throw new IllegalStateException("Client not connected");
909                            }
910                            // Is this client authenticated?
911                            if (!authenticated) {
912                                    throw new IllegalStateException("Client not authenticated");
913                            }
914                            // Send the REIN command.
915                            communication.sendFTPCommand("REIN");
916                            FTPReply r = communication.readFTPReply();
917                            if (!r.isSuccessCode()) {
918                                    throw new FTPException(r);
919                            } else {
920                                    // Ok. Not authenticated, now.
921                                    authenticated = false;
922                                    username = null;
923                                    password = null;
924                            }
925                    }
926            }
927    
928            /**
929             * This method performs a "noop" operation with the server.
930             * 
931             * @throws IllegalStateException
932             *             If the client is not connected or not authenticated.
933             * @throws IOException
934             *             If an I/O error occurs.
935             * @throws FTPIllegalReplyException
936             *             If the server replies in an illegal way.
937             * @throws FTPException
938             *             If login fails.
939             */
940            public void noop() throws IllegalStateException, IOException,
941                            FTPIllegalReplyException, FTPException {
942                    synchronized (lock) {
943                            // Is this client connected?
944                            if (!connected) {
945                                    throw new IllegalStateException("Client not connected");
946                            }
947                            // Is this client authenticated?
948                            if (!authenticated) {
949                                    throw new IllegalStateException("Client not authenticated");
950                            }
951                            // Send the noop.
952                            communication.sendFTPCommand("NOOP");
953                            FTPReply r = communication.readFTPReply();
954                            if (!r.isSuccessCode()) {
955                                    throw new FTPException(r);
956                            }
957                    }
958            }
959    
960            /**
961             * This method sends a custom command to the server. Don't use this method
962             * to send standard commands already supported by the client: this should
963             * cause unexpected results.
964             * 
965             * @param command
966             *            The command line.
967             * @return The reply supplied by the server, parsed and served in an object
968             *         way mode.
969             * @throws IllegalStateException
970             *             If this client is not connected.
971             * @throws IOException
972             *             If a I/O error occurs.
973             * @throws FTPIllegalReplyException
974             *             If the server replies in an illegal way.
975             */
976            public FTPReply sendCustomCommand(String command)
977                            throws IllegalStateException, IOException, FTPIllegalReplyException {
978                    synchronized (lock) {
979                            // Is this client connected?
980                            if (!connected) {
981                                    throw new IllegalStateException("Client not connected");
982                            }
983                            // Send the command and return the reply.
984                            communication.sendFTPCommand(command);
985                            return communication.readFTPReply();
986                    }
987            }
988    
989            /**
990             * This method sends a SITE specific command to the server.
991             * 
992             * @param command
993             *            The site command.
994             * @return The reply supplied by the server, parsed and served in an object
995             *         way mode.
996             * @throws IllegalStateException
997             *             If this client is not connected.
998             * @throws IOException
999             *             If a I/O error occurs.
1000             * @throws FTPIllegalReplyException
1001             *             If the server replies in an illegal way.
1002             */
1003            public FTPReply sendSiteCommand(String command)
1004                            throws IllegalStateException, IOException, FTPIllegalReplyException {
1005                    synchronized (lock) {
1006                            // Is this client connected?
1007                            if (!connected) {
1008                                    throw new IllegalStateException("Client not connected");
1009                            }
1010                            // Send the command and return the reply.
1011                            communication.sendFTPCommand("SITE " + command);
1012                            return communication.readFTPReply();
1013                    }
1014            }
1015    
1016            /**
1017             * Call this method to switch the user current account. Be careful with
1018             * this: some FTP servers don't implement this feature, even though it is a
1019             * standard FTP one.
1020             * 
1021             * @param account
1022             *            The account.
1023             * @throws IllegalStateException
1024             *             If the client is not connected or not authenticated.
1025             * @throws IOException
1026             *             If an I/O error occurs.
1027             * @throws FTPIllegalReplyException
1028             *             If the server replies in an illegal way.
1029             * @throws FTPException
1030             *             If login fails.
1031             */
1032            public void changeAccount(String account) throws IllegalStateException,
1033                            IOException, FTPIllegalReplyException, FTPException {
1034                    synchronized (lock) {
1035                            // Is this client connected?
1036                            if (!connected) {
1037                                    throw new IllegalStateException("Client not connected");
1038                            }
1039                            // Is this client authenticated?
1040                            if (!authenticated) {
1041                                    throw new IllegalStateException("Client not authenticated");
1042                            }
1043                            // Send the ACCT command.
1044                            communication.sendFTPCommand("ACCT " + account);
1045                            FTPReply r = communication.readFTPReply();
1046                            if (!r.isSuccessCode()) {
1047                                    throw new FTPException(r);
1048                            }
1049                    }
1050            }
1051    
1052            /**
1053             * This method asks and returns the current working directory.
1054             * 
1055             * @return path The path to the current working directory.
1056             * @throws IllegalStateException
1057             *             If the client is not connected or not authenticated.
1058             * @throws IOException
1059             *             If an I/O error occurs.
1060             * @throws FTPIllegalReplyException
1061             *             If the server replies in an illegal way.
1062             * @throws FTPException
1063             *             If the operation fails.
1064             */
1065            public String currentDirectory() throws IllegalStateException, IOException,
1066                            FTPIllegalReplyException, FTPException {
1067                    synchronized (lock) {
1068                            // Is this client connected?
1069                            if (!connected) {
1070                                    throw new IllegalStateException("Client not connected");
1071                            }
1072                            // Is this client authenticated?
1073                            if (!authenticated) {
1074                                    throw new IllegalStateException("Client not authenticated");
1075                            }
1076                            // Send the PWD command.
1077                            communication.sendFTPCommand("PWD");
1078                            FTPReply r = communication.readFTPReply();
1079                            if (!r.isSuccessCode()) {
1080                                    throw new FTPException(r);
1081                            }
1082                            // Parse the response.
1083                            String[] messages = r.getMessages();
1084                            if (messages.length != 1) {
1085                                    throw new FTPIllegalReplyException();
1086                            }
1087                            Matcher m = PWD_PATTERN.matcher(messages[0]);
1088                            if (m.find()) {
1089                                    return messages[0].substring(m.start() + 1, m.end() - 1);
1090                            } else {
1091                                    throw new FTPIllegalReplyException();
1092                            }
1093                    }
1094            }
1095    
1096            /**
1097             * This method changes the current working directory.
1098             * 
1099             * @param path
1100             *            The path to the new working directory.
1101             * @throws IllegalStateException
1102             *             If the client is not connected or not authenticated.
1103             * @throws IOException
1104             *             If an I/O error occurs.
1105             * @throws FTPIllegalReplyException
1106             *             If the server replies in an illegal way.
1107             * @throws FTPException
1108             *             If the operation fails.
1109             */
1110            public void changeDirectory(String path) throws IllegalStateException,
1111                            IOException, FTPIllegalReplyException, FTPException {
1112                    synchronized (lock) {
1113                            // Is this client connected?
1114                            if (!connected) {
1115                                    throw new IllegalStateException("Client not connected");
1116                            }
1117                            // Is this client authenticated?
1118                            if (!authenticated) {
1119                                    throw new IllegalStateException("Client not authenticated");
1120                            }
1121                            // Send the CWD command.
1122                            communication.sendFTPCommand("CWD " + path);
1123                            FTPReply r = communication.readFTPReply();
1124                            if (!r.isSuccessCode()) {
1125                                    throw new FTPException(r);
1126                            }
1127                    }
1128            }
1129    
1130            /**
1131             * This method changes the current working directory to the parent one.
1132             * 
1133             * @throws IllegalStateException
1134             *             If the client is not connected or not authenticated.
1135             * @throws IOException
1136             *             If an I/O error occurs.
1137             * @throws FTPIllegalReplyException
1138             *             If the server replies in an illegal way.
1139             * @throws FTPException
1140             *             If the operation fails.
1141             */
1142            public void changeDirectoryUp() throws IllegalStateException, IOException,
1143                            FTPIllegalReplyException, FTPException {
1144                    synchronized (lock) {
1145                            // Is this client connected?
1146                            if (!connected) {
1147                                    throw new IllegalStateException("Client not connected");
1148                            }
1149                            // Is this client authenticated?
1150                            if (!authenticated) {
1151                                    throw new IllegalStateException("Client not authenticated");
1152                            }
1153                            // Send the CWD command.
1154                            communication.sendFTPCommand("CDUP");
1155                            FTPReply r = communication.readFTPReply();
1156                            if (!r.isSuccessCode()) {
1157                                    throw new FTPException(r);
1158                            }
1159                    }
1160            }
1161    
1162            /**
1163             * This method asks and returns the last modification date of a file or
1164             * directory.
1165             * 
1166             * @param path
1167             *            The path to the file or the directory.
1168             * @return The file/directory last modification date.
1169             * @throws IllegalStateException
1170             *             If the client is not connected or not authenticated.
1171             * @throws IOException
1172             *             If an I/O error occurs.
1173             * @throws FTPIllegalReplyException
1174             *             If the server replies in an illegal way.
1175             * @throws FTPException
1176             *             If the operation fails.
1177             */
1178            public Date modifiedDate(String path) throws IllegalStateException,
1179                            IOException, FTPIllegalReplyException, FTPException {
1180                    synchronized (lock) {
1181                            // Is this client connected?
1182                            if (!connected) {
1183                                    throw new IllegalStateException("Client not connected");
1184                            }
1185                            // Is this client authenticated?
1186                            if (!authenticated) {
1187                                    throw new IllegalStateException("Client not authenticated");
1188                            }
1189                            // Send the MDTM command.
1190                            communication.sendFTPCommand("MDTM " + path);
1191                            FTPReply r = communication.readFTPReply();
1192                            if (!r.isSuccessCode()) {
1193                                    throw new FTPException(r);
1194                            }
1195                            String[] messages = r.getMessages();
1196                            if (messages.length != 1) {
1197                                    throw new FTPIllegalReplyException();
1198                            } else {
1199                                    try {
1200                                            return MDTM_DATE_FORMAT.parse(messages[0]);
1201                                    } catch (ParseException e) {
1202                                            throw new FTPIllegalReplyException();
1203                                    }
1204                            }
1205                    }
1206            }
1207    
1208            /**
1209             * This method asks and returns a file size in bytes.
1210             * 
1211             * @param path
1212             *            The path to the file.
1213             * @return The file size in bytes.
1214             * @throws IllegalStateException
1215             *             If the client is not connected or not authenticated.
1216             * @throws IOException
1217             *             If an I/O error occurs.
1218             * @throws FTPIllegalReplyException
1219             *             If the server replies in an illegal way.
1220             * @throws FTPException
1221             *             If the operation fails.
1222             */
1223            public long fileSize(String path) throws IllegalStateException,
1224                            IOException, FTPIllegalReplyException, FTPException {
1225                    synchronized (lock) {
1226                            // Is this client connected?
1227                            if (!connected) {
1228                                    throw new IllegalStateException("Client not connected");
1229                            }
1230                            // Is this client authenticated?
1231                            if (!authenticated) {
1232                                    throw new IllegalStateException("Client not authenticated");
1233                            }
1234                            // Send the SIZE command.
1235                            communication.sendFTPCommand("SIZE " + path);
1236                            FTPReply r = communication.readFTPReply();
1237                            if (!r.isSuccessCode()) {
1238                                    throw new FTPException(r);
1239                            }
1240                            String[] messages = r.getMessages();
1241                            if (messages.length != 1) {
1242                                    throw new FTPIllegalReplyException();
1243                            } else {
1244                                    try {
1245                                            return Long.parseLong(messages[0]);
1246                                    } catch (Throwable t) {
1247                                            throw new FTPIllegalReplyException();
1248                                    }
1249                            }
1250                    }
1251            }
1252    
1253            /**
1254             * This method renames a remote file or directory. It can also be used to
1255             * move a file or a directory.
1256             * 
1257             * In example:
1258             * 
1259             * <pre>
1260             * client.rename("oldname", "newname"); // This one renames
1261             * </pre>
1262             * 
1263             * <pre>
1264             * client.rename("the/old/path/oldname", "/a/new/path/newname"); // This one moves
1265             * </pre>
1266             * 
1267             * @param oldPath
1268             *            The current path of the file (or directory).
1269             * @param newPath
1270             *            The new path for the file (or directory).
1271             * @throws IllegalStateException
1272             *             If the client is not connected or not authenticated.
1273             * @throws IOException
1274             *             If an I/O error occurs.
1275             * @throws FTPIllegalReplyException
1276             *             If the server replies in an illegal way.
1277             * @throws FTPException
1278             *             If the operation fails.
1279             */
1280            public void rename(String oldPath, String newPath)
1281                            throws IllegalStateException, IOException,
1282                            FTPIllegalReplyException, FTPException {
1283                    synchronized (lock) {
1284                            // Is this client connected?
1285                            if (!connected) {
1286                                    throw new IllegalStateException("Client not connected");
1287                            }
1288                            // Is this client authenticated?
1289                            if (!authenticated) {
1290                                    throw new IllegalStateException("Client not authenticated");
1291                            }
1292                            // Send the RNFR command.
1293                            communication.sendFTPCommand("RNFR " + oldPath);
1294                            FTPReply r = communication.readFTPReply();
1295                            if (r.getCode() != 350) {
1296                                    throw new FTPException(r);
1297                            }
1298                            // Send the RNFR command.
1299                            communication.sendFTPCommand("RNTO " + newPath);
1300                            r = communication.readFTPReply();
1301                            if (!r.isSuccessCode()) {
1302                                    throw new FTPException(r);
1303                            }
1304                    }
1305            }
1306    
1307            /**
1308             * This method deletes a remote file.
1309             * 
1310             * @param path
1311             *            The path to the file.
1312             * @throws IllegalStateException
1313             *             If the client is not connected or not authenticated.
1314             * @throws IOException
1315             *             If an I/O error occurs.
1316             * @throws FTPIllegalReplyException
1317             *             If the server replies in an illegal way.
1318             * @throws FTPException
1319             *             If the operation fails.
1320             */
1321            public void deleteFile(String path) throws IllegalStateException,
1322                            IOException, FTPIllegalReplyException, FTPException {
1323                    synchronized (lock) {
1324                            // Is this client connected?
1325                            if (!connected) {
1326                                    throw new IllegalStateException("Client not connected");
1327                            }
1328                            // Is this client authenticated?
1329                            if (!authenticated) {
1330                                    throw new IllegalStateException("Client not authenticated");
1331                            }
1332                            // Send the DELE command.
1333                            communication.sendFTPCommand("DELE " + path);
1334                            FTPReply r = communication.readFTPReply();
1335                            if (!r.isSuccessCode()) {
1336                                    throw new FTPException(r);
1337                            }
1338                    }
1339            }
1340    
1341            /**
1342             * This method deletes a remote directory.
1343             * 
1344             * @param path
1345             *            The path to the directory.
1346             * @throws IllegalStateException
1347             *             If the client is not connected or not authenticated.
1348             * @throws IOException
1349             *             If an I/O error occurs.
1350             * @throws FTPIllegalReplyException
1351             *             If the server replies in an illegal way.
1352             * @throws FTPException
1353             *             If the operation fails.
1354             */
1355            public void deleteDirectory(String path) throws IllegalStateException,
1356                            IOException, FTPIllegalReplyException, FTPException {
1357                    synchronized (lock) {
1358                            // Is this client connected?
1359                            if (!connected) {
1360                                    throw new IllegalStateException("Client not connected");
1361                            }
1362                            // Is this client authenticated?
1363                            if (!authenticated) {
1364                                    throw new IllegalStateException("Client not authenticated");
1365                            }
1366                            // Send the RMD command.
1367                            communication.sendFTPCommand("RMD " + path);
1368                            FTPReply r = communication.readFTPReply();
1369                            if (!r.isSuccessCode()) {
1370                                    throw new FTPException(r);
1371                            }
1372                    }
1373            }
1374    
1375            /**
1376             * This method creates a new remote directory in the current working one.
1377             * 
1378             * @param directoryName
1379             *            The name of the new directory.
1380             * @throws IllegalStateException
1381             *             If the client is not connected or not authenticated.
1382             * @throws IOException
1383             *             If an I/O error occurs.
1384             * @throws FTPIllegalReplyException
1385             *             If the server replies in an illegal way.
1386             * @throws FTPException
1387             *             If the operation fails.
1388             */
1389            public void createDirectory(String directoryName)
1390                            throws IllegalStateException, IOException,
1391                            FTPIllegalReplyException, FTPException {
1392                    synchronized (lock) {
1393                            // Is this client connected?
1394                            if (!connected) {
1395                                    throw new IllegalStateException("Client not connected");
1396                            }
1397                            // Is this client authenticated?
1398                            if (!authenticated) {
1399                                    throw new IllegalStateException("Client not authenticated");
1400                            }
1401                            // Send the MKD command.
1402                            communication.sendFTPCommand("MKD " + directoryName);
1403                            FTPReply r = communication.readFTPReply();
1404                            if (!r.isSuccessCode()) {
1405                                    throw new FTPException(r);
1406                            }
1407                    }
1408            }
1409    
1410            /**
1411             * This method calls the HELP command on the remote server, returning a list
1412             * of lines with the help contents.
1413             * 
1414             * @return The help contents, splitted by line.
1415             * @throws IllegalStateException
1416             *             If the client is not connected or not authenticated.
1417             * @throws IOException
1418             *             If an I/O error occurs.
1419             * @throws FTPIllegalReplyException
1420             *             If the server replies in an illegal way.
1421             * @throws FTPException
1422             *             If the operation fails.
1423             */
1424            public String[] help() throws IllegalStateException, IOException,
1425                            FTPIllegalReplyException, FTPException {
1426                    synchronized (lock) {
1427                            // Is this client connected?
1428                            if (!connected) {
1429                                    throw new IllegalStateException("Client not connected");
1430                            }
1431                            // Is this client authenticated?
1432                            if (!authenticated) {
1433                                    throw new IllegalStateException("Client not authenticated");
1434                            }
1435                            // Send the HELP command.
1436                            communication.sendFTPCommand("HELP");
1437                            FTPReply r = communication.readFTPReply();
1438                            if (!r.isSuccessCode()) {
1439                                    throw new FTPException(r);
1440                            }
1441                            return r.getMessages();
1442                    }
1443            }
1444    
1445            /**
1446             * This method returns the remote server status, as the result of a FTP STAT
1447             * command.
1448             * 
1449             * @return The remote server status, splitted by line.
1450             * @throws IllegalStateException
1451             *             If the client is not connected or not authenticated.
1452             * @throws IOException
1453             *             If an I/O error occurs.
1454             * @throws FTPIllegalReplyException
1455             *             If the server replies in an illegal way.
1456             * @throws FTPException
1457             *             If the operation fails.
1458             */
1459            public String[] serverStatus() throws IllegalStateException, IOException,
1460                            FTPIllegalReplyException, FTPException {
1461                    synchronized (lock) {
1462                            // Is this client connected?
1463                            if (!connected) {
1464                                    throw new IllegalStateException("Client not connected");
1465                            }
1466                            // Is this client authenticated?
1467                            if (!authenticated) {
1468                                    throw new IllegalStateException("Client not authenticated");
1469                            }
1470                            // Send the STAT command.
1471                            communication.sendFTPCommand("STAT");
1472                            FTPReply r = communication.readFTPReply();
1473                            if (!r.isSuccessCode()) {
1474                                    throw new FTPException(r);
1475                            }
1476                            return r.getMessages();
1477                    }
1478            }
1479    
1480            /**
1481             * This method lists the entries of the current working directory parsing
1482             * the reply to a FTP LIST command.
1483             * 
1484             * The response to the LIST command is parsed through the FTPListParser
1485             * objects registered on the client. The distribution of ftp4j contains some
1486             * standard parsers already registered on every FTPClient object created. If
1487             * they don't work in your case (a FTPListParseException is thrown), you can
1488             * build your own parser implementing the FTPListParser interface and add it
1489             * to the client by calling its addListParser() method.
1490             * 
1491             * Calling this method blocks the current thread until the operation is
1492             * completed. The operation could be interrupted by another thread calling
1493             * abortCurrentDataTransfer(). The list() method will break with a
1494             * FTPAbortedException.
1495             * 
1496             * @param fileSpec
1497             *            A file filter string. Depending on the server implementation,
1498             *            wildcard characters could be accepted.
1499             * @return The list of the files (and directories) in the current working
1500             *         directory.
1501             * @throws IllegalStateException
1502             *             If the client is not connected or not authenticated.
1503             * @throws IOException
1504             *             If an I/O error occurs.
1505             * @throws FTPIllegalReplyException
1506             *             If the server replies in an illegal way.
1507             * @throws FTPException
1508             *             If the operation fails.
1509             * @throws FTPDataTransferException
1510             *             If a I/O occurs in the data transfer connection. If you
1511             *             receive this exception the transfer failed, but the main
1512             *             connection with the remote FTP server is in theory still
1513             *             working.
1514             * @throws FTPAbortedException
1515             *             If operation is aborted by another thread.
1516             * @throws FTPListParseException
1517             *             If none of the registered parsers can handle the response
1518             *             sent by the server.
1519             * @see FTPListParser
1520             * @see FTPClient#addListParser(FTPListParser)
1521             * @see FTPClient#getListParsers()
1522             * @see FTPClient#abortCurrentDataTransfer(boolean)
1523             * @see FTPClient#listNames()
1524             * @since 1.2
1525             */
1526            public FTPFile[] list(String fileSpec) throws IllegalStateException,
1527                            IOException, FTPIllegalReplyException, FTPException,
1528                            FTPDataTransferException, FTPAbortedException,
1529                            FTPListParseException {
1530                    synchronized (lock) {
1531                            // Is this client connected?
1532                            if (!connected) {
1533                                    throw new IllegalStateException("Client not connected");
1534                            }
1535                            // Is this client authenticated?
1536                            if (!authenticated) {
1537                                    throw new IllegalStateException("Client not authenticated");
1538                            }
1539                            // Prepares the connection for the data transfer.
1540                            FTPDataTransferConnectionProvider provider;
1541                            // Active or passive?
1542                            if (passive) {
1543                                    provider = openPassiveDataTransferChannel();
1544                            } else {
1545                                    provider = openActiveDataTransferChannel();
1546                            }
1547                            // ASCII, please!
1548                            communication.sendFTPCommand("TYPE A");
1549                            FTPReply r = communication.readFTPReply();
1550                            if (!r.isSuccessCode()) {
1551                                    provider.dispose();
1552                                    throw new FTPException(r);
1553                            }
1554                            // Send the list command.
1555                            String command = "LIST";
1556                            if (fileSpec != null && fileSpec.length() > 0) {
1557                                    command += " " + fileSpec;
1558                            }
1559                            communication.sendFTPCommand(command);
1560                            FTPConnection dtConnection;
1561                            try {
1562                                    dtConnection = provider.openDataTransferConnection();
1563                                    r = communication.readFTPReply();
1564                                    if (r.getCode() != 150 && r.getCode() != 125) {
1565                                            throw new FTPException(r);
1566                                    }
1567                            } finally {
1568                                    provider.dispose();
1569                            }
1570                            // Change the operation status.
1571                            synchronized (abortLock) {
1572                                    ongoingDataTransfer = true;
1573                                    aborted = false;
1574                            }
1575                            // Fetch the list from the data transfer connection.
1576                            ArrayList lines = new ArrayList();
1577                            NVTASCIIReader dataReader = null;
1578                            try {
1579                                    // Opens the data transfer connection.
1580                                    dataTransferInputStream = dtConnection.getInputStream();
1581                                    // Let's do it!
1582                                    dataReader = new NVTASCIIReader(dataTransferInputStream,
1583                                                    pickCharset());
1584                                    String line;
1585                                    while ((line = dataReader.readLine()) != null) {
1586                                            if (line.length() > 0) {
1587                                                    lines.add(line);
1588                                            }
1589                                    }
1590                            } catch (IOException e) {
1591                                    synchronized (abortLock) {
1592                                            if (aborted) {
1593                                                    throw new FTPAbortedException();
1594                                            } else {
1595                                                    throw new FTPDataTransferException(
1596                                                                    "I/O error in data transfer", e);
1597                                            }
1598                                    }
1599                            } finally {
1600                                    if (dataReader != null) {
1601                                            try {
1602                                                    dataReader.close();
1603                                            } catch (Throwable t) {
1604                                                    ;
1605                                            }
1606                                    }
1607                                    try {
1608                                            dtConnection.close();
1609                                    } catch (Throwable t) {
1610                                            ;
1611                                    }
1612                                    // Consume the result reply of the transfer.
1613                                    communication.readFTPReply();
1614                                    // Set to null the instance-level input stream.
1615                                    dataTransferInputStream = null;
1616                                    // Change the operation status.
1617                                    synchronized (abortLock) {
1618                                            ongoingDataTransfer = false;
1619                                            aborted = false;
1620                                    }
1621                            }
1622                            // Build an array of lines.
1623                            int size = lines.size();
1624                            String[] list = new String[size];
1625                            for (int i = 0; i < size; i++) {
1626                                    list[i] = (String) lines.get(i);
1627                            }
1628                            // Parse the list.
1629                            FTPFile[] ret = null;
1630                            if (parser == null) {
1631                                    // Try to parse the list with every parser available.
1632                                    for (Iterator i = listParsers.iterator(); i.hasNext();) {
1633                                            FTPListParser aux = (FTPListParser) i.next();
1634                                            try {
1635                                                    // Let's try!
1636                                                    ret = aux.parse(list);
1637                                                    // This parser smells good!
1638                                                    parser = aux;
1639                                                    // Leave the loop.
1640                                                    break;
1641                                            } catch (FTPListParseException e) {
1642                                                    // Let's try the next one.
1643                                                    continue;
1644                                            }
1645                                    }
1646                            } else {
1647                                    ret = parser.parse(list);
1648                            }
1649                            if (ret == null) {
1650                                    // None of the parsers can handle the list response.
1651                                    throw new FTPListParseException();
1652                            } else {
1653                                    // Return the parsed list.
1654                                    return ret;
1655                            }
1656                    }
1657            }
1658    
1659            /**
1660             * This method lists the entries of the current working directory parsing
1661             * the reply to a FTP LIST command.
1662             * 
1663             * The response to the LIST command is parsed through the FTPListParser
1664             * objects registered on the client. The distribution of ftp4j contains some
1665             * standard parsers already registered on every FTPClient object created. If
1666             * they don't work in your case (a FTPListParseException is thrown), you can
1667             * build your own parser implementing the FTPListParser interface and add it
1668             * to the client by calling its addListParser() method.
1669             * 
1670             * Calling this method blocks the current thread until the operation is
1671             * completed. The operation could be interrupted by another thread calling
1672             * abortCurrentDataTransfer(). The list() method will break with a
1673             * FTPAbortedException.
1674             * 
1675             * @return The list of the files (and directories) in the current working
1676             *         directory.
1677             * @throws IllegalStateException
1678             *             If the client is not connected or not authenticated.
1679             * @throws IOException
1680             *             If an I/O error occurs.
1681             * @throws FTPIllegalReplyException
1682             *             If the server replies in an illegal way.
1683             * @throws FTPException
1684             *             If the operation fails.
1685             * @throws FTPDataTransferException
1686             *             If a I/O occurs in the data transfer connection. If you
1687             *             receive this exception the transfer failed, but the main
1688             *             connection with the remote FTP server is in theory still
1689             *             working.
1690             * @throws FTPAbortedException
1691             *             If operation is aborted by another thread.
1692             * @throws FTPListParseException
1693             *             If none of the registered parsers can handle the response
1694             *             sent by the server.
1695             * @see FTPListParser
1696             * @see FTPClient#addListParser(FTPListParser)
1697             * @see FTPClient#getListParsers()
1698             * @see FTPClient#abortCurrentDataTransfer(boolean)
1699             * @see FTPClient#listNames()
1700             */
1701            public FTPFile[] list() throws IllegalStateException, IOException,
1702                            FTPIllegalReplyException, FTPException, FTPDataTransferException,
1703                            FTPAbortedException, FTPListParseException {
1704                    return list(null);
1705            }
1706    
1707            /**
1708             * This method lists the entries of the current working directory with a FTP
1709             * NLST command.
1710             * 
1711             * The response consists in an array of string, each one reporting the name
1712             * of a file or a directory placed in the current working directory. For a
1713             * more detailed directory listing procedure look at the list() method.
1714             * 
1715             * Calling this method blocks the current thread until the operation is
1716             * completed. The operation could be interrupted by another thread calling
1717             * abortCurrentDataTransfer(). The listNames() method will break with a
1718             * FTPAbortedException.
1719             * 
1720             * @return The list of the files (and directories) in the current working
1721             *         directory.
1722             * @throws IllegalStateException
1723             *             If the client is not connected or not authenticated.
1724             * @throws IOException
1725             *             If an I/O error occurs.
1726             * @throws FTPIllegalReplyException
1727             *             If the server replies in an illegal way.
1728             * @throws FTPException
1729             *             If the operation fails.
1730             * @throws FTPDataTransferException
1731             *             If a I/O occurs in the data transfer connection. If you
1732             *             receive this exception the transfer failed, but the main
1733             *             connection with the remote FTP server is in theory still
1734             *             working.
1735             * @throws FTPAbortedException
1736             *             If operation is aborted by another thread.
1737             * @throws FTPListParseException
1738             *             If none of the registered parsers can handle the response
1739             *             sent by the server.
1740             * @see FTPClient#abortCurrentDataTransfer(boolean)
1741             * @see FTPClient#list()
1742             */
1743            public String[] listNames() throws IllegalStateException, IOException,
1744                            FTPIllegalReplyException, FTPException, FTPDataTransferException,
1745                            FTPAbortedException, FTPListParseException {
1746                    synchronized (lock) {
1747                            // Is this client connected?
1748                            if (!connected) {
1749                                    throw new IllegalStateException("Client not connected");
1750                            }
1751                            // Is this client authenticated?
1752                            if (!authenticated) {
1753                                    throw new IllegalStateException("Client not authenticated");
1754                            }
1755                            // Prepares the connection for the data transfer.
1756                            FTPDataTransferConnectionProvider provider;
1757                            // Active or passive?
1758                            if (passive) {
1759                                    provider = openPassiveDataTransferChannel();
1760                            } else {
1761                                    provider = openActiveDataTransferChannel();
1762                            }
1763                            // ASCII, please!
1764                            communication.sendFTPCommand("TYPE A");
1765                            FTPReply r = communication.readFTPReply();
1766                            if (!r.isSuccessCode()) {
1767                                    provider.dispose();
1768                                    throw new FTPException(r);
1769                            }
1770                            // Send the NLST command.
1771                            communication.sendFTPCommand("NLST");
1772                            FTPConnection dtConnection;
1773                            try {
1774                                    dtConnection = provider.openDataTransferConnection();
1775                                    r = communication.readFTPReply();
1776                                    if (r.getCode() != 150 && r.getCode() != 125) {
1777                                            throw new FTPException(r);
1778                                    }
1779                            } finally {
1780                                    provider.dispose();
1781                            }
1782                            // Change the operation status.
1783                            synchronized (abortLock) {
1784                                    ongoingDataTransfer = true;
1785                                    aborted = false;
1786                            }
1787                            // Fetch the list from the data transfer connection.
1788                            ArrayList lines = new ArrayList();
1789                            NVTASCIIReader dataReader = null;
1790                            try {
1791                                    // Opens the data transfer connection.
1792                                    dataTransferInputStream = dtConnection.getInputStream();
1793                                    // Let's do it!
1794                                    dataReader = new NVTASCIIReader(dataTransferInputStream,
1795                                                    pickCharset());
1796                                    String line;
1797                                    while ((line = dataReader.readLine()) != null) {
1798                                            if (line.length() > 0) {
1799                                                    lines.add(line);
1800                                            }
1801                                    }
1802                            } catch (IOException e) {
1803                                    synchronized (abortLock) {
1804                                            if (aborted) {
1805                                                    throw new FTPAbortedException();
1806                                            } else {
1807                                                    throw new FTPDataTransferException(
1808                                                                    "I/O error in data transfer", e);
1809                                            }
1810                                    }
1811                            } finally {
1812                                    if (dataReader != null) {
1813                                            try {
1814                                                    dataReader.close();
1815                                            } catch (Throwable t) {
1816                                                    ;
1817                                            }
1818                                    }
1819                                    try {
1820                                            dtConnection.close();
1821                                    } catch (Throwable t) {
1822                                            ;
1823                                    }
1824                                    // Consume the result reply of the transfer.
1825                                    communication.readFTPReply();
1826                                    // Set to null the instance-level input stream.
1827                                    dataTransferInputStream = null;
1828                                    // Change the operation status.
1829                                    synchronized (abortLock) {
1830                                            ongoingDataTransfer = false;
1831                                            aborted = false;
1832                                    }
1833                            }
1834                            // Build an array.
1835                            int size = lines.size();
1836                            String[] list = new String[size];
1837                            for (int i = 0; i < size; i++) {
1838                                    list[i] = (String) lines.get(i);
1839                            }
1840                            return list;
1841                    }
1842            }
1843    
1844            /**
1845             * This method uploads a file to the remote server.
1846             * 
1847             * Calling this method blocks the current thread until the operation is
1848             * completed. The operation could be interrupted by another thread calling
1849             * abortCurrentDataTransfer(). The method will break with a
1850             * FTPAbortedException.
1851             * 
1852             * @param file
1853             *            The file to upload.
1854             * @throws IllegalStateException
1855             *             If the client is not connected or not authenticated.
1856             * @throws FileNotFoundException
1857             *             If the supplied file cannot be found.
1858             * @throws IOException
1859             *             If an I/O error occurs.
1860             * @throws FTPIllegalReplyException
1861             *             If the server replies in an illegal way.
1862             * @throws FTPException
1863             *             If the operation fails.
1864             * @throws FTPDataTransferException
1865             *             If a I/O occurs in the data transfer connection. If you
1866             *             receive this exception the transfer failed, but the main
1867             *             connection with the remote FTP server is in theory still
1868             *             working.
1869             * @throws FTPAbortedException
1870             *             If operation is aborted by another thread.
1871             * @see FTPClient#abortCurrentDataTransfer(boolean)
1872             */
1873            public void upload(File file) throws IllegalStateException,
1874                            FileNotFoundException, IOException, FTPIllegalReplyException,
1875                            FTPException, FTPDataTransferException, FTPAbortedException {
1876                    upload(file, 0, null);
1877            }
1878    
1879            /**
1880             * This method uploads a file to the remote server.
1881             * 
1882             * Calling this method blocks the current thread until the operation is
1883             * completed. The operation could be interrupted by another thread calling
1884             * abortCurrentDataTransfer(). The method will break with a
1885             * FTPAbortedException.
1886             * 
1887             * @param file
1888             *            The file to upload.
1889             * @param listener
1890             *            The listener for the operation. Could be null.
1891             * @throws IllegalStateException
1892             *             If the client is not connected or not authenticated.
1893             * @throws FileNotFoundException
1894             *             If the supplied file cannot be found.
1895             * @throws IOException
1896             *             If an I/O error occurs.
1897             * @throws FTPIllegalReplyException
1898             *             If the server replies in an illegal way.
1899             * @throws FTPException
1900             *             If the operation fails.
1901             * @throws FTPDataTransferException
1902             *             If a I/O occurs in the data transfer connection. If you
1903             *             receive this exception the transfer failed, but the main
1904             *             connection with the remote FTP server is in theory still
1905             *             working.
1906             * @throws FTPAbortedException
1907             *             If operation is aborted by another thread.
1908             * @see FTPClient#abortCurrentDataTransfer(boolean)
1909             */
1910            public void upload(File file, FTPDataTransferListener listener)
1911                            throws IllegalStateException, FileNotFoundException, IOException,
1912                            FTPIllegalReplyException, FTPException, FTPDataTransferException,
1913                            FTPAbortedException {
1914                    upload(file, 0, listener);
1915            }
1916    
1917            /**
1918             * This method resumes an upload of a file to the remote server.
1919             * 
1920             * Calling this method blocks the current thread until the operation is
1921             * completed. The operation could be interrupted by another thread calling
1922             * abortCurrentDataTransfer(). The method will break with a
1923             * FTPAbortedException.
1924             * 
1925             * @param file
1926             *            The file to upload.
1927             * @param restartAt
1928             *            The restart point (number of bytes already uploaded).
1929             * @throws IllegalStateException
1930             *             If the client is not connected or not authenticated.
1931             * @throws FileNotFoundException
1932             *             If the supplied file cannot be found.
1933             * @throws IOException
1934             *             If an I/O error occurs.
1935             * @throws FTPIllegalReplyException
1936             *             If the server replies in an illegal way.
1937             * @throws FTPException
1938             *             If the operation fails.
1939             * @throws FTPDataTransferException
1940             *             If a I/O occurs in the data transfer connection. If you
1941             *             receive this exception the transfer failed, but the main
1942             *             connection with the remote FTP server is in theory still
1943             *             working.
1944             * @throws FTPAbortedException
1945             *             If operation is aborted by another thread.
1946             * @see FTPClient#abortCurrentDataTransfer(boolean)
1947             */
1948            public void upload(File file, long restartAt) throws IllegalStateException,
1949                            FileNotFoundException, IOException, FTPIllegalReplyException,
1950                            FTPException, FTPDataTransferException, FTPAbortedException {
1951                    upload(file, restartAt, null);
1952            }
1953    
1954            /**
1955             * This method resumes an upload of a file to the remote server.
1956             * 
1957             * Calling this method blocks the current thread until the operation is
1958             * completed. The operation could be interrupted by another thread calling
1959             * abortCurrentDataTransfer(). The method will break with a
1960             * FTPAbortedException.
1961             * 
1962             * @param file
1963             *            The file to upload.
1964             * @param restartAt
1965             *            The restart point (number of bytes already uploaded).
1966             * @param listener
1967             *            The listener for the operation. Could be null.
1968             * @throws IllegalStateException
1969             *             If the client is not connected or not authenticated.
1970             * @throws FileNotFoundException
1971             *             If the supplied file cannot be found.
1972             * @throws IOException
1973             *             If an I/O error occurs.
1974             * @throws FTPIllegalReplyException
1975             *             If the server replies in an illegal way.
1976             * @throws FTPException
1977             *             If the operation fails.
1978             * @throws FTPDataTransferException
1979             *             If a I/O occurs in the data transfer connection. If you
1980             *             receive this exception the transfer failed, but the main
1981             *             connection with the remote FTP server is in theory still
1982             *             working.
1983             * @throws FTPAbortedException
1984             *             If operation is aborted by another thread.
1985             * @see FTPClient#abortCurrentDataTransfer(boolean)
1986             */
1987            public void upload(File file, long restartAt,
1988                            FTPDataTransferListener listener) throws IllegalStateException,
1989                            FileNotFoundException, IOException, FTPIllegalReplyException,
1990                            FTPException, FTPDataTransferException, FTPAbortedException {
1991                    if (!file.exists()) {
1992                            throw new FileNotFoundException(file.getAbsolutePath());
1993                    }
1994                    InputStream inputStream = null;
1995                    try {
1996                            inputStream = new FileInputStream(file);
1997                    } catch (IOException e) {
1998                            throw new FTPDataTransferException(e);
1999                    }
2000                    try {
2001                            upload(file.getName(), inputStream, restartAt, restartAt, file
2002                                            .length(), listener);
2003                    } catch (IllegalStateException e) {
2004                            throw e;
2005                    } catch (IOException e) {
2006                            throw e;
2007                    } catch (FTPIllegalReplyException e) {
2008                            throw e;
2009                    } catch (FTPException e) {
2010                            throw e;
2011                    } catch (FTPDataTransferException e) {
2012                            throw e;
2013                    } catch (FTPAbortedException e) {
2014                            throw e;
2015                    } finally {
2016                            if (inputStream != null) {
2017                                    try {
2018                                            inputStream.close();
2019                                    } catch (Throwable t) {
2020                                            ;
2021                                    }
2022                            }
2023                    }
2024            }
2025    
2026            /**
2027             * This method resumes an upload to the remote server.
2028             * 
2029             * Note that no byte is skipped in the given inputStream. The restartAt
2030             * value is, in this case, an information for the remote server.
2031             * 
2032             * Calling this method blocks the current thread until the operation is
2033             * completed. The operation could be interrupted by another thread calling
2034             * abortCurrentDataTransfer(). The method will break with a
2035             * FTPAbortedException.
2036             * 
2037             * @param fileName
2038             *            The name of the remote file.
2039             * @param inputStream
2040             *            The source of data.
2041             * @param restartAt
2042             *            The restart point (number of bytes already uploaded).
2043             * @param streamOffset
2044             *            The offset to skip in the stream.
2045             * @param streamLength
2046             *            The length of the data of the stream to upload.
2047             * @param listener
2048             *            The listener for the operation. Could be null.
2049             * @throws IllegalStateException
2050             *             If the client is not connected or not authenticated.
2051             * @throws IOException
2052             *             If an I/O error occurs.
2053             * @throws FTPIllegalReplyException
2054             *             If the server replies in an illegal way.
2055             * @throws FTPException
2056             *             If the operation fails.
2057             * @throws FTPDataTransferException
2058             *             If a I/O occurs in the data transfer connection. If you
2059             *             receive this exception the transfer failed, but the main
2060             *             connection with the remote FTP server is in theory still
2061             *             working.
2062             * @throws FTPAbortedException
2063             *             If operation is aborted by another thread.
2064             * @see FTPClient#abortCurrentDataTransfer(boolean)
2065             */
2066            public void upload(String fileName, InputStream inputStream,
2067                            long restartAt, long streamOffset, long streamLength,
2068                            FTPDataTransferListener listener) throws IllegalStateException,
2069                            IOException, FTPIllegalReplyException, FTPException,
2070                            FTPDataTransferException, FTPAbortedException {
2071                    synchronized (lock) {
2072                            // Is this client connected?
2073                            if (!connected) {
2074                                    throw new IllegalStateException("Client not connected");
2075                            }
2076                            // Is this client authenticated?
2077                            if (!authenticated) {
2078                                    throw new IllegalStateException("Client not authenticated");
2079                            }
2080                            // Prepares the connection for the data transfer.
2081                            FTPDataTransferConnectionProvider provider;
2082                            // Active or passive?
2083                            if (passive) {
2084                                    provider = openPassiveDataTransferChannel();
2085                            } else {
2086                                    provider = openActiveDataTransferChannel();
2087                            }
2088    
2089                            // Select the type of contents.
2090                            int tp = type;
2091                            if (tp == TYPE_AUTO) {
2092                                    tp = detectType(fileName);
2093                            }
2094                            if (tp == TYPE_TEXTUAL) {
2095                                    communication.sendFTPCommand("TYPE A");
2096                            } else if (tp == TYPE_BINARY) {
2097                                    communication.sendFTPCommand("TYPE I");
2098                            }
2099                            FTPReply r = communication.readFTPReply();
2100                            if (!r.isSuccessCode()) {
2101                                    throw new FTPException(r);
2102                            }
2103                            // Send the REST command.
2104                            communication.sendFTPCommand("REST " + restartAt);
2105                            r = communication.readFTPReply();
2106                            if (r.getCode() != 350) {
2107                                    provider.dispose();
2108                                    throw new FTPException(r);
2109                            }
2110                            // Send the STOR command.
2111                            communication.sendFTPCommand("STOR " + fileName);
2112                            FTPConnection dtConnection;
2113                            try {
2114                                    dtConnection = provider.openDataTransferConnection();
2115                                    r = communication.readFTPReply();
2116                                    if (r.getCode() != 150 && r.getCode() != 125) {
2117                                            throw new FTPException(r);
2118                                    }
2119                            } finally {
2120                                    provider.dispose();
2121                            }
2122                            // Change the operation status.
2123                            synchronized (abortLock) {
2124                                    ongoingDataTransfer = true;
2125                                    aborted = false;
2126                            }
2127                            // Prepare and start a NOOPer thread.
2128                            NOOPer nooper = new NOOPer();
2129                            nooper.start();
2130                            // Upload the stream.
2131                            long done = 0;
2132                            try {
2133                                    // Skips.
2134                                    inputStream.skip(streamOffset);
2135                                    // Opens the data transfer connection.
2136                                    dataTransferOutputStream = dtConnection.getOutputStream();
2137                                    // Listeners.
2138                                    if (listener != null) {
2139                                            listener.started();
2140                                    }
2141                                    // Let's do it!
2142                                    if (tp == TYPE_TEXTUAL) {
2143                                            Reader reader = new InputStreamReader(inputStream);
2144                                            Writer writer = new OutputStreamWriter(
2145                                                            dataTransferOutputStream, "UTF-8");
2146                                            char[] buffer = new char[1024];
2147                                            while (done < streamLength) {
2148                                                    int l = reader.read(buffer, 0, (int) Math.min(
2149                                                                    buffer.length, streamLength - done));
2150                                                    if (l == -1) {
2151                                                            throw new IOException("End of stream reached");
2152                                                    } else {
2153                                                            writer.write(buffer, 0, l);
2154                                                            done += l;
2155                                                            if (listener != null) {
2156                                                                    listener.transferred(l);
2157                                                            }
2158                                                    }
2159                                            }
2160                                    } else if (tp == TYPE_BINARY) {
2161                                            byte[] buffer = new byte[1024];
2162                                            while (done < streamLength) {
2163                                                    int l = inputStream.read(buffer, 0, (int) Math.min(
2164                                                                    buffer.length, streamLength - done));
2165                                                    if (l == -1) {
2166                                                            throw new IOException("End of stream reached");
2167                                                    } else {
2168                                                            dataTransferOutputStream.write(buffer, 0, l);
2169                                                            done += l;
2170                                                            if (listener != null) {
2171                                                                    listener.transferred(l);
2172                                                            }
2173                                                    }
2174                                            }
2175                                    }
2176                            } catch (IOException e) {
2177                                    synchronized (abortLock) {
2178                                            if (aborted) {
2179                                                    if (listener != null) {
2180                                                            listener.aborted();
2181                                                    }
2182                                                    throw new FTPAbortedException();
2183                                            } else {
2184                                                    if (listener != null) {
2185                                                            listener.failed();
2186                                                    }
2187                                                    throw new FTPDataTransferException(
2188                                                                    "I/O error in data transfer", e);
2189                                            }
2190                                    }
2191                            } finally {
2192                                    // Stop nooping.
2193                                    nooper.interrupt();
2194                                    for (;;) {
2195                                            try {
2196                                                    nooper.join();
2197                                                    break;
2198                                            } catch (InterruptedException e) {
2199                                                    continue;
2200                                            }
2201                                    }
2202                                    // Closing stream and data connection.
2203                                    if (dataTransferOutputStream != null) {
2204                                            try {
2205                                                    dataTransferOutputStream.close();
2206                                            } catch (Throwable t) {
2207                                                    ;
2208                                            }
2209                                    }
2210                                    try {
2211                                            dtConnection.close();
2212                                    } catch (Throwable t) {
2213                                            ;
2214                                    }
2215                                    // Set to null the instance-level input stream.
2216                                    dataTransferOutputStream = null;
2217                                    // Consume the result reply of the transfer.
2218                                    communication.readFTPReply();
2219                                    // Change the operation status.
2220                                    synchronized (abortLock) {
2221                                            ongoingDataTransfer = false;
2222                                            aborted = false;
2223                                    }
2224                            }
2225                            if (listener != null) {
2226                                    listener.completed();
2227                            }
2228                    }
2229            }
2230    
2231            /**
2232             * This method downloads a remote file from the server to a local file.
2233             * 
2234             * Calling this method blocks the current thread until the operation is
2235             * completed. The operation could be interrupted by another thread calling
2236             * abortCurrentDataTransfer(). The method will break with a
2237             * FTPAbortedException.
2238             * 
2239             * @param remoteFileName
2240             *            The name of the file to download.
2241             * @param localFile
2242             *            The local file.
2243             * @throws IllegalStateException
2244             *             If the client is not connected or not authenticated.
2245             * @throws FileNotFoundException
2246             *             If the supplied file cannot be found.
2247             * @throws IOException
2248             *             If an I/O error occurs.
2249             * @throws FTPIllegalReplyException
2250             *             If the server replies in an illegal way.
2251             * @throws FTPException
2252             *             If the operation fails.
2253             * @throws FTPDataTransferException
2254             *             If a I/O occurs in the data transfer connection. If you
2255             *             receive this exception the transfer failed, but the main
2256             *             connection with the remote FTP server is in theory still
2257             *             working.
2258             * @throws FTPAbortedException
2259             *             If operation is aborted by another thread.
2260             * @see FTPClient#abortCurrentDataTransfer(boolean)
2261             */
2262            public void download(String remoteFileName, File localFile)
2263                            throws IllegalStateException, FileNotFoundException, IOException,
2264                            FTPIllegalReplyException, FTPException, FTPDataTransferException,
2265                            FTPAbortedException {
2266                    download(remoteFileName, localFile, 0, null);
2267            }
2268    
2269            /**
2270             * This method downloads a remote file from the server to a local file.
2271             * 
2272             * Calling this method blocks the current thread until the operation is
2273             * completed. The operation could be interrupted by another thread calling
2274             * abortCurrentDataTransfer(). The method will break with a
2275             * FTPAbortedException.
2276             * 
2277             * @param remoteFileName
2278             *            The name of the file to download.
2279             * @param localFile
2280             *            The local file.
2281             * @param listener
2282             *            The listener for the operation. Could be null.
2283             * @throws IllegalStateException
2284             *             If the client is not connected or not authenticated.
2285             * @throws FileNotFoundException
2286             *             If the supplied file cannot be found.
2287             * @throws IOException
2288             *             If an I/O error occurs.
2289             * @throws FTPIllegalReplyException
2290             *             If the server replies in an illegal way.
2291             * @throws FTPException
2292             *             If the operation fails.
2293             * @throws FTPDataTransferException
2294             *             If a I/O occurs in the data transfer connection. If you
2295             *             receive this exception the transfer failed, but the main
2296             *             connection with the remote FTP server is in theory still
2297             *             working.
2298             * @throws FTPAbortedException
2299             *             If operation is aborted by another thread.
2300             * @see FTPClient#abortCurrentDataTransfer(boolean)
2301             */
2302            public void download(String remoteFileName, File localFile,
2303                            FTPDataTransferListener listener) throws IllegalStateException,
2304                            FileNotFoundException, IOException, FTPIllegalReplyException,
2305                            FTPException, FTPDataTransferException, FTPAbortedException {
2306                    download(remoteFileName, localFile, 0, listener);
2307            }
2308    
2309            /**
2310             * This method resumes a download operation from the remote server to a
2311             * local file.
2312             * 
2313             * Calling this method blocks the current thread until the operation is
2314             * completed. The operation could be interrupted by another thread calling
2315             * abortCurrentDataTransfer(). The method will break with a
2316             * FTPAbortedException.
2317             * 
2318             * @param remoteFileName
2319             *            The name of the file to download.
2320             * @param localFile
2321             *            The local file.
2322             * @param restartAt
2323             *            The restart point (number of bytes already downloaded).
2324             * @throws IllegalStateException
2325             *             If the client is not connected or not authenticated.
2326             * @throws FileNotFoundException
2327             *             If the supplied file cannot be found.
2328             * @throws IOException
2329             *             If an I/O error occurs.
2330             * @throws FTPIllegalReplyException
2331             *             If the server replies in an illegal way.
2332             * @throws FTPException
2333             *             If the operation fails.
2334             * @throws FTPDataTransferException
2335             *             If a I/O occurs in the data transfer connection. If you
2336             *             receive this exception the transfer failed, but the main
2337             *             connection with the remote FTP server is in theory still
2338             *             working.
2339             * @throws FTPAbortedException
2340             *             If operation is aborted by another thread.
2341             * @see FTPClient#abortCurrentDataTransfer(boolean)
2342             */
2343            public void download(String remoteFileName, File localFile, long restartAt)
2344                            throws IllegalStateException, FileNotFoundException, IOException,
2345                            FTPIllegalReplyException, FTPException, FTPDataTransferException,
2346                            FTPAbortedException {
2347                    download(remoteFileName, localFile, restartAt, null);
2348            }
2349    
2350            /**
2351             * This method resumes a download operation from the remote server to a
2352             * local file.
2353             * 
2354             * Calling this method blocks the current thread until the operation is
2355             * completed. The operation could be interrupted by another thread calling
2356             * abortCurrentDataTransfer(). The method will break with a
2357             * FTPAbortedException.
2358             * 
2359             * @param remoteFileName
2360             *            The name of the file to download.
2361             * @param localFile
2362             *            The local file.
2363             * @param restartAt
2364             *            The restart point (number of bytes already downloaded).
2365             * @param listener
2366             *            The listener for the operation. Could be null.
2367             * @throws IllegalStateException
2368             *             If the client is not connected or not authenticated.
2369             * @throws FileNotFoundException
2370             *             If the supplied file cannot be found.
2371             * @throws IOException
2372             *             If an I/O error occurs.
2373             * @throws FTPIllegalReplyException
2374             *             If the server replies in an illegal way.
2375             * @throws FTPException
2376             *             If the operation fails.
2377             * @throws FTPDataTransferException
2378             *             If a I/O occurs in the data transfer connection. If you
2379             *             receive this exception the transfer failed, but the main
2380             *             connection with the remote FTP server is in theory still
2381             *             working.
2382             * @throws FTPAbortedException
2383             *             If operation is aborted by another thread.
2384             * @see FTPClient#abortCurrentDataTransfer(boolean)
2385             */
2386            public void download(String remoteFileName, File localFile, long restartAt,
2387                            FTPDataTransferListener listener) throws IllegalStateException,
2388                            FileNotFoundException, IOException, FTPIllegalReplyException,
2389                            FTPException, FTPDataTransferException, FTPAbortedException {
2390                    OutputStream outputStream = null;
2391                    try {
2392                            outputStream = new FileOutputStream(localFile, true);
2393                    } catch (IOException e) {
2394                            throw new FTPDataTransferException(e);
2395                    }
2396                    try {
2397                            download(remoteFileName, outputStream, restartAt, listener);
2398                    } catch (IllegalStateException e) {
2399                            throw e;
2400                    } catch (IOException e) {
2401                            throw e;
2402                    } catch (FTPIllegalReplyException e) {
2403                            throw e;
2404                    } catch (FTPException e) {
2405                            throw e;
2406                    } catch (FTPDataTransferException e) {
2407                            throw e;
2408                    } catch (FTPAbortedException e) {
2409                            throw e;
2410                    } finally {
2411                            if (outputStream != null) {
2412                                    try {
2413                                            outputStream.close();
2414                                    } catch (Throwable t) {
2415                                            ;
2416                                    }
2417                            }
2418                    }
2419            }
2420    
2421            /**
2422             * This method resumes a download operation from the remote server.
2423             * 
2424             * Calling this method blocks the current thread until the operation is
2425             * completed. The operation could be interrupted by another thread calling
2426             * abortCurrentDataTransfer(). The method will break with a
2427             * FTPAbortedException.
2428             * 
2429             * @param fileName
2430             *            The name of the remote file.
2431             * @param outputStream
2432             *            The destination stream of data read during the download.
2433             * @param restartAt
2434             *            The restart point (number of bytes already downloaded).
2435             * @param listener
2436             *            The listener for the operation. Could be null.
2437             * @throws IllegalStateException
2438             *             If the client is not connected or not authenticated.
2439             * @throws IOException
2440             *             If an I/O error occurs.
2441             * @throws FTPIllegalReplyException
2442             *             If the server replies in an illegal way.
2443             * @throws FTPException
2444             *             If the operation fails.
2445             * @throws FTPDataTransferException
2446             *             If a I/O occurs in the data transfer connection. If you
2447             *             receive this exception the transfer failed, but the main
2448             *             connection with the remote FTP server is in theory still
2449             *             working.
2450             * @throws FTPAbortedException
2451             *             If operation is aborted by another thread.
2452             * @see FTPClient#abortCurrentDataTransfer(boolean)
2453             */
2454            public void download(String fileName, OutputStream outputStream,
2455                            long restartAt, FTPDataTransferListener listener)
2456                            throws IllegalStateException, IOException,
2457                            FTPIllegalReplyException, FTPException, FTPDataTransferException,
2458                            FTPAbortedException {
2459                    synchronized (lock) {
2460                            // Is this client connected?
2461                            if (!connected) {
2462                                    throw new IllegalStateException("Client not connected");
2463                            }
2464                            // Is this client authenticated?
2465                            if (!authenticated) {
2466                                    throw new IllegalStateException("Client not authenticated");
2467                            }
2468                            // Prepares the connection for the data transfer.
2469                            FTPDataTransferConnectionProvider provider;
2470                            // Active or passive?
2471                            if (passive) {
2472                                    provider = openPassiveDataTransferChannel();
2473                            } else {
2474                                    provider = openActiveDataTransferChannel();
2475                            }
2476                            // Select the type of contents.
2477                            int tp = type;
2478                            if (tp == TYPE_AUTO) {
2479                                    tp = detectType(fileName);
2480                            }
2481                            if (tp == TYPE_TEXTUAL) {
2482                                    communication.sendFTPCommand("TYPE A");
2483                            } else if (tp == TYPE_BINARY) {
2484                                    communication.sendFTPCommand("TYPE I");
2485                            }
2486                            FTPReply r = communication.readFTPReply();
2487                            if (!r.isSuccessCode()) {
2488                                    throw new FTPException(r);
2489                            }
2490                            // Send the REST command.
2491                            communication.sendFTPCommand("REST " + restartAt);
2492                            r = communication.readFTPReply();
2493                            if (r.getCode() != 350) {
2494                                    provider.dispose();
2495                                    throw new FTPException(r);
2496                            }
2497                            // Send the RETR command.
2498                            communication.sendFTPCommand("RETR " + fileName);
2499                            FTPConnection dtConnection;
2500                            try {
2501                                    dtConnection = provider.openDataTransferConnection();
2502                                    r = communication.readFTPReply();
2503                                    if (r.getCode() != 150 && r.getCode() != 125) {
2504                                            throw new FTPException(r);
2505                                    }
2506                            } finally {
2507                                    provider.dispose();
2508                            }
2509                            // Change the operation status.
2510                            synchronized (abortLock) {
2511                                    ongoingDataTransfer = true;
2512                                    aborted = false;
2513                            }
2514                            // Prepare and start a NOOP thread.
2515                            NOOPer nooper = new NOOPer();
2516                            nooper.start();
2517                            // Download the stream.
2518                            try {
2519                                    // Opens the data transfer connection.
2520                                    dataTransferInputStream = dtConnection.getInputStream();
2521                                    // Listeners.
2522                                    if (listener != null) {
2523                                            listener.started();
2524                                    }
2525                                    // Let's do it!
2526                                    if (tp == TYPE_TEXTUAL) {
2527                                            Reader reader = new InputStreamReader(
2528                                                            dataTransferInputStream, "UTF-8");
2529                                            Writer writer = new OutputStreamWriter(outputStream);
2530                                            char[] buffer = new char[1024];
2531                                            int l;
2532                                            while ((l = reader.read(buffer, 0, buffer.length)) != -1) {
2533                                                    writer.write(buffer, 0, l);
2534                                                    if (listener != null) {
2535                                                            listener.transferred(l);
2536                                                    }
2537                                            }
2538                                    } else if (tp == TYPE_BINARY) {
2539                                            byte[] buffer = new byte[1024];
2540                                            int l;
2541                                            while ((l = dataTransferInputStream.read(buffer, 0,
2542                                                            buffer.length)) != -1) {
2543                                                    outputStream.write(buffer, 0, l);
2544                                                    if (listener != null) {
2545                                                            listener.transferred(l);
2546                                                    }
2547                                            }
2548                                    }
2549                            } catch (IOException e) {
2550                                    synchronized (abortLock) {
2551                                            if (aborted) {
2552                                                    if (listener != null) {
2553                                                            listener.aborted();
2554                                                    }
2555                                                    throw new FTPAbortedException();
2556                                            } else {
2557                                                    if (listener != null) {
2558                                                            listener.failed();
2559                                                    }
2560                                                    throw new FTPDataTransferException(
2561                                                                    "I/O error in data transfer", e);
2562                                            }
2563                                    }
2564                            } finally {
2565                                    // Stop nooping.
2566                                    nooper.interrupt();
2567                                    for (;;) {
2568                                            try {
2569                                                    nooper.join();
2570                                                    break;
2571                                            } catch (InterruptedException e) {
2572                                                    continue;
2573                                            }
2574                                    }
2575                                    // Closing stream and data connection.
2576                                    if (dataTransferInputStream != null) {
2577                                            try {
2578                                                    dataTransferInputStream.close();
2579                                            } catch (Throwable t) {
2580                                                    ;
2581                                            }
2582                                    }
2583                                    try {
2584                                            dtConnection.close();
2585                                    } catch (Throwable t) {
2586                                            ;
2587                                    }
2588                                    // Set to null the instance-level input stream.
2589                                    dataTransferInputStream = null;
2590                                    // Consume the result reply of the transfer.
2591                                    communication.readFTPReply();
2592                                    // Change the operation status.
2593                                    synchronized (abortLock) {
2594                                            ongoingDataTransfer = false;
2595                                            aborted = false;
2596                                    }
2597                            }
2598                            if (listener != null) {
2599                                    listener.completed();
2600                            }
2601                    }
2602            }
2603    
2604            /**
2605             * This method detects the type for a file transfer.
2606             */
2607            private int detectType(String fileName) throws IOException,
2608                            FTPIllegalReplyException, FTPException {
2609                    int start = fileName.lastIndexOf('.') + 1;
2610                    int stop = fileName.length();
2611                    if (start > 0 && start < stop - 1) {
2612                            String ext = fileName.substring(start, stop);
2613                            ext = ext.toLowerCase();
2614                            if (textualExtensionRecognizer.isTextualExt(ext)) {
2615                                    return TYPE_TEXTUAL;
2616                            } else {
2617                                    return TYPE_BINARY;
2618                            }
2619                    } else {
2620                            return TYPE_BINARY;
2621                    }
2622            }
2623    
2624            /**
2625             * This method opens a data transfer channel in active mode.
2626             */
2627            private FTPDataTransferConnectionProvider openActiveDataTransferChannel()
2628                            throws IOException, FTPIllegalReplyException, FTPException,
2629                            FTPDataTransferException {
2630                    // Create a FTPDataTransferServer object.
2631                    FTPDataTransferServer server = new FTPDataTransferServer();
2632                    int port = server.getPort();
2633                    int p1 = port >>> 8;
2634                    int p2 = port & 0xff;
2635                    int[] addr = pickLocalAddress();
2636                    // Send the port command.
2637                    communication.sendFTPCommand("PORT " + addr[0] + "," + addr[1] + ","
2638                                    + addr[2] + "," + addr[3] + "," + p1 + "," + p2);
2639                    FTPReply r = communication.readFTPReply();
2640                    if (!r.isSuccessCode()) {
2641                            // Disposes.
2642                            server.dispose();
2643                            // Closes the already open connection (if any).
2644                            try {
2645                                    FTPConnection aux = server.openDataTransferConnection();
2646                                    aux.close();
2647                            } catch (Throwable t) {
2648                                    ;
2649                            }
2650                            // Throws the exception.
2651                            throw new FTPException(r);
2652                    }
2653                    return server;
2654            }
2655    
2656            /**
2657             * This method opens a data transfer channel in passive mode.
2658             */
2659            private FTPDataTransferConnectionProvider openPassiveDataTransferChannel()
2660                            throws IOException, FTPIllegalReplyException, FTPException,
2661                            FTPDataTransferException {
2662                    // Send the PASV command.
2663                    communication.sendFTPCommand("PASV");
2664                    // Read the reply.
2665                    FTPReply r = communication.readFTPReply();
2666                    if (!r.isSuccessCode()) {
2667                            throw new FTPException(r);
2668                    }
2669                    // Use a regexp to extract the remote address and port.
2670                    String addressAndPort = null;
2671                    String[] messages = r.getMessages();
2672                    for (int i = 0; i < messages.length; i++) {
2673                            Matcher m = PASV_PATTERN.matcher(messages[i]);
2674                            if (m.find()) {
2675                                    int start = m.start();
2676                                    int end = m.end();
2677                                    addressAndPort = messages[i].substring(start, end);
2678                                    break;
2679                            }
2680                    }
2681                    if (addressAndPort == null) {
2682                            // The remote server has not sent the coordinates for the
2683                            // data transfer connection.
2684                            throw new FTPIllegalReplyException();
2685                    }
2686                    // Parse the string extracted from the reply.
2687                    StringTokenizer st = new StringTokenizer(addressAndPort, ",");
2688                    int b1 = Integer.parseInt(st.nextToken());
2689                    int b2 = Integer.parseInt(st.nextToken());
2690                    int b3 = Integer.parseInt(st.nextToken());
2691                    int b4 = Integer.parseInt(st.nextToken());
2692                    int p1 = Integer.parseInt(st.nextToken());
2693                    int p2 = Integer.parseInt(st.nextToken());
2694                    final InetAddress remoteAddress = InetAddress.getByAddress(new byte[] {
2695                                    (byte) b1, (byte) b2, (byte) b3, (byte) b4 });
2696                    final int remotePort = (p1 << 8) | p2;
2697                    FTPDataTransferConnectionProvider provider = new FTPDataTransferConnectionProvider() {
2698    
2699                            public FTPConnection openDataTransferConnection()
2700                                            throws FTPDataTransferException {
2701                                    // Establish the connection.
2702                                    FTPConnection dtConnection;
2703                                    try {
2704                                            dtConnection = connector.connectForDataTransferChannel(
2705                                                            remoteAddress.getHostAddress(), remotePort);
2706                                    } catch (IOException e) {
2707                                            throw new FTPDataTransferException(
2708                                                            "Cannot connect to the remote server", e);
2709                                    }
2710                                    return dtConnection;
2711                            }
2712    
2713                            public void dispose() {
2714                                    // nothing to do
2715                            }
2716    
2717                    };
2718                    return provider;
2719            }
2720    
2721            /**
2722             * If there's any ongoing data transfer operation, this method aborts it.
2723             * 
2724             * @param sendAborCommand
2725             *            If true the client will negotiate the abort procedure with the
2726             *            server, through the standard FTP ABOR command. Otherwise the
2727             *            open data transfer connection will be closed without any
2728             *            advise has sent to the server.
2729             * @throws IOException
2730             *             If the ABOR command cannot be sent due to any I/O error. This
2731             *             could happen only if force is false.
2732             * @throws FTPIllegalReplyException
2733             *             If the server reply to the ABOR command is illegal. This
2734             *             could happen only if force is false.
2735             */
2736            public void abortCurrentDataTransfer(boolean sendAborCommand)
2737                            throws IOException, FTPIllegalReplyException {
2738                    synchronized (abortLock) {
2739                            if (ongoingDataTransfer && !aborted) {
2740                                    if (sendAborCommand) {
2741                                            communication.sendFTPCommand("ABOR");
2742                                            communication.readFTPReply();
2743                                    }
2744                                    if (dataTransferInputStream != null) {
2745                                            try {
2746                                                    dataTransferInputStream.close();
2747                                            } catch (Throwable t) {
2748                                                    ;
2749                                            }
2750                                    }
2751                                    if (dataTransferOutputStream != null) {
2752                                            try {
2753                                                    dataTransferOutputStream.close();
2754                                            } catch (Throwable t) {
2755                                                    ;
2756                                            }
2757                                    }
2758                                    aborted = true;
2759                            }
2760                    }
2761            }
2762    
2763            /**
2764             * Returns the name of the charset that should be used in textual
2765             * transmissions.
2766             * 
2767             * @return The name of the charset that should be used in textual
2768             *         transmissions.
2769             */
2770            private String pickCharset() {
2771                    if (charset != null) {
2772                            return charset;
2773                    } else if (utf8Supported) {
2774                            return "UTF-8";
2775                    } else {
2776                            return System.getProperty("file.encoding");
2777                    }
2778            }
2779    
2780            /**
2781             * Picks the local address for an active data transfer operation.
2782             * 
2783             * @return The local address as a 4 integer values array.
2784             * @throws IOException
2785             *             If an unexpected I/O error occurs while trying to resolve the
2786             *             local address.
2787             */
2788            private int[] pickLocalAddress() throws IOException {
2789                    // Forced address?
2790                    int[] ret = pickForcedLocalAddress();
2791                    // Auto-detect?
2792                    if (ret == null) {
2793                            ret = pickAutoDetectedLocalAddress();
2794                    }
2795                    // Returns.
2796                    return ret;
2797            }
2798    
2799            /**
2800             * If a local address for active data transfers has been supplied through
2801             * the {@link FTPKeys#ACTIVE_DT_HOST_ADDRESS}, it returns it as a 4 elements
2802             * integer array; otherwise it returns null.
2803             * 
2804             * @return The forced local address, or null.
2805             */
2806            private int[] pickForcedLocalAddress() {
2807                    int[] ret = null;
2808                    String aux = System.getProperty(FTPKeys.ACTIVE_DT_HOST_ADDRESS);
2809                    if (aux != null) {
2810                            boolean valid = false;
2811                            StringTokenizer st = new StringTokenizer(aux, ".");
2812                            if (st.countTokens() == 4) {
2813                                    valid = true;
2814                                    int[] arr = new int[4];
2815                                    for (int i = 0; i < 4; i++) {
2816                                            String tk = st.nextToken();
2817                                            try {
2818                                                    arr[i] = Integer.parseInt(tk);
2819                                            } catch (NumberFormatException e) {
2820                                                    arr[i] = -1;
2821                                            }
2822                                            if (arr[i] < 0 || arr[i] > 255) {
2823                                                    valid = false;
2824                                                    break;
2825                                            }
2826                                    }
2827                                    if (valid) {
2828                                            ret = arr;
2829                                    }
2830                            }
2831                            if (!valid) {
2832                                    // warning to the developer
2833                                    System.err.println("WARNING: invalid value \"" + aux
2834                                                    + "\" for the " + FTPKeys.ACTIVE_DT_HOST_ADDRESS
2835                                                    + " system property. The value should "
2836                                                    + "be in the x.x.x.x form.");
2837                            }
2838                    }
2839                    return ret;
2840            }
2841    
2842            /**
2843             * Auto-detects the local network address, and returns it in the form of a 4
2844             * elements integer array.
2845             * 
2846             * @return The detected local address.
2847             * @throws IOException
2848             *             If an unexpected I/O error occurs while trying to resolve the
2849             *             local address.
2850             */
2851            private int[] pickAutoDetectedLocalAddress() throws IOException {
2852                    InetAddress addressObj = InetAddress.getLocalHost();
2853                    byte[] addr = addressObj.getAddress();
2854                    int b1 = addr[0] & 0xff;
2855                    int b2 = addr[1] & 0xff;
2856                    int b3 = addr[2] & 0xff;
2857                    int b4 = addr[3] & 0xff;
2858                    int[] ret = { b1, b2, b3, b4 };
2859                    return ret;
2860            }
2861    
2862            public String toString() {
2863                    synchronized (lock) {
2864                            StringBuffer buffer = new StringBuffer();
2865                            buffer.append(getClass().getName());
2866                            buffer.append(" [connected=");
2867                            buffer.append(connected);
2868                            if (connected) {
2869                                    buffer.append(", host=");
2870                                    buffer.append(host);
2871                                    buffer.append(", port=");
2872                                    buffer.append(port);
2873                            }
2874                            buffer.append(", authenticated=");
2875                            buffer.append(authenticated);
2876                            if (authenticated) {
2877                                    buffer.append(", username=");
2878                                    buffer.append(username);
2879                                    buffer.append(", password=");
2880                                    StringBuffer buffer2 = new StringBuffer();
2881                                    for (int i = 0; i < password.length(); i++) {
2882                                            buffer2.append('*');
2883                                    }
2884                                    buffer.append(buffer2);
2885                            }
2886                            buffer.append(", transfer mode=");
2887                            buffer.append(passive ? "passive" : "active");
2888                            buffer.append(", transfer type=");
2889                            switch (type) {
2890                            case TYPE_AUTO:
2891                                    buffer.append("auto");
2892                                    break;
2893                            case TYPE_BINARY:
2894                                    buffer.append("binary");
2895                                    break;
2896                            case TYPE_TEXTUAL:
2897                                    buffer.append("textual");
2898                                    break;
2899                            }
2900                            buffer.append(", connector=");
2901                            buffer.append(connector);
2902                            buffer.append(", textualExtensionRecognizer=");
2903                            buffer.append(textualExtensionRecognizer);
2904                            FTPListParser[] listParsers = getListParsers();
2905                            if (listParsers.length > 0) {
2906                                    buffer.append(", listParsers=");
2907                                    for (int i = 0; i < listParsers.length; i++) {
2908                                            if (i > 0) {
2909                                                    buffer.append(", ");
2910                                            }
2911                                            buffer.append(listParsers[i]);
2912                                    }
2913                            }
2914                            FTPCommunicationListener[] communicationListeners = getCommunicationListeners();
2915                            if (communicationListeners.length > 0) {
2916                                    buffer.append(", communicationListeners=");
2917                                    for (int i = 0; i < communicationListeners.length; i++) {
2918                                            if (i > 0) {
2919                                                    buffer.append(", ");
2920                                            }
2921                                            buffer.append(communicationListeners[i]);
2922                                    }
2923                            }
2924                            buffer.append("]");
2925                            return buffer.toString();
2926                    }
2927            }
2928    
2929            /**
2930             * A NOOPer thread used during long data trasnfers.
2931             */
2932            private class NOOPer extends Thread {
2933    
2934                    public void run() {
2935                            long delay;
2936                            try {
2937                                    String aux = System
2938                                                    .getProperty(FTPKeys.DT_AUTO_NOOP_DELAY, "0");
2939                                    delay = Long.parseLong(aux);
2940                            } catch (NumberFormatException e) {
2941                                    delay = 0;
2942                            }
2943                            if (delay > 0) {
2944                                    while (!Thread.interrupted()) {
2945                                            // Sleep.
2946                                            try {
2947                                                    Thread.sleep(delay);
2948                                            } catch (InterruptedException e) {
2949                                                    break;
2950                                            }
2951                                            // Send NOOP.
2952                                            try {
2953                                                    communication.sendFTPCommand("NOOP");
2954                                                    communication.readFTPReply();
2955                                            } catch (Throwable t) {
2956                                                    ;
2957                                            }
2958                                    }
2959                            }
2960                    }
2961    
2962            }
2963    
2964    }