001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018package org.apache.commons.net.examples.ftp; 019 020import java.io.FileInputStream; 021import java.io.FileOutputStream; 022import java.io.IOException; 023import java.io.InputStream; 024import java.io.OutputStream; 025import java.io.PrintWriter; 026import java.net.InetAddress; 027import java.net.UnknownHostException; 028import java.time.Duration; 029import java.util.Arrays; 030 031import org.apache.commons.net.PrintCommandListener; 032import org.apache.commons.net.ftp.FTP; 033import org.apache.commons.net.ftp.FTPClient; 034import org.apache.commons.net.ftp.FTPClientConfig; 035import org.apache.commons.net.ftp.FTPConnectionClosedException; 036import org.apache.commons.net.ftp.FTPFile; 037import org.apache.commons.net.ftp.FTPHTTPClient; 038import org.apache.commons.net.ftp.FTPReply; 039import org.apache.commons.net.ftp.FTPSClient; 040import org.apache.commons.net.io.CopyStreamEvent; 041import org.apache.commons.net.io.CopyStreamListener; 042import org.apache.commons.net.util.TrustManagerUtils; 043 044/** 045 * 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 046 * 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 047 * binary transfer is assumed (default is ASCII). See below for further options. 048 */ 049public final class FTPClientExample { 050 051 public static final String USAGE = "Expected Parameters: [options] <hostname> <username> <password> [<remote file> [<local file>]]\n" 052 + "\nDefault behavior is to download a file and use ASCII transfer mode.\n" + "\t-a - use local active mode (default is local passive)\n" 053 + "\t-A - anonymous login (omit username and password parameters)\n" + "\t-b - use binary transfer mode\n" 054 + "\t-c cmd - issue arbitrary command (remote is used as a parameter if provided) \n" 055 + "\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" 056 + "\t-E - encoding to use for control channel\n" + "\t-f - issue FEAT command (remote and local files are ignored)\n" 057 + "\t-h - list hidden files (applies to -l and -n only)\n" + "\t-i - issue SIZE command for a file\n" 058 + "\t-k secs - use keep-alive timer (setControlKeepAliveTimeout)\n" + "\t-l - list files using LIST (remote is used as the pathname if provided)\n" 059 + "\t Files are listed twice: first in raw mode, then as the formatted parsed data.\n" 060 + "\t N.B. if the wrong server-type is used, output may be lost. Use -U or -S as necessary.\n" 061 + "\t-L - use lenient future dates (server dates may be up to 1 day into future)\n" 062 + "\t-m - list file details using MDTM (remote is used as the pathname if provided)\n" 063 + "\t-n - list file names using NLST (remote is used as the pathname if provided)\n" 064 + "\t-p true|false|protocol[,true|false] - use FTPSClient with the specified protocol and/or isImplicit setting\n" 065 + "\t-s - store file on server (upload)\n" + "\t-S - systemType set server system type (e.g. UNIX VMS WINDOWS)\n" 066 + "\t-t - list file details using MLST (remote is used as the pathname if provided)\n" + "\t-U - save unparseable responses\n" 067 + "\t-w msec - wait time for keep-alive reply (setControlKeepAliveReplyTimeout)\n" 068 + "\t-T all|valid|none - use one of the built-in TrustManager implementations (none = JVM default)\n" 069 + "\t-y format - set default date format string\n" + "\t-Y format - set recent date format string\n" 070 + "\t-Z timezone - set the server time zone for parsing LIST responses\n" 071 + "\t-z timezone - set the time zone for displaying MDTM, LIST, MLSD, MLST responses\n" 072 + "\t-PrH server[:port] - HTTP Proxy host and optional port[80] \n" + "\t-PrU user - HTTP Proxy server username\n" 073 + "\t-PrP password - HTTP Proxy server password\n" + "\t-# - add hash display during transfers\n"; 074 075 private static CopyStreamListener createListener() { 076 return new CopyStreamListener() { 077 private long megsTotal; 078 079 @Override 080 public void bytesTransferred(final CopyStreamEvent event) { 081 bytesTransferred(event.getTotalBytesTransferred(), event.getBytesTransferred(), event.getStreamSize()); 082 } 083 084 @Override 085 public void bytesTransferred(final long totalBytesTransferred, final int bytesTransferred, final long streamSize) { 086 final long megs = totalBytesTransferred / 1000000; 087 for (long l = megsTotal; l < megs; l++) { 088 System.err.print("#"); 089 } 090 megsTotal = megs; 091 } 092 }; 093 } 094 095 public static void main(final String[] args) throws UnknownHostException { 096 boolean storeFile = false, binaryTransfer = false, error = false, listFiles = false, listNames = false, hidden = false; 097 boolean localActive = false, useEpsvWithIPv4 = false, feat = false, printHash = false; 098 boolean mlst = false, mlsd = false, mdtm = false, saveUnparseable = false; 099 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 username = 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 username = "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 (username != 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 (username == null) { 222 username = 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(username, 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}