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