1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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.net.InetAddress;
26 import java.net.UnknownHostException;
27 import java.time.Duration;
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.io.Util;
42 import org.apache.commons.net.util.TrustManagerUtils;
43
44
45
46
47
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;
104 String protocol = null;
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)
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) {
253 ftps = new FTPSClient(protocol);
254 } else {
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
283 ftp.addProtocolCommandListener(new PrintCommandListener(Util.newPrintWriter(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
310
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
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
344
345 ftp.setFileType(FTP.ASCII_FILE_TYPE);
346 }
347
348
349
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
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
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
415 if (remote != null) {
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
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
437 } else {
438 System.out.println("Failed: " + ftp.getReplyString());
439 }
440 } else if (doCommand != null) {
441 if (ftp.doCommand(doCommand, remote)) {
442
443
444
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();
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
475 }
476 }
477 }
478
479 System.exit(error ? 1 : 0);
480 }
481
482 private static void showCslStats(final FTPClient ftp) {
483 @SuppressWarnings("deprecation")
484 final int[] stats = ftp.getCslDebug();
485 System.out.println("CslDebug=" + Arrays.toString(stats));
486
487 }
488 }