0001: /**
0002: *
0003: * edtFTPj
0004: *
0005: * Copyright (C) 2000-2003 Enterprise Distributed Technologies Ltd
0006: *
0007: * www.enterprisedt.com
0008: *
0009: * This library is free software; you can redistribute it and/or
0010: * modify it under the terms of the GNU Lesser General Public
0011: * License as published by the Free Software Foundation; either
0012: * version 2.1 of the License, or (at your option) any later version.
0013: *
0014: * This library is distributed in the hope that it will be useful,
0015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0017: * Lesser General Public License for more details.
0018: *
0019: * You should have received a copy of the GNU Lesser General Public
0020: * License along with this library; if not, write to the Free Software
0021: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
0022: *
0023: * Bug fixes, suggestions and comments should be should posted on
0024: * http://www.enterprisedt.com/forums/index.php
0025: *
0026: * Change Log:
0027: *
0028: * $Log: FTPClient.java,v $
0029: * Revision 1.93 2008-01-09 03:54:21 bruceb
0030: * executeCommand() now returns reply code
0031: *
0032: * Revision 1.92 2007-12-18 07:54:58 bruceb
0033: * many small changes to prepare for FileTransferClient
0034: *
0035: * Revision 1.91 2007-11-29 02:32:09 hans
0036: * Added DEFAULT_LISTING_LOCALES and made related changes to initializers.
0037: *
0038: * Revision 1.90 2007-11-13 07:14:04 bruceb
0039: * ListenOnAllInterfaces
0040: *
0041: * Revision 1.89 2007-11-07 23:53:50 bruceb
0042: * refactoring for FXP
0043: *
0044: * Revision 1.88 2007-10-12 05:21:13 bruceb
0045: * can set multiple locales (and 2 set by default)
0046: *
0047: * Revision 1.87 2007-08-15 03:47:39 bruceb
0048: * fix separator
0049: *
0050: * Revision 1.86 2007-08-09 00:50:20 bruceb
0051: * added 250 reply to some commands
0052: *
0053: * Revision 1.85 2007-08-07 04:46:25 bruceb
0054: * added counts for transfers and deletes plus getLastReply()
0055: *
0056: * Revision 1.84 2007-07-18 02:16:33 bruceb
0057: * ignore size() exception in resume
0058: *
0059: * Revision 1.83 2007-07-05 05:28:27 bruceb
0060: * add getters for message collections
0061: *
0062: * Revision 1.82 2007-06-14 04:12:48 bruceb
0063: * added setForceUniqueNames()
0064: *
0065: * Revision 1.81 2007-06-06 22:20:11 bruceb
0066: * FTP_LINE_SEPARATOR now public
0067: *
0068: * Revision 1.80 2007-05-29 04:17:24 bruceb
0069: * modify connected() method, and null out control when quitting
0070: *
0071: * Revision 1.79 2007-05-15 01:02:58 bruceb
0072: * file factory change if syst doesn't work
0073: *
0074: * Revision 1.78 2007/04/24 01:58:03 bruceb
0075: * more debug for validateTransferOnError
0076: *
0077: * Revision 1.77 2007/04/23 23:43:20 bruceb
0078: * check on dirname in dirDetails()
0079: *
0080: * Revision 1.76 2007/04/21 04:24:46 bruceb
0081: * fix to cope with any ascii file
0082: *
0083: * Revision 1.75 2007/03/22 04:03:47 bruceb
0084: * added getOutputStream()
0085: *
0086: * Revision 1.74 2007/03/19 22:07:36 bruceb
0087: * set control to null
0088: *
0089: * Revision 1.73 2007/03/13 02:45:08 bruceb
0090: * deleteOnFailure flag added
0091: *
0092: * Revision 1.72 2007/03/09 05:04:01 bruceb
0093: * fixed bugs in ASCII put & get
0094: *
0095: * Revision 1.71 2007/02/26 07:17:54 bruceb
0096: * make various method package or protected visibility so they can be accessed by subclasses etc
0097: *
0098: * Revision 1.70 2007/02/07 23:02:26 bruceb
0099: * fixed failure to throw cancellation exception
0100: *
0101: * Revision 1.69 2007/02/06 07:19:24 bruceb
0102: * fixed autodetect bug re actual mode not being changed on the server
0103: *
0104: * Revision 1.68 2007/02/04 23:02:40 bruceb
0105: * more error logging and extra codes, set strict validation off
0106: *
0107: * Revision 1.67 2007/02/01 06:05:18 bruceb
0108: * enable cancelling of large directory listings
0109: *
0110: * Revision 1.66 2007/01/15 23:05:14 bruceb
0111: * added fileDetails()
0112: *
0113: * Revision 1.65 2007/01/12 02:04:56 bruceb
0114: * extracted string matchers & fixed exists()
0115: *
0116: * Revision 1.64 2007/01/10 02:37:39 bruceb
0117: * modify exists to use RETR if necessary
0118: *
0119: * Revision 1.63 2006/12/12 01:04:54 hans
0120: * Fixed bug in exists method and added logging of raw directory listing.
0121: *
0122: * Revision 1.62 2006/10/27 15:44:06 bruceb
0123: * added sendServerWakeup()
0124: *
0125: * Revision 1.61 2006/10/17 11:03:41 bruceb
0126: * fix setActivePortRange comment, include using single port info
0127: *
0128: * Revision 1.60 2006/10/17 10:28:43 bruceb
0129: * refactored to get setupDataSocket()
0130: *
0131: * Revision 1.59 2006/10/11 08:38:00 bruceb
0132: * controlEncoding applied to directory listings
0133: *
0134: * Revision 1.58 2006/09/11 12:34:00 bruceb
0135: * added exists() method
0136: *
0137: * Revision 1.57 2006/08/23 08:48:51 bruceb
0138: * don't null out FileFactory when quit() is called
0139: *
0140: * Revision 1.56 2006/07/27 14:12:01 bruceb
0141: * IPV6 changes and fixed bug re control channel messages after unexpected close on data connection
0142: *
0143: * Revision 1.55 2006/05/22 01:54:42 hans
0144: * Made remoteHost protected.
0145: *
0146: * Revision 1.54 2006/02/16 19:47:09 hans
0147: * Added comment
0148: *
0149: * Revision 1.53 2005/11/15 21:02:32 bruceb
0150: * more debug
0151: *
0152: * Revision 1.52 2005/11/10 19:46:13 bruceb
0153: * delegate resume comments to FTPClientInterface
0154: *
0155: * Revision 1.51 2005/11/10 13:40:28 bruceb
0156: * more elaborate versioning info to debug
0157: *
0158: * Revision 1.50 2005/11/09 21:15:38 bruceb
0159: * autodetect file types
0160: *
0161: * Revision 1.49 2005/10/10 20:42:56 bruceb
0162: * append now in FTPClientInterface
0163: *
0164: * Revision 1.48 2005/09/29 16:03:06 bruceb
0165: * permit 350 return from STOR
0166: *
0167: * Revision 1.47 2005/09/21 10:38:06 bruceb
0168: * fix for LIST error re empty dir (proFTPD/TLS)
0169: *
0170: * Revision 1.46 2005/09/20 09:44:36 bruceb
0171: * extra no files found string, SYST accepts 213
0172: *
0173: * Revision 1.45 2005/09/02 21:03:04 bruceb
0174: * no abort() with cancel
0175: *
0176: * Revision 1.44 2005/08/26 17:48:16 bruceb
0177: * passive ip address setting + ASCII optimisation
0178: *
0179: * Revision 1.43 2005/06/17 18:25:56 bruceb
0180: * fix javadoc
0181: *
0182: * Revision 1.42 2005/06/16 21:39:49 hans
0183: * deprecated ControlPort accessors and removed comments for FTPClientInterface methods
0184: *
0185: * Revision 1.41 2005/06/10 15:44:38 bruceb
0186: * added noOperation() and connected()
0187: *
0188: * Revision 1.40 2005/06/03 11:25:17 bruceb
0189: * ascii fixes, setActivePortRange
0190: *
0191: * Revision 1.41 2005/05/24 11:32:28 bruceb
0192: * version + timestamp info in static block
0193: *
0194: * Revision 1.40 2005/05/15 19:46:28 bruceb
0195: * changes for testing setActivePortRange + STOR accepting 350 nonstrict
0196: *
0197: * Revision 1.39 2005/04/01 13:58:15 bruceb
0198: * restructured dir() exception handling + quote() change
0199: *
0200: * Revision 1.38 2005/03/18 11:04:32 bruceb
0201: * deprecated constructors
0202: *
0203: * Revision 1.37 2005/03/11 14:40:11 bruceb
0204: * added cdup() and changed buffer defaults
0205: *
0206: * Revision 1.36 2005/03/03 21:07:14 bruceb
0207: * implement interface & augment login doco
0208: *
0209: * Revision 1.35 2005/02/04 12:40:35 bruceb
0210: * tidied javadoc
0211: *
0212: * Revision 1.34 2005/02/04 12:28:51 bruceb
0213: * when getting, if file exists and is readonly, exception is thrown
0214: *
0215: * Revision 1.33 2005/01/28 13:55:39 bruceb
0216: * added ACCT handling
0217: *
0218: * Revision 1.32 2005/01/14 20:27:02 bruceb
0219: * exception restructuring + ABOR
0220: *
0221: * Revision 1.31 2004/11/19 08:28:10 bruceb
0222: * added setPORTIP()
0223: *
0224: * Revision 1.30 2004/10/18 15:54:48 bruceb
0225: * clearSOCKS added, set encoding for control sock, locale for parser
0226: *
0227: * Revision 1.29 2004/09/21 21:28:28 bruceb
0228: * fixed javadoc comment
0229: *
0230: * Revision 1.28 2004/09/18 14:27:57 bruceb
0231: * features() throw exception if not supported
0232: *
0233: * Revision 1.27 2004/09/18 09:33:47 bruceb
0234: * 1.1.8 tweaks
0235: *
0236: * Revision 1.26 2004/09/17 14:12:38 bruceb
0237: * fixed javadoc re filemasks
0238: *
0239: * Revision 1.25 2004/09/14 06:24:03 bruceb
0240: * fixed javadoc comment
0241: *
0242: * Revision 1.24 2004/08/31 13:48:29 bruceb
0243: * resume,features,restructure
0244: *
0245: * Revision 1.23 2004/07/23 08:34:32 bruceb
0246: * strict replies or not, better tfr monitor reporting
0247: *
0248: * Revision 1.22 2004/06/25 11:47:46 bruceb
0249: * made 1.1.x compatible
0250: *
0251: * Revision 1.21 2004/06/11 10:20:35 bruceb
0252: * permit 200 to be returned from various cmds
0253: *
0254: * Revision 1.20 2004/05/22 16:52:57 bruceb
0255: * message listener
0256: *
0257: * Revision 1.19 2004/05/15 22:37:22 bruceb
0258: * put debugResponses back in
0259: *
0260: * Revision 1.18 2004/05/13 23:00:34 hans
0261: * changed comment
0262: *
0263: * Revision 1.17 2004/05/08 21:14:41 bruceb
0264: * checkConnection stuff
0265: *
0266: * Revision 1.14 2004/04/19 21:54:06 bruceb
0267: * final tweaks to dirDetails() re caching
0268: *
0269: * Revision 1.13 2004/04/18 11:16:44 bruceb
0270: * made validateTransfer() public
0271: *
0272: * Revision 1.12 2004/04/17 18:37:38 bruceb
0273: * new parse functionality
0274: *
0275: * Revision 1.11 2004/03/23 20:26:49 bruceb
0276: * tweak to size(), catch exceptions on puts()
0277: *
0278: * Revision 1.10 2003/11/15 11:23:55 bruceb
0279: * changes required for ssl subclasses
0280: *
0281: * Revision 1.6 2003/05/31 14:53:44 bruceb
0282: * 1.2.2 changes
0283: *
0284: * Revision 1.5 2003/01/29 22:46:08 bruceb
0285: * minor changes
0286: *
0287: * Revision 1.4 2002/11/19 22:01:25 bruceb
0288: * changes for 1.2
0289: *
0290: * Revision 1.3 2001/10/09 20:53:46 bruceb
0291: * Active mode changes
0292: *
0293: * Revision 1.1 2001/10/05 14:42:03 bruceb
0294: * moved from old project
0295: *
0296: */package com.enterprisedt.net.ftp;
0297:
0298: import java.io.BufferedInputStream;
0299: import java.io.BufferedOutputStream;
0300: import java.io.ByteArrayInputStream;
0301: import java.io.ByteArrayOutputStream;
0302: import java.io.DataInputStream;
0303: import java.io.DataOutputStream;
0304: import java.io.File;
0305: import java.io.FileInputStream;
0306: import java.io.FileOutputStream;
0307: import java.io.IOException;
0308: import java.io.InputStream;
0309: import java.io.InputStreamReader;
0310: import java.io.LineNumberReader;
0311: import java.io.OutputStream;
0312: import java.net.InetAddress;
0313: import java.net.ServerSocket;
0314: import java.text.ParseException;
0315: import java.text.ParsePosition;
0316: import java.text.SimpleDateFormat;
0317: import java.util.Date;
0318: import java.util.Locale;
0319: import java.util.Properties;
0320: import java.util.TimeZone;
0321: import java.util.Vector;
0322:
0323: import com.enterprisedt.util.debug.Level;
0324: import com.enterprisedt.util.debug.Logger;
0325:
0326: /**
0327: * Supports client-side FTP. Most common
0328: * FTP operations are present in this class.
0329: *
0330: * @author Bruce Blackshaw
0331: * @version $Revision: 1.93 $
0332: */
0333: public class FTPClient implements FTPClientInterface {
0334:
0335: /**
0336: * Revision control id
0337: */
0338: public static String cvsId = "@(#)$Id: FTPClient.java,v 1.93 2008-01-09 03:54:21 bruceb Exp $";
0339:
0340: /**
0341: * Default byte interval for transfer monitor
0342: */
0343: final public static int DEFAULT_MONITOR_INTERVAL = 65535;
0344:
0345: /**
0346: * Default transfer buffer size
0347: */
0348: final public static int DEFAULT_BUFFER_SIZE = 16384;
0349:
0350: /**
0351: * Maximum port number
0352: */
0353: final private static int MAX_PORT = 65535;
0354:
0355: /**
0356: * Default timeout
0357: */
0358: final public static int DEFAULT_TIMEOUT = 60 * 1000;
0359:
0360: /**
0361: * Short value for a timeout
0362: */
0363: final private static int SHORT_TIMEOUT = 500;
0364:
0365: /**
0366: * Default encoding used for control data
0367: */
0368: final public static String DEFAULT_ENCODING = "US-ASCII";
0369:
0370: /**
0371: * SOCKS port property name
0372: */
0373: final private static String SOCKS_PORT = "socksProxyPort";
0374:
0375: /**
0376: * SOCKS host property name
0377: */
0378: final private static String SOCKS_HOST = "socksProxyHost";
0379:
0380: /**
0381: * Line separator
0382: */
0383: final private static byte[] LINE_SEPARATOR = System.getProperty(
0384: "line.separator").getBytes();
0385:
0386: /**
0387: * Used for ASCII translation
0388: */
0389: final public static byte CARRIAGE_RETURN = 13;
0390:
0391: /**
0392: * Used for ASCII translation
0393: */
0394: final public static byte LINE_FEED = 10;
0395:
0396: /**
0397: * Used for ASCII translation
0398: */
0399: final public static byte[] FTP_LINE_SEPARATOR = { CARRIAGE_RETURN,
0400: LINE_FEED };
0401:
0402: /**
0403: * Marker in reply for STOU reply with filename
0404: */
0405: final private static String STOU_FILENAME_MARKER = "FILE:";
0406:
0407: /**
0408: * Store command
0409: */
0410: final private static String STORE_CMD = "STOR ";
0411:
0412: /**
0413: * Store unique command
0414: */
0415: final private static String STORE_UNIQ_CMD = "STOU ";
0416:
0417: /**
0418: * Default locales
0419: */
0420: public static Locale[] DEFAULT_LISTING_LOCALES;
0421:
0422: /**
0423: * Logging object
0424: */
0425: private static Logger log = Logger.getLogger("FTPClient");
0426:
0427: /**
0428: * Format to interpret MTDM timestamp
0429: */
0430: private SimpleDateFormat tsFormat = new SimpleDateFormat(
0431: "yyyyMMddHHmmss");
0432:
0433: /**
0434: * Socket responsible for controlling
0435: * the connection
0436: */
0437: protected FTPControlSocket control = null;
0438:
0439: /**
0440: * Socket responsible for transferring
0441: * the data
0442: */
0443: protected FTPDataSocket data = null;
0444:
0445: /**
0446: * Socket timeout for both data and control. In
0447: * milliseconds
0448: */
0449: protected int timeout = DEFAULT_TIMEOUT;
0450:
0451: /**
0452: * Interval in seconds in between server wakeups. O is
0453: * not enabled
0454: */
0455: protected int serverWakeupInterval = 0;
0456:
0457: /**
0458: * Address of the remote server.
0459: */
0460: protected InetAddress remoteAddr;
0461:
0462: /**
0463: * Name/IP of remote host
0464: */
0465: protected String remoteHost;
0466:
0467: /**
0468: * Id of instance
0469: */
0470: protected String id;
0471:
0472: /**
0473: * Control port number.
0474: */
0475: protected int controlPort = FTPControlSocket.CONTROL_PORT;
0476:
0477: /**
0478: * If true, uses the original host IP if an internal IP address
0479: * is returned by the server in PASV mode
0480: */
0481: private boolean autoPassiveIPSubstitution = false;
0482:
0483: /**
0484: * IP address to force to use in active mode
0485: */
0486: private String activeIP = null;
0487:
0488: /**
0489: * Encoding used on control socket
0490: */
0491: protected String controlEncoding = DEFAULT_ENCODING;
0492:
0493: /**
0494: * Use strict return codes if true
0495: */
0496: private boolean strictReturnCodes = false;
0497:
0498: /**
0499: * Matcher for directory empty
0500: */
0501: protected DirectoryEmptyStrings dirEmptyStrings = new DirectoryEmptyStrings();
0502:
0503: /**
0504: * Matcher for transfer complete
0505: */
0506: protected TransferCompleteStrings transferCompleteStrings = new TransferCompleteStrings();
0507:
0508: /**
0509: * Matcher for permission denied
0510: */
0511: protected FileNotFoundStrings fileNotFoundStrings = new FileNotFoundStrings();
0512: /**
0513: * Can be used to cancel a transfer
0514: */
0515: private boolean cancelTransfer = false;
0516:
0517: /**
0518: * If true, a file transfer is being resumed
0519: */
0520: private boolean resume = false;
0521:
0522: /**
0523: * MDTM supported flag
0524: */
0525: private boolean mdtmSupported = true;
0526:
0527: /**
0528: * SIZE supported flag
0529: */
0530: private boolean sizeSupported = true;
0531:
0532: /**
0533: * Resume byte marker point
0534: */
0535: private long resumeMarker = 0;
0536:
0537: /**
0538: * Delete partial files on transfer failure?
0539: */
0540: private boolean deleteOnFailure = true;
0541:
0542: /**
0543: * If true, filetypes are autodetected and transfer mode changed to binary/ASCII as
0544: * required
0545: */
0546: protected boolean detectTransferMode = false;
0547:
0548: /**
0549: * Lowest port in active mode port range
0550: */
0551: private int lowPort = -1;
0552:
0553: /**
0554: * Highest port in active mode port range
0555: */
0556: private int highPort = -1;
0557:
0558: /**
0559: * Command sent to server for storing a file
0560: */
0561: private String storeCommand = STORE_CMD;
0562:
0563: /**
0564: * Bytes transferred in between monitor callbacks
0565: */
0566: protected long monitorInterval = DEFAULT_MONITOR_INTERVAL;
0567:
0568: /**
0569: * Size of transfer buffers
0570: */
0571: protected int transferBufferSize = DEFAULT_BUFFER_SIZE;
0572:
0573: /**
0574: * Count of downloaded files
0575: */
0576: private int downloadCount = 0;
0577:
0578: /**
0579: * Count of uploaded files
0580: */
0581: private int uploadCount = 0;
0582:
0583: /**
0584: * Count of deleted files
0585: */
0586: private int deleteCount = 0;
0587:
0588: /**
0589: * Listen to all interfaces in active mode
0590: */
0591: private boolean listenOnAllInterfaces = true;
0592:
0593: /**
0594: * Parses LIST output
0595: */
0596: private FTPFileFactory fileFactory = null;
0597:
0598: /**
0599: * Locales for date parsing
0600: */
0601: private Locale[] listingLocales;
0602:
0603: /**
0604: * Parses the MLSD and MLST formats
0605: */
0606: private MLSXEntryParser mlsxParser = new MLSXEntryParser();
0607:
0608: /**
0609: * Progress monitor
0610: */
0611: protected FTPProgressMonitor monitor = null;
0612:
0613: /**
0614: * Message listener
0615: */
0616: protected FTPMessageListener messageListener = null;
0617:
0618: /**
0619: * File transfer listener
0620: */
0621: protected FTPProgressMonitorEx monitorEx = null;
0622:
0623: /**
0624: * Record of the transfer type - make the default ASCII
0625: */
0626: protected FTPTransferType transferType = FTPTransferType.ASCII;
0627:
0628: /**
0629: * Record of the connect mode - make the default PASV (as this was
0630: * the original mode supported)
0631: */
0632: private FTPConnectMode connectMode = FTPConnectMode.PASV;
0633:
0634: /**
0635: * Holds the last valid reply from the server on the control socket
0636: */
0637: protected FTPReply lastValidReply;
0638:
0639: /**
0640: * Holds the last reply from the server on the control socket
0641: */
0642: protected FTPReply lastReply;
0643:
0644: /**
0645: * set default listing locales.
0646: */
0647: static {
0648: DEFAULT_LISTING_LOCALES = new Locale[2];
0649: DEFAULT_LISTING_LOCALES[0] = Locale.ENGLISH;
0650: DEFAULT_LISTING_LOCALES[1] = Locale.getDefault();
0651: }
0652:
0653: /**
0654: * Instance initializer. Sets formatter to GMT.
0655: */
0656: {
0657: tsFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
0658: listingLocales = DEFAULT_LISTING_LOCALES;
0659: }
0660:
0661: /**
0662: * Get the version of edtFTPj
0663: *
0664: * @return int array of {major,middle,minor} version numbers
0665: */
0666: public static int[] getVersion() {
0667: return VersionDetails.getVersion();
0668: }
0669:
0670: /**
0671: * Get the build timestamp
0672: *
0673: * @return d-MMM-yyyy HH:mm:ss z build timestamp
0674: */
0675: public static String getBuildTimestamp() {
0676: return VersionDetails.getBuildTimestamp();
0677: }
0678:
0679: /**
0680: * Constructor. Creates the control
0681: * socket
0682: *
0683: * @param remoteHost the remote hostname
0684: * @deprecated use setter methods to set properties
0685: */
0686: public FTPClient(String remoteHost) throws IOException,
0687: FTPException {
0688:
0689: this (remoteHost, FTPControlSocket.CONTROL_PORT, 0);
0690: }
0691:
0692: /**
0693: * Constructor. Creates the control
0694: * socket
0695: *
0696: * @param remoteHost the remote hostname
0697: * @param controlPort port for control stream (-1 for default port)
0698: * @deprecated use setter methods to set properties
0699: */
0700: public FTPClient(String remoteHost, int controlPort)
0701: throws IOException, FTPException {
0702:
0703: this (remoteHost, controlPort, 0);
0704: }
0705:
0706: /**
0707: * Constructor. Creates the control
0708: * socket
0709: *
0710: * @param remoteHost the remote hostname
0711: * @param controlPort port for control stream (use -1 for the default port)
0712: * @param timeout the length of the timeout, in milliseconds
0713: * (pass in 0 for no timeout)
0714: * @deprecated use setter methods to set properties
0715: */
0716: public FTPClient(String remoteHost, int controlPort, int timeout)
0717: throws IOException, FTPException {
0718:
0719: this (InetAddress.getByName(remoteHost), controlPort, timeout);
0720: }
0721:
0722: /**
0723: * Constructor. Creates the control
0724: * socket
0725: *
0726: * @param remoteHost the remote hostname
0727: * @param controlPort port for control stream (use -1 for the default port)
0728: * @param timeout the length of the timeout, in milliseconds
0729: * (pass in 0 for no timeout)
0730: * @param encoding character encoding used for data
0731: * @deprecated use setter methods to set properties
0732: */
0733: public FTPClient(String remoteHost, int controlPort, int timeout,
0734: String encoding) throws IOException, FTPException {
0735:
0736: this (InetAddress.getByName(remoteHost), controlPort, timeout,
0737: encoding);
0738: }
0739:
0740: /**
0741: * Constructor. Creates the control
0742: * socket
0743: *
0744: * @param remoteAddr the address of the
0745: * remote host
0746: * @deprecated use setter methods to set properties
0747: */
0748: public FTPClient(InetAddress remoteAddr) throws IOException,
0749: FTPException {
0750:
0751: this (remoteAddr, FTPControlSocket.CONTROL_PORT, 0);
0752: }
0753:
0754: /**
0755: * Constructor. Creates the control
0756: * socket. Allows setting of control port (normally
0757: * set by default to 21).
0758: *
0759: * @param remoteAddr the address of the
0760: * remote host
0761: * @param controlPort port for control stream
0762: * @deprecated use setter methods to set properties
0763: */
0764: public FTPClient(InetAddress remoteAddr, int controlPort)
0765: throws IOException, FTPException {
0766:
0767: this (remoteAddr, controlPort, 0);
0768: }
0769:
0770: /**
0771: * Constructor. Creates the control
0772: * socket. Allows setting of control port (normally
0773: * set by default to 21).
0774: *
0775: * @param remoteAddr the address of the
0776: * remote host
0777: * @param controlPort port for control stream (-1 for default port)
0778: * @param timeout the length of the timeout, in milliseconds
0779: * (pass in 0 for no timeout)
0780: * @deprecated use setter methods to set properties
0781: */
0782: public FTPClient(InetAddress remoteAddr, int controlPort,
0783: int timeout) throws IOException, FTPException {
0784: if (controlPort < 0)
0785: controlPort = FTPControlSocket.CONTROL_PORT;
0786: initialize(new FTPControlSocket(remoteAddr, controlPort,
0787: timeout, DEFAULT_ENCODING, null));
0788: }
0789:
0790: /**
0791: * Constructor. Creates the control
0792: * socket. Allows setting of control port (normally
0793: * set by default to 21).
0794: *
0795: * @param remoteAddr the address of the
0796: * remote host
0797: * @param controlPort port for control stream (-1 for default port)
0798: * @param timeout the length of the timeout, in milliseconds
0799: * (pass in 0 for no timeout)
0800: * @param encoding character encoding used for data
0801: * @deprecated use setter methods to set properties
0802: */
0803: public FTPClient(InetAddress remoteAddr, int controlPort,
0804: int timeout, String encoding) throws IOException,
0805: FTPException {
0806: if (controlPort < 0)
0807: controlPort = FTPControlSocket.CONTROL_PORT;
0808: initialize(new FTPControlSocket(remoteAddr, controlPort,
0809: timeout, encoding, null));
0810: }
0811:
0812: /**
0813: * Default constructor should now always be used together with setter methods
0814: * in preference to other constructors (now deprecated). The {@link #connect()}
0815: * method is used to perform the actual connection to the remote host - but only
0816: * for this constructor. Deprecated constructors connect in the constructor and
0817: * connect() is not required (and cannot be called).
0818: */
0819: public FTPClient() {
0820: log.debug(VersionDetails.report(this ));
0821: }
0822:
0823: /**
0824: * Connects to the server at the address and port number defined
0825: * in the constructor. Must be performed <b>before</b> login() or user() is
0826: * called.
0827: *
0828: * @throws IOException Thrown if there is a TCP/IP-related error.
0829: * @throws FTPException Thrown if there is an error related to the FTP protocol.
0830: */
0831: public void connect() throws IOException, FTPException {
0832:
0833: checkConnection(false);
0834:
0835: log.debug("Connecting to " + remoteAddr + ":" + controlPort);
0836:
0837: initialize(new FTPControlSocket(remoteAddr, controlPort,
0838: timeout, controlEncoding, messageListener));
0839: }
0840:
0841: /**
0842: * Is this client connected?
0843: *
0844: * @return true if connected, false otherwise
0845: */
0846: public boolean connected() {
0847: if (control == null)
0848: return false;
0849: else {
0850: try {
0851: if (!SocketUtils.isConnected(control.controlSock)) {
0852: control = null;
0853: return false;
0854: } else {
0855: return true;
0856: }
0857: } catch (IOException e) {
0858: return false;
0859: }
0860: }
0861: }
0862:
0863: /**
0864: * Checks if the client has connected to the server and throws an exception if it hasn't.
0865: * This is only intended to be used by subclasses
0866: *
0867: * @throws FTPException Thrown if the client has not connected to the server.
0868: */
0869: protected void checkConnection(boolean shouldBeConnected)
0870: throws FTPException {
0871: if (shouldBeConnected && !connected())
0872: throw new FTPException(
0873: "The FTP client has not yet connected to the server. "
0874: + "The requested action cannot be performed until after a connection has been established.");
0875: else if (!shouldBeConnected && connected())
0876: throw new FTPException(
0877: "The FTP client has already been connected to the server. "
0878: + "The requested action must be performed before a connection is established.");
0879: }
0880:
0881: /**
0882: * Set the control socket explicitly
0883: *
0884: * @param control control socket reference
0885: */
0886: protected void initialize(FTPControlSocket control)
0887: throws IOException {
0888: this .control = control;
0889: control.setMessageListener(messageListener);
0890: control.setStrictReturnCodes(strictReturnCodes);
0891: control.setListenOnAllInterfaces(listenOnAllInterfaces);
0892: control.setTimeout(timeout);
0893: control.setAutoPassiveIPSubstitution(autoPassiveIPSubstitution);
0894: if (activeIP != null)
0895: control.setActivePortIPAddress(activeIP);
0896: if (lowPort > 0 && highPort > 0)
0897: control.setActivePortRange(lowPort, highPort);
0898: }
0899:
0900: /**
0901: * Switch debug of responses on or off
0902: *
0903: * @param on true if you wish to have responses to
0904: * the log stream, false otherwise
0905: * @deprecated use the Logger class to switch debugging on and off
0906: */
0907: public void debugResponses(boolean on) {
0908: if (on)
0909: Logger.setLevel(Level.DEBUG);
0910: else
0911: Logger.setLevel(Level.OFF);
0912: }
0913:
0914: /**
0915: * Get the identifying string for this instance
0916: */
0917: public String getId() {
0918: return id;
0919: }
0920:
0921: /**
0922: * Set the identifying string for this instance
0923: *
0924: * @param id identifying string
0925: */
0926: public void setId(String id) {
0927: this .id = id;
0928: }
0929:
0930: /**
0931: * Get the number of files downloaded since the count was
0932: * reset
0933: *
0934: * @return download file count
0935: */
0936: public int getDownloadCount() {
0937: return downloadCount;
0938: }
0939:
0940: /**
0941: * Reset the count of downloaded files to zero.
0942: *
0943: */
0944: public void resetDownloadCount() {
0945: downloadCount = 0;
0946: }
0947:
0948: /**
0949: * Get the number of files uploaded since the count was
0950: * reset
0951: *
0952: * @return upload file count
0953: */
0954: public int getUploadCount() {
0955: return uploadCount;
0956: }
0957:
0958: /**
0959: * Reset the count of uploaded files to zero.
0960: *
0961: */
0962: public void resetUploadCount() {
0963: uploadCount = 0;
0964: }
0965:
0966: /**
0967: * Get the number of files deleted since the count was
0968: * reset
0969: *
0970: * @return deleted file count
0971: */
0972: public int getDeleteCount() {
0973: return deleteCount;
0974: }
0975:
0976: /**
0977: * Reset the count of deleted files to zero.
0978: *
0979: */
0980: public void resetDeleteCount() {
0981: deleteCount = 0;
0982: }
0983:
0984: /**
0985: * Set strict checking of FTP return codes. If strict
0986: * checking is on (the default) code must exactly match the expected
0987: * code. If strict checking is off, only the first digit must match.
0988: *
0989: * @param strict true for strict checking, false for loose checking
0990: */
0991: public void setStrictReturnCodes(boolean strict) {
0992: this .strictReturnCodes = strict;
0993: if (control != null)
0994: control.setStrictReturnCodes(strict);
0995: }
0996:
0997: /**
0998: * Determine if strict checking of return codes is switched on. If it is
0999: * (the default), all return codes must exactly match the expected code.
1000: * If strict checking is off, only the first digit must match.
1001: *
1002: * @return true if strict return code checking, false if non-strict.
1003: */
1004: public boolean isStrictReturnCodes() {
1005: return strictReturnCodes;
1006: }
1007:
1008: /**
1009: * Listen on all interfaces for active mode transfers (the default).
1010: *
1011: * @param listenOnAll true if listen on all interfaces, false to listen on the control interface
1012: */
1013: public void setListenOnAllInterfaces(boolean listenOnAll) {
1014: this .listenOnAllInterfaces = listenOnAll;
1015: if (control != null)
1016: control.setListenOnAllInterfaces(listenOnAll);
1017: }
1018:
1019: /**
1020: * Are we listening on all interfaces in active mode, which is the default?
1021: *
1022: * @return true if listening on all interfaces, false if listening just on the control interface
1023: */
1024: public boolean getListenOnAllInterfaces() {
1025: return listenOnAllInterfaces;
1026: }
1027:
1028: /**
1029: * Get class that holds fragments of server messages that indicate a file was
1030: * not found. New messages can be added.
1031: * <p>
1032: * The fragments are used when it is necessary to examine the message
1033: * returned by a server to see if it is saying a file was not found.
1034: * If an FTP server is returning a different message that still clearly
1035: * indicates a file was not found, use this property to add a new server
1036: * fragment to the repository via the add method. It would be helpful to
1037: * email support at enterprisedt dot com to inform us of the message so
1038: * it can be added to the next build.
1039: *
1040: * @return messages class
1041: */
1042: public FileNotFoundStrings getFileNotFoundMessages() {
1043: return fileNotFoundStrings;
1044: }
1045:
1046: /**
1047: * Set a new instance of the strings class
1048: *
1049: * @param fileNotFoundStrings new instance
1050: */
1051: public void setFileNotFoundMessages(
1052: FileNotFoundStrings fileNotFoundStrings) {
1053: this .fileNotFoundStrings = fileNotFoundStrings;
1054: }
1055:
1056: /**
1057: * Get class that holds fragments of server messages that indicate a transfer completed.
1058: * New messages can be added.
1059: * <p>
1060: * The fragments are used when it is necessary to examine the message
1061: * returned by a server to see if it is saying a transfer completed.
1062: * If an FTP server is returning a different message that still clearly
1063: * indicates a transfer failed, use this property to add a new server
1064: * fragment to the repository via the add method. It would be helpful to
1065: * email support at enterprisedt dot com to inform us of the message so
1066: * it can be added to the next build.
1067: *
1068: * @return messages class
1069: */
1070: public TransferCompleteStrings getTransferCompleteMessages() {
1071: return transferCompleteStrings;
1072: }
1073:
1074: /**
1075: * Set a new instance of the strings class
1076: *
1077: * @param transferCompleteStrings new instance
1078: */
1079: public void setTransferCompleteMessages(
1080: TransferCompleteStrings transferCompleteStrings) {
1081: this .transferCompleteStrings = transferCompleteStrings;
1082: }
1083:
1084: /**
1085: * Get class that holds fragments of server messages that indicate a
1086: * directory is empty. New messages can be added.
1087: * <p>
1088: * The fragments are used when it is necessary to examine the message
1089: * returned by a server to see if it is saying a directory is empty.
1090: * If an FTP server is returning a different message that still clearly
1091: * indicates a directory is empty, use this property to add a new server
1092: * fragment to the repository via the add method. It would be helpful to
1093: * email support at enterprisedt dot com to inform us of the message so
1094: * it can be added to the next build.
1095: *
1096: * @return messages class
1097: */
1098: public DirectoryEmptyStrings getDirectoryEmptyMessages() {
1099: return dirEmptyStrings;
1100: }
1101:
1102: /**
1103: * Set a new instance of the strings class
1104: *
1105: * @param dirEmptyStrings new instance
1106: */
1107: public void setDirectoryEmptyMessages(
1108: DirectoryEmptyStrings dirEmptyStrings) {
1109: this .dirEmptyStrings = dirEmptyStrings;
1110: }
1111:
1112: /* (non-Javadoc)
1113: * @see com.enterprisedt.net.ftp.FTPClientInterface#setDetectTransferMode(boolean)
1114: */
1115: public void setDetectTransferMode(boolean detectTransferMode) {
1116: this .detectTransferMode = detectTransferMode;
1117: }
1118:
1119: /* (non-Javadoc)
1120: * @see com.enterprisedt.net.ftp.FTPClientInterface#getDetectTransferMode()
1121: */
1122: public boolean getDetectTransferMode() {
1123: return detectTransferMode;
1124: }
1125:
1126: /**
1127: * Set to true if the STOU command is always to be used when
1128: * uploading files, even if a filename is supplied. Normally
1129: * STOU is only used if the supplied remote filename is null or
1130: * the empty string.
1131: *
1132: * @param forceUnique true if STOU is always to be used
1133: */
1134: public void setForceUniqueNames(boolean forceUnique) {
1135: if (forceUnique)
1136: storeCommand = STORE_UNIQ_CMD;
1137: else
1138: storeCommand = STORE_CMD;
1139: }
1140:
1141: /**
1142: * Switch the transfer mode if requested and if necessary
1143: *
1144: * @param filename filename of file to be transferred
1145: * @throws FTPException
1146: * @throws IOException
1147: */
1148: protected void chooseTransferMode(String filename)
1149: throws IOException, FTPException {
1150: if (detectTransferMode) {
1151: if (filename == null) {
1152: log
1153: .warn("Cannot choose transfer mode as filename not supplied");
1154: return;
1155: }
1156: if (FileTypes.ASCII.matches(filename)
1157: && transferType.equals(FTPTransferType.BINARY)) {
1158: setType(FTPTransferType.ASCII);
1159: log
1160: .debug("Autodetect on - changed transfer type to ASCII");
1161: } else if (FileTypes.BINARY.matches(filename)
1162: && transferType.equals(FTPTransferType.ASCII)) {
1163: setType(FTPTransferType.BINARY);
1164: log
1165: .debug("Autodetect on - changed transfer type to binary");
1166: }
1167: }
1168: }
1169:
1170: /**
1171: * Set the SO_TIMEOUT in milliseconds on the underlying socket.
1172: * If set this means the socket will block in a read operation
1173: * only for this length of time - useful if the FTP sever has
1174: * hung, for example. The default is 0, which is an infinite timeout.
1175: *
1176: * Note that for JREs 1.4+, the timeout is also used when first
1177: * connecting to the remote host.
1178: *
1179: * @param millis The length of the timeout, in milliseconds
1180: */
1181: public void setTimeout(int millis) throws IOException {
1182:
1183: this .timeout = millis;
1184: if (control != null)
1185: control.setTimeout(millis);
1186: }
1187:
1188: /**
1189: * Get the TCP timeout
1190: *
1191: * @return timeout that is used, in milliseconds
1192: */
1193: public int getTimeout() {
1194: return timeout;
1195: }
1196:
1197: /**
1198: * Returns the control-port being connected to on the remote server.
1199: *
1200: * Note that this method replaces {@link #getControlPort()}.
1201: *
1202: * @return Returns the port being connected to on the remote server.
1203: */
1204: public int getRemotePort() {
1205: return controlPort;
1206: }
1207:
1208: /**
1209: * Set the control to connect to on the remote server. Can only do this if
1210: * not already connected.
1211: *
1212: * Note that this method replaces {@link #setControlPort(int)}.
1213: *
1214: * @param remotePort The port to use.
1215: * @throws FTPException Thrown if the client is already connected to the server.
1216: */
1217: public void setRemotePort(int remotePort) throws FTPException {
1218: checkConnection(false);
1219: this .controlPort = remotePort;
1220: }
1221:
1222: /**
1223: * Returns the control-port being connected to on the remote server.
1224: * @return Returns the port being connected to on the remote server.
1225: * @deprecated Use {@link com.enterprisedt.net.ftp.FTPClientInterface#getRemotePort()} instead.
1226: */
1227: public int getControlPort() {
1228: return controlPort;
1229: }
1230:
1231: /**
1232: * Set the control to connect to on the remote server. Can only do this if
1233: * not already connected.
1234: *
1235: * @param controlPort The port to use.
1236: * @throws FTPException Thrown if the client is already connected to the server.
1237: * @deprecated Use {@link com.enterprisedt.net.ftp.FTPClientInterface#setRemotePort(int)} instead.
1238: */
1239: public void setControlPort(int controlPort) throws FTPException {
1240: checkConnection(false);
1241: this .controlPort = controlPort;
1242: }
1243:
1244: /**
1245: * @return Returns the remoteAddr.
1246: */
1247: public InetAddress getRemoteAddr() {
1248: return remoteAddr;
1249: }
1250:
1251: /**
1252: * Set the remote address
1253: *
1254: * @param remoteAddr The remoteAddr to set.
1255: * @throws FTPException
1256: */
1257: public void setRemoteAddr(InetAddress remoteAddr)
1258: throws FTPException {
1259: checkConnection(false);
1260: this .remoteAddr = remoteAddr;
1261: this .remoteHost = remoteAddr.getHostName();
1262: }
1263:
1264: /*
1265: * (non-Javadoc)
1266: * @see com.enterprisedt.net.ftp.FTPClientInterface#getRemoteHost()
1267: */
1268: public String getRemoteHost() {
1269: return remoteHost;
1270: }
1271:
1272: /*
1273: * (non-Javadoc)
1274: * @see com.enterprisedt.net.ftp.FTPClientInterface#setRemoteHost(java.lang.String)
1275: */
1276: public void setRemoteHost(String remoteHost) throws IOException,
1277: FTPException {
1278: checkConnection(false);
1279: this .remoteHost = remoteHost;
1280: this .remoteAddr = InetAddress.getByName(remoteHost);
1281: }
1282:
1283: /**
1284: * Is automatic substitution of the remote host IP set to
1285: * be on for passive mode connections?
1286: *
1287: * @return true if set on, false otherwise
1288: */
1289: public boolean isAutoPassiveIPSubstitution() {
1290: return autoPassiveIPSubstitution;
1291: }
1292:
1293: /**
1294: * Set automatic substitution of the remote host IP on if
1295: * in passive mode
1296: *
1297: * @param autoPassiveIPSubstitution true if set to on, false otherwise
1298: */
1299: public void setAutoPassiveIPSubstitution(
1300: boolean autoPassiveIPSubstitution) {
1301: this .autoPassiveIPSubstitution = autoPassiveIPSubstitution;
1302: if (control != null)
1303: control
1304: .setAutoPassiveIPSubstitution(autoPassiveIPSubstitution);
1305: }
1306:
1307: /**
1308: * Get server wakeup interval in seconds. A value of 0
1309: * means it is disabled (the default).
1310: *
1311: * @return interval in seconds
1312: */
1313: public int getServerWakeupInterval() {
1314: return serverWakeupInterval;
1315: }
1316:
1317: /**
1318: * Set server wakeup interval in seconds. A value of 0
1319: * means it is disabled (the default). This may hang or confuse
1320: * the FTP server - use with caution.
1321: *
1322: * @param interval interval in seconds
1323: */
1324: public void setServerWakeupInterval(int interval) {
1325: this .serverWakeupInterval = interval;
1326: }
1327:
1328: /**
1329: * Get the encoding used for the control connection
1330: *
1331: * @return Returns the current controlEncoding.
1332: */
1333: public String getControlEncoding() {
1334: return controlEncoding;
1335: }
1336:
1337: /**
1338: * Set the control socket's encoding. Can only do this if
1339: * not connected
1340: *
1341: * @param controlEncoding The controlEncoding to set, which is the name of a Charset
1342: * @see java.nio.charset.Charset
1343: * @throws FTPException
1344: */
1345: public void setControlEncoding(String controlEncoding)
1346: throws FTPException {
1347: checkConnection(false);
1348: this .controlEncoding = controlEncoding;
1349: }
1350:
1351: /**
1352: * @return Returns the messageListener.
1353: */
1354: public FTPMessageListener getMessageListener() {
1355: return messageListener;
1356: }
1357:
1358: /**
1359: * Set a listener that handles all FTP messages
1360: *
1361: * @param listener message listener
1362: */
1363: public void setMessageListener(FTPMessageListener listener) {
1364: this .messageListener = listener;
1365: if (control != null)
1366: control.setMessageListener(listener);
1367: }
1368:
1369: /**
1370: * Get reference to the transfer listener
1371: *
1372: * @return FTPProgressMonitorEx
1373: */
1374: public FTPProgressMonitorEx getProgressMonitorEx() {
1375: return monitorEx;
1376: }
1377:
1378: /**
1379: * Set reference to the transfer listener
1380: *
1381: * @param monitorEx transfer listener
1382: */
1383: public void setProgressMonitorEx(FTPProgressMonitorEx monitorEx) {
1384: this .monitorEx = monitorEx;
1385: }
1386:
1387: /**
1388: * Set the connect mode
1389: *
1390: * @param mode ACTIVE or PASV mode
1391: */
1392: public void setConnectMode(FTPConnectMode mode) {
1393: connectMode = mode;
1394: }
1395:
1396: /**
1397: * @return Returns the connectMode.
1398: */
1399: public FTPConnectMode getConnectMode() {
1400: return connectMode;
1401: }
1402:
1403: /*
1404: * (non-Javadoc)
1405: * @see com.enterprisedt.net.ftp.FTPClientInterface#setProgressMonitor(com.enterprisedt.net.ftp.FTPProgressMonitor, long)
1406: */
1407: public void setProgressMonitor(FTPProgressMonitor monitor,
1408: long interval) {
1409: this .monitor = monitor;
1410: this .monitorInterval = interval;
1411: }
1412:
1413: /*
1414: * (non-Javadoc)
1415: * @see com.enterprisedt.net.ftp.FTPClientInterface#setProgressMonitor(com.enterprisedt.net.ftp.FTPProgressMonitor)
1416: */
1417: public void setProgressMonitor(FTPProgressMonitor monitor) {
1418: this .monitor = monitor;
1419: }
1420:
1421: /**
1422: * Get the reference to the progress monitor
1423: *
1424: * @return progress monitor
1425: */
1426: public FTPProgressMonitor getProgressMonitor() {
1427: return monitor;
1428: }
1429:
1430: /*
1431: * (non-Javadoc)
1432: * @see com.enterprisedt.net.ftp.FTPClientInterface#getMonitorInterval()
1433: */
1434: public long getMonitorInterval() {
1435: return monitorInterval;
1436: }
1437:
1438: /**
1439: * Set the number of bytes transferred between each callback on the
1440: * progress monitor
1441: *
1442: * param interval bytes to be transferred before a callback
1443: */
1444: public void setMonitorInterval(long interval) {
1445: this .monitorInterval = interval;
1446: }
1447:
1448: /**
1449: * Set the size of the buffers used in writing to and reading from
1450: * the data sockets
1451: *
1452: * @param size new size of buffer in bytes
1453: */
1454: public void setTransferBufferSize(int size) {
1455: transferBufferSize = size;
1456: }
1457:
1458: /**
1459: * Get the size of the buffers used in writing to and reading from
1460: * the data sockets
1461: *
1462: * @return transfer buffer size
1463: */
1464: public int getTransferBufferSize() {
1465: return transferBufferSize;
1466: }
1467:
1468: /*
1469: * (non-Javadoc)
1470: * @see com.enterprisedt.net.ftp.FTPClientInterface#cancelTransfer()
1471: */
1472: public void cancelTransfer() {
1473: cancelTransfer = true;
1474: log.warn("cancelTransfer() called");
1475: }
1476:
1477: /**
1478: * Has the current transfer been cancelled?
1479: *
1480: * @return true if cancel, false otherwise
1481: */
1482: public boolean isTransferCancelled() {
1483: return cancelTransfer;
1484: }
1485:
1486: /**
1487: * If true, delete partially written files when exceptions are thrown
1488: * during a download
1489: *
1490: * @return true if delete local file on error
1491: */
1492: public boolean isDeleteOnFailure() {
1493: return deleteOnFailure;
1494: }
1495:
1496: /**
1497: * Switch on or off the automatic deletion of partially written files
1498: * that are left when an exception is thrown during a download
1499: *
1500: * @param deleteOnFailure true if delete when a failure occurs
1501: */
1502: public void setDeleteOnFailure(boolean deleteOnFailure) {
1503: this .deleteOnFailure = deleteOnFailure;
1504: }
1505:
1506: /**
1507: * We can force PORT to send a fixed IP address, which can be useful with certain
1508: * NAT configurations. Must be connected to the remote host to call this method.
1509: *
1510: * @param IPAddress IP address to force, in 192.168.1.0 form
1511: * @deprecated
1512: */
1513: public void setPORTIP(String IPAddress) throws FTPException {
1514: setActiveIPAddress(IPAddress);
1515: }
1516:
1517: /**
1518: * We can force PORT to send a fixed IP address, which can be useful with certain
1519: * NAT configurations. Must be connected to the remote host to call this method.
1520: *
1521: * @param activeIP IP address to force, in 192.168.1.0 form or in IPV6 form, e.g.
1522: * 1080::8:800:200C:417A
1523: */
1524: public void setActiveIPAddress(String activeIP) throws FTPException {
1525:
1526: this .activeIP = activeIP;
1527: if (control != null)
1528: control.setActivePortIPAddress(activeIP);
1529: }
1530:
1531: /**
1532: * Get the active IP address that is set.
1533: *
1534: * @return active IP address or null if not set
1535: */
1536: public String getActiveIPAddress() {
1537: return activeIP;
1538: }
1539:
1540: /**
1541: * Force a certain range of ports to be used in active mode. This is
1542: * generally so that a port range can be configured in a firewall. Note
1543: * that if lowest == highest, a single port will be used. This works well
1544: * for uploads, but downloads generally require multiple ports, as most
1545: * servers fail to create a connection repeatedly for the same port.
1546: *
1547: * @param lowest Lower limit of range.
1548: * @param highest Upper limit of range.
1549: */
1550: public void setActivePortRange(int lowest, int highest)
1551: throws FTPException {
1552:
1553: this .lowPort = lowest;
1554: this .highPort = highest;
1555:
1556: if (lowest < 0 || lowest > highest || highest > MAX_PORT)
1557: throw new FTPException("Invalid port range specified");
1558:
1559: if (control != null)
1560: control.setActivePortRange(lowest, highest);
1561:
1562: log.debug("setActivePortRange(" + lowest + "," + highest + ")");
1563: }
1564:
1565: /**
1566: * Get the lower limit of the port range for active mode.
1567: *
1568: * @return lower limit, or -1 if not set
1569: */
1570: public int getActiveLowPort() {
1571: return lowPort;
1572: }
1573:
1574: /**
1575: * Get the upper limit of the port range for active mode.
1576: *
1577: * @return upper limit, or -1 if not set
1578: */
1579: public int getActiveHighPort() {
1580: return highPort;
1581: }
1582:
1583: /**
1584: * Login into an account on the FTP server. This
1585: * call completes the entire login process. Note that
1586: * connect() must be called first.
1587: *
1588: * @param user user name
1589: * @param password user's password
1590: */
1591: public void login(String user, String password) throws IOException,
1592: FTPException {
1593:
1594: checkConnection(true);
1595:
1596: user(user);
1597:
1598: if (lastValidReply.getReplyCode().equals("230"))
1599: return;
1600: else {
1601: password(password);
1602: }
1603: }
1604:
1605: /**
1606: * Login into an account on the FTP server. This call completes the
1607: * entire login process. This method permits additional account information
1608: * to be supplied. FTP servers can use combinations of these parameters in
1609: * many different ways, e.g. to pass in proxy details via this method, some
1610: * servers use the "user" as 'ftpUser + "@" + ftpHost + " " + ftpProxyUser',
1611: * the "password" as the FTP user's password, and the accountInfo as the proxy
1612: * password. Note that connect() must be called first.
1613: *
1614: * @param user user name
1615: * @param password user's password
1616: * @param accountInfo account info string
1617: */
1618: public void login(String user, String password, String accountInfo)
1619: throws IOException, FTPException {
1620:
1621: checkConnection(true);
1622:
1623: user(user);
1624:
1625: if (lastValidReply.getReplyCode().equals("230")) // no pwd
1626: return;
1627: else {
1628: password(password);
1629: if (lastValidReply.getReplyCode().equals("332")) // requires acct info
1630: account(accountInfo);
1631: }
1632: }
1633:
1634: /**
1635: * Supply the user name to log into an account
1636: * on the FTP server. Must be followed by the
1637: * password() method - but we allow for no password.
1638: * Note that connect() must be called first.
1639: *
1640: * @param user user name
1641: */
1642: public void user(String user) throws IOException, FTPException {
1643:
1644: checkConnection(true);
1645:
1646: lastReply = control.sendCommand("USER " + user);
1647:
1648: // we allow for a site with no password - 230 response
1649: String[] validCodes = { "230", "331" };
1650: lastValidReply = control.validateReply(lastReply, validCodes);
1651: }
1652:
1653: /**
1654: * Supplies the password for a previously supplied
1655: * username to log into the FTP server. Must be
1656: * preceeded by the user() method
1657: *
1658: * @param password The password.
1659: */
1660: public void password(String password) throws IOException,
1661: FTPException {
1662:
1663: checkConnection(true);
1664:
1665: lastReply = control.sendCommand("PASS " + password);
1666:
1667: // we allow for a site with no passwords (202) or requiring
1668: // ACCT info (332)
1669: String[] validCodes = { "230", "202", "332" };
1670: lastValidReply = control.validateReply(lastReply, validCodes);
1671: }
1672:
1673: /**
1674: * Supply account information string to the server. This can be
1675: * used for a variety of purposes - for example, the server could
1676: * indicate that a password has expired (by sending 332 in reply to
1677: * PASS) and a new password automatically supplied via ACCT. It
1678: * is up to the server how it uses this string.
1679: *
1680: * @param accountInfo account information string
1681: */
1682: public void account(String accountInfo) throws IOException,
1683: FTPException {
1684:
1685: checkConnection(true);
1686:
1687: lastReply = control.sendCommand("ACCT " + accountInfo);
1688:
1689: // ok or not implemented
1690: String[] validCodes = { "230", "202" };
1691: lastValidReply = control.validateReply(lastReply, validCodes);
1692: }
1693:
1694: /**
1695: * Set up SOCKS v4/v5 proxy settings. This can be used if there
1696: * is a SOCKS proxy server in place that must be connected thru.
1697: * Note that setting these properties directs <b>all</b> TCP
1698: * sockets in this JVM to the SOCKS proxy
1699: *
1700: * @param port SOCKS proxy port
1701: * @param host SOCKS proxy hostname
1702: */
1703: public static void initSOCKS(String port, String host) {
1704: Properties props = System.getProperties();
1705: props.put(SOCKS_PORT, port);
1706: props.put(SOCKS_HOST, host);
1707: System.setProperties(props);
1708: }
1709:
1710: /**
1711: * Set up SOCKS username and password for SOCKS username/password
1712: * authentication. Often, no authentication will be required
1713: * but the SOCKS server may be configured to request these.
1714: *
1715: * @param username the SOCKS username
1716: * @param password the SOCKS password
1717: */
1718: public static void initSOCKSAuthentication(String username,
1719: String password) {
1720: Properties props = System.getProperties();
1721: props.put("java.net.socks.username", username);
1722: props.put("java.net.socks.password", password);
1723: System.setProperties(props);
1724: }
1725:
1726: /**
1727: * Clear SOCKS settings. Note that setting these properties affects
1728: * <b>all</b> TCP sockets in this JVM
1729: */
1730: public static void clearSOCKS() {
1731:
1732: Properties prop = System.getProperties();
1733: prop.remove(SOCKS_HOST);
1734: prop.remove(SOCKS_PORT);
1735: System.setProperties(prop);
1736: }
1737:
1738: /**
1739: * Get the name of the remote host
1740: *
1741: * @return remote host name
1742: */
1743: String getRemoteHostName() {
1744: return control.getRemoteHostName();
1745: }
1746:
1747: /**
1748: * Issue arbitrary ftp commands to the FTP server.
1749: *
1750: * @param command ftp command to be sent to server
1751: * @param validCodes valid return codes for this command. If null
1752: * is supplied no validation is performed
1753: *
1754: * @return the text returned by the FTP server
1755: */
1756: public String quote(String command, String[] validCodes)
1757: throws IOException, FTPException {
1758:
1759: checkConnection(true);
1760:
1761: lastReply = control.sendCommand(command);
1762:
1763: // allow for no validation to be supplied
1764: if (validCodes != null) {
1765: lastValidReply = control.validateReply(lastReply,
1766: validCodes);
1767: } else { // no validation
1768: lastValidReply = lastReply; // assume valid
1769: }
1770: return lastValidReply.getReplyText();
1771: }
1772:
1773: /**
1774: * Issue arbitrary ftp commands to the FTP server.
1775: *
1776: * @param command ftp command to be sent to server
1777: * @return the raw text returned by the FTP server including reply code
1778: */
1779: public String quote(String command) throws IOException,
1780: FTPException {
1781:
1782: checkConnection(true);
1783:
1784: lastValidReply = control.sendCommand(command);
1785: return lastValidReply.getRawReply();
1786: }
1787:
1788: /*
1789: * (non-Javadoc)
1790: * @see com.enterprisedt.net.ftp.FTPClientInterface#exists(java.lang.String)
1791: */
1792: public boolean exists(String remoteFile) throws IOException,
1793: FTPException {
1794: checkConnection(true);
1795:
1796: // first try the SIZE command
1797: if (sizeSupported) {
1798: lastReply = control.sendCommand("SIZE " + remoteFile);
1799: char ch = lastReply.getReplyCode().charAt(0);
1800: if (ch == '2')
1801: return true;
1802: if (ch == '5'
1803: && fileNotFoundStrings.matches(lastReply
1804: .getReplyText()))
1805: return false;
1806:
1807: sizeSupported = false;
1808: log.debug("SIZE not supported - trying MDTM");
1809: }
1810:
1811: // then try the MDTM command
1812: if (mdtmSupported) {
1813: lastReply = control.sendCommand("MDTM " + remoteFile);
1814: char ch = lastReply.getReplyCode().charAt(0);
1815: if (ch == '2')
1816: return true;
1817: if (ch == '5'
1818: && fileNotFoundStrings.matches(lastReply
1819: .getReplyText()))
1820: return false;
1821:
1822: mdtmSupported = false;
1823: log.debug("MDTM not supported - trying RETR");
1824: }
1825:
1826: // ok, now try RETR since nothing else is supported
1827: ServerSocket sock = new ServerSocket(0);
1828: short port = (short) sock.getLocalPort();
1829: sock.close();
1830: control.sendPORTCommand(port);
1831:
1832: // send the retrieve command
1833: lastReply = control.sendCommand("RETR " + remoteFile);
1834: char ch = lastReply.getReplyCode().charAt(0);
1835:
1836: // normally return 125 etc. But could return 425 unable to create data
1837: // connection, which means the file exists but can't connect to our (non-
1838: // existent) server socket
1839: if (ch == '1' || ch == '2' || ch == '4')
1840: return true;
1841: if (ch == '5'
1842: && fileNotFoundStrings
1843: .matches(lastReply.getReplyText()))
1844: return false;
1845:
1846: String msg = "Unable to determine if file '" + remoteFile
1847: + "' exists.";
1848: log.warn(msg);
1849: throw new FTPException(msg);
1850: }
1851:
1852: /**
1853: * Read reply from control socket
1854: *
1855: * @return
1856: * @throws IOException
1857: */
1858: FTPReply readReply() throws IOException {
1859: return control.readReply();
1860: }
1861:
1862: /**
1863: * Get the PASV address string (including port numbers)
1864: * @param pasvReply
1865: * @return
1866: */
1867: String getPASVAddress(String pasvReply) {
1868: int start = -1;
1869: int i = 0;
1870: while (i < pasvReply.length()) {
1871: if (Character.isDigit(pasvReply.charAt(i))) {
1872: start = i;
1873: break;
1874: }
1875: i++;
1876: }
1877: int end = -1;
1878: i = pasvReply.length() - 1;
1879: while (i >= 0) {
1880: if (Character.isDigit(pasvReply.charAt(i))) {
1881: end = i;
1882: break;
1883: }
1884: i--;
1885: }
1886: if (start < 0 || end < 0)
1887: return null;
1888:
1889: return pasvReply.substring(start, end + 1);
1890: }
1891:
1892: /**
1893: * Send a command to the server and get the reply
1894: * @param command command
1895: * @return FTPReply
1896: * @throws IOException
1897: */
1898: public FTPReply sendCommand(String command) throws IOException {
1899: return control.sendCommand(command);
1900: }
1901:
1902: /**
1903: * Validate an FTPReply
1904: *
1905: * @param reply reply object
1906: * @param expectedReplyCode expected code
1907: * @throws FTPException
1908: */
1909: public void validateReply(FTPReply reply, String expectedReplyCode)
1910: throws FTPException {
1911: control.validateReply(reply, expectedReplyCode);
1912: }
1913:
1914: /**
1915: * Validate an FTPReply
1916: *
1917: * @param reply reply object
1918: * @param expectedReplyCodes expected codes
1919: * @throws FTPException
1920: */
1921: public void validateReply(FTPReply reply,
1922: String[] expectedReplyCodes) throws FTPException {
1923: control.validateReply(reply, expectedReplyCodes);
1924: }
1925:
1926: /*
1927: * (non-Javadoc)
1928: * @see com.enterprisedt.net.ftp.FTPClientInterface#size(java.lang.String)
1929: */
1930: public long size(String remoteFile) throws IOException,
1931: FTPException {
1932:
1933: checkConnection(true);
1934:
1935: lastReply = control.sendCommand("SIZE " + remoteFile);
1936: lastValidReply = control.validateReply(lastReply, "213");
1937:
1938: // parse the reply string .
1939: String replyText = lastValidReply.getReplyText();
1940:
1941: // trim off any trailing characters after a space, e.g. webstar
1942: // responds to SIZE with 213 55564 bytes
1943: int spacePos = replyText.indexOf(' ');
1944: if (spacePos >= 0)
1945: replyText = replyText.substring(0, spacePos);
1946:
1947: // parse the reply
1948: try {
1949: return Long.parseLong(replyText);
1950: } catch (NumberFormatException ex) {
1951: throw new FTPException("Failed to parse reply: "
1952: + replyText);
1953: }
1954: }
1955:
1956: /*
1957: * (non-Javadoc)
1958: * @see com.enterprisedt.net.ftp.FTPClientInterface#resume()
1959: */
1960: public void resume() throws FTPException {
1961: if (transferType.equals(FTPTransferType.ASCII))
1962: throw new FTPException(
1963: "Resume only supported for BINARY transfers");
1964: resume = true;
1965: log.info("Resume=true");
1966: }
1967:
1968: /*
1969: * (non-Javadoc)
1970: * @see com.enterprisedt.net.ftp.FTPClientInterface#cancelResume()
1971: */
1972: public void cancelResume() throws IOException, FTPException {
1973: restart(0);
1974: resume = false;
1975: }
1976:
1977: /**
1978: * Force the resume flag off. Internal use only.
1979: */
1980: protected void forceResumeOff() {
1981: resume = false;
1982: }
1983:
1984: /**
1985: * Issue the RESTart command to the remote server. This indicates the byte
1986: * position that REST is performed at. For put, bytes start at this point, while
1987: * for get, bytes are fetched from this point.
1988: *
1989: * @param size the REST param, the mark at which the restart is
1990: * performed on the remote file. For STOR, this is retrieved
1991: * by SIZE
1992: * @throws IOException
1993: * @throws FTPException
1994: */
1995: public void restart(long size) throws IOException, FTPException {
1996: lastReply = control.sendCommand("REST " + size);
1997: lastValidReply = control.validateReply(lastReply, "350");
1998: }
1999:
2000: /*
2001: * (non-Javadoc)
2002: * @see com.enterprisedt.net.ftp.FTPClientInterface#put(java.lang.String, java.lang.String)
2003: */
2004: public String put(String localPath, String remoteFile)
2005: throws IOException, FTPException {
2006:
2007: return put(localPath, remoteFile, false);
2008: }
2009:
2010: /*
2011: * (non-Javadoc)
2012: * @see com.enterprisedt.net.ftp.FTPClientInterface#put(java.io.InputStream, java.lang.String)
2013: */
2014: public String put(InputStream srcStream, String remoteFile)
2015: throws IOException, FTPException {
2016:
2017: return put(srcStream, remoteFile, false);
2018: }
2019:
2020: /*
2021: * (non-Javadoc)
2022: * @see com.enterprisedt.net.ftp.FTPClientInterface#put(java.lang.String, java.lang.String, boolean)
2023: */
2024: public String put(String localPath, String remoteFile,
2025: boolean append) throws IOException, FTPException {
2026:
2027: InputStream srcStream = new FileInputStream(localPath);
2028: return put(srcStream, remoteFile, append);
2029: }
2030:
2031: /*
2032: * (non-Javadoc)
2033: * @see com.enterprisedt.net.ftp.FTPClientInterface#put(java.io.InputStream,
2034: * java.lang.String, boolean)
2035: */
2036: public String put(InputStream srcStream, String remoteFile,
2037: boolean append) throws IOException, FTPException {
2038:
2039: FTPTransferType previousType = transferType;
2040: chooseTransferMode(remoteFile);
2041: boolean resetMode = true;
2042: Exception e = null;
2043: try {
2044: if (monitorEx != null)
2045: monitorEx.transferStarted(TransferDirection.UPLOAD,
2046: remoteFile);
2047: remoteFile = putData(srcStream, remoteFile, append);
2048: validateTransfer();
2049: uploadCount++;
2050: } catch (FTPException ex) {
2051: e = ex;
2052: throw ex;
2053: } catch (IOException ex) {
2054: e = ex;
2055: resetMode = false;
2056: validateTransferOnError(ex);
2057: throw ex;
2058: } finally {
2059: if (monitorEx != null)
2060: monitorEx.transferComplete(TransferDirection.UPLOAD,
2061: remoteFile);
2062: if (resetMode)
2063: resetTransferMode(previousType);
2064: }
2065: return remoteFile;
2066: }
2067:
2068: /**
2069: * Validate that the put() or get() was successful. This method is not
2070: * for general use.
2071: */
2072: public void validateTransfer() throws IOException, FTPException {
2073:
2074: checkConnection(true);
2075:
2076: // check the control response
2077: String[] validCodes = { "225", "226", "250", "426", "450" };
2078: lastReply = control.readReply();
2079:
2080: // permit 426/450 error if we cancelled the transfer, otherwise
2081: // throw an exception
2082: String code = lastReply.getReplyCode();
2083: if ((code.equals("426") || code.equals("450"))
2084: && !cancelTransfer)
2085: throw new FTPException(lastReply);
2086:
2087: lastValidReply = control.validateReply(lastReply, validCodes);
2088:
2089: if (cancelTransfer) {
2090: log.warn("Transfer has been cancelled!");
2091: throw new FTPTransferCancelledException();
2092: }
2093: }
2094:
2095: /**
2096: * Validate a transfer when an error has occurred on the data channel.
2097: * Set a very short transfer in case things have hung. Set it back
2098: * at the end.
2099: *
2100: * @throws IOException
2101: * @throws FTPException
2102: */
2103: protected void validateTransferOnError(IOException ex)
2104: throws IOException, FTPException {
2105:
2106: log.debug("Validate transfer on error after exception", ex);
2107: checkConnection(true);
2108:
2109: control.setTimeout(SHORT_TIMEOUT);
2110: try {
2111: validateTransfer();
2112: } catch (Exception e) {
2113: log.warn("Validate transfer on error failed", e);
2114: } finally {
2115: control.setTimeout(timeout);
2116: }
2117: }
2118:
2119: /**
2120: * Close the data socket
2121: */
2122: private void closeDataSocket() {
2123: if (data != null) {
2124: try {
2125: data.close();
2126: data = null;
2127: } catch (IOException ex) {
2128: log.warn("Caught exception closing data socket", ex);
2129: }
2130: }
2131: }
2132:
2133: /**
2134: * Close stream for data socket. Not for
2135: * general use!
2136: *
2137: * @param stream stream reference
2138: */
2139: protected void closeDataSocket(InputStream stream) {
2140: if (stream != null) {
2141: try {
2142: stream.close();
2143: } catch (IOException ex) {
2144: log.warn("Caught exception closing data socket", ex);
2145: }
2146: }
2147:
2148: closeDataSocket();
2149: }
2150:
2151: /**
2152: * Close stream for data socket
2153: *
2154: * @param stream stream reference
2155: */
2156: protected void closeDataSocket(OutputStream stream) {
2157: if (stream != null) {
2158: try {
2159: stream.close();
2160: } catch (IOException ex) {
2161: log.warn("Caught exception closing data socket", ex);
2162: }
2163: }
2164:
2165: closeDataSocket();
2166: }
2167:
2168: /**
2169: * Set up the data socket
2170: *
2171: * @throws FTPException
2172: * @throws IOException
2173: */
2174: protected void setupDataSocket() throws IOException, FTPException {
2175:
2176: data = control.createDataSocket(connectMode);
2177: data.setTimeout(timeout);
2178: }
2179:
2180: /**
2181: * Request the server to set up the put
2182: *
2183: * @param remoteFile
2184: * name of remote file in current directory
2185: * @param append
2186: * true if appending, false otherwise
2187: */
2188: protected String initPut(String remoteFile, boolean append)
2189: throws IOException, FTPException {
2190:
2191: checkConnection(true);
2192:
2193: // if a remote filename isn't supplied, assume STOU is to be used
2194: boolean storeUnique = (remoteFile == null || remoteFile
2195: .length() == 0);
2196: if (storeUnique) {
2197: remoteFile = "";
2198: // check STOU isn't used with append
2199: if (append) {
2200: String msg = "A remote filename must be supplied when appending";
2201: log.error(msg);
2202: throw new FTPException(msg);
2203: }
2204: }
2205:
2206: // reset the cancel flag
2207: cancelTransfer = false;
2208:
2209: boolean close = false;
2210: try {
2211: // set up data channel
2212: setupDataSocket();
2213:
2214: // if resume is requested, we must obtain the size of the
2215: // remote file and issue REST
2216: if (resume) {
2217: if (transferType.equals(FTPTransferType.ASCII))
2218: throw new FTPException(
2219: "Resume only supported for BINARY transfers");
2220: try {
2221: resumeMarker = 0;
2222: resumeMarker = size(remoteFile);
2223: } catch (FTPException ex) {
2224: log.warn("Failed to find size of file '"
2225: + remoteFile + "' for resuming ("
2226: + ex.getMessage() + ")");
2227: }
2228: restart(resumeMarker);
2229: }
2230:
2231: // send the command to store
2232: String cmd = append ? "APPE " : (storeUnique ? "STOU"
2233: : storeCommand);
2234: lastReply = control.sendCommand(cmd + remoteFile);
2235:
2236: // Can get a 125 or a 150, also allow 350 (for Global eXchange Services server)
2237: // JScape returns 151
2238: String[] validCodes = { "125", "150", "151", "350" };
2239: lastValidReply = control.validateReply(lastReply,
2240: validCodes);
2241:
2242: String replyText = lastValidReply.getReplyText();
2243: if (storeUnique) {
2244: int pos = replyText.indexOf(STOU_FILENAME_MARKER);
2245: if (pos >= 0) {
2246: pos += STOU_FILENAME_MARKER.length();
2247: remoteFile = replyText.substring(pos).trim();
2248: } else { // couldn't find marker, just return last word of reply
2249: // e.g. 150 Opening BINARY mode data connection for FTP0000004.
2250: log.debug("Could not find " + STOU_FILENAME_MARKER
2251: + " in reply - using last word instead.");
2252: pos = replyText.lastIndexOf(' ');
2253: remoteFile = replyText.substring(++pos);
2254: int len = remoteFile.length();
2255: if (len > 0 && remoteFile.charAt(len - 1) == '.') {
2256: remoteFile = remoteFile.substring(0, len - 1);
2257: }
2258: }
2259: }
2260: return remoteFile;
2261: } catch (IOException ex) {
2262: close = true;
2263: log.error("Caught and rethrowing exception in initPut()",
2264: ex);
2265: throw ex;
2266: } catch (FTPException ex) {
2267: close = true;
2268: log.error("Caught and rethrowing exception in initPut()",
2269: ex);
2270: throw ex;
2271: } finally {
2272: if (close) {
2273: resume = false;
2274: closeDataSocket();
2275: }
2276: }
2277: }
2278:
2279: /**
2280: * Put data. For ASCII, translate line terminators, coping
2281: * with \r\n, \r or \n in the local file
2282: *
2283: * @param srcStream input stream of data to put
2284: * @param remoteFile name of remote file we are writing to
2285: * @param append true if appending, false otherwise
2286: */
2287: private String putData(InputStream srcStream, String remoteFile,
2288: boolean append) throws IOException, FTPException {
2289:
2290: IOException storedEx = null;
2291: BufferedInputStream in = null;
2292: BufferedOutputStream out = null;
2293: long size = 0;
2294: try {
2295: in = new BufferedInputStream(srcStream);
2296:
2297: remoteFile = initPut(remoteFile, append);
2298:
2299: // get an output stream
2300: out = new BufferedOutputStream(new DataOutputStream(
2301: getOutputStream()), transferBufferSize * 2);
2302:
2303: // if resuming, we skip over the unwanted bytes
2304: if (resume) {
2305: in.skip(resumeMarker);
2306: }
2307:
2308: byte[] buf = new byte[transferBufferSize];
2309: byte[] prevBuf = new byte[FTP_LINE_SEPARATOR.length];
2310: int matchpos = 0;
2311:
2312: // read a chunk at a time and write to the data socket
2313: long monitorCount = 0;
2314: int count = 0;
2315: boolean isASCII = getType() == FTPTransferType.ASCII;
2316: long start = System.currentTimeMillis();
2317:
2318: while ((count = in.read(buf)) > 0 && !cancelTransfer) {
2319: if (isASCII) { // we want to allow \r\n, \r and \n
2320: for (int i = 0; i < count; i++) {
2321: // LF without preceding CR (i.e. Unix text file)
2322: if (buf[i] == LINE_FEED && matchpos == 0) {
2323: out.write(CARRIAGE_RETURN);
2324: out.write(LINE_FEED);
2325: size += 2;
2326: monitorCount += 2;
2327: } else if (buf[i] == FTP_LINE_SEPARATOR[matchpos]) {
2328: prevBuf[matchpos] = buf[i];
2329: matchpos++;
2330: if (matchpos == FTP_LINE_SEPARATOR.length) {
2331: out.write(CARRIAGE_RETURN);
2332: out.write(LINE_FEED);
2333: size += 2;
2334: monitorCount += 2;
2335: matchpos = 0;
2336: }
2337: } else { // no match current char
2338: // this must be a matching \r if we matched first char
2339: if (matchpos > 0) {
2340: out.write(CARRIAGE_RETURN);
2341: out.write(LINE_FEED);
2342: size += 2;
2343: monitorCount += 2;
2344: }
2345: out.write(buf[i]);
2346: size++;
2347: monitorCount++;
2348: matchpos = 0;
2349: }
2350: }
2351: } else { // binary
2352: out.write(buf, 0, count);
2353: size += count;
2354: monitorCount += count;
2355: }
2356:
2357: if (monitor != null && monitorCount > monitorInterval) {
2358: monitor.bytesTransferred(size);
2359: monitorCount = 0;
2360: }
2361: if (serverWakeupInterval > 0
2362: && System.currentTimeMillis() - start > serverWakeupInterval * 1000) {
2363: start = System.currentTimeMillis();
2364: sendServerWakeup();
2365: }
2366: }
2367: // write out anything left at the end that has been saved
2368: // - must be a \r which we convert into a line terminator
2369: if (isASCII && matchpos > 0) {
2370: out.write(CARRIAGE_RETURN);
2371: out.write(LINE_FEED);
2372: size += 2;
2373: monitorCount += 2;
2374: }
2375: } catch (IOException ex) {
2376: storedEx = ex;
2377: log
2378: .error(
2379: "Caught and rethrowing exception in getDataAfterInitGet()",
2380: ex);
2381: } finally {
2382: resume = false;
2383: try {
2384: if (in != null)
2385: in.close();
2386: } catch (IOException ex) {
2387: log.warn("Caught exception closing input stream", ex);
2388: }
2389:
2390: closeDataSocket(out);
2391:
2392: // if we failed to write the file, rethrow the exception
2393: if (storedEx != null)
2394: throw storedEx;
2395:
2396: // notify the final transfer size
2397: if (monitor != null)
2398: monitor.bytesTransferred(size);
2399: // log bytes transferred
2400: log.debug("Transferred " + size + " bytes to remote host");
2401: }
2402: return remoteFile;
2403: }
2404:
2405: /*
2406: * (non-Javadoc)
2407: * @see com.enterprisedt.net.ftp.FTPClientInterface#put(byte[], java.lang.String)
2408: */
2409: public String put(byte[] bytes, String remoteFile)
2410: throws IOException, FTPException {
2411:
2412: return put(bytes, remoteFile, false);
2413: }
2414:
2415: /*
2416: * (non-Javadoc)
2417: * @see com.enterprisedt.net.ftp.FTPClientInterface#put(byte[], java.lang.String, boolean)
2418: */
2419: public String put(byte[] bytes, String remoteFile, boolean append)
2420: throws IOException, FTPException {
2421:
2422: ByteArrayInputStream input = new ByteArrayInputStream(bytes);
2423: return put(input, remoteFile, append);
2424:
2425: }
2426:
2427: /*
2428: * (non-Javadoc)
2429: * @see com.enterprisedt.net.ftp.FTPClientInterface#get(java.lang.String, java.lang.String)
2430: */
2431: public void get(String localPath, String remoteFile)
2432: throws IOException, FTPException {
2433:
2434: File localFile = new File(localPath);
2435: if (localFile.isDirectory()) {
2436: localPath = localPath + File.separator + remoteFile;
2437: log.debug("Setting local path to " + localPath);
2438: }
2439:
2440: FTPTransferType previousType = transferType;
2441: chooseTransferMode(remoteFile);
2442: boolean resetMode = true;
2443: Exception e = null;
2444: try {
2445: if (monitorEx != null)
2446: monitorEx.transferStarted(TransferDirection.DOWNLOAD,
2447: remoteFile);
2448: getData(localPath, remoteFile);
2449: validateTransfer();
2450: downloadCount++;
2451: } catch (FTPException ex) {
2452: e = ex;
2453: throw ex;
2454: } catch (IOException ex) {
2455: e = ex;
2456: resetMode = false;
2457: validateTransferOnError(ex);
2458: throw ex;
2459: } finally {
2460: if (monitorEx != null)
2461: monitorEx.transferComplete(TransferDirection.DOWNLOAD,
2462: remoteFile);
2463: if (resetMode)
2464: resetTransferMode(previousType);
2465: }
2466: }
2467:
2468: /*
2469: * (non-Javadoc)
2470: * @see com.enterprisedt.net.ftp.FTPClientInterface#get(java.io.OutputStream, java.lang.String)
2471: */
2472: public void get(OutputStream destStream, String remoteFile)
2473: throws IOException, FTPException {
2474:
2475: FTPTransferType previousType = transferType;
2476: chooseTransferMode(remoteFile);
2477: boolean resetMode = true;
2478: Exception e = null;
2479: try {
2480: if (monitorEx != null)
2481: monitorEx.transferStarted(TransferDirection.DOWNLOAD,
2482: remoteFile);
2483: getData(destStream, remoteFile);
2484: validateTransfer();
2485: downloadCount++;
2486: } catch (FTPException ex) {
2487: e = ex;
2488: throw ex;
2489: } catch (IOException ex) {
2490: e = ex;
2491: resetMode = false;
2492: validateTransferOnError(ex);
2493: throw ex;
2494: } finally {
2495: if (monitorEx != null)
2496: monitorEx.transferComplete(TransferDirection.DOWNLOAD,
2497: remoteFile);
2498: if (resetMode)
2499: resetTransferMode(previousType);
2500: }
2501: }
2502:
2503: /**
2504: * Reset the transfer mode back to what it should be, if
2505: * it has changed.
2506: *
2507: * @param previousType previous transfer type
2508: * @throws IOException
2509: * @throws FTPException
2510: */
2511: public void resetTransferMode(FTPTransferType previousType)
2512: throws IOException, FTPException {
2513:
2514: if (!transferType.equals(previousType)) {
2515: setType(previousType);
2516: }
2517: }
2518:
2519: /**
2520: * Request to the server that the get is set up
2521: *
2522: * @param remoteFile name of remote file
2523: */
2524: protected void initGet(String remoteFile) throws IOException,
2525: FTPException {
2526:
2527: checkConnection(true);
2528:
2529: // reset the cancel flag
2530: cancelTransfer = false;
2531:
2532: boolean close = false;
2533: try {
2534: // set up data channel
2535: setupDataSocket();
2536:
2537: // if resume is requested, we must issue REST
2538: if (resume) {
2539: if (transferType.equals(FTPTransferType.ASCII))
2540: throw new FTPException(
2541: "Resume only supported for BINARY transfers");
2542: restart(resumeMarker);
2543: }
2544:
2545: // send the retrieve command
2546: lastReply = control.sendCommand("RETR " + remoteFile);
2547:
2548: // Can get a 125 or a 150
2549: String[] validCodes1 = { "125", "150" };
2550: lastValidReply = control.validateReply(lastReply,
2551: validCodes1);
2552: } catch (IOException ex) {
2553: close = true;
2554: log.error("Caught and rethrowing exception in initGet()",
2555: ex);
2556: throw ex;
2557: } catch (FTPException ex) {
2558: close = true;
2559: log.error("Caught and rethrowing exception in initGet()",
2560: ex);
2561: throw ex;
2562: } finally {
2563: if (close) {
2564: resume = false;
2565: closeDataSocket();
2566: }
2567: }
2568: }
2569:
2570: /**
2571: * Get as binary file, i.e. straight transfer of data
2572: *
2573: * @param localPath full path of local file to write to
2574: * @param remoteFile name of remote file
2575: */
2576: private void getData(String localPath, String remoteFile)
2577: throws IOException, FTPException {
2578:
2579: // B. McKeown: Need to store the local file name so the file can be
2580: // deleted if necessary.
2581: File localFile = new File(localPath);
2582: if (localFile.exists()) {
2583: // if resuming, we must find the marker
2584: if (!localFile.canWrite())
2585: throw new FTPException(localPath
2586: + " is readonly - cannot write");
2587: if (resume)
2588: resumeMarker = localFile.length();
2589: }
2590:
2591: // B.McKeown:
2592: // Call initGet() before creating the FileOutputStream.
2593: // This will prevent being left with an empty file if a FTPException
2594: // is thrown by initGet().
2595: initGet(remoteFile);
2596:
2597: // create the buffered output stream for writing the file
2598: FileOutputStream out = new FileOutputStream(localPath, resume);
2599:
2600: try {
2601: getDataAfterInitGet(out);
2602: } catch (IOException ex) {
2603: if (deleteOnFailure) {
2604: localFile.delete();
2605: log.debug("Deleting local file '"
2606: + localFile.getAbsolutePath() + "'");
2607: } else {
2608: log.debug("Possibly partial local file not deleted");
2609: }
2610: throw ex;
2611: }
2612: }
2613:
2614: /**
2615: * Get as binary file, i.e. straight transfer of data
2616: *
2617: * @param destStream stream to write to
2618: * @param remoteFile name of remote file
2619: */
2620: private void getData(OutputStream destStream, String remoteFile)
2621: throws IOException, FTPException {
2622:
2623: initGet(remoteFile);
2624:
2625: getDataAfterInitGet(destStream);
2626: }
2627:
2628: /**
2629: * Get the data input stream. Not for general use!
2630: *
2631: * @return
2632: * @throws IOException
2633: */
2634: InputStream getInputStream() throws IOException {
2635: return data.getInputStream();
2636: }
2637:
2638: /**
2639: * Get the data input stream. Not for general use!
2640: *
2641: * @return
2642: * @throws IOException
2643: */
2644: OutputStream getOutputStream() throws IOException {
2645: return data.getOutputStream();
2646: }
2647:
2648: /**
2649: * Get as binary file, i.e. straight transfer of data
2650: *
2651: * @param destStream stream to write to
2652: */
2653: private void getDataAfterInitGet(OutputStream destStream)
2654: throws IOException, FTPException {
2655:
2656: // create the buffered output stream for writing the file
2657: BufferedOutputStream out = new BufferedOutputStream(destStream);
2658:
2659: BufferedInputStream in = null;
2660: long size = 0;
2661: IOException storedEx = null;
2662: try {
2663: // get an input stream to read data from ... AFTER we have
2664: // the ok to go ahead AND AFTER we've successfully opened a
2665: // stream for the local file
2666: in = new BufferedInputStream(new DataInputStream(data
2667: .getInputStream()));
2668:
2669: // do the retrieving
2670: long monitorCount = 0;
2671: byte[] chunk = new byte[transferBufferSize];
2672: int count;
2673: boolean isASCII = getType() == FTPTransferType.ASCII;
2674: long start = System.currentTimeMillis();
2675:
2676: byte[] prevBuf = new byte[FTP_LINE_SEPARATOR.length];
2677: int matchpos = 0;
2678:
2679: // read from socket & write to file in chunks
2680: while ((count = readChunk(in, chunk, transferBufferSize)) >= 0
2681: && !cancelTransfer) {
2682: if (isASCII) {
2683: for (int i = 0; i < count; i++) {
2684: if (chunk[i] == FTP_LINE_SEPARATOR[matchpos]) {
2685: prevBuf[matchpos] = chunk[i];
2686: matchpos++;
2687: if (matchpos == FTP_LINE_SEPARATOR.length) {
2688: out.write(LINE_SEPARATOR);
2689: size += LINE_SEPARATOR.length;
2690: monitorCount += LINE_SEPARATOR.length;
2691: matchpos = 0;
2692: }
2693: } else { // no match
2694: // write out existing matches
2695: if (matchpos > 0) {
2696: out.write(prevBuf, 0, matchpos);
2697: size += matchpos;
2698: monitorCount += matchpos;
2699: }
2700: out.write(chunk[i]);
2701: size++;
2702: monitorCount++;
2703: matchpos = 0;
2704: }
2705: }
2706: } else { // binary
2707: out.write(chunk, 0, count);
2708: size += count;
2709: monitorCount += count;
2710: }
2711:
2712: if (monitor != null && monitorCount > monitorInterval) {
2713: monitor.bytesTransferred(size);
2714: monitorCount = 0;
2715: }
2716:
2717: if (serverWakeupInterval > 0
2718: && System.currentTimeMillis() - start > serverWakeupInterval * 1000) {
2719: start = System.currentTimeMillis();
2720: sendServerWakeup();
2721: }
2722: }
2723:
2724: // write out anything left at the end that has been saved
2725: if (isASCII && matchpos > 0) {
2726: out.write(prevBuf, 0, matchpos);
2727: size += matchpos;
2728: monitorCount += matchpos;
2729: }
2730: } catch (IOException ex) {
2731: storedEx = ex;
2732: log
2733: .error(
2734: "Caught and rethrowing exception in getDataAfterInitGet()",
2735: ex);
2736: } finally {
2737: try {
2738: if (out != null)
2739: out.close();
2740: } catch (IOException ex) {
2741: log.warn("Caught exception closing output stream", ex);
2742: }
2743:
2744: resume = false;
2745:
2746: // close streams
2747: closeDataSocket(in);
2748:
2749: // if we failed to write the file, rethrow the exception
2750: if (storedEx != null)
2751: throw storedEx;
2752: else if (monitor != null)
2753: monitor.bytesTransferred(size);
2754:
2755: // log bytes transferred
2756: log
2757: .debug("Transferred " + size
2758: + " bytes from remote host");
2759: }
2760: }
2761:
2762: /*
2763: * (non-Javadoc)
2764: * @see com.enterprisedt.net.ftp.FTPClientInterface#get(java.lang.String)
2765: */
2766: public byte[] get(String remoteFile) throws IOException,
2767: FTPException {
2768:
2769: FTPTransferType previousType = transferType;
2770: chooseTransferMode(remoteFile);
2771: boolean resetMode = true;
2772: Exception e = null;
2773: try {
2774: if (monitorEx != null)
2775: monitorEx.transferStarted(TransferDirection.DOWNLOAD,
2776: remoteFile);
2777: ByteArrayOutputStream result = new ByteArrayOutputStream(
2778: transferBufferSize);
2779: getData(result, remoteFile);
2780: validateTransfer();
2781: downloadCount++;
2782: return result == null ? null : result.toByteArray();
2783: } catch (FTPException ex) {
2784: e = ex;
2785: throw ex;
2786: } catch (IOException ex) {
2787: e = ex;
2788: resetMode = false;
2789: validateTransferOnError(ex);
2790: throw ex;
2791: } finally {
2792: if (monitorEx != null)
2793: monitorEx.transferComplete(TransferDirection.DOWNLOAD,
2794: remoteFile);
2795: if (resetMode)
2796: resetTransferMode(previousType);
2797: }
2798: }
2799:
2800: /**
2801: * Run a site-specific command on the
2802: * server. Support for commands is dependent
2803: * on the server
2804: *
2805: * @param command the site command to run
2806: * @return true if command ok, false if
2807: * command not implemented
2808: */
2809: public boolean site(String command) throws IOException,
2810: FTPException {
2811:
2812: checkConnection(true);
2813:
2814: // send the retrieve command
2815: lastReply = control.sendCommand("SITE " + command);
2816:
2817: // Can get a 200 (ok) or 202 (not impl). Some
2818: // FTP servers return 502 (not impl). Added 250 for leitch
2819: String[] validCodes = { "200", "202", "250", "502" };
2820: lastValidReply = control.validateReply(lastReply, validCodes);
2821:
2822: // return true or false? 200 is ok, 202/502 not
2823: // implemented
2824: if (lastReply.getReplyCode().equals("200"))
2825: return true;
2826: else
2827: return false;
2828: }
2829:
2830: /**
2831: * List a directory's contents
2832: *
2833: * @param dirname the name of the directory (<b>not</b> a file mask)
2834: * @return a string containing the line separated
2835: * directory listing
2836: * @deprecated As of FTP 1.1, replaced by {@link #dir(String)}
2837: */
2838: public String list(String dirname) throws IOException, FTPException {
2839:
2840: return list(dirname, false);
2841: }
2842:
2843: /**
2844: * List a directory's contents as one string. A detailed
2845: * listing is available, otherwise just filenames are provided.
2846: * The detailed listing varies in details depending on OS and
2847: * FTP server.
2848: *
2849: * @param dirname the name of the directory(<b>not</b> a file mask)
2850: * @param full true if detailed listing required
2851: * false otherwise
2852: * @return a string containing the line separated
2853: * directory listing
2854: * @deprecated As of FTP 1.1, replaced by {@link #dir(String,boolean)}
2855: */
2856: public String list(String dirname, boolean full)
2857: throws IOException, FTPException {
2858:
2859: String[] list = dir(dirname, full);
2860:
2861: StringBuffer result = new StringBuffer();
2862: String sep = System.getProperty("line.separator");
2863:
2864: // loop thru results and make into one string
2865: for (int i = 0; i < list.length; i++) {
2866: result.append(list[i]);
2867: result.append(sep);
2868: }
2869:
2870: return result.toString();
2871: }
2872:
2873: /**
2874: * Override the chosen file factory with a user created one - meaning
2875: * that a specific parser has been selected
2876: *
2877: * @param fileFactory
2878: */
2879: public void setFTPFileFactory(FTPFileFactory fileFactory) {
2880: this .fileFactory = fileFactory;
2881: }
2882:
2883: /**
2884: * Set the locale for date parsing of dir listings
2885: *
2886: * @param locale new locale to use
2887: * @deprecated @see FTPClient#setParserLocales(Locale[])
2888: */
2889: public void setParserLocale(Locale locale) {
2890: listingLocales = new Locale[1];
2891: listingLocales[0] = locale;
2892: }
2893:
2894: /**
2895: * Set the list of locales to be tried for date parsing of dir listings
2896: *
2897: * @param locales locales to use
2898: */
2899: public void setParserLocales(Locale[] locales) {
2900: listingLocales = locales;
2901: }
2902:
2903: /**
2904: * Uses the MLST command to find out details about the
2905: * named file. A single filename should be supplied. Note
2906: * that the MLST command is not supported by many servers.
2907: *
2908: * @param name name of a file
2909: * @return if it exists, an FTPFile object
2910: */
2911: public FTPFile fileDetails(String name) throws IOException,
2912: FTPException, ParseException {
2913:
2914: checkConnection(true);
2915:
2916: lastReply = control.sendCommand("MLST " + name);
2917: lastValidReply = control.validateReply(lastReply, "250");
2918:
2919: if (lastReply.getReplyData() != null)
2920: return mlsxParser.parse(lastReply.getReplyData()[0]);
2921:
2922: log.warn("No file data returned from MLST");
2923: return null;
2924: }
2925:
2926: /*
2927: * (non-Javadoc)
2928: * @see com.enterprisedt.net.ftp.FTPClientInterface#dirDetails(java.lang.String)
2929: */
2930: public FTPFile[] dirDetails(String dirname) throws IOException,
2931: FTPException, ParseException {
2932:
2933: // create the factory
2934: if (fileFactory == null) {
2935: try {
2936: fileFactory = new FTPFileFactory(system());
2937: } catch (FTPException ex) {
2938: log
2939: .warn(
2940: "SYST command failed - setting Unix as default parser",
2941: ex);
2942: fileFactory = new FTPFileFactory(
2943: FTPFileFactory.UNIX_STR);
2944: }
2945: }
2946: fileFactory.setLocales(listingLocales);
2947:
2948: String path = pwd();
2949:
2950: // add dirname to path if it looks like a directory name
2951: // and has no obvious wildcards
2952: if (dirname != null && dirname.length() > 0
2953: && dirname.indexOf('*') < 0 && dirname.indexOf('?') < 0) {
2954:
2955: path += "/" + dirname;
2956: }
2957:
2958: // get the details and parse. Set the directory for each file
2959: FTPFile[] result = fileFactory.parse(dir(dirname, true));
2960: for (int i = 0; i < result.length; i++) {
2961: result[i].setPath(path);
2962: }
2963:
2964: return result;
2965: }
2966:
2967: /*
2968: * (non-Javadoc)
2969: * @see com.enterprisedt.net.ftp.FTPClientInterface#dir()
2970: */
2971: public String[] dir() throws IOException, FTPException {
2972:
2973: return dir(null, false);
2974: }
2975:
2976: /*
2977: * (non-Javadoc)
2978: * @see com.enterprisedt.net.ftp.FTPClientInterface#dir(java.lang.String)
2979: */
2980: public String[] dir(String dirname) throws IOException,
2981: FTPException {
2982:
2983: return dir(dirname, false);
2984: }
2985:
2986: /*
2987: * (non-Javadoc)
2988: * @see com.enterprisedt.net.ftp.FTPClientInterface#dir(java.lang.String, boolean)
2989: */
2990: public String[] dir(String dirname, boolean full)
2991: throws IOException, FTPException {
2992:
2993: checkConnection(true);
2994:
2995: // reset the cancel flag
2996: cancelTransfer = false;
2997:
2998: try {
2999: // set up data channel
3000: setupDataSocket();
3001:
3002: // send the retrieve command
3003: String command = full ? "LIST " : "NLST ";
3004: if (dirname != null)
3005: command += dirname;
3006:
3007: // some FTP servers bomb out if NLST has whitespace appended
3008: command = command.trim();
3009: lastReply = control.sendCommand(command);
3010:
3011: // check the control response. wu-ftp returns 550 if the
3012: // directory is empty, so we handle 550 appropriately. Similarly
3013: // proFTPD returns 450 or 226 (depending on NLST or LIST)
3014: String[] validCodes1 = { "125", "150", "226", "450", "550" };
3015: lastValidReply = control.validateReply(lastReply,
3016: validCodes1);
3017:
3018: // an empty array of files for 450/550
3019: String[] result = new String[0];
3020:
3021: // a normal reply ... extract the file list
3022: String replyCode = lastValidReply.getReplyCode();
3023: if (!replyCode.equals("450") && !replyCode.equals("550")
3024: && !replyCode.equals("226")) {
3025: // get a character input stream to read data from .
3026: LineNumberReader in = null;
3027: Vector lines = new Vector();
3028: try {
3029: in = new LineNumberReader(new InputStreamReader(
3030: data.getInputStream(), controlEncoding));
3031:
3032: // read a line at a time
3033: String line = null;
3034: while ((line = readLine(in)) != null
3035: && !cancelTransfer) {
3036: lines.addElement(line);
3037: log.log(Level.ALL, line, null);
3038: }
3039: } catch (IOException ex) {
3040: validateTransferOnError(ex);
3041: throw ex;
3042: } finally {
3043: try {
3044: if (in != null)
3045: in.close();
3046: } catch (IOException ex) {
3047: log
3048: .error(
3049: "Failed to close socket in dir()",
3050: ex);
3051: }
3052: closeDataSocket();
3053: }
3054:
3055: // check the control response
3056: String[] validCodes2 = { "226", "250" };
3057: lastReply = control.readReply();
3058: lastValidReply = control.validateReply(lastReply,
3059: validCodes2);
3060:
3061: // empty array is default
3062: if (!lines.isEmpty()) {
3063: result = new String[lines.size()];
3064: lines.copyInto(result);
3065: }
3066: } else { // throw exception if not "No files" or other message
3067: String replyText = lastValidReply.getReplyText()
3068: .toUpperCase();
3069: if (!dirEmptyStrings.matches(replyText)
3070: && !transferCompleteStrings.matches(replyText))
3071: throw new FTPException(lastReply);
3072: }
3073: return result;
3074: } finally {
3075: closeDataSocket();
3076: }
3077: }
3078:
3079: /**
3080: * Attempts to read a specified number of bytes from the given
3081: * <code>InputStream</code> and place it in the given byte-array. The
3082: * purpose of this method is to permit subclasses to execute any additional
3083: * code necessary when performing this operation.
3084: *
3085: * @param in
3086: * The <code>InputStream</code> to read from.
3087: * @param chunk
3088: * The byte-array to place read bytes in.
3089: * @param chunksize
3090: * Number of bytes to read.
3091: * @return Number of bytes actually read.
3092: * @throws IOException
3093: * Thrown if there was an error while reading.
3094: */
3095: public int readChunk(BufferedInputStream in, byte[] chunk,
3096: int chunksize) throws IOException {
3097:
3098: return in.read(chunk, 0, chunksize);
3099: }
3100:
3101: /**
3102: * Attempts to read a single character from the given <code>InputStream</code>.
3103: * The purpose of this method is to permit subclasses to execute
3104: * any additional code necessary when performing this operation.
3105: * @param in The <code>LineNumberReader</code> to read from.
3106: * @return The character read.
3107: * @throws IOException Thrown if there was an error while reading.
3108: */
3109: protected int readChar(LineNumberReader in) throws IOException {
3110:
3111: return in.read();
3112: }
3113:
3114: /**
3115: * Attempts to read a single line from the given <code>InputStream</code>.
3116: * The purpose of this method is to permit subclasses to execute
3117: * any additional code necessary when performing this operation.
3118: * @param in The <code>LineNumberReader</code> to read from.
3119: * @return The string read.
3120: * @throws IOException Thrown if there was an error while reading.
3121: */
3122: protected String readLine(LineNumberReader in) throws IOException {
3123:
3124: return in.readLine();
3125: }
3126:
3127: /**
3128: * Gets the latest valid reply from the server
3129: *
3130: * @return reply object encapsulating last valid server response
3131: */
3132: public FTPReply getLastValidReply() {
3133: return lastValidReply;
3134: }
3135:
3136: /**
3137: * Gets the last reply from the server, whether valid or not
3138: *
3139: * @return reply object encapsulating last server response
3140: */
3141: public FTPReply getLastReply() {
3142: return lastReply;
3143: }
3144:
3145: /**
3146: * Get the current transfer type
3147: *
3148: * @return the current type of the transfer,
3149: * i.e. BINARY or ASCII
3150: */
3151: public FTPTransferType getType() {
3152: return transferType;
3153: }
3154:
3155: /**
3156: * Set the transfer type
3157: *
3158: * @param type the transfer type to
3159: * set the server to
3160: */
3161: public void setType(FTPTransferType type) throws IOException,
3162: FTPException {
3163:
3164: checkConnection(true);
3165:
3166: // determine the character to send
3167: String typeStr = FTPTransferType.ASCII_CHAR;
3168: if (type.equals(FTPTransferType.BINARY))
3169: typeStr = FTPTransferType.BINARY_CHAR;
3170:
3171: // send the command
3172: String[] validCodes = { "200", "250" };
3173: lastReply = control.sendCommand("TYPE " + typeStr);
3174: lastValidReply = control.validateReply(lastReply, validCodes);
3175:
3176: // record the type
3177: transferType = type;
3178: }
3179:
3180: /*
3181: * (non-Javadoc)
3182: * @see com.enterprisedt.net.ftp.FTPClientInterface#delete(java.lang.String)
3183: */
3184: public void delete(String remoteFile) throws IOException,
3185: FTPException {
3186:
3187: checkConnection(true);
3188: String[] validCodes = { "200", "250" };
3189: lastReply = control.sendCommand("DELE " + remoteFile);
3190: lastValidReply = control.validateReply(lastReply, validCodes);
3191: deleteCount++;
3192: }
3193:
3194: /*
3195: * (non-Javadoc)
3196: * @see com.enterprisedt.net.ftp.FTPClientInterface#rename(java.lang.String, java.lang.String)
3197: */
3198: public void rename(String from, String to) throws IOException,
3199: FTPException {
3200:
3201: checkConnection(true);
3202:
3203: lastReply = control.sendCommand("RNFR " + from);
3204: lastValidReply = control.validateReply(lastReply, "350");
3205:
3206: lastReply = control.sendCommand("RNTO " + to);
3207: lastValidReply = control.validateReply(lastReply, "250");
3208: }
3209:
3210: /*
3211: * (non-Javadoc)
3212: * @see com.enterprisedt.net.ftp.FTPClientInterface#rmdir(java.lang.String)
3213: */
3214: public void rmdir(String dir) throws IOException, FTPException {
3215:
3216: checkConnection(true);
3217:
3218: lastReply = control.sendCommand("RMD " + dir);
3219:
3220: // some servers return 200,257, technically incorrect but
3221: // we cater for it ...
3222: String[] validCodes = { "200", "250", "257" };
3223: lastValidReply = control.validateReply(lastReply, validCodes);
3224: }
3225:
3226: /*
3227: * (non-Javadoc)
3228: * @see com.enterprisedt.net.ftp.FTPClientInterface#mkdir(java.lang.String)
3229: */
3230: public void mkdir(String dir) throws IOException, FTPException {
3231:
3232: checkConnection(true);
3233:
3234: lastReply = control.sendCommand("MKD " + dir);
3235:
3236: // some servers return 200,257, technically incorrect but
3237: // we cater for it ...
3238: String[] validCodes = { "200", "250", "257" };
3239: lastValidReply = control.validateReply(lastReply, validCodes);
3240: }
3241:
3242: /*
3243: * (non-Javadoc)
3244: * @see com.enterprisedt.net.ftp.FTPClientInterface#chdir(java.lang.String)
3245: */
3246: public void chdir(String dir) throws IOException, FTPException {
3247:
3248: checkConnection(true);
3249:
3250: lastReply = control.sendCommand("CWD " + dir);
3251: lastValidReply = control.validateReply(lastReply, "250");
3252: }
3253:
3254: /*
3255: * (non-Javadoc)
3256: * @see com.enterprisedt.net.ftp.FTPClientInterface#cdup()
3257: */
3258: public void cdup() throws IOException, FTPException {
3259:
3260: checkConnection(true);
3261:
3262: lastReply = control.sendCommand("CDUP");
3263: String[] validCodes = { "200", "250" };
3264: lastValidReply = control.validateReply(lastReply, validCodes);
3265: }
3266:
3267: /*
3268: * (non-Javadoc)
3269: * @see com.enterprisedt.net.ftp.FTPClientInterface#modtime(java.lang.String)
3270: */
3271: public Date modtime(String remoteFile) throws IOException,
3272: FTPException {
3273:
3274: checkConnection(true);
3275:
3276: lastReply = control.sendCommand("MDTM " + remoteFile);
3277: lastValidReply = control.validateReply(lastReply, "213");
3278:
3279: // parse the reply string ...
3280: Date ts = tsFormat.parse(lastValidReply.getReplyText(),
3281: new ParsePosition(0));
3282: return ts;
3283: }
3284:
3285: /*
3286: * (non-Javadoc)
3287: * @see com.enterprisedt.net.ftp.FTPClientInterface#pwd()
3288: */
3289: public String pwd() throws IOException, FTPException {
3290:
3291: checkConnection(true);
3292:
3293: lastReply = control.sendCommand("PWD");
3294: lastValidReply = control.validateReply(lastReply, "257");
3295:
3296: // get the reply text and extract the dir
3297: // listed in quotes, if we can find it. Otherwise
3298: // just return the whole reply string
3299: String text = lastValidReply.getReplyText();
3300: int start = text.indexOf('"');
3301: int end = text.lastIndexOf('"');
3302: if (start >= 0 && end > start)
3303: return text.substring(start + 1, end);
3304: else
3305: return text;
3306: }
3307:
3308: /**
3309: * Get the server supplied features
3310: *
3311: * @return string containing server features, or null if no features or not
3312: * supported
3313: */
3314: public String[] features() throws IOException, FTPException {
3315:
3316: checkConnection(true);
3317:
3318: lastReply = control.sendCommand("FEAT");
3319: String[] validCodes = { "211", "500", "502" };
3320: lastValidReply = control.validateReply(lastReply, validCodes);
3321: if (lastValidReply.getReplyCode().equals("211"))
3322: return lastValidReply.getReplyData();
3323: else
3324: throw new FTPException(lastReply);
3325: }
3326:
3327: /**
3328: * Get the type of the OS at the server
3329: *
3330: * @return the type of server OS
3331: */
3332: public String system() throws IOException, FTPException {
3333:
3334: checkConnection(true);
3335:
3336: lastReply = control.sendCommand("SYST");
3337: String[] validCodes = { "200", "213", "215", "250" }; // added 250 for leitch
3338: lastValidReply = control.validateReply(lastReply, validCodes);
3339: return lastValidReply.getReplyText();
3340: }
3341:
3342: /**
3343: * Send a "no operation" message that does nothing. Can be
3344: * called periodically to prevent the connection timing out
3345: */
3346: public void noOperation() throws IOException, FTPException {
3347:
3348: checkConnection(true);
3349:
3350: lastReply = control.sendCommand("NOOP");
3351: String[] validCodes = { "200", "250" }; // added 250 for leitch
3352: lastValidReply = control.validateReply(lastReply, validCodes);
3353: }
3354:
3355: /**
3356: * Sends stat message to enquire about the status of a
3357: * transfer.
3358: */
3359: public String stat() throws IOException, FTPException {
3360:
3361: checkConnection(true);
3362:
3363: lastReply = control.sendCommand("STAT");
3364: String[] validCodes = { "211", "212", "213" };
3365: lastValidReply = control.validateReply(lastReply, validCodes);
3366: return lastValidReply.getReplyText();
3367: }
3368:
3369: /**
3370: * Wake up the server during a transfer to prevent a
3371: * timeout from occuring. This may hang or confuse the server -
3372: * use with caution.
3373: *
3374: * @throws IOException
3375: * @throws FTPException
3376: *
3377: */
3378: public void sendServerWakeup() throws IOException, FTPException {
3379: noOperation();
3380: }
3381:
3382: /**
3383: * Tries to keep the current connection alive by
3384: * sending an innocuous commmand to signal that the
3385: * client is still active
3386: */
3387: public void keepAlive() throws IOException, FTPException {
3388: log.debug("keepAlive() called");
3389: int op = (int) Math.ceil(Math.random() * 2);
3390: switch (op) {
3391: case 1:
3392: noOperation();
3393: break;
3394: case 2:
3395: pwd();
3396: break;
3397: default:
3398: pwd();
3399: }
3400: }
3401:
3402: /**
3403: * Get the help text for the specified command
3404: *
3405: * @param command name of the command to get help on
3406: * @return help text from the server for the supplied command
3407: */
3408: public String help(String command) throws IOException, FTPException {
3409:
3410: checkConnection(true);
3411:
3412: lastReply = control.sendCommand("HELP " + command);
3413: String[] validCodes = { "211", "214" };
3414: lastValidReply = control.validateReply(lastReply, validCodes);
3415: return lastValidReply.getReplyText();
3416: }
3417:
3418: /**
3419: * Abort the current action
3420: */
3421: protected void abort() throws IOException, FTPException {
3422:
3423: checkConnection(true);
3424:
3425: lastReply = control.sendCommand("ABOR");
3426: String[] validCodes = { "426", "226" };
3427: lastValidReply = control.validateReply(lastReply, validCodes);
3428: }
3429:
3430: /*
3431: * (non-Javadoc)
3432: * @see com.enterprisedt.net.ftp.FTPClientInterface#quit()
3433: */
3434: public void quit() throws IOException, FTPException {
3435:
3436: checkConnection(true);
3437:
3438: try {
3439: lastReply = control.sendCommand("QUIT");
3440: String[] validCodes = { "221", "226" };
3441: lastValidReply = control.validateReply(lastReply,
3442: validCodes);
3443: } finally { // ensure we clean up the connection
3444: try {
3445: control.logout();
3446: } finally {
3447: control = null;
3448: }
3449: }
3450: }
3451:
3452: /*
3453: * (non-Javadoc)
3454: * @see com.enterprisedt.net.ftp.FTPClientInterface#quitImmediately()
3455: */
3456: public void quitImmediately() throws IOException, FTPException {
3457:
3458: checkConnection(true);
3459:
3460: cancelTransfer();
3461: try {
3462: control.controlSock.close();
3463: } finally {
3464: control = null;
3465: }
3466: }
3467:
3468: /**
3469: * String representation
3470: */
3471: public String toString() {
3472: StringBuffer result = new StringBuffer("[");
3473: result.append("FTP").append(",").append(remoteHost).append(",")
3474: .append(controlPort).append(",").append(getId())
3475: .append("]");
3476: return result.toString();
3477: }
3478:
3479: }
|