FTPClientExample.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */

  17. package org.apache.commons.net.examples.ftp;

  18. import java.io.FileInputStream;
  19. import java.io.FileOutputStream;
  20. import java.io.IOException;
  21. import java.io.InputStream;
  22. import java.io.OutputStream;
  23. import java.net.InetAddress;
  24. import java.net.UnknownHostException;
  25. import java.time.Duration;
  26. import java.util.Arrays;

  27. import org.apache.commons.net.PrintCommandListener;
  28. import org.apache.commons.net.ftp.FTP;
  29. import org.apache.commons.net.ftp.FTPClient;
  30. import org.apache.commons.net.ftp.FTPClientConfig;
  31. import org.apache.commons.net.ftp.FTPConnectionClosedException;
  32. import org.apache.commons.net.ftp.FTPFile;
  33. import org.apache.commons.net.ftp.FTPHTTPClient;
  34. import org.apache.commons.net.ftp.FTPReply;
  35. import org.apache.commons.net.ftp.FTPSClient;
  36. import org.apache.commons.net.io.CopyStreamEvent;
  37. import org.apache.commons.net.io.CopyStreamListener;
  38. import org.apache.commons.net.io.Util;
  39. import org.apache.commons.net.util.TrustManagerUtils;

  40. /**
  41.  * This is an example program demonstrating how to use the FTPClient class. This program connects to an FTP server and retrieves the specified file. If the -s
  42.  * flag is used, it stores the local file at the FTP server. Just so you can see what's happening, all reply strings are printed. If the -b flag is used, a
  43.  * binary transfer is assumed (default is ASCII). See below for further options.
  44.  */
  45. public final class FTPClientExample {

  46.     public static final String USAGE = "Expected Parameters: [options] <hostname> <user> <password> [<remote file> [<local file>]]\n"
  47.             + "\nDefault behavior is to download a file and use ASCII transfer mode.\n" + "\t-a - use local active mode (default is local passive)\n"
  48.             + "\t-A - anonymous login (omit user and password parameters)\n" + "\t-b - use binary transfer mode\n"
  49.             + "\t-c cmd - issue arbitrary command (remote is used as a parameter if provided) \n"
  50.             + "\t-d - list directory details using MLSD (remote is used as the pathname if provided)\n" + "\t-e - use EPSV with IPv4 (default false)\n"
  51.             + "\t-E - encoding to use for control channel\n" + "\t-f - issue FEAT command (remote and local files are ignored)\n"
  52.             + "\t-h - list hidden files (applies to -l and -n only)\n" + "\t-i - issue SIZE command for a file\n"
  53.             + "\t-k secs - use keep-alive timer (setControlKeepAliveTimeout)\n" + "\t-l - list files using LIST (remote is used as the pathname if provided)\n"
  54.             + "\t     Files are listed twice: first in raw mode, then as the formatted parsed data.\n"
  55.             + "\t     N.B. if the wrong server-type is used, output may be lost. Use -U or -S as necessary.\n"
  56.             + "\t-L - use lenient future dates (server dates may be up to 1 day into future)\n"
  57.             + "\t-m - list file details using MDTM (remote is used as the pathname if provided)\n"
  58.             + "\t-n - list file names using NLST (remote is used as the pathname if provided)\n"
  59.             + "\t-p true|false|protocol[,true|false] - use FTPSClient with the specified protocol and/or isImplicit setting\n"
  60.             + "\t-s - store file on server (upload)\n" + "\t-S - systemType set server system type (e.g. UNIX VMS WINDOWS)\n"
  61.             + "\t-t - list file details using MLST (remote is used as the pathname if provided)\n" + "\t-U - save unparseable responses\n"
  62.             + "\t-w msec - wait time for keep-alive reply (setControlKeepAliveReplyTimeout)\n"
  63.             + "\t-T  all|valid|none - use one of the built-in TrustManager implementations (none = JVM default)\n"
  64.             + "\t-y format - set default date format string\n" + "\t-Y format - set recent date format string\n"
  65.             + "\t-Z timezone - set the server time zone for parsing LIST responses\n"
  66.             + "\t-z timezone - set the time zone for displaying MDTM, LIST, MLSD, MLST responses\n"
  67.             + "\t-PrH server[:port] - HTTP Proxy host and optional port[80] \n" + "\t-PrU user - HTTP Proxy server user\n"
  68.             + "\t-PrP password - HTTP Proxy server password\n" + "\t-# - add hash display during transfers\n";

  69.     private static CopyStreamListener createListener() {
  70.         return new CopyStreamListener() {
  71.             private long megsTotal;

  72.             @Override
  73.             public void bytesTransferred(final CopyStreamEvent event) {
  74.                 bytesTransferred(event.getTotalBytesTransferred(), event.getBytesTransferred(), event.getStreamSize());
  75.             }

  76.             @Override
  77.             public void bytesTransferred(final long totalBytesTransferred, final int bytesTransferred, final long streamSize) {
  78.                 final long megs = totalBytesTransferred / 1000000;
  79.                 for (long l = megsTotal; l < megs; l++) {
  80.                     System.err.print("#");
  81.                 }
  82.                 megsTotal = megs;
  83.             }
  84.         };
  85.     }

  86.     public static void main(final String[] args) throws UnknownHostException {
  87.         boolean storeFile = false, binaryTransfer = false, error = false, listFiles = false, listNames = false, hidden = false;
  88.         boolean localActive = false, useEpsvWithIPv4 = false, feat = false, printHash = false;
  89.         boolean mlst = false, mlsd = false, mdtm = false, saveUnparseable = false;
  90.         boolean size = false;
  91.         boolean lenient = false;
  92.         long keepAliveTimeoutSeconds = -1;
  93.         int controlKeepAliveReplyTimeoutMillis = -1;
  94.         int minParams = 5; // listings require 3 params
  95.         String protocol = null; // SSL protocol
  96.         String doCommand = null;
  97.         String trustmgr = null;
  98.         String proxyHost = null;
  99.         int proxyPort = 80;
  100.         String proxyUser = null;
  101.         String proxyPassword = null;
  102.         String user = null;
  103.         String password = null;
  104.         String encoding = null;
  105.         String serverTimeZoneId = null;
  106.         String displayTimeZoneId = null;
  107.         String serverType = null;
  108.         String defaultDateFormat = null;
  109.         String recentDateFormat = null;

  110.         int base = 0;
  111.         for (base = 0; base < args.length; base++) {
  112.             if (args[base].equals("-s")) {
  113.                 storeFile = true;
  114.             } else if (args[base].equals("-a")) {
  115.                 localActive = true;
  116.             } else if (args[base].equals("-A")) {
  117.                 user = "anonymous";
  118.                 password = System.getProperty("user.name") + "@" + InetAddress.getLocalHost().getHostName();
  119.             } else if (args[base].equals("-b")) {
  120.                 binaryTransfer = true;
  121.             } else if (args[base].equals("-c")) {
  122.                 doCommand = args[++base];
  123.                 minParams = 3;
  124.             } else if (args[base].equals("-d")) {
  125.                 mlsd = true;
  126.                 minParams = 3;
  127.             } else if (args[base].equals("-e")) {
  128.                 useEpsvWithIPv4 = true;
  129.             } else if (args[base].equals("-E")) {
  130.                 encoding = args[++base];
  131.             } else if (args[base].equals("-f")) {
  132.                 feat = true;
  133.                 minParams = 3;
  134.             } else if (args[base].equals("-h")) {
  135.                 hidden = true;
  136.             } else if (args[base].equals("-i")) {
  137.                 size = true;
  138.                 minParams = 3;
  139.             } else if (args[base].equals("-k")) {
  140.                 keepAliveTimeoutSeconds = Long.parseLong(args[++base]);
  141.             } else if (args[base].equals("-l")) {
  142.                 listFiles = true;
  143.                 minParams = 3;
  144.             } else if (args[base].equals("-m")) {
  145.                 mdtm = true;
  146.                 minParams = 3;
  147.             } else if (args[base].equals("-L")) {
  148.                 lenient = true;
  149.             } else if (args[base].equals("-n")) {
  150.                 listNames = true;
  151.                 minParams = 3;
  152.             } else if (args[base].equals("-p")) {
  153.                 protocol = args[++base];
  154.             } else if (args[base].equals("-S")) {
  155.                 serverType = args[++base];
  156.             } else if (args[base].equals("-t")) {
  157.                 mlst = true;
  158.                 minParams = 3;
  159.             } else if (args[base].equals("-U")) {
  160.                 saveUnparseable = true;
  161.             } else if (args[base].equals("-w")) {
  162.                 controlKeepAliveReplyTimeoutMillis = Integer.parseInt(args[++base]);
  163.             } else if (args[base].equals("-T")) {
  164.                 trustmgr = args[++base];
  165.             } else if (args[base].equals("-y")) {
  166.                 defaultDateFormat = args[++base];
  167.             } else if (args[base].equals("-Y")) {
  168.                 recentDateFormat = args[++base];
  169.             } else if (args[base].equals("-Z")) {
  170.                 serverTimeZoneId = args[++base];
  171.             } else if (args[base].equals("-z")) {
  172.                 displayTimeZoneId = args[++base];
  173.             } else if (args[base].equals("-PrH")) {
  174.                 proxyHost = args[++base];
  175.                 final String[] parts = proxyHost.split(":");
  176.                 if (parts.length == 2) {
  177.                     proxyHost = parts[0];
  178.                     proxyPort = Integer.parseInt(parts[1]);
  179.                 }
  180.             } else if (args[base].equals("-PrU")) {
  181.                 proxyUser = args[++base];
  182.             } else if (args[base].equals("-PrP")) {
  183.                 proxyPassword = args[++base];
  184.             } else if (args[base].equals("-#")) {
  185.                 printHash = true;
  186.             } else {
  187.                 break;
  188.             }
  189.         }

  190.         final int remain = args.length - base;
  191.         if (user != null) {
  192.             minParams -= 2;
  193.         }
  194.         if (remain < minParams) // server, user, pass, remote, local [protocol]
  195.         {
  196.             if (args.length > 0) {
  197.                 System.err.println("Actual Parameters: " + Arrays.toString(args));
  198.             }
  199.             System.err.println(USAGE);
  200.             System.exit(1);
  201.         }

  202.         String server = args[base++];
  203.         int port = 0;
  204.         final String[] parts = server.split(":");
  205.         if (parts.length == 2) {
  206.             server = parts[0];
  207.             port = Integer.parseInt(parts[1]);
  208.         }
  209.         if (user == null) {
  210.             user = args[base++];
  211.             password = args[base++];
  212.         }

  213.         String remote = null;
  214.         if (args.length - base > 0) {
  215.             remote = args[base++];
  216.         }

  217.         String local = null;
  218.         if (args.length - base > 0) {
  219.             local = args[base++];
  220.         }

  221.         final FTPClient ftp;
  222.         if (protocol == null) {
  223.             if (proxyHost != null) {
  224.                 System.out.println("Using HTTP proxy server: " + proxyHost);
  225.                 ftp = new FTPHTTPClient(proxyHost, proxyPort, proxyUser, proxyPassword);
  226.             } else {
  227.                 ftp = new FTPClient();
  228.             }
  229.         } else {
  230.             final FTPSClient ftps;
  231.             if (protocol.equals("true")) {
  232.                 ftps = new FTPSClient(true);
  233.             } else if (protocol.equals("false")) {
  234.                 ftps = new FTPSClient(false);
  235.             } else {
  236.                 final String[] prot = protocol.split(",");
  237.                 if (prot.length == 1) { // Just protocol
  238.                     ftps = new FTPSClient(protocol);
  239.                 } else { // protocol,true|false
  240.                     ftps = new FTPSClient(prot[0], Boolean.parseBoolean(prot[1]));
  241.                 }
  242.             }
  243.             ftp = ftps;
  244.             if ("all".equals(trustmgr)) {
  245.                 ftps.setTrustManager(TrustManagerUtils.getAcceptAllTrustManager());
  246.             } else if ("valid".equals(trustmgr)) {
  247.                 ftps.setTrustManager(TrustManagerUtils.getValidateServerCertificateTrustManager());
  248.             } else if ("none".equals(trustmgr)) {
  249.                 ftps.setTrustManager(null);
  250.             }
  251.         }

  252.         if (printHash) {
  253.             ftp.setCopyStreamListener(createListener());
  254.         }
  255.         if (keepAliveTimeoutSeconds >= 0) {
  256.             ftp.setControlKeepAliveTimeout(Duration.ofSeconds(keepAliveTimeoutSeconds));
  257.         }
  258.         if (controlKeepAliveReplyTimeoutMillis >= 0) {
  259.             ftp.setControlKeepAliveReplyTimeout(Duration.ofMillis(controlKeepAliveReplyTimeoutMillis));
  260.         }
  261.         if (encoding != null) {
  262.             ftp.setControlEncoding(encoding);
  263.         }
  264.         ftp.setListHiddenFiles(hidden);

  265.         // suppress login details
  266.         ftp.addProtocolCommandListener(new PrintCommandListener(Util.newPrintWriter(System.out), true));

  267.         final FTPClientConfig config;
  268.         if (serverType != null) {
  269.             config = new FTPClientConfig(serverType);
  270.         } else {
  271.             config = new FTPClientConfig();
  272.         }
  273.         config.setUnparseableEntries(saveUnparseable);
  274.         if (defaultDateFormat != null) {
  275.             config.setDefaultDateFormatStr(defaultDateFormat);
  276.         }
  277.         if (recentDateFormat != null) {
  278.             config.setRecentDateFormatStr(recentDateFormat);
  279.         }
  280.         ftp.configure(config);

  281.         try {
  282.             final int reply;
  283.             if (port > 0) {
  284.                 ftp.connect(server, port);
  285.             } else {
  286.                 ftp.connect(server);
  287.             }
  288.             System.out.println("Connected to " + server + " on " + (port > 0 ? port : ftp.getDefaultPort()));

  289.             // After connection attempt, you should check the reply code to verify
  290.             // success.
  291.             reply = ftp.getReplyCode();

  292.             if (!FTPReply.isPositiveCompletion(reply)) {
  293.                 ftp.disconnect();
  294.                 System.err.println("FTP server refused connection.");
  295.                 System.exit(1);
  296.             }
  297.         } catch (final IOException e) {
  298.             if (ftp.isConnected()) {
  299.                 try {
  300.                     ftp.disconnect();
  301.                 } catch (final IOException f) {
  302.                     // do nothing
  303.                 }
  304.             }
  305.             System.err.println("Could not connect to server.");
  306.             e.printStackTrace();
  307.             System.exit(1);
  308.         }

  309.         __main: try {
  310.             if (!ftp.login(user, password)) {
  311.                 ftp.logout();
  312.                 error = true;
  313.                 break __main;
  314.             }

  315.             System.out.println("Remote system is " + ftp.getSystemType());

  316.             if (binaryTransfer) {
  317.                 ftp.setFileType(FTP.BINARY_FILE_TYPE);
  318.             } else {
  319.                 // in theory this should not be necessary as servers should default to ASCII,
  320.                 // but they don't all do so - see NET-500
  321.                 ftp.setFileType(FTP.ASCII_FILE_TYPE);
  322.             }

  323.             // Use passive mode as default because most of us are
  324.             // behind firewalls these days.
  325.             if (localActive) {
  326.                 ftp.enterLocalActiveMode();
  327.             } else {
  328.                 ftp.enterLocalPassiveMode();
  329.             }

  330.             ftp.setUseEPSVwithIPv4(useEpsvWithIPv4);

  331.             if (storeFile) {
  332.                 try (final InputStream input = new FileInputStream(local)) {
  333.                     ftp.storeFile(remote, input);
  334.                 }

  335.                 if (keepAliveTimeoutSeconds > 0) {
  336.                     showCslStats(ftp);
  337.                 }
  338.             }
  339.             // Allow multiple list types for single invocation
  340.             else if (listFiles || mlsd || mdtm || mlst || listNames || size) {
  341.                 if (mlsd) {
  342.                     for (final FTPFile f : ftp.mlistDir(remote)) {
  343.                         System.out.println(f.getRawListing());
  344.                         System.out.println(f.toFormattedString(displayTimeZoneId));
  345.                     }
  346.                 }
  347.                 if (mdtm) {
  348.                     final FTPFile f = ftp.mdtmFile(remote);
  349.                     if (f != null) {
  350.                         System.out.println(f.getRawListing());
  351.                         System.out.println(f.toFormattedString(displayTimeZoneId));
  352.                     } else {
  353.                         System.out.println("File not found");
  354.                     }
  355.                 }
  356.                 if (mlst) {
  357.                     final FTPFile f = ftp.mlistFile(remote);
  358.                     if (f != null) {
  359.                         System.out.println(f.toFormattedString(displayTimeZoneId));
  360.                     }
  361.                 }
  362.                 if (listNames) {
  363.                     for (final String s : ftp.listNames(remote)) {
  364.                         System.out.println(s);
  365.                     }
  366.                 }
  367.                 if (size) {
  368.                     System.out.println("Size=" + ftp.getSize(remote));
  369.                 }
  370.                 // Do this last because it changes the client
  371.                 if (listFiles) {
  372.                     if (lenient || serverTimeZoneId != null) {
  373.                         config.setLenientFutureDates(lenient);
  374.                         if (serverTimeZoneId != null) {
  375.                             config.setServerTimeZoneId(serverTimeZoneId);
  376.                         }
  377.                         ftp.configure(config);
  378.                     }

  379.                     for (final FTPFile f : ftp.listFiles(remote)) {
  380.                         System.out.println(f.getRawListing());
  381.                         System.out.println(f.toFormattedString(displayTimeZoneId));
  382.                     }
  383.                 }
  384.             } else if (feat) {
  385.                 // boolean feature check
  386.                 if (remote != null) { // See if the command is present
  387.                     if (ftp.hasFeature(remote)) {
  388.                         System.out.println("Has feature: " + remote);
  389.                     } else if (FTPReply.isPositiveCompletion(ftp.getReplyCode())) {
  390.                         System.out.println("FEAT " + remote + " was not detected");
  391.                     } else {
  392.                         System.out.println("Command failed: " + ftp.getReplyString());
  393.                     }

  394.                     // Strings feature check
  395.                     final String[] features = ftp.featureValues(remote);
  396.                     if (features != null) {
  397.                         for (final String f : features) {
  398.                             System.out.println("FEAT " + remote + "=" + f + ".");
  399.                         }
  400.                     } else if (FTPReply.isPositiveCompletion(ftp.getReplyCode())) {
  401.                         System.out.println("FEAT " + remote + " is not present");
  402.                     } else {
  403.                         System.out.println("Command failed: " + ftp.getReplyString());
  404.                     }
  405.                 } else if (ftp.features()) {
  406. //                        Command listener has already printed the output
  407.                 } else {
  408.                     System.out.println("Failed: " + ftp.getReplyString());
  409.                 }
  410.             } else if (doCommand != null) {
  411.                 if (ftp.doCommand(doCommand, remote)) {
  412. //                  Command listener has already printed the output
  413. //                    for(String s : ftp.getReplyStrings()) {
  414. //                        System.out.println(s);
  415. //                    }
  416.                 } else {
  417.                     System.out.println("Failed: " + ftp.getReplyString());
  418.                 }
  419.             } else {
  420.                 try (final OutputStream output = new FileOutputStream(local)) {
  421.                     ftp.retrieveFile(remote, output);
  422.                 }

  423.                 if (keepAliveTimeoutSeconds > 0) {
  424.                     showCslStats(ftp);
  425.                 }
  426.             }

  427.             ftp.noop(); // check that control connection is working OK

  428.             ftp.logout();
  429.         } catch (final FTPConnectionClosedException e) {
  430.             error = true;
  431.             System.err.println("Server closed connection.");
  432.             e.printStackTrace();
  433.         } catch (final IOException e) {
  434.             error = true;
  435.             e.printStackTrace();
  436.         } finally {
  437.             if (ftp.isConnected()) {
  438.                 try {
  439.                     ftp.disconnect();
  440.                 } catch (final IOException f) {
  441.                     // do nothing
  442.                 }
  443.             }
  444.         }

  445.         System.exit(error ? 1 : 0);
  446.     } // end main

  447.     private static void showCslStats(final FTPClient ftp) {
  448.         @SuppressWarnings("deprecation") // debug code
  449.         final int[] stats = ftp.getCslDebug();
  450.         System.out.println("CslDebug=" + Arrays.toString(stats));

  451.     }
  452. }