001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018package examples.ftp;
019
020import java.io.FileInputStream;
021import java.io.FileOutputStream;
022import java.io.IOException;
023import java.io.InputStream;
024import java.io.OutputStream;
025import java.io.PrintWriter;
026import java.net.InetAddress;
027import java.net.UnknownHostException;
028import java.util.Arrays;
029
030import org.apache.commons.net.PrintCommandListener;
031import org.apache.commons.net.ftp.FTP;
032import org.apache.commons.net.ftp.FTPClient;
033import org.apache.commons.net.ftp.FTPHTTPClient;
034import org.apache.commons.net.ftp.FTPClientConfig;
035import org.apache.commons.net.ftp.FTPConnectionClosedException;
036import org.apache.commons.net.ftp.FTPFile;
037import org.apache.commons.net.ftp.FTPReply;
038import org.apache.commons.net.ftp.FTPSClient;
039import org.apache.commons.net.io.CopyStreamEvent;
040import org.apache.commons.net.io.CopyStreamListener;
041import org.apache.commons.net.util.TrustManagerUtils;
042
043/**
044 * This is an example program demonstrating how to use the FTPClient class.
045 * This program connects to an FTP server and retrieves the specified
046 * file.  If the -s flag is used, it stores the local file at the FTP server.
047 * Just so you can see what's happening, all reply strings are printed.
048 * If the -b flag is used, a binary transfer is assumed (default is ASCII).
049 * See below for further options.
050 */
051public final class FTPClientExample
052{
053
054    public static final String USAGE =
055        "Expected Parameters: [options] <hostname> <username> <password> [<remote file> [<local file>]]\n" +
056        "\nDefault behavior is to download a file and use ASCII transfer mode.\n" +
057        "\t-a - use local active mode (default is local passive)\n" +
058        "\t-A - anonymous login (omit username and password parameters)\n" +
059        "\t-b - use binary transfer mode\n" +
060        "\t-c cmd - issue arbitrary command (remote is used as a parameter if provided) \n" +
061        "\t-d - list directory details using MLSD (remote is used as the pathname if provided)\n" +
062        "\t-e - use EPSV with IPv4 (default false)\n" +
063        "\t-E - encoding to use for control channel\n" +
064        "\t-f - issue FEAT command (remote and local files are ignored)\n" +
065        "\t-h - list hidden files (applies to -l and -n only)\n" +
066        "\t-k secs - use keep-alive timer (setControlKeepAliveTimeout)\n" +
067        "\t-l - list files using LIST (remote is used as the pathname if provided)\n" +
068        "\t     Files are listed twice: first in raw mode, then as the formatted parsed data.\n" +
069        "\t     N.B. if the wrong server-type is used, output may be lost. Use -U or -S as necessary.\n" +
070        "\t-L - use lenient future dates (server dates may be up to 1 day into future)\n" +
071        "\t-m - list file details using MDTM (remote is used as the pathname if provided)\n" +
072        "\t-n - list file names using NLST (remote is used as the pathname if provided)\n" +
073        "\t-p true|false|protocol[,true|false] - use FTPSClient with the specified protocol and/or isImplicit setting\n" +
074        "\t-s - store file on server (upload)\n" +
075        "\t-S - systemType set server system type (e.g. UNIX VMS WINDOWS)\n" +
076        "\t-t - list file details using MLST (remote is used as the pathname if provided)\n" +
077        "\t-U - save unparseable responses\n" +
078        "\t-w msec - wait time for keep-alive reply (setControlKeepAliveReplyTimeout)\n" +
079        "\t-T  all|valid|none - use one of the built-in TrustManager implementations (none = JVM default)\n" +
080        "\t-y format - set default date format string\n" +
081        "\t-Y format - set recent date format string\n" +
082        "\t-Z timezone - set the server timezone for parsing LIST responses\n" +
083        "\t-z timezone - set the timezone for displaying MDTM, LIST, MLSD, MLST responses\n" +
084        "\t-PrH server[:port] - HTTP Proxy host and optional port[80] \n" +
085        "\t-PrU user - HTTP Proxy server username\n" +
086        "\t-PrP password - HTTP Proxy server password\n" +
087        "\t-# - add hash display during transfers\n";
088
089    public static void main(String[] args) throws UnknownHostException
090    {
091        boolean storeFile = false, binaryTransfer = false, error = false, listFiles = false, listNames = false, hidden = false;
092        boolean localActive = false, useEpsvWithIPv4 = false, feat = false, printHash = false;
093        boolean mlst = false, mlsd = false, mdtm = false, saveUnparseable = false;
094        boolean lenient = false;
095        long keepAliveTimeout = -1;
096        int controlKeepAliveReplyTimeout = -1;
097        int minParams = 5; // listings require 3 params
098        String protocol = null; // SSL protocol
099        String doCommand = null;
100        String trustmgr = null;
101        String proxyHost = null;
102        int proxyPort = 80;
103        String proxyUser = null;
104        String proxyPassword = null;
105        String username = null;
106        String password = null;
107        String encoding = null;
108        String serverTimeZoneId = null;
109        String displayTimeZoneId = null;
110        String serverType = null;
111        String defaultDateFormat = null;
112        String recentDateFormat = null;
113
114
115        int base = 0;
116        for (base = 0; base < args.length; base++)
117        {
118            if (args[base].equals("-s")) {
119                storeFile = true;
120            }
121            else if (args[base].equals("-a")) {
122                localActive = true;
123            }
124            else if (args[base].equals("-A")) {
125                username = "anonymous";
126                password = System.getProperty("user.name")+"@"+InetAddress.getLocalHost().getHostName();
127            }
128            else if (args[base].equals("-b")) {
129                binaryTransfer = true;
130            }
131            else if (args[base].equals("-c")) {
132                doCommand = args[++base];
133                minParams = 3;
134            }
135            else if (args[base].equals("-d")) {
136                mlsd = true;
137                minParams = 3;
138            }
139            else if (args[base].equals("-e")) {
140                useEpsvWithIPv4 = true;
141            }
142            else if (args[base].equals("-E")) {
143                encoding = args[++base];
144            }
145            else if (args[base].equals("-f")) {
146                feat = true;
147                minParams = 3;
148            }
149            else if (args[base].equals("-h")) {
150                hidden = true;
151            }
152            else if (args[base].equals("-k")) {
153                keepAliveTimeout = Long.parseLong(args[++base]);
154            }
155            else if (args[base].equals("-l")) {
156                listFiles = true;
157                minParams = 3;
158            }
159            else if (args[base].equals("-m")) {
160                mdtm = true;
161                minParams = 3;
162            }
163            else if (args[base].equals("-L")) {
164                lenient = true;
165            }
166            else if (args[base].equals("-n")) {
167                listNames = true;
168                minParams = 3;
169            }
170            else if (args[base].equals("-p")) {
171                protocol = args[++base];
172            }
173            else if (args[base].equals("-S")) {
174                serverType = args[++base];
175            }
176            else if (args[base].equals("-t")) {
177                mlst = true;
178                minParams = 3;
179            }
180            else if (args[base].equals("-U")) {
181                saveUnparseable = true;
182            }
183            else if (args[base].equals("-w")) {
184                controlKeepAliveReplyTimeout = Integer.parseInt(args[++base]);
185            }
186            else if (args[base].equals("-T")) {
187                trustmgr = args[++base];
188            }
189            else if (args[base].equals("-y")) {
190                defaultDateFormat = args[++base];
191            }
192            else if (args[base].equals("-Y")) {
193                recentDateFormat = args[++base];
194            }
195            else if (args[base].equals("-Z")) {
196                serverTimeZoneId = args[++base];
197            }
198            else if (args[base].equals("-z")) {
199                displayTimeZoneId = args[++base];
200            }
201            else if (args[base].equals("-PrH")) {
202                proxyHost = args[++base];
203                String parts[] = proxyHost.split(":");
204                if (parts.length == 2){
205                    proxyHost=parts[0];
206                    proxyPort=Integer.parseInt(parts[1]);
207                }
208            }
209            else if (args[base].equals("-PrU")) {
210                proxyUser = args[++base];
211            }
212            else if (args[base].equals("-PrP")) {
213                proxyPassword = args[++base];
214            }
215            else if (args[base].equals("-#")) {
216                printHash = true;
217            }
218            else {
219                break;
220            }
221        }
222
223        int remain = args.length - base;
224        if (username != null) {
225            minParams -= 2;
226        }
227        if (remain < minParams) // server, user, pass, remote, local [protocol]
228        {
229            if (args.length > 0) {
230                System.err.println("Actual Parameters: " + Arrays.toString(args));
231            }
232            System.err.println(USAGE);
233            System.exit(1);
234        }
235
236        String server = args[base++];
237        int port = 0;
238        String parts[] = server.split(":");
239        if (parts.length == 2){
240            server=parts[0];
241            port=Integer.parseInt(parts[1]);
242        }
243        if (username == null) {
244            username = args[base++];
245            password = args[base++];
246        }
247
248        String remote = null;
249        if (args.length - base > 0) {
250            remote = args[base++];
251        }
252
253        String local = null;
254        if (args.length - base > 0) {
255            local = args[base++];
256        }
257
258        final FTPClient ftp;
259        if (protocol == null ) {
260            if(proxyHost !=null) {
261                System.out.println("Using HTTP proxy server: " + proxyHost);
262                ftp = new FTPHTTPClient(proxyHost, proxyPort, proxyUser, proxyPassword);
263            }
264            else {
265                ftp = new FTPClient();
266            }
267        } else {
268            FTPSClient ftps;
269            if (protocol.equals("true")) {
270                ftps = new FTPSClient(true);
271            } else if (protocol.equals("false")) {
272                ftps = new FTPSClient(false);
273            } else {
274                String prot[] = protocol.split(",");
275                if (prot.length == 1) { // Just protocol
276                    ftps = new FTPSClient(protocol);
277                } else { // protocol,true|false
278                    ftps = new FTPSClient(prot[0], Boolean.parseBoolean(prot[1]));
279                }
280            }
281            ftp = ftps;
282            if ("all".equals(trustmgr)) {
283                ftps.setTrustManager(TrustManagerUtils.getAcceptAllTrustManager());
284            } else if ("valid".equals(trustmgr)) {
285                ftps.setTrustManager(TrustManagerUtils.getValidateServerCertificateTrustManager());
286            } else if ("none".equals(trustmgr)) {
287                ftps.setTrustManager(null);
288            }
289        }
290
291        if (printHash) {
292            ftp.setCopyStreamListener(createListener());
293        }
294        if (keepAliveTimeout >= 0) {
295            ftp.setControlKeepAliveTimeout(keepAliveTimeout);
296        }
297        if (controlKeepAliveReplyTimeout >= 0) {
298            ftp.setControlKeepAliveReplyTimeout(controlKeepAliveReplyTimeout);
299        }
300        if (encoding != null) {
301            ftp.setControlEncoding(encoding);
302        }
303        ftp.setListHiddenFiles(hidden);
304
305        // suppress login details
306        ftp.addProtocolCommandListener(new PrintCommandListener(new PrintWriter(System.out), true));
307
308        final FTPClientConfig config;
309        if (serverType != null) {
310            config = new FTPClientConfig(serverType);
311        } else {
312            config = new FTPClientConfig();
313        }
314        config.setUnparseableEntries(saveUnparseable);
315        if (defaultDateFormat != null) {
316            config.setDefaultDateFormatStr(defaultDateFormat);
317        }
318        if (recentDateFormat != null) {
319            config.setRecentDateFormatStr(recentDateFormat);
320        }
321        ftp.configure(config);
322
323        try
324        {
325            int reply;
326            if (port > 0) {
327                ftp.connect(server, port);
328            } else {
329                ftp.connect(server);
330            }
331            System.out.println("Connected to " + server + " on " + (port>0 ? port : ftp.getDefaultPort()));
332
333            // After connection attempt, you should check the reply code to verify
334            // success.
335            reply = ftp.getReplyCode();
336
337            if (!FTPReply.isPositiveCompletion(reply))
338            {
339                ftp.disconnect();
340                System.err.println("FTP server refused connection.");
341                System.exit(1);
342            }
343        }
344        catch (IOException e)
345        {
346            if (ftp.isConnected())
347            {
348                try
349                {
350                    ftp.disconnect();
351                }
352                catch (IOException f)
353                {
354                    // do nothing
355                }
356            }
357            System.err.println("Could not connect to server.");
358            e.printStackTrace();
359            System.exit(1);
360        }
361
362__main:
363        try
364        {
365            if (!ftp.login(username, password))
366            {
367                ftp.logout();
368                error = true;
369                break __main;
370            }
371
372            System.out.println("Remote system is " + ftp.getSystemType());
373
374            if (binaryTransfer) {
375                ftp.setFileType(FTP.BINARY_FILE_TYPE);
376            } else {
377                // in theory this should not be necessary as servers should default to ASCII
378                // but they don't all do so - see NET-500
379                ftp.setFileType(FTP.ASCII_FILE_TYPE);
380            }
381
382            // Use passive mode as default because most of us are
383            // behind firewalls these days.
384            if (localActive) {
385                ftp.enterLocalActiveMode();
386            } else {
387                ftp.enterLocalPassiveMode();
388            }
389
390            ftp.setUseEPSVwithIPv4(useEpsvWithIPv4);
391
392            if (storeFile)
393            {
394                InputStream input;
395
396                input = new FileInputStream(local);
397
398                ftp.storeFile(remote, input);
399
400                input.close();
401            }
402            // Allow multiple list types for single invocation
403            else if (listFiles || mlsd || mdtm || mlst || listNames)
404            {
405                if (mlsd) {
406                    for (FTPFile f : ftp.mlistDir(remote)) {
407                        System.out.println(f.getRawListing());
408                        System.out.println(f.toFormattedString(displayTimeZoneId));
409                    }
410                }
411                if (mdtm) {
412                    FTPFile f = ftp.mdtmFile(remote);
413                    if (f != null) {
414                        System.out.println(f.getRawListing());
415                        System.out.println(f.toFormattedString(displayTimeZoneId));
416                    } else {
417                        System.out.println("File not found");
418                    }
419                }
420                if (mlst) {
421                    FTPFile f = ftp.mlistFile(remote);
422                    if (f != null){
423                        System.out.println(f.toFormattedString(displayTimeZoneId));
424                    }
425                }
426                if (listNames) {
427                    for (String s : ftp.listNames(remote)) {
428                        System.out.println(s);
429                    }
430                }
431                // Do this last because it changes the client
432                if (listFiles) {
433                    if (lenient || serverTimeZoneId != null) {
434                        config.setLenientFutureDates(lenient);
435                        if (serverTimeZoneId != null) {
436                            config.setServerTimeZoneId(serverTimeZoneId);
437                        }
438                        ftp.configure(config );
439                    }
440
441                    for (FTPFile f : ftp.listFiles(remote)) {
442                        System.out.println(f.getRawListing());
443                        System.out.println(f.toFormattedString(displayTimeZoneId));
444                    }
445                }
446            }
447            else if (feat)
448            {
449                // boolean feature check
450                if (remote != null) { // See if the command is present
451                    if (ftp.hasFeature(remote)) {
452                        System.out.println("Has feature: "+remote);
453                    } else {
454                        if (FTPReply.isPositiveCompletion(ftp.getReplyCode())) {
455                            System.out.println("FEAT "+remote+" was not detected");
456                        } else {
457                            System.out.println("Command failed: "+ftp.getReplyString());
458                        }
459                    }
460
461                    // Strings feature check
462                    String []features = ftp.featureValues(remote);
463                    if (features != null) {
464                        for(String f : features) {
465                            System.out.println("FEAT "+remote+"="+f+".");
466                        }
467                    } else {
468                        if (FTPReply.isPositiveCompletion(ftp.getReplyCode())) {
469                            System.out.println("FEAT "+remote+" is not present");
470                        } else {
471                            System.out.println("Command failed: "+ftp.getReplyString());
472                        }
473                    }
474                } else {
475                    if (ftp.features()) {
476//                        Command listener has already printed the output
477                    } else {
478                        System.out.println("Failed: "+ftp.getReplyString());
479                    }
480                }
481            }
482            else if (doCommand != null)
483            {
484                if (ftp.doCommand(doCommand, remote)) {
485//                  Command listener has already printed the output
486//                    for(String s : ftp.getReplyStrings()) {
487//                        System.out.println(s);
488//                    }
489                } else {
490                    System.out.println("Failed: "+ftp.getReplyString());
491                }
492            }
493            else
494            {
495                OutputStream output;
496
497                output = new FileOutputStream(local);
498
499                ftp.retrieveFile(remote, output);
500
501                output.close();
502            }
503
504            ftp.noop(); // check that control connection is working OK
505
506            ftp.logout();
507        }
508        catch (FTPConnectionClosedException e)
509        {
510            error = true;
511            System.err.println("Server closed connection.");
512            e.printStackTrace();
513        }
514        catch (IOException e)
515        {
516            error = true;
517            e.printStackTrace();
518        }
519        finally
520        {
521            if (ftp.isConnected())
522            {
523                try
524                {
525                    ftp.disconnect();
526                }
527                catch (IOException f)
528                {
529                    // do nothing
530                }
531            }
532        }
533
534        System.exit(error ? 1 : 0);
535    } // end main
536
537    private static CopyStreamListener createListener(){
538        return new CopyStreamListener(){
539            private long megsTotal = 0;
540
541            @Override
542            public void bytesTransferred(CopyStreamEvent event) {
543                bytesTransferred(event.getTotalBytesTransferred(), event.getBytesTransferred(), event.getStreamSize());
544            }
545
546            @Override
547            public void bytesTransferred(long totalBytesTransferred,
548                    int bytesTransferred, long streamSize) {
549                long megs = totalBytesTransferred / 1000000;
550                for (long l = megsTotal; l < megs; l++) {
551                    System.err.print("#");
552                }
553                megsTotal = megs;
554            }
555        };
556    }
557}
558