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.1
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                            // The connection for the data transfer.
1540                            FTPConnection dtConnection;
1541                            // Active or passive?
1542                            if (passive) {
1543                                    dtConnection = openPassiveDataTransferChannel();
1544                            } else {
1545                                    dtConnection = openActiveDataTransferChannel();
1546                            }
1547                            // ASCII, please!
1548                            communication.sendFTPCommand("TYPE A");
1549                            FTPReply r = communication.readFTPReply();
1550                            if (!r.isSuccessCode()) {
1551                                    try {
1552                                            dtConnection.close();
1553                                    } catch (Throwable t) {
1554                                            ;
1555                                    }
1556                                    throw new FTPException(r);
1557                            }
1558                            // Send the list command.
1559                            String command = "LIST";
1560                            if (fileSpec != null && fileSpec.length() > 0) {
1561                                    command += " " + fileSpec;
1562                            }
1563                            communication.sendFTPCommand(command);
1564                            r = communication.readFTPReply();
1565                            if (r.getCode() != 150 && r.getCode() != 125) {
1566                                    try {
1567                                            dtConnection.close();
1568                                    } catch (Throwable t) {
1569                                            ;
1570                                    }
1571                                    throw new FTPException(r);
1572                            }
1573                            // Change the operation status.
1574                            synchronized (abortLock) {
1575                                    ongoingDataTransfer = true;
1576                                    aborted = false;
1577                            }
1578                            dataTransferInputStream = dtConnection.getInputStream();
1579                            // Fetch the list from the data transfer connection.
1580                            ArrayList lines = new ArrayList();
1581                            NVTASCIIReader dataReader = null;
1582                            try {
1583                                    dataReader = new NVTASCIIReader(dataTransferInputStream,
1584                                                    pickCharset());
1585                                    String line;
1586                                    while ((line = dataReader.readLine()) != null) {
1587                                            if (line.length() > 0) {
1588                                                    lines.add(line);
1589                                            }
1590                                    }
1591                            } catch (IOException e) {
1592                                    synchronized (abortLock) {
1593                                            if (aborted) {
1594                                                    throw new FTPAbortedException();
1595                                            } else {
1596                                                    throw new FTPDataTransferException(
1597                                                                    "I/O error in data transfer", e);
1598                                            }
1599                                    }
1600                            } finally {
1601                                    if (dataReader != null) {
1602                                            try {
1603                                                    dataReader.close();
1604                                            } catch (Throwable t) {
1605                                                    ;
1606                                            }
1607                                    }
1608                                    try {
1609                                            dtConnection.close();
1610                                    } catch (Throwable t) {
1611                                            ;
1612                                    }
1613                                    // Consume the result reply of the transfer.
1614                                    communication.readFTPReply();
1615                                    // Set to null the instance-level input stream.
1616                                    dataTransferInputStream = null;
1617                                    // Change the operation status.
1618                                    synchronized (abortLock) {
1619                                            ongoingDataTransfer = false;
1620                                            aborted = false;
1621                                    }
1622                            }
1623                            // Build an array of lines.
1624                            int size = lines.size();
1625                            String[] list = new String[size];
1626                            for (int i = 0; i < size; i++) {
1627                                    list[i] = (String) lines.get(i);
1628                            }
1629                            // Parse the list.
1630                            FTPFile[] ret = null;
1631                            if (parser == null) {
1632                                    // Try to parse the list with every parser available.
1633                                    for (Iterator i = listParsers.iterator(); i.hasNext();) {
1634                                            FTPListParser aux = (FTPListParser) i.next();
1635                                            try {
1636                                                    // Let's try!
1637                                                    ret = aux.parse(list);
1638                                                    // This parser smells good!
1639                                                    parser = aux;
1640                                                    // Leave the loop.
1641                                                    break;
1642                                            } catch (FTPListParseException e) {
1643                                                    // Let's try the next one.
1644                                                    continue;
1645                                            }
1646                                    }
1647                            } else {
1648                                    ret = parser.parse(list);
1649                            }
1650                            if (ret == null) {
1651                                    // None of the parsers can handle the list response.
1652                                    throw new FTPListParseException();
1653                            } else {
1654                                    // Return the parsed list.
1655                                    return ret;
1656                            }
1657                    }
1658            }
1659    
1660            /**
1661             * This method lists the entries of the current working directory parsing
1662             * the reply to a FTP LIST command.
1663             * 
1664             * The response to the LIST command is parsed through the FTPListParser
1665             * objects registered on the client. The distribution of ftp4j contains some
1666             * standard parsers already registered on every FTPClient object created. If
1667             * they don't work in your case (a FTPListParseException is thrown), you can
1668             * build your own parser implementing the FTPListParser interface and add it
1669             * to the client by calling its addListParser() method.
1670             * 
1671             * Calling this method blocks the current thread until the operation is
1672             * completed. The operation could be interrupted by another thread calling
1673             * abortCurrentDataTransfer(). The list() method will break with a
1674             * FTPAbortedException.
1675             * 
1676             * @return The list of the files (and directories) in the current working
1677             *         directory.
1678             * @throws IllegalStateException
1679             *             If the client is not connected or not authenticated.
1680             * @throws IOException
1681             *             If an I/O error occurs.
1682             * @throws FTPIllegalReplyException
1683             *             If the server replies in an illegal way.
1684             * @throws FTPException
1685             *             If the operation fails.
1686             * @throws FTPDataTransferException
1687             *             If a I/O occurs in the data transfer connection. If you
1688             *             receive this exception the transfer failed, but the main
1689             *             connection with the remote FTP server is in theory still
1690             *             working.
1691             * @throws FTPAbortedException
1692             *             If operation is aborted by another thread.
1693             * @throws FTPListParseException
1694             *             If none of the registered parsers can handle the response
1695             *             sent by the server.
1696             * @see FTPListParser
1697             * @see FTPClient#addListParser(FTPListParser)
1698             * @see FTPClient#getListParsers()
1699             * @see FTPClient#abortCurrentDataTransfer(boolean)
1700             * @see FTPClient#listNames()
1701             */
1702            public FTPFile[] list() throws IllegalStateException, IOException,
1703                            FTPIllegalReplyException, FTPException, FTPDataTransferException,
1704                            FTPAbortedException, FTPListParseException {
1705                    return list(null);
1706            }
1707    
1708            /**
1709             * This method lists the entries of the current working directory with a FTP
1710             * NLST command.
1711             * 
1712             * The response consists in an array of string, each one reporting the name
1713             * of a file or a directory placed in the current working directory. For a
1714             * more detailed directory listing procedure look at the list() method.
1715             * 
1716             * Calling this method blocks the current thread until the operation is
1717             * completed. The operation could be interrupted by another thread calling
1718             * abortCurrentDataTransfer(). The listNames() method will break with a
1719             * FTPAbortedException.
1720             * 
1721             * @return The list of the files (and directories) in the current working
1722             *         directory.
1723             * @throws IllegalStateException
1724             *             If the client is not connected or not authenticated.
1725             * @throws IOException
1726             *             If an I/O error occurs.
1727             * @throws FTPIllegalReplyException
1728             *             If the server replies in an illegal way.
1729             * @throws FTPException
1730             *             If the operation fails.
1731             * @throws FTPDataTransferException
1732             *             If a I/O occurs in the data transfer connection. If you
1733             *             receive this exception the transfer failed, but the main
1734             *             connection with the remote FTP server is in theory still
1735             *             working.
1736             * @throws FTPAbortedException
1737             *             If operation is aborted by another thread.
1738             * @throws FTPListParseException
1739             *             If none of the registered parsers can handle the response
1740             *             sent by the server.
1741             * @see FTPClient#abortCurrentDataTransfer(boolean)
1742             * @see FTPClient#list()
1743             */
1744            public String[] listNames() throws IllegalStateException, IOException,
1745                            FTPIllegalReplyException, FTPException, FTPDataTransferException,
1746                            FTPAbortedException, FTPListParseException {
1747                    synchronized (lock) {
1748                            // Is this client connected?
1749                            if (!connected) {
1750                                    throw new IllegalStateException("Client not connected");
1751                            }
1752                            // Is this client authenticated?
1753                            if (!authenticated) {
1754                                    throw new IllegalStateException("Client not authenticated");
1755                            }
1756                            // The connection for the data transfer.
1757                            FTPConnection dtConnection;
1758                            // Active or passive?
1759                            if (passive) {
1760                                    dtConnection = openPassiveDataTransferChannel();
1761                            } else {
1762                                    dtConnection = openActiveDataTransferChannel();
1763                            }
1764                            // ASCII, please!
1765                            communication.sendFTPCommand("TYPE A");
1766                            FTPReply r = communication.readFTPReply();
1767                            if (!r.isSuccessCode()) {
1768                                    try {
1769                                            dtConnection.close();
1770                                    } catch (Throwable t) {
1771                                            ;
1772                                    }
1773                                    throw new FTPException(r);
1774                            }
1775                            // Send the NLST command.
1776                            communication.sendFTPCommand("NLST");
1777                            r = communication.readFTPReply();
1778                            if (r.getCode() != 150 && r.getCode() != 125) {
1779                                    try {
1780                                            dtConnection.close();
1781                                    } catch (Throwable t) {
1782                                            ;
1783                                    }
1784                                    throw new FTPException(r);
1785                            }
1786                            // Change the operation status.
1787                            synchronized (abortLock) {
1788                                    ongoingDataTransfer = true;
1789                                    aborted = false;
1790                            }
1791                            dataTransferInputStream = dtConnection.getInputStream();
1792                            // Fetch the list from the data transfer connection.
1793                            ArrayList lines = new ArrayList();
1794                            NVTASCIIReader dataReader = null;
1795                            try {
1796                                    dataReader = new NVTASCIIReader(dataTransferInputStream,
1797                                                    pickCharset());
1798                                    String line;
1799                                    while ((line = dataReader.readLine()) != null) {
1800                                            if (line.length() > 0) {
1801                                                    lines.add(line);
1802                                            }
1803                                    }
1804                            } catch (IOException e) {
1805                                    synchronized (abortLock) {
1806                                            if (aborted) {
1807                                                    throw new FTPAbortedException();
1808                                            } else {
1809                                                    throw new FTPDataTransferException(
1810                                                                    "I/O error in data transfer", e);
1811                                            }
1812                                    }
1813                            } finally {
1814                                    if (dataReader != null) {
1815                                            try {
1816                                                    dataReader.close();
1817                                            } catch (Throwable t) {
1818                                                    ;
1819                                            }
1820                                    }
1821                                    try {
1822                                            dtConnection.close();
1823                                    } catch (Throwable t) {
1824                                            ;
1825                                    }
1826                                    // Consume the result reply of the transfer.
1827                                    communication.readFTPReply();
1828                                    // Set to null the instance-level input stream.
1829                                    dataTransferInputStream = null;
1830                                    // Change the operation status.
1831                                    synchronized (abortLock) {
1832                                            ongoingDataTransfer = false;
1833                                            aborted = false;
1834                                    }
1835                            }
1836                            // Build an array.
1837                            int size = lines.size();
1838                            String[] list = new String[size];
1839                            for (int i = 0; i < size; i++) {
1840                                    list[i] = (String) lines.get(i);
1841                            }
1842                            return list;
1843                    }
1844            }
1845    
1846            /**
1847             * This method uploads a file to the remote server.
1848             * 
1849             * Calling this method blocks the current thread until the operation is
1850             * completed. The operation could be interrupted by another thread calling
1851             * abortCurrentDataTransfer(). The method will break with a
1852             * FTPAbortedException.
1853             * 
1854             * @param file
1855             *            The file to upload.
1856             * @throws IllegalStateException
1857             *             If the client is not connected or not authenticated.
1858             * @throws FileNotFoundException
1859             *             If the supplied file cannot be found.
1860             * @throws IOException
1861             *             If an I/O error occurs.
1862             * @throws FTPIllegalReplyException
1863             *             If the server replies in an illegal way.
1864             * @throws FTPException
1865             *             If the operation fails.
1866             * @throws FTPDataTransferException
1867             *             If a I/O occurs in the data transfer connection. If you
1868             *             receive this exception the transfer failed, but the main
1869             *             connection with the remote FTP server is in theory still
1870             *             working.
1871             * @throws FTPAbortedException
1872             *             If operation is aborted by another thread.
1873             * @see FTPClient#abortCurrentDataTransfer(boolean)
1874             */
1875            public void upload(File file) throws IllegalStateException,
1876                            FileNotFoundException, IOException, FTPIllegalReplyException,
1877                            FTPException, FTPDataTransferException, FTPAbortedException {
1878                    upload(file, 0, null);
1879            }
1880    
1881            /**
1882             * This method uploads a file to the remote server.
1883             * 
1884             * Calling this method blocks the current thread until the operation is
1885             * completed. The operation could be interrupted by another thread calling
1886             * abortCurrentDataTransfer(). The method will break with a
1887             * FTPAbortedException.
1888             * 
1889             * @param file
1890             *            The file to upload.
1891             * @param listener
1892             *            The listener for the operation. Could be null.
1893             * @throws IllegalStateException
1894             *             If the client is not connected or not authenticated.
1895             * @throws FileNotFoundException
1896             *             If the supplied file cannot be found.
1897             * @throws IOException
1898             *             If an I/O error occurs.
1899             * @throws FTPIllegalReplyException
1900             *             If the server replies in an illegal way.
1901             * @throws FTPException
1902             *             If the operation fails.
1903             * @throws FTPDataTransferException
1904             *             If a I/O occurs in the data transfer connection. If you
1905             *             receive this exception the transfer failed, but the main
1906             *             connection with the remote FTP server is in theory still
1907             *             working.
1908             * @throws FTPAbortedException
1909             *             If operation is aborted by another thread.
1910             * @see FTPClient#abortCurrentDataTransfer(boolean)
1911             */
1912            public void upload(File file, FTPDataTransferListener listener)
1913                            throws IllegalStateException, FileNotFoundException, IOException,
1914                            FTPIllegalReplyException, FTPException, FTPDataTransferException,
1915                            FTPAbortedException {
1916                    upload(file, 0, listener);
1917            }
1918    
1919            /**
1920             * This method resumes an upload of a file to the remote server.
1921             * 
1922             * Calling this method blocks the current thread until the operation is
1923             * completed. The operation could be interrupted by another thread calling
1924             * abortCurrentDataTransfer(). The method will break with a
1925             * FTPAbortedException.
1926             * 
1927             * @param file
1928             *            The file to upload.
1929             * @param restartAt
1930             *            The restart point (number of bytes already uploaded).
1931             * @throws IllegalStateException
1932             *             If the client is not connected or not authenticated.
1933             * @throws FileNotFoundException
1934             *             If the supplied file cannot be found.
1935             * @throws IOException
1936             *             If an I/O error occurs.
1937             * @throws FTPIllegalReplyException
1938             *             If the server replies in an illegal way.
1939             * @throws FTPException
1940             *             If the operation fails.
1941             * @throws FTPDataTransferException
1942             *             If a I/O occurs in the data transfer connection. If you
1943             *             receive this exception the transfer failed, but the main
1944             *             connection with the remote FTP server is in theory still
1945             *             working.
1946             * @throws FTPAbortedException
1947             *             If operation is aborted by another thread.
1948             * @see FTPClient#abortCurrentDataTransfer(boolean)
1949             */
1950            public void upload(File file, long restartAt) throws IllegalStateException,
1951                            FileNotFoundException, IOException, FTPIllegalReplyException,
1952                            FTPException, FTPDataTransferException, FTPAbortedException {
1953                    upload(file, restartAt, null);
1954            }
1955    
1956            /**
1957             * This method resumes an upload of a file to the remote server.
1958             * 
1959             * Calling this method blocks the current thread until the operation is
1960             * completed. The operation could be interrupted by another thread calling
1961             * abortCurrentDataTransfer(). The method will break with a
1962             * FTPAbortedException.
1963             * 
1964             * @param file
1965             *            The file to upload.
1966             * @param restartAt
1967             *            The restart point (number of bytes already uploaded).
1968             * @param listener
1969             *            The listener for the operation. Could be null.
1970             * @throws IllegalStateException
1971             *             If the client is not connected or not authenticated.
1972             * @throws FileNotFoundException
1973             *             If the supplied file cannot be found.
1974             * @throws IOException
1975             *             If an I/O error occurs.
1976             * @throws FTPIllegalReplyException
1977             *             If the server replies in an illegal way.
1978             * @throws FTPException
1979             *             If the operation fails.
1980             * @throws FTPDataTransferException
1981             *             If a I/O occurs in the data transfer connection. If you
1982             *             receive this exception the transfer failed, but the main
1983             *             connection with the remote FTP server is in theory still
1984             *             working.
1985             * @throws FTPAbortedException
1986             *             If operation is aborted by another thread.
1987             * @see FTPClient#abortCurrentDataTransfer(boolean)
1988             */
1989            public void upload(File file, long restartAt,
1990                            FTPDataTransferListener listener) throws IllegalStateException,
1991                            FileNotFoundException, IOException, FTPIllegalReplyException,
1992                            FTPException, FTPDataTransferException, FTPAbortedException {
1993                    if (!file.exists()) {
1994                            throw new FileNotFoundException(file.getAbsolutePath());
1995                    }
1996                    InputStream inputStream = null;
1997                    try {
1998                            inputStream = new FileInputStream(file);
1999                    } catch (IOException e) {
2000                            throw new FTPDataTransferException(e);
2001                    }
2002                    try {
2003                            upload(file.getName(), inputStream, restartAt, restartAt, file
2004                                            .length(), listener);
2005                    } catch (IllegalStateException e) {
2006                            throw e;
2007                    } catch (IOException e) {
2008                            throw e;
2009                    } catch (FTPIllegalReplyException e) {
2010                            throw e;
2011                    } catch (FTPException e) {
2012                            throw e;
2013                    } catch (FTPDataTransferException e) {
2014                            throw e;
2015                    } catch (FTPAbortedException e) {
2016                            throw e;
2017                    } finally {
2018                            if (inputStream != null) {
2019                                    try {
2020                                            inputStream.close();
2021                                    } catch (Throwable t) {
2022                                            ;
2023                                    }
2024                            }
2025                    }
2026            }
2027    
2028            /**
2029             * This method resumes an upload to the remote server.
2030             * 
2031             * Note that no byte is skipped in the given inputStream. The restartAt
2032             * value is, in this case, an information for the remote server.
2033             * 
2034             * Calling this method blocks the current thread until the operation is
2035             * completed. The operation could be interrupted by another thread calling
2036             * abortCurrentDataTransfer(). The method will break with a
2037             * FTPAbortedException.
2038             * 
2039             * @param fileName
2040             *            The name of the remote file.
2041             * @param inputStream
2042             *            The source of data.
2043             * @param restartAt
2044             *            The restart point (number of bytes already uploaded).
2045             * @param streamOffset
2046             *            The offset to skip in the stream.
2047             * @param streamLength
2048             *            The length of the data of the stream to upload.
2049             * @param listener
2050             *            The listener for the operation. Could be null.
2051             * @throws IllegalStateException
2052             *             If the client is not connected or not authenticated.
2053             * @throws IOException
2054             *             If an I/O error occurs.
2055             * @throws FTPIllegalReplyException
2056             *             If the server replies in an illegal way.
2057             * @throws FTPException
2058             *             If the operation fails.
2059             * @throws FTPDataTransferException
2060             *             If a I/O occurs in the data transfer connection. If you
2061             *             receive this exception the transfer failed, but the main
2062             *             connection with the remote FTP server is in theory still
2063             *             working.
2064             * @throws FTPAbortedException
2065             *             If operation is aborted by another thread.
2066             * @see FTPClient#abortCurrentDataTransfer(boolean)
2067             */
2068            public void upload(String fileName, InputStream inputStream,
2069                            long restartAt, long streamOffset, long streamLength,
2070                            FTPDataTransferListener listener) throws IllegalStateException,
2071                            IOException, FTPIllegalReplyException, FTPException,
2072                            FTPDataTransferException, FTPAbortedException {
2073                    synchronized (lock) {
2074                            // Is this client connected?
2075                            if (!connected) {
2076                                    throw new IllegalStateException("Client not connected");
2077                            }
2078                            // Is this client authenticated?
2079                            if (!authenticated) {
2080                                    throw new IllegalStateException("Client not authenticated");
2081                            }
2082                            // The connection for the data transfer.
2083                            FTPConnection dtConnection;
2084                            // Active or passive?
2085                            if (passive) {
2086                                    dtConnection = openPassiveDataTransferChannel();
2087                            } else {
2088                                    dtConnection = openActiveDataTransferChannel();
2089                            }
2090                            // Select the type of contents.
2091                            int tp = type;
2092                            if (tp == TYPE_AUTO) {
2093                                    tp = detectType(fileName);
2094                            }
2095                            if (tp == TYPE_TEXTUAL) {
2096                                    communication.sendFTPCommand("TYPE A");
2097                            } else if (tp == TYPE_BINARY) {
2098                                    communication.sendFTPCommand("TYPE I");
2099                            }
2100                            FTPReply r = communication.readFTPReply();
2101                            if (!r.isSuccessCode()) {
2102                                    throw new FTPException(r);
2103                            }
2104                            // Send the REST command.
2105                            communication.sendFTPCommand("REST " + restartAt);
2106                            r = communication.readFTPReply();
2107                            if (r.getCode() != 350) {
2108                                    try {
2109                                            dtConnection.close();
2110                                    } catch (Throwable t) {
2111                                            ;
2112                                    }
2113                                    throw new FTPException(r);
2114                            }
2115                            // Send the STOR command.
2116                            communication.sendFTPCommand("STOR " + fileName);
2117                            r = communication.readFTPReply();
2118                            if (r.getCode() != 150 && r.getCode() != 125) {
2119                                    try {
2120                                            dtConnection.close();
2121                                    } catch (Throwable t) {
2122                                            ;
2123                                    }
2124                                    throw new FTPException(r);
2125                            }
2126                            // Change the operation status.
2127                            synchronized (abortLock) {
2128                                    ongoingDataTransfer = true;
2129                                    aborted = false;
2130                            }
2131                            dataTransferOutputStream = dtConnection.getOutputStream();
2132                            // Prepare and start a NOOP thread.
2133                            Runnable nooper = new Runnable() {
2134                                    public void run() {
2135                                            while (!Thread.interrupted()) {
2136                                                    // Sleep.
2137                                                    try {
2138                                                            Thread.sleep(10000);
2139                                                    } catch (InterruptedException e) {
2140                                                            break;
2141                                                    }
2142                                                    // Send NOOP.
2143                                                    try {
2144                                                            communication.sendFTPCommand("NOOP");
2145                                                            communication.readFTPReply();
2146                                                    } catch (Throwable t) {
2147                                                            ;
2148                                                    }
2149                                            }
2150                                    }
2151                            };
2152                            Thread nooperThread = new Thread(nooper);
2153                            nooperThread.start();
2154                            // Upload the stream.
2155                            long done = 0;
2156                            try {
2157                                    inputStream.skip(streamOffset);
2158                                    if (listener != null) {
2159                                            listener.started();
2160                                    }
2161                                    if (tp == TYPE_TEXTUAL) {
2162                                            Reader reader = new InputStreamReader(inputStream);
2163                                            Writer writer = new OutputStreamWriter(
2164                                                            dataTransferOutputStream, "UTF-8");
2165                                            char[] buffer = new char[1024];
2166                                            while (done < streamLength) {
2167                                                    int l = reader.read(buffer, 0, (int) Math.min(
2168                                                                    buffer.length, streamLength - done));
2169                                                    if (l == -1) {
2170                                                            throw new IOException("End of stream reached");
2171                                                    } else {
2172                                                            writer.write(buffer, 0, l);
2173                                                            done += l;
2174                                                            if (listener != null) {
2175                                                                    listener.transferred(l);
2176                                                            }
2177                                                    }
2178                                            }
2179                                    } else if (tp == TYPE_BINARY) {
2180                                            byte[] buffer = new byte[1024];
2181                                            while (done < streamLength) {
2182                                                    int l = inputStream.read(buffer, 0, (int) Math.min(
2183                                                                    buffer.length, streamLength - done));
2184                                                    if (l == -1) {
2185                                                            throw new IOException("End of stream reached");
2186                                                    } else {
2187                                                            dataTransferOutputStream.write(buffer, 0, l);
2188                                                            done += l;
2189                                                            if (listener != null) {
2190                                                                    listener.transferred(l);
2191                                                            }
2192                                                    }
2193                                            }
2194                                    }
2195                            } catch (IOException e) {
2196                                    synchronized (abortLock) {
2197                                            if (aborted) {
2198                                                    if (listener != null) {
2199                                                            listener.aborted();
2200                                                    }
2201                                                    throw new FTPAbortedException();
2202                                            } else {
2203                                                    if (listener != null) {
2204                                                            listener.failed();
2205                                                    }
2206                                                    throw new FTPDataTransferException(
2207                                                                    "I/O error in data transfer", e);
2208                                            }
2209                                    }
2210                            } finally {
2211                                    // Stop nooping.
2212                                    nooperThread.interrupt();
2213                                    for (;;) {
2214                                            try {
2215                                                    nooperThread.join();
2216                                                    break;
2217                                            } catch (InterruptedException e) {
2218                                                    continue;
2219                                            }
2220                                    }
2221                                    // Closing stream and data connection.
2222                                    if (dataTransferOutputStream != null) {
2223                                            try {
2224                                                    dataTransferOutputStream.close();
2225                                            } catch (Throwable t) {
2226                                                    ;
2227                                            }
2228                                    }
2229                                    try {
2230                                            dtConnection.close();
2231                                    } catch (Throwable t) {
2232                                            ;
2233                                    }
2234                                    // Set to null the instance-level input stream.
2235                                    dataTransferOutputStream = null;
2236                                    // Consume the result reply of the transfer.
2237                                    communication.readFTPReply();
2238                                    // Change the operation status.
2239                                    synchronized (abortLock) {
2240                                            ongoingDataTransfer = false;
2241                                            aborted = false;
2242                                    }
2243                            }
2244                            if (listener != null) {
2245                                    listener.completed();
2246                            }
2247                    }
2248            }
2249    
2250            /**
2251             * This method downloads a remote file from the server to a local file.
2252             * 
2253             * Calling this method blocks the current thread until the operation is
2254             * completed. The operation could be interrupted by another thread calling
2255             * abortCurrentDataTransfer(). The method will break with a
2256             * FTPAbortedException.
2257             * 
2258             * @param remoteFileName
2259             *            The name of the file to download.
2260             * @param localFile
2261             *            The local file.
2262             * @throws IllegalStateException
2263             *             If the client is not connected or not authenticated.
2264             * @throws FileNotFoundException
2265             *             If the supplied file cannot be found.
2266             * @throws IOException
2267             *             If an I/O error occurs.
2268             * @throws FTPIllegalReplyException
2269             *             If the server replies in an illegal way.
2270             * @throws FTPException
2271             *             If the operation fails.
2272             * @throws FTPDataTransferException
2273             *             If a I/O occurs in the data transfer connection. If you
2274             *             receive this exception the transfer failed, but the main
2275             *             connection with the remote FTP server is in theory still
2276             *             working.
2277             * @throws FTPAbortedException
2278             *             If operation is aborted by another thread.
2279             * @see FTPClient#abortCurrentDataTransfer(boolean)
2280             */
2281            public void download(String remoteFileName, File localFile)
2282                            throws IllegalStateException, FileNotFoundException, IOException,
2283                            FTPIllegalReplyException, FTPException, FTPDataTransferException,
2284                            FTPAbortedException {
2285                    download(remoteFileName, localFile, 0, null);
2286            }
2287    
2288            /**
2289             * This method downloads a remote file from the server to a local file.
2290             * 
2291             * Calling this method blocks the current thread until the operation is
2292             * completed. The operation could be interrupted by another thread calling
2293             * abortCurrentDataTransfer(). The method will break with a
2294             * FTPAbortedException.
2295             * 
2296             * @param remoteFileName
2297             *            The name of the file to download.
2298             * @param localFile
2299             *            The local file.
2300             * @param listener
2301             *            The listener for the operation. Could be null.
2302             * @throws IllegalStateException
2303             *             If the client is not connected or not authenticated.
2304             * @throws FileNotFoundException
2305             *             If the supplied file cannot be found.
2306             * @throws IOException
2307             *             If an I/O error occurs.
2308             * @throws FTPIllegalReplyException
2309             *             If the server replies in an illegal way.
2310             * @throws FTPException
2311             *             If the operation fails.
2312             * @throws FTPDataTransferException
2313             *             If a I/O occurs in the data transfer connection. If you
2314             *             receive this exception the transfer failed, but the main
2315             *             connection with the remote FTP server is in theory still
2316             *             working.
2317             * @throws FTPAbortedException
2318             *             If operation is aborted by another thread.
2319             * @see FTPClient#abortCurrentDataTransfer(boolean)
2320             */
2321            public void download(String remoteFileName, File localFile,
2322                            FTPDataTransferListener listener) throws IllegalStateException,
2323                            FileNotFoundException, IOException, FTPIllegalReplyException,
2324                            FTPException, FTPDataTransferException, FTPAbortedException {
2325                    download(remoteFileName, localFile, 0, listener);
2326            }
2327    
2328            /**
2329             * This method resumes a download operation from the remote server to a
2330             * local file.
2331             * 
2332             * Calling this method blocks the current thread until the operation is
2333             * completed. The operation could be interrupted by another thread calling
2334             * abortCurrentDataTransfer(). The method will break with a
2335             * FTPAbortedException.
2336             * 
2337             * @param remoteFileName
2338             *            The name of the file to download.
2339             * @param localFile
2340             *            The local file.
2341             * @param restartAt
2342             *            The restart point (number of bytes already downloaded).
2343             * @throws IllegalStateException
2344             *             If the client is not connected or not authenticated.
2345             * @throws FileNotFoundException
2346             *             If the supplied file cannot be found.
2347             * @throws IOException
2348             *             If an I/O error occurs.
2349             * @throws FTPIllegalReplyException
2350             *             If the server replies in an illegal way.
2351             * @throws FTPException
2352             *             If the operation fails.
2353             * @throws FTPDataTransferException
2354             *             If a I/O occurs in the data transfer connection. If you
2355             *             receive this exception the transfer failed, but the main
2356             *             connection with the remote FTP server is in theory still
2357             *             working.
2358             * @throws FTPAbortedException
2359             *             If operation is aborted by another thread.
2360             * @see FTPClient#abortCurrentDataTransfer(boolean)
2361             */
2362            public void download(String remoteFileName, File localFile, long restartAt)
2363                            throws IllegalStateException, FileNotFoundException, IOException,
2364                            FTPIllegalReplyException, FTPException, FTPDataTransferException,
2365                            FTPAbortedException {
2366                    download(remoteFileName, localFile, restartAt, null);
2367            }
2368    
2369            /**
2370             * This method resumes a download operation from the remote server to a
2371             * local file.
2372             * 
2373             * Calling this method blocks the current thread until the operation is
2374             * completed. The operation could be interrupted by another thread calling
2375             * abortCurrentDataTransfer(). The method will break with a
2376             * FTPAbortedException.
2377             * 
2378             * @param remoteFileName
2379             *            The name of the file to download.
2380             * @param localFile
2381             *            The local file.
2382             * @param restartAt
2383             *            The restart point (number of bytes already downloaded).
2384             * @param listener
2385             *            The listener for the operation. Could be null.
2386             * @throws IllegalStateException
2387             *             If the client is not connected or not authenticated.
2388             * @throws FileNotFoundException
2389             *             If the supplied file cannot be found.
2390             * @throws IOException
2391             *             If an I/O error occurs.
2392             * @throws FTPIllegalReplyException
2393             *             If the server replies in an illegal way.
2394             * @throws FTPException
2395             *             If the operation fails.
2396             * @throws FTPDataTransferException
2397             *             If a I/O occurs in the data transfer connection. If you
2398             *             receive this exception the transfer failed, but the main
2399             *             connection with the remote FTP server is in theory still
2400             *             working.
2401             * @throws FTPAbortedException
2402             *             If operation is aborted by another thread.
2403             * @see FTPClient#abortCurrentDataTransfer(boolean)
2404             */
2405            public void download(String remoteFileName, File localFile, long restartAt,
2406                            FTPDataTransferListener listener) throws IllegalStateException,
2407                            FileNotFoundException, IOException, FTPIllegalReplyException,
2408                            FTPException, FTPDataTransferException, FTPAbortedException {
2409                    OutputStream outputStream = null;
2410                    try {
2411                            outputStream = new FileOutputStream(localFile, true);
2412                    } catch (IOException e) {
2413                            throw new FTPDataTransferException(e);
2414                    }
2415                    try {
2416                            download(remoteFileName, outputStream, restartAt, listener);
2417                    } catch (IllegalStateException e) {
2418                            throw e;
2419                    } catch (IOException e) {
2420                            throw e;
2421                    } catch (FTPIllegalReplyException e) {
2422                            throw e;
2423                    } catch (FTPException e) {
2424                            throw e;
2425                    } catch (FTPDataTransferException e) {
2426                            throw e;
2427                    } catch (FTPAbortedException e) {
2428                            throw e;
2429                    } finally {
2430                            if (outputStream != null) {
2431                                    try {
2432                                            outputStream.close();
2433                                    } catch (Throwable t) {
2434                                            ;
2435                                    }
2436                            }
2437                    }
2438            }
2439    
2440            /**
2441             * This method resumes a download operation from the remote server.
2442             * 
2443             * Calling this method blocks the current thread until the operation is
2444             * completed. The operation could be interrupted by another thread calling
2445             * abortCurrentDataTransfer(). The method will break with a
2446             * FTPAbortedException.
2447             * 
2448             * @param fileName
2449             *            The name of the remote file.
2450             * @param outputStream
2451             *            The destination stream of data read during the download.
2452             * @param restartAt
2453             *            The restart point (number of bytes already downloaded).
2454             * @param listener
2455             *            The listener for the operation. Could be null.
2456             * @throws IllegalStateException
2457             *             If the client is not connected or not authenticated.
2458             * @throws IOException
2459             *             If an I/O error occurs.
2460             * @throws FTPIllegalReplyException
2461             *             If the server replies in an illegal way.
2462             * @throws FTPException
2463             *             If the operation fails.
2464             * @throws FTPDataTransferException
2465             *             If a I/O occurs in the data transfer connection. If you
2466             *             receive this exception the transfer failed, but the main
2467             *             connection with the remote FTP server is in theory still
2468             *             working.
2469             * @throws FTPAbortedException
2470             *             If operation is aborted by another thread.
2471             * @see FTPClient#abortCurrentDataTransfer(boolean)
2472             */
2473            public void download(String fileName, OutputStream outputStream,
2474                            long restartAt, FTPDataTransferListener listener)
2475                            throws IllegalStateException, IOException,
2476                            FTPIllegalReplyException, FTPException, FTPDataTransferException,
2477                            FTPAbortedException {
2478                    synchronized (lock) {
2479                            // Is this client connected?
2480                            if (!connected) {
2481                                    throw new IllegalStateException("Client not connected");
2482                            }
2483                            // Is this client authenticated?
2484                            if (!authenticated) {
2485                                    throw new IllegalStateException("Client not authenticated");
2486                            }
2487                            // The connection for the data transfer.
2488                            FTPConnection dtConnection;
2489                            // Active or passive?
2490                            if (passive) {
2491                                    dtConnection = openPassiveDataTransferChannel();
2492                            } else {
2493                                    dtConnection = openActiveDataTransferChannel();
2494                            }
2495                            // Select the type of contents.
2496                            int tp = type;
2497                            if (tp == TYPE_AUTO) {
2498                                    tp = detectType(fileName);
2499                            }
2500                            if (tp == TYPE_TEXTUAL) {
2501                                    communication.sendFTPCommand("TYPE A");
2502                            } else if (tp == TYPE_BINARY) {
2503                                    communication.sendFTPCommand("TYPE I");
2504                            }
2505                            FTPReply r = communication.readFTPReply();
2506                            if (!r.isSuccessCode()) {
2507                                    throw new FTPException(r);
2508                            }
2509                            // Send the REST command.
2510                            communication.sendFTPCommand("REST " + restartAt);
2511                            r = communication.readFTPReply();
2512                            if (r.getCode() != 350) {
2513                                    try {
2514                                            dtConnection.close();
2515                                    } catch (Throwable t) {
2516                                            ;
2517                                    }
2518                                    throw new FTPException(r);
2519                            }
2520                            // Send the RETR command.
2521                            communication.sendFTPCommand("RETR " + fileName);
2522                            r = communication.readFTPReply();
2523                            if (r.getCode() != 150 && r.getCode() != 125) {
2524                                    try {
2525                                            dtConnection.close();
2526                                    } catch (Throwable t) {
2527                                            ;
2528                                    }
2529                                    throw new FTPException(r);
2530                            }
2531                            // Change the operation status.
2532                            synchronized (abortLock) {
2533                                    ongoingDataTransfer = true;
2534                                    aborted = false;
2535                            }
2536                            dataTransferInputStream = dtConnection.getInputStream();
2537                            // Prepare and start a NOOP thread.
2538                            Runnable nooper = new Runnable() {
2539                                    public void run() {
2540                                            while (!Thread.interrupted()) {
2541                                                    // Sleep.
2542                                                    try {
2543                                                            Thread.sleep(10000);
2544                                                    } catch (InterruptedException e) {
2545                                                            break;
2546                                                    }
2547                                                    // Send NOOP.
2548                                                    try {
2549                                                            communication.sendFTPCommand("NOOP");
2550                                                            communication.readFTPReply();
2551                                                    } catch (Throwable t) {
2552                                                            ;
2553                                                    }
2554                                            }
2555                                    }
2556                            };
2557                            Thread nooperThread = new Thread(nooper);
2558                            nooperThread.start();
2559                            // Download the stream.
2560                            try {
2561                                    if (listener != null) {
2562                                            listener.started();
2563                                    }
2564                                    if (tp == TYPE_TEXTUAL) {
2565                                            Reader reader = new InputStreamReader(
2566                                                            dataTransferInputStream, "UTF-8");
2567                                            Writer writer = new OutputStreamWriter(outputStream);
2568                                            char[] buffer = new char[1024];
2569                                            int l;
2570                                            while ((l = reader.read(buffer, 0, buffer.length)) != -1) {
2571                                                    writer.write(buffer, 0, l);
2572                                                    if (listener != null) {
2573                                                            listener.transferred(l);
2574                                                    }
2575                                            }
2576                                    } else if (tp == TYPE_BINARY) {
2577                                            byte[] buffer = new byte[1024];
2578                                            int l;
2579                                            while ((l = dataTransferInputStream.read(buffer, 0,
2580                                                            buffer.length)) != -1) {
2581                                                    outputStream.write(buffer, 0, l);
2582                                                    if (listener != null) {
2583                                                            listener.transferred(l);
2584                                                    }
2585                                            }
2586                                    }
2587                            } catch (IOException e) {
2588                                    synchronized (abortLock) {
2589                                            if (aborted) {
2590                                                    if (listener != null) {
2591                                                            listener.aborted();
2592                                                    }
2593                                                    throw new FTPAbortedException();
2594                                            } else {
2595                                                    if (listener != null) {
2596                                                            listener.failed();
2597                                                    }
2598                                                    throw new FTPDataTransferException(
2599                                                                    "I/O error in data transfer", e);
2600                                            }
2601                                    }
2602                            } finally {
2603                                    // Stop nooping.
2604                                    nooperThread.interrupt();
2605                                    for (;;) {
2606                                            try {
2607                                                    nooperThread.join();
2608                                                    break;
2609                                            } catch (InterruptedException e) {
2610                                                    continue;
2611                                            }
2612                                    }
2613                                    // Closing stream and data connection.
2614                                    if (dataTransferInputStream != null) {
2615                                            try {
2616                                                    dataTransferInputStream.close();
2617                                            } catch (Throwable t) {
2618                                                    ;
2619                                            }
2620                                    }
2621                                    try {
2622                                            dtConnection.close();
2623                                    } catch (Throwable t) {
2624                                            ;
2625                                    }
2626                                    // Set to null the instance-level input stream.
2627                                    dataTransferInputStream = null;
2628                                    // Consume the result reply of the transfer.
2629                                    communication.readFTPReply();
2630                                    // Change the operation status.
2631                                    synchronized (abortLock) {
2632                                            ongoingDataTransfer = false;
2633                                            aborted = false;
2634                                    }
2635                            }
2636                            if (listener != null) {
2637                                    listener.completed();
2638                            }
2639                    }
2640            }
2641    
2642            /**
2643             * This method detects the type for a file transfer.
2644             */
2645            private int detectType(String fileName) throws IOException,
2646                            FTPIllegalReplyException, FTPException {
2647                    int start = fileName.lastIndexOf('.');
2648                    int stop = fileName.length();
2649                    if (start > 0 && start < stop - 1) {
2650                            String ext = fileName.substring(start, stop);
2651                            ext = ext.toLowerCase();
2652                            if (textualExtensionRecognizer.isTextualExt(ext)) {
2653                                    return TYPE_TEXTUAL;
2654                            } else {
2655                                    return TYPE_BINARY;
2656                            }
2657                    } else {
2658                            return TYPE_BINARY;
2659                    }
2660            }
2661    
2662            /**
2663             * This method opens a data transfer channel in active mode.
2664             */
2665            private FTPConnection openActiveDataTransferChannel() throws IOException,
2666                            FTPIllegalReplyException, FTPException, FTPDataTransferException {
2667                    // Create a FTPDataTransferServer object.
2668                    FTPDataTransferServer server = new FTPDataTransferServer();
2669                    int port = server.getPort();
2670                    int p1 = port >>> 8;
2671                    int p2 = port & 0xff;
2672                    byte[] addr = InetAddress.getLocalHost().getAddress();
2673                    int b1 = addr[0] & 0xff;
2674                    int b2 = addr[1] & 0xff;
2675                    int b3 = addr[2] & 0xff;
2676                    int b4 = addr[3] & 0xff;
2677                    // Send the port command.
2678                    communication.sendFTPCommand("PORT " + b1 + "," + b2 + "," + b3 + ","
2679                                    + b4 + "," + p1 + "," + p2);
2680                    FTPReply r = communication.readFTPReply();
2681                    if (!r.isSuccessCode()) {
2682                            server.close();
2683                            throw new FTPException(r);
2684                    }
2685                    return server.getConnection();
2686            }
2687    
2688            /**
2689             * This method opens a data transfer channel in passive mode.
2690             */
2691            private FTPConnection openPassiveDataTransferChannel() throws IOException,
2692                            FTPIllegalReplyException, FTPException, FTPDataTransferException {
2693                    // Send the PASV command.
2694                    communication.sendFTPCommand("PASV");
2695                    // Read the reply.
2696                    FTPReply r = communication.readFTPReply();
2697                    if (!r.isSuccessCode()) {
2698                            throw new FTPException(r);
2699                    }
2700                    // Use a regexp to extract the remote address and port.
2701                    String addressAndPort = null;
2702                    String[] messages = r.getMessages();
2703                    for (int i = 0; i < messages.length; i++) {
2704                            Matcher m = PASV_PATTERN.matcher(messages[i]);
2705                            if (m.find()) {
2706                                    int start = m.start();
2707                                    int end = m.end();
2708                                    addressAndPort = messages[i].substring(start, end);
2709                                    break;
2710                            }
2711                    }
2712                    if (addressAndPort == null) {
2713                            // The remote server has not sent the coordinates for the
2714                            // data transfer connection.
2715                            throw new FTPIllegalReplyException();
2716                    }
2717                    // Parse the string extracted from the reply.
2718                    StringTokenizer st = new StringTokenizer(addressAndPort, ",");
2719                    int b1 = Integer.parseInt(st.nextToken());
2720                    int b2 = Integer.parseInt(st.nextToken());
2721                    int b3 = Integer.parseInt(st.nextToken());
2722                    int b4 = Integer.parseInt(st.nextToken());
2723                    int p1 = Integer.parseInt(st.nextToken());
2724                    int p2 = Integer.parseInt(st.nextToken());
2725                    InetAddress remoteAddress = InetAddress.getByAddress(new byte[] {
2726                                    (byte) b1, (byte) b2, (byte) b3, (byte) b4 });
2727                    int remotePort = (p1 << 8) | p2;
2728                    // Establish the connection.
2729                    final FTPConnection dtConnection;
2730                    try {
2731                            dtConnection = connector.connectForDataTransferChannel(
2732                                            remoteAddress.getHostAddress(), remotePort);
2733                    } catch (IOException e) {
2734                            throw new FTPDataTransferException(
2735                                            "Cannot connect to the remote server", e);
2736                    }
2737                    return dtConnection;
2738            }
2739    
2740            /**
2741             * If there's any ongoing data transfer operation, this method aborts it.
2742             * 
2743             * @param sendAborCommand
2744             *            If true the client will negotiate the abort procedure with the
2745             *            server, through the standard FTP ABOR command. Otherwise the
2746             *            open data transfer connection will be closed without any
2747             *            advise has sent to the server.
2748             * @throws IOException
2749             *             If the ABOR command cannot be sent due to any I/O error. This
2750             *             could happen only if force is false.
2751             * @throws FTPIllegalReplyException
2752             *             If the server reply to the ABOR command is illegal. This
2753             *             could happen only if force is false.
2754             */
2755            public void abortCurrentDataTransfer(boolean sendAborCommand)
2756                            throws IOException, FTPIllegalReplyException {
2757                    synchronized (abortLock) {
2758                            if (ongoingDataTransfer && !aborted) {
2759                                    if (sendAborCommand) {
2760                                            communication.sendFTPCommand("ABOR");
2761                                            communication.readFTPReply();
2762                                    }
2763                                    if (dataTransferInputStream != null) {
2764                                            try {
2765                                                    dataTransferInputStream.close();
2766                                            } catch (Throwable t) {
2767                                                    ;
2768                                            }
2769                                    }
2770                                    if (dataTransferOutputStream != null) {
2771                                            try {
2772                                                    dataTransferOutputStream.close();
2773                                            } catch (Throwable t) {
2774                                                    ;
2775                                            }
2776                                    }
2777                                    aborted = true;
2778                            }
2779                    }
2780            }
2781    
2782            /**
2783             * Returns the name of the charset that should be used in textual
2784             * transmissions.
2785             * 
2786             * @return The name of the charset that should be used in textual
2787             *         transmissions.
2788             */
2789            private String pickCharset() {
2790                    if (charset != null) {
2791                            return charset;
2792                    } else if (utf8Supported) {
2793                            return "UTF-8";
2794                    } else {
2795                            return System.getProperty("file.encoding");
2796                    }
2797            }
2798    
2799            public String toString() {
2800                    synchronized (lock) {
2801                            StringBuffer buffer = new StringBuffer();
2802                            buffer.append(getClass().getName());
2803                            buffer.append(" [connected=");
2804                            buffer.append(connected);
2805                            if (connected) {
2806                                    buffer.append(", host=");
2807                                    buffer.append(host);
2808                                    buffer.append(", port=");
2809                                    buffer.append(port);
2810                            }
2811                            buffer.append(", authenticated=");
2812                            buffer.append(authenticated);
2813                            if (authenticated) {
2814                                    buffer.append(", username=");
2815                                    buffer.append(username);
2816                                    buffer.append(", password=");
2817                                    StringBuffer buffer2 = new StringBuffer();
2818                                    for (int i = 0; i < password.length(); i++) {
2819                                            buffer2.append('*');
2820                                    }
2821                                    buffer.append(buffer2);
2822                            }
2823                            buffer.append(", transfer mode=");
2824                            buffer.append(passive ? "passive" : "active");
2825                            buffer.append(", transfer type=");
2826                            switch (type) {
2827                            case TYPE_AUTO:
2828                                    buffer.append("auto");
2829                                    break;
2830                            case TYPE_BINARY:
2831                                    buffer.append("binary");
2832                                    break;
2833                            case TYPE_TEXTUAL:
2834                                    buffer.append("textual");
2835                                    break;
2836                            }
2837                            buffer.append(", connector=");
2838                            buffer.append(connector);
2839                            buffer.append(", textualExtensionRecognizer=");
2840                            buffer.append(textualExtensionRecognizer);
2841                            FTPListParser[] listParsers = getListParsers();
2842                            if (listParsers.length > 0) {
2843                                    buffer.append(", listParsers=");
2844                                    for (int i = 0; i < listParsers.length; i++) {
2845                                            if (i > 0) {
2846                                                    buffer.append(", ");
2847                                            }
2848                                            buffer.append(listParsers[i]);
2849                                    }
2850                            }
2851                            FTPCommunicationListener[] communicationListeners = getCommunicationListeners();
2852                            if (communicationListeners.length > 0) {
2853                                    buffer.append(", communicationListeners=");
2854                                    for (int i = 0; i < communicationListeners.length; i++) {
2855                                            if (i > 0) {
2856                                                    buffer.append(", ");
2857                                            }
2858                                            buffer.append(communicationListeners[i]);
2859                                    }
2860                            }
2861                            buffer.append("]");
2862                            return buffer.toString();
2863                    }
2864            }
2865    
2866    }