View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.net.examples.ftp;
19  
20  import java.io.FileInputStream;
21  import java.io.FileOutputStream;
22  import java.io.IOException;
23  import java.io.InputStream;
24  import java.io.OutputStream;
25  import java.io.PrintWriter;
26  import java.net.InetAddress;
27  import java.net.UnknownHostException;
28  import java.time.Duration;
29  import java.util.Arrays;
30  
31  import org.apache.commons.net.PrintCommandListener;
32  import org.apache.commons.net.ftp.FTP;
33  import org.apache.commons.net.ftp.FTPClient;
34  import org.apache.commons.net.ftp.FTPClientConfig;
35  import org.apache.commons.net.ftp.FTPConnectionClosedException;
36  import org.apache.commons.net.ftp.FTPFile;
37  import org.apache.commons.net.ftp.FTPHTTPClient;
38  import org.apache.commons.net.ftp.FTPReply;
39  import org.apache.commons.net.ftp.FTPSClient;
40  import org.apache.commons.net.io.CopyStreamEvent;
41  import org.apache.commons.net.io.CopyStreamListener;
42  import org.apache.commons.net.util.TrustManagerUtils;
43  
44  /**
45   * 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
46   * 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
47   * binary transfer is assumed (default is ASCII). See below for further options.
48   */
49  public final class FTPClientExample {
50  
51      public static final String USAGE = "Expected Parameters: [options] <hostname> <user> <password> [<remote file> [<local file>]]\n"
52              + "\nDefault behavior is to download a file and use ASCII transfer mode.\n" + "\t-a - use local active mode (default is local passive)\n"
53              + "\t-A - anonymous login (omit user and password parameters)\n" + "\t-b - use binary transfer mode\n"
54              + "\t-c cmd - issue arbitrary command (remote is used as a parameter if provided) \n"
55              + "\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"
56              + "\t-E - encoding to use for control channel\n" + "\t-f - issue FEAT command (remote and local files are ignored)\n"
57              + "\t-h - list hidden files (applies to -l and -n only)\n" + "\t-i - issue SIZE command for a file\n"
58              + "\t-k secs - use keep-alive timer (setControlKeepAliveTimeout)\n" + "\t-l - list files using LIST (remote is used as the pathname if provided)\n"
59              + "\t     Files are listed twice: first in raw mode, then as the formatted parsed data.\n"
60              + "\t     N.B. if the wrong server-type is used, output may be lost. Use -U or -S as necessary.\n"
61              + "\t-L - use lenient future dates (server dates may be up to 1 day into future)\n"
62              + "\t-m - list file details using MDTM (remote is used as the pathname if provided)\n"
63              + "\t-n - list file names using NLST (remote is used as the pathname if provided)\n"
64              + "\t-p true|false|protocol[,true|false] - use FTPSClient with the specified protocol and/or isImplicit setting\n"
65              + "\t-s - store file on server (upload)\n" + "\t-S - systemType set server system type (e.g. UNIX VMS WINDOWS)\n"
66              + "\t-t - list file details using MLST (remote is used as the pathname if provided)\n" + "\t-U - save unparseable responses\n"
67              + "\t-w msec - wait time for keep-alive reply (setControlKeepAliveReplyTimeout)\n"
68              + "\t-T  all|valid|none - use one of the built-in TrustManager implementations (none = JVM default)\n"
69              + "\t-y format - set default date format string\n" + "\t-Y format - set recent date format string\n"
70              + "\t-Z timezone - set the server time zone for parsing LIST responses\n"
71              + "\t-z timezone - set the time zone for displaying MDTM, LIST, MLSD, MLST responses\n"
72              + "\t-PrH server[:port] - HTTP Proxy host and optional port[80] \n" + "\t-PrU user - HTTP Proxy server user\n"
73              + "\t-PrP password - HTTP Proxy server password\n" + "\t-# - add hash display during transfers\n";
74  
75      private static CopyStreamListener createListener() {
76          return new CopyStreamListener() {
77              private long megsTotal;
78  
79              @Override
80              public void bytesTransferred(final CopyStreamEvent event) {
81                  bytesTransferred(event.getTotalBytesTransferred(), event.getBytesTransferred(), event.getStreamSize());
82              }
83  
84              @Override
85              public void bytesTransferred(final long totalBytesTransferred, final int bytesTransferred, final long streamSize) {
86                  final long megs = totalBytesTransferred / 1000000;
87                  for (long l = megsTotal; l < megs; l++) {
88                      System.err.print("#");
89                  }
90                  megsTotal = megs;
91              }
92          };
93      }
94  
95      public static void main(final String[] args) throws UnknownHostException {
96          boolean storeFile = false, binaryTransfer = false, error = false, listFiles = false, listNames = false, hidden = false;
97          boolean localActive = false, useEpsvWithIPv4 = false, feat = false, printHash = false;
98          boolean mlst = false, mlsd = false, mdtm = false, saveUnparseable = false;
99          boolean size = false;
100         boolean lenient = false;
101         long keepAliveTimeoutSeconds = -1;
102         int controlKeepAliveReplyTimeoutMillis = -1;
103         int minParams = 5; // listings require 3 params
104         String protocol = null; // SSL protocol
105         String doCommand = null;
106         String trustmgr = null;
107         String proxyHost = null;
108         int proxyPort = 80;
109         String proxyUser = null;
110         String proxyPassword = null;
111         String user = null;
112         String password = null;
113         String encoding = null;
114         String serverTimeZoneId = null;
115         String displayTimeZoneId = null;
116         String serverType = null;
117         String defaultDateFormat = null;
118         String recentDateFormat = null;
119 
120         int base = 0;
121         for (base = 0; base < args.length; base++) {
122             if (args[base].equals("-s")) {
123                 storeFile = true;
124             } else if (args[base].equals("-a")) {
125                 localActive = true;
126             } else if (args[base].equals("-A")) {
127                 user = "anonymous";
128                 password = System.getProperty("user.name") + "@" + InetAddress.getLocalHost().getHostName();
129             } else if (args[base].equals("-b")) {
130                 binaryTransfer = true;
131             } else if (args[base].equals("-c")) {
132                 doCommand = args[++base];
133                 minParams = 3;
134             } else if (args[base].equals("-d")) {
135                 mlsd = true;
136                 minParams = 3;
137             } else if (args[base].equals("-e")) {
138                 useEpsvWithIPv4 = true;
139             } else if (args[base].equals("-E")) {
140                 encoding = args[++base];
141             } else if (args[base].equals("-f")) {
142                 feat = true;
143                 minParams = 3;
144             } else if (args[base].equals("-h")) {
145                 hidden = true;
146             } else if (args[base].equals("-i")) {
147                 size = true;
148                 minParams = 3;
149             } else if (args[base].equals("-k")) {
150                 keepAliveTimeoutSeconds = Long.parseLong(args[++base]);
151             } else if (args[base].equals("-l")) {
152                 listFiles = true;
153                 minParams = 3;
154             } else if (args[base].equals("-m")) {
155                 mdtm = true;
156                 minParams = 3;
157             } else if (args[base].equals("-L")) {
158                 lenient = true;
159             } else if (args[base].equals("-n")) {
160                 listNames = true;
161                 minParams = 3;
162             } else if (args[base].equals("-p")) {
163                 protocol = args[++base];
164             } else if (args[base].equals("-S")) {
165                 serverType = args[++base];
166             } else if (args[base].equals("-t")) {
167                 mlst = true;
168                 minParams = 3;
169             } else if (args[base].equals("-U")) {
170                 saveUnparseable = true;
171             } else if (args[base].equals("-w")) {
172                 controlKeepAliveReplyTimeoutMillis = Integer.parseInt(args[++base]);
173             } else if (args[base].equals("-T")) {
174                 trustmgr = args[++base];
175             } else if (args[base].equals("-y")) {
176                 defaultDateFormat = args[++base];
177             } else if (args[base].equals("-Y")) {
178                 recentDateFormat = args[++base];
179             } else if (args[base].equals("-Z")) {
180                 serverTimeZoneId = args[++base];
181             } else if (args[base].equals("-z")) {
182                 displayTimeZoneId = args[++base];
183             } else if (args[base].equals("-PrH")) {
184                 proxyHost = args[++base];
185                 final String[] parts = proxyHost.split(":");
186                 if (parts.length == 2) {
187                     proxyHost = parts[0];
188                     proxyPort = Integer.parseInt(parts[1]);
189                 }
190             } else if (args[base].equals("-PrU")) {
191                 proxyUser = args[++base];
192             } else if (args[base].equals("-PrP")) {
193                 proxyPassword = args[++base];
194             } else if (args[base].equals("-#")) {
195                 printHash = true;
196             } else {
197                 break;
198             }
199         }
200 
201         final int remain = args.length - base;
202         if (user != null) {
203             minParams -= 2;
204         }
205         if (remain < minParams) // server, user, pass, remote, local [protocol]
206         {
207             if (args.length > 0) {
208                 System.err.println("Actual Parameters: " + Arrays.toString(args));
209             }
210             System.err.println(USAGE);
211             System.exit(1);
212         }
213 
214         String server = args[base++];
215         int port = 0;
216         final String[] parts = server.split(":");
217         if (parts.length == 2) {
218             server = parts[0];
219             port = Integer.parseInt(parts[1]);
220         }
221         if (user == null) {
222             user = args[base++];
223             password = args[base++];
224         }
225 
226         String remote = null;
227         if (args.length - base > 0) {
228             remote = args[base++];
229         }
230 
231         String local = null;
232         if (args.length - base > 0) {
233             local = args[base++];
234         }
235 
236         final FTPClient ftp;
237         if (protocol == null) {
238             if (proxyHost != null) {
239                 System.out.println("Using HTTP proxy server: " + proxyHost);
240                 ftp = new FTPHTTPClient(proxyHost, proxyPort, proxyUser, proxyPassword);
241             } else {
242                 ftp = new FTPClient();
243             }
244         } else {
245             final FTPSClient ftps;
246             if (protocol.equals("true")) {
247                 ftps = new FTPSClient(true);
248             } else if (protocol.equals("false")) {
249                 ftps = new FTPSClient(false);
250             } else {
251                 final String[] prot = protocol.split(",");
252                 if (prot.length == 1) { // Just protocol
253                     ftps = new FTPSClient(protocol);
254                 } else { // protocol,true|false
255                     ftps = new FTPSClient(prot[0], Boolean.parseBoolean(prot[1]));
256                 }
257             }
258             ftp = ftps;
259             if ("all".equals(trustmgr)) {
260                 ftps.setTrustManager(TrustManagerUtils.getAcceptAllTrustManager());
261             } else if ("valid".equals(trustmgr)) {
262                 ftps.setTrustManager(TrustManagerUtils.getValidateServerCertificateTrustManager());
263             } else if ("none".equals(trustmgr)) {
264                 ftps.setTrustManager(null);
265             }
266         }
267 
268         if (printHash) {
269             ftp.setCopyStreamListener(createListener());
270         }
271         if (keepAliveTimeoutSeconds >= 0) {
272             ftp.setControlKeepAliveTimeout(Duration.ofSeconds(keepAliveTimeoutSeconds));
273         }
274         if (controlKeepAliveReplyTimeoutMillis >= 0) {
275             ftp.setControlKeepAliveReplyTimeout(Duration.ofMillis(controlKeepAliveReplyTimeoutMillis));
276         }
277         if (encoding != null) {
278             ftp.setControlEncoding(encoding);
279         }
280         ftp.setListHiddenFiles(hidden);
281 
282         // suppress login details
283         ftp.addProtocolCommandListener(new PrintCommandListener(new PrintWriter(System.out), true));
284 
285         final FTPClientConfig config;
286         if (serverType != null) {
287             config = new FTPClientConfig(serverType);
288         } else {
289             config = new FTPClientConfig();
290         }
291         config.setUnparseableEntries(saveUnparseable);
292         if (defaultDateFormat != null) {
293             config.setDefaultDateFormatStr(defaultDateFormat);
294         }
295         if (recentDateFormat != null) {
296             config.setRecentDateFormatStr(recentDateFormat);
297         }
298         ftp.configure(config);
299 
300         try {
301             final int reply;
302             if (port > 0) {
303                 ftp.connect(server, port);
304             } else {
305                 ftp.connect(server);
306             }
307             System.out.println("Connected to " + server + " on " + (port > 0 ? port : ftp.getDefaultPort()));
308 
309             // After connection attempt, you should check the reply code to verify
310             // success.
311             reply = ftp.getReplyCode();
312 
313             if (!FTPReply.isPositiveCompletion(reply)) {
314                 ftp.disconnect();
315                 System.err.println("FTP server refused connection.");
316                 System.exit(1);
317             }
318         } catch (final IOException e) {
319             if (ftp.isConnected()) {
320                 try {
321                     ftp.disconnect();
322                 } catch (final IOException f) {
323                     // do nothing
324                 }
325             }
326             System.err.println("Could not connect to server.");
327             e.printStackTrace();
328             System.exit(1);
329         }
330 
331         __main: try {
332             if (!ftp.login(user, password)) {
333                 ftp.logout();
334                 error = true;
335                 break __main;
336             }
337 
338             System.out.println("Remote system is " + ftp.getSystemType());
339 
340             if (binaryTransfer) {
341                 ftp.setFileType(FTP.BINARY_FILE_TYPE);
342             } else {
343                 // in theory this should not be necessary as servers should default to ASCII,
344                 // but they don't all do so - see NET-500
345                 ftp.setFileType(FTP.ASCII_FILE_TYPE);
346             }
347 
348             // Use passive mode as default because most of us are
349             // behind firewalls these days.
350             if (localActive) {
351                 ftp.enterLocalActiveMode();
352             } else {
353                 ftp.enterLocalPassiveMode();
354             }
355 
356             ftp.setUseEPSVwithIPv4(useEpsvWithIPv4);
357 
358             if (storeFile) {
359                 try (final InputStream input = new FileInputStream(local)) {
360                     ftp.storeFile(remote, input);
361                 }
362 
363                 if (keepAliveTimeoutSeconds > 0) {
364                     showCslStats(ftp);
365                 }
366             }
367             // Allow multiple list types for single invocation
368             else if (listFiles || mlsd || mdtm || mlst || listNames || size) {
369                 if (mlsd) {
370                     for (final FTPFile f : ftp.mlistDir(remote)) {
371                         System.out.println(f.getRawListing());
372                         System.out.println(f.toFormattedString(displayTimeZoneId));
373                     }
374                 }
375                 if (mdtm) {
376                     final FTPFile f = ftp.mdtmFile(remote);
377                     if (f != null) {
378                         System.out.println(f.getRawListing());
379                         System.out.println(f.toFormattedString(displayTimeZoneId));
380                     } else {
381                         System.out.println("File not found");
382                     }
383                 }
384                 if (mlst) {
385                     final FTPFile f = ftp.mlistFile(remote);
386                     if (f != null) {
387                         System.out.println(f.toFormattedString(displayTimeZoneId));
388                     }
389                 }
390                 if (listNames) {
391                     for (final String s : ftp.listNames(remote)) {
392                         System.out.println(s);
393                     }
394                 }
395                 if (size) {
396                     System.out.println("Size=" + ftp.getSize(remote));
397                 }
398                 // Do this last because it changes the client
399                 if (listFiles) {
400                     if (lenient || serverTimeZoneId != null) {
401                         config.setLenientFutureDates(lenient);
402                         if (serverTimeZoneId != null) {
403                             config.setServerTimeZoneId(serverTimeZoneId);
404                         }
405                         ftp.configure(config);
406                     }
407 
408                     for (final FTPFile f : ftp.listFiles(remote)) {
409                         System.out.println(f.getRawListing());
410                         System.out.println(f.toFormattedString(displayTimeZoneId));
411                     }
412                 }
413             } else if (feat) {
414                 // boolean feature check
415                 if (remote != null) { // See if the command is present
416                     if (ftp.hasFeature(remote)) {
417                         System.out.println("Has feature: " + remote);
418                     } else if (FTPReply.isPositiveCompletion(ftp.getReplyCode())) {
419                         System.out.println("FEAT " + remote + " was not detected");
420                     } else {
421                         System.out.println("Command failed: " + ftp.getReplyString());
422                     }
423 
424                     // Strings feature check
425                     final String[] features = ftp.featureValues(remote);
426                     if (features != null) {
427                         for (final String f : features) {
428                             System.out.println("FEAT " + remote + "=" + f + ".");
429                         }
430                     } else if (FTPReply.isPositiveCompletion(ftp.getReplyCode())) {
431                         System.out.println("FEAT " + remote + " is not present");
432                     } else {
433                         System.out.println("Command failed: " + ftp.getReplyString());
434                     }
435                 } else if (ftp.features()) {
436 //                        Command listener has already printed the output
437                 } else {
438                     System.out.println("Failed: " + ftp.getReplyString());
439                 }
440             } else if (doCommand != null) {
441                 if (ftp.doCommand(doCommand, remote)) {
442 //                  Command listener has already printed the output
443 //                    for(String s : ftp.getReplyStrings()) {
444 //                        System.out.println(s);
445 //                    }
446                 } else {
447                     System.out.println("Failed: " + ftp.getReplyString());
448                 }
449             } else {
450                 try (final OutputStream output = new FileOutputStream(local)) {
451                     ftp.retrieveFile(remote, output);
452                 }
453 
454                 if (keepAliveTimeoutSeconds > 0) {
455                     showCslStats(ftp);
456                 }
457             }
458 
459             ftp.noop(); // check that control connection is working OK
460 
461             ftp.logout();
462         } catch (final FTPConnectionClosedException e) {
463             error = true;
464             System.err.println("Server closed connection.");
465             e.printStackTrace();
466         } catch (final IOException e) {
467             error = true;
468             e.printStackTrace();
469         } finally {
470             if (ftp.isConnected()) {
471                 try {
472                     ftp.disconnect();
473                 } catch (final IOException f) {
474                     // do nothing
475                 }
476             }
477         }
478 
479         System.exit(error ? 1 : 0);
480     } // end main
481 
482     private static void showCslStats(final FTPClient ftp) {
483         @SuppressWarnings("deprecation") // debug code
484         final int[] stats = ftp.getCslDebug();
485         System.out.println("CslDebug=" + Arrays.toString(stats));
486 
487     }
488 }