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.nio.charset.Charset;
28 import java.time.Duration;
29 import java.util.Arrays;
30
31 import org.apache.commons.net.examples.PrintCommandListeners;
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
46
47
48
49 public final class FTPClientExample {
50
51
52
53
54
55 public static final String USAGE = "Expected Parameters: [options] <hostname> <user> <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 user 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 path 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 path 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 path if provided)\n"
73 + "\t-n - list file names using NLST (remote is used as the path if provided)\n"
74 + "\t--OPTS \"opts-cmd command-name command-options\" - Sends the OPTS\n"
75 + "\t-p true|false|protocol[,true|false] - use FTPSClient with the specified protocol and/or isImplicit setting\n"
76 + "\t-s - store file on server (upload)\n"
77 + "\t-S - systemType set server system type (e.g. Unix VMS WINDOWS)\n"
78 + "\t-t - list file details using MLST (remote is used as the path if provided)\n"
79 + "\t-U - save unparseable responses\n"
80 + "\t-w msec - wait time for keep-alive reply (setControlKeepAliveReplyTimeout)\n"
81 + "\t-T all|valid|none - use one of the built-in TrustManager implementations (none = JVM default)\n"
82 + "\t-y format - set default date format string\n"
83 + "\t-Y format - set recent date format string\n"
84 + "\t-Z timezone - set the server time zone for parsing LIST responses\n"
85 + "\t-z timezone - set the time zone for displaying MDTM, LIST, MLSD, MLST responses\n"
86 + "\t-PrH server[:port] - HTTP Proxy host and optional port[80] \n"
87 + "\t-PrU user - HTTP Proxy server user\n"
88 + "\t-PrP password - HTTP Proxy server password\n"
89 + "\t-# - add hash display during transfers\n";
90
91
92 private static void configure(final FTPClient ftp, final FTPClientConfig config, final String defaultDateFormat, final String recentDateFormat,
93 final boolean saveUnparseable) {
94 config.setUnparseableEntries(saveUnparseable);
95 if (defaultDateFormat != null) {
96 config.setDefaultDateFormatStr(defaultDateFormat);
97 }
98 if (recentDateFormat != null) {
99 config.setRecentDateFormatStr(recentDateFormat);
100 }
101 ftp.configure(config);
102 }
103
104 private static CopyStreamListener createListener() {
105 return new CopyStreamListener() {
106 private long megsTotal;
107
108 @Override
109 public void bytesTransferred(final CopyStreamEvent event) {
110 bytesTransferred(event.getTotalBytesTransferred(), event.getBytesTransferred(), event.getStreamSize());
111 }
112
113 @Override
114 public void bytesTransferred(final long totalBytesTransferred, final int bytesTransferred, final long streamSize) {
115 final long megs = totalBytesTransferred / 1000000;
116 for (long l = megsTotal; l < megs; l++) {
117 System.err.print("#");
118 }
119 megsTotal = megs;
120 }
121 };
122 }
123
124 public static void main(final String[] args) throws UnknownHostException {
125 boolean storeFile = false;
126 boolean binaryTransfer = false;
127 boolean error = false;
128 boolean listFiles = false;
129 boolean listNames = false;
130 boolean hidden = false;
131 boolean localActive = false;
132 boolean useEpsvWithIPv4 = false;
133 boolean feat = false;
134 boolean printHash = false;
135 boolean mlst = false;
136 boolean mlsd = false;
137 boolean mdtm = false;
138 boolean saveUnparseable = false;
139 boolean size = false;
140 boolean lenient = false;
141 long keepAliveTimeoutSeconds = -1;
142 int controlKeepAliveReplyTimeoutMillis = -1;
143 int minParams = 5;
144 String protocol = null;
145 String doCommand = null;
146 String trustManager = null;
147 String proxyHost = null;
148 int proxyPort = 80;
149 String proxyUser = null;
150 String proxyPassword = null;
151 String user = null;
152 String password = null;
153 Charset encoding = null;
154 String serverTimeZoneId = null;
155 String displayTimeZoneId = null;
156 String serverType = null;
157 String defaultDateFormat = null;
158 String recentDateFormat = null;
159 String opts = null;
160
161 int base = 0;
162 for (base = 0; base < args.length; base++) {
163 if (args[base].equals("-s")) {
164 storeFile = true;
165 } else if (args[base].equals("-a")) {
166 localActive = true;
167 } else if (args[base].equals("-A")) {
168 user = "anonymous";
169 password = System.getProperty("user.name") + "@" + InetAddress.getLocalHost().getHostName();
170 } else if (args[base].equals("-b")) {
171 binaryTransfer = true;
172 } else if (args[base].equals("-c")) {
173 doCommand = args[++base];
174 minParams = 3;
175 } else if (args[base].equals("-d")) {
176 mlsd = true;
177 minParams = 3;
178 } else if (args[base].equals("-e")) {
179 useEpsvWithIPv4 = true;
180 } else if (args[base].equals("-E")) {
181 encoding = Charset.forName(args[++base]);
182 } else if (args[base].equals("-f")) {
183 feat = true;
184 minParams = 3;
185 } else if (args[base].equals("-h")) {
186 hidden = true;
187 } else if (args[base].equals("-i")) {
188 size = true;
189 minParams = 3;
190 } else if (args[base].equals("-k")) {
191 keepAliveTimeoutSeconds = Long.parseLong(args[++base]);
192 } else if (args[base].equals("-l")) {
193 listFiles = true;
194 minParams = 3;
195 } else if (args[base].equals("-m")) {
196 mdtm = true;
197 minParams = 3;
198 } else if (args[base].equals("-L")) {
199 lenient = true;
200 } else if (args[base].equals("--OPTS")) {
201 opts = args[++base];
202 } else if (args[base].equals("-n")) {
203 listNames = true;
204 minParams = 3;
205 } else if (args[base].equals("-p")) {
206 protocol = args[++base];
207 } else if (args[base].equals("-S")) {
208 serverType = args[++base];
209 } else if (args[base].equals("-t")) {
210 mlst = true;
211 minParams = 3;
212 } else if (args[base].equals("-U")) {
213 saveUnparseable = true;
214 } else if (args[base].equals("-w")) {
215 controlKeepAliveReplyTimeoutMillis = Integer.parseInt(args[++base]);
216 } else if (args[base].equals("-T")) {
217 trustManager = args[++base];
218 } else if (args[base].equals("-y")) {
219 defaultDateFormat = args[++base];
220 } else if (args[base].equals("-Y")) {
221 recentDateFormat = args[++base];
222 } else if (args[base].equals("-Z")) {
223 serverTimeZoneId = args[++base];
224 } else if (args[base].equals("-z")) {
225 displayTimeZoneId = args[++base];
226 } else if (args[base].equals("-PrH")) {
227 proxyHost = args[++base];
228 final String[] parts = proxyHost.split(":");
229 if (parts.length == 2) {
230 proxyHost = parts[0];
231 proxyPort = Integer.parseInt(parts[1]);
232 }
233 } else if (args[base].equals("-PrU")) {
234 proxyUser = args[++base];
235 } else if (args[base].equals("-PrP")) {
236 proxyPassword = args[++base];
237 } else if (args[base].equals("-#")) {
238 printHash = true;
239 } else {
240 break;
241 }
242 }
243
244 final int remain = args.length - base;
245 if (user != null) {
246 minParams -= 2;
247 }
248 if (remain < minParams) {
249
250 if (args.length > 0) {
251 System.err.println("Actual Parameters: " + Arrays.toString(args));
252 }
253 System.err.println(USAGE);
254 System.exit(1);
255 }
256
257 String server = args[base++];
258 int port = 0;
259 final String[] parts = server.split(":");
260 if (parts.length == 2) {
261 server = parts[0];
262 port = Integer.parseInt(parts[1]);
263 }
264 if (user == null) {
265 user = args[base++];
266 password = args[base++];
267 }
268
269 String remote = null;
270 if (args.length - base > 0) {
271 remote = args[base++];
272 }
273
274 String local = null;
275 if (args.length - base > 0) {
276 local = args[base++];
277 }
278
279 final FTPClient ftp;
280 if (protocol == null) {
281 if (proxyHost != null) {
282 System.out.println("Using HTTP proxy server: " + proxyHost);
283 ftp = new FTPHTTPClient(proxyHost, proxyPort, proxyUser, proxyPassword);
284 } else {
285 ftp = new FTPClient();
286 }
287 } else {
288 final FTPSClient ftps;
289 if (protocol.equals("true")) {
290 ftps = new FTPSClient(true);
291 } else if (protocol.equals("false")) {
292 ftps = new FTPSClient(false);
293 } else {
294 final String[] prot = protocol.split(",");
295 if (prot.length == 1) {
296 ftps = new FTPSClient(protocol);
297 } else {
298 ftps = new FTPSClient(prot[0], Boolean.parseBoolean(prot[1]));
299 }
300 }
301 ftp = ftps;
302 if (trustManager != null) {
303 switch (trustManager) {
304 case "all":
305 ftps.setTrustManager(TrustManagerUtils.getAcceptAllTrustManager());
306 break;
307 case "valid":
308 ftps.setTrustManager(TrustManagerUtils.getValidateServerCertificateTrustManager());
309 break;
310 case "none":
311 ftps.setTrustManager(null);
312 break;
313 default:
314 break;
315 }
316 }
317 }
318
319 if (printHash) {
320 ftp.setCopyStreamListener(createListener());
321 }
322 if (keepAliveTimeoutSeconds >= 0) {
323 ftp.setControlKeepAliveTimeout(Duration.ofSeconds(keepAliveTimeoutSeconds));
324 }
325 if (controlKeepAliveReplyTimeoutMillis >= 0) {
326 ftp.setControlKeepAliveReplyTimeout(Duration.ofMillis(controlKeepAliveReplyTimeoutMillis));
327 }
328 if (encoding != null) {
329 ftp.setControlEncoding(encoding);
330 }
331 ftp.setListHiddenFiles(hidden);
332
333
334 ftp.addProtocolCommandListener(PrintCommandListeners.sysOutPrintCommandListener());
335
336 final FTPClientConfig config;
337 if (serverType != null) {
338 config = new FTPClientConfig(serverType);
339 } else {
340 config = new FTPClientConfig();
341 }
342 configure(ftp, config, defaultDateFormat, recentDateFormat, saveUnparseable);
343
344 try {
345 final int reply;
346 if (port > 0) {
347 ftp.connect(server, port);
348 } else {
349 ftp.connect(server);
350 }
351 System.out.println("Connected to " + server + " on " + (port > 0 ? port : ftp.getDefaultPort()));
352
353
354
355 reply = ftp.getReplyCode();
356
357 if (!FTPReply.isPositiveCompletion(reply)) {
358 ftp.disconnect();
359 System.err.println("FTP server refused connection.");
360 System.exit(1);
361 }
362 } catch (final IOException e) {
363 if (ftp.isConnected()) {
364 try {
365 ftp.disconnect();
366 } catch (final IOException f) {
367
368 }
369 }
370 System.err.println("Could not connect to server.");
371 e.printStackTrace();
372 System.exit(1);
373 }
374
375 __main: try {
376 if (!ftp.login(user, password)) {
377 ftp.logout();
378 error = true;
379 break __main;
380 }
381
382 System.out.println("Remote system is " + ftp.getSystemType());
383 if (!ftp.getSystemType().contains(config.getServerSystemKey())) {
384 configure(ftp, new FTPClientConfig(ftp.getSystemType()), defaultDateFormat, recentDateFormat, saveUnparseable);
385 }
386 if (opts != null) {
387 ftp.opts(opts);
388 }
389
390 if (binaryTransfer) {
391 ftp.setFileType(FTP.BINARY_FILE_TYPE);
392 } else {
393
394
395 ftp.setFileType(FTP.ASCII_FILE_TYPE);
396 }
397
398
399
400 if (localActive) {
401 ftp.enterLocalActiveMode();
402 } else {
403 ftp.enterLocalPassiveMode();
404 }
405
406 ftp.setUseEPSVwithIPv4(useEpsvWithIPv4);
407
408 if (storeFile) {
409 try (InputStream input = new FileInputStream(local)) {
410 ftp.storeFile(remote, input);
411 }
412
413 if (keepAliveTimeoutSeconds > 0) {
414 showCslStats(ftp);
415 }
416 } else if (listFiles || mlsd || mdtm || mlst || listNames || size) {
417
418 if (mlsd) {
419 for (final FTPFile f : ftp.mlistDir(remote)) {
420 System.out.println(f.getRawListing());
421 System.out.println(f.toFormattedString(displayTimeZoneId));
422 }
423 }
424 if (mdtm) {
425 final FTPFile f = ftp.mdtmFile(remote);
426 if (f != null) {
427 System.out.println(f.getRawListing());
428 System.out.println(f.toFormattedString(displayTimeZoneId));
429 } else {
430 System.out.println("File not found");
431 }
432 }
433 if (mlst) {
434 final FTPFile f = ftp.mlistFile(remote);
435 if (f != null) {
436 System.out.println(f.toFormattedString(displayTimeZoneId));
437 }
438 }
439 if (listNames) {
440 for (final String s : ftp.listNames(remote)) {
441 System.out.println(s);
442 }
443 }
444 if (size) {
445 System.out.println("Size=" + ftp.getSize(remote));
446 }
447
448 if (listFiles) {
449 if (lenient || serverTimeZoneId != null) {
450 config.setLenientFutureDates(lenient);
451 if (serverTimeZoneId != null) {
452 config.setServerTimeZoneId(serverTimeZoneId);
453 }
454 ftp.configure(config);
455 }
456
457 for (final FTPFile f : ftp.listFiles(remote)) {
458 System.out.println(f.getRawListing());
459 System.out.println(f.toFormattedString(displayTimeZoneId));
460 }
461 }
462 } else if (feat) {
463
464 if (remote != null) {
465 if (ftp.hasFeature(remote)) {
466 System.out.println("Has feature: " + remote);
467 } else if (FTPReply.isPositiveCompletion(ftp.getReplyCode())) {
468 System.out.println("FEAT " + remote + " was not detected");
469 } else {
470 System.out.println("Command failed: " + ftp.getReplyString());
471 }
472
473
474 final String[] features = ftp.featureValues(remote);
475 if (features != null) {
476 for (final String f : features) {
477 System.out.println("FEAT " + remote + "=" + f + ".");
478 }
479 } else if (FTPReply.isPositiveCompletion(ftp.getReplyCode())) {
480 System.out.println("FEAT " + remote + " is not present");
481 } else {
482 System.out.println("Command failed: " + ftp.getReplyString());
483 }
484 } else if (ftp.features()) {
485
486 } else {
487 System.out.println("Failed: " + ftp.getReplyString());
488 }
489 } else if (doCommand != null) {
490 if (ftp.doCommand(doCommand, remote)) {
491
492
493
494
495 } else {
496 System.out.println("Failed: " + ftp.getReplyString());
497 }
498 } else {
499 try (OutputStream output = new FileOutputStream(local)) {
500 ftp.retrieveFile(remote, output);
501 }
502
503 if (keepAliveTimeoutSeconds > 0) {
504 showCslStats(ftp);
505 }
506 }
507
508 ftp.noop();
509
510 ftp.logout();
511 } catch (final FTPConnectionClosedException e) {
512 error = true;
513 System.err.println("Server closed connection.");
514 e.printStackTrace();
515 } catch (final IOException e) {
516 error = true;
517 e.printStackTrace();
518 } finally {
519 if (ftp.isConnected()) {
520 try {
521 ftp.disconnect();
522 } catch (final IOException f) {
523
524 }
525 }
526 }
527
528 System.exit(error ? 1 : 0);
529 }
530
531 private static void showCslStats(final FTPClient ftp) {
532 @SuppressWarnings("deprecation")
533 final int[] stats = ftp.getCslDebug();
534 System.out.println("CslDebug=" + Arrays.toString(stats));
535
536 }
537 }