0001: /**
0002: *
0003: * Java FTP client library.
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 sent to bruce@enterprisedt.com
0024: *
0025: * Change Log:
0026: *
0027: * $Log: FTPClient.java,v $
0028: * Revision 1.1.1.1 2005/06/23 15:23:01 smontoro
0029: * hipergate backend
0030: *
0031: * Revision 1.1 2004/02/07 03:15:20 hipergate
0032: * v2.0 pre-alpha
0033: *
0034: * Revision 1.6 2003/05/31 14:53:44 bruceb
0035: * 1.2.2 changes
0036: *
0037: * Revision 1.5 2003/01/29 22:46:08 bruceb
0038: * minor changes
0039: *
0040: * Revision 1.4 2002/11/19 22:01:25 bruceb
0041: * changes for 1.2
0042: *
0043: * Revision 1.3 2001/10/09 20:53:46 bruceb
0044: * Active mode changes
0045: *
0046: * Revision 1.1 2001/10/05 14:42:03 bruceb
0047: * moved from old project
0048: *
0049: */package com.enterprisedt.net.ftp;
0050:
0051: import java.io.IOException;
0052: import java.io.LineNumberReader;
0053: import java.io.InputStream;
0054: import java.io.InputStreamReader;
0055: import java.io.OutputStream;
0056: import java.io.OutputStreamWriter;
0057: import java.io.PrintWriter;
0058: import java.io.BufferedWriter;
0059: import java.io.FileWriter;
0060: import java.io.DataInputStream;
0061: import java.io.DataOutputStream;
0062: import java.io.BufferedInputStream;
0063: import java.io.BufferedOutputStream;
0064: import java.io.ByteArrayOutputStream;
0065: import java.io.FileOutputStream;
0066: import java.io.FileInputStream;
0067: import java.io.File;
0068:
0069: import java.text.SimpleDateFormat;
0070: import java.text.ParsePosition;
0071:
0072: import java.net.InetAddress;
0073: import java.util.Date;
0074: import java.util.Vector;
0075: import java.util.Properties;
0076:
0077: /**
0078: * Supports client-side FTP. Most common
0079: * FTP operations are present in this class.
0080: *
0081: * @author Bruce Blackshaw
0082: * @version $Revision: 1.1.1.1 $
0083: *
0084: */
0085: public class FTPClient {
0086:
0087: /**
0088: * Revision control id
0089: */
0090: private static String cvsId = "@(#)$Id: FTPClient.java,v 1.1.1.1 2005/06/23 15:23:01 smontoro Exp $";
0091:
0092: /**
0093: * Format to interpret MTDM timestamp
0094: */
0095: private SimpleDateFormat tsFormat = new SimpleDateFormat(
0096: "yyyyMMddHHmmss");
0097:
0098: /**
0099: * Socket responsible for controlling
0100: * the connection
0101: */
0102: private FTPControlSocket control = null;
0103:
0104: /**
0105: * Socket responsible for transferring
0106: * the data
0107: */
0108: private FTPDataSocket data = null;
0109:
0110: /**
0111: * Socket timeout for both data and control. In
0112: * milliseconds
0113: */
0114: private int timeout = 0;
0115:
0116: /**
0117: * Record of the transfer type - make the default ASCII
0118: */
0119: private FTPTransferType transferType = FTPTransferType.ASCII;
0120:
0121: /**
0122: * Record of the connect mode - make the default PASV (as this was
0123: * the original mode supported)
0124: */
0125: private FTPConnectMode connectMode = FTPConnectMode.PASV;
0126:
0127: /**
0128: * Holds the last valid reply from the server on the control socket
0129: */
0130: private FTPReply lastValidReply;
0131:
0132: /**
0133: * Constructor. Creates the control
0134: * socket
0135: *
0136: * @param remoteHost the remote hostname
0137: */
0138: public FTPClient(String remoteHost) throws IOException,
0139: FTPException {
0140:
0141: control = new FTPControlSocket(remoteHost,
0142: FTPControlSocket.CONTROL_PORT, null, 0);
0143: }
0144:
0145: /**
0146: * Constructor. Creates the control
0147: * socket
0148: *
0149: * @param remoteHost the remote hostname
0150: * @param controlPort port for control stream
0151: */
0152: public FTPClient(String remoteHost, int controlPort)
0153: throws IOException, FTPException {
0154:
0155: control = new FTPControlSocket(remoteHost, controlPort, null, 0);
0156: }
0157:
0158: /**
0159: * Constructor. Creates the control
0160: * socket
0161: *
0162: * @param remoteAddr the address of the
0163: * remote host
0164: */
0165: public FTPClient(InetAddress remoteAddr) throws IOException,
0166: FTPException {
0167:
0168: control = new FTPControlSocket(remoteAddr,
0169: FTPControlSocket.CONTROL_PORT, null, 0);
0170: }
0171:
0172: /**
0173: * Constructor. Creates the control
0174: * socket. Allows setting of control port (normally
0175: * set by default to 21).
0176: *
0177: * @param remoteAddr the address of the
0178: * remote host
0179: * @param controlPort port for control stream
0180: */
0181: public FTPClient(InetAddress remoteAddr, int controlPort)
0182: throws IOException, FTPException {
0183:
0184: control = new FTPControlSocket(remoteAddr, controlPort, null, 0);
0185: }
0186:
0187: /**
0188: * Constructor. Creates the control
0189: * socket
0190: *
0191: * @param remoteHost the remote hostname
0192: * @param millis the length of the timeout, in milliseconds
0193: */
0194: public FTPClient(String remoteHost, PrintWriter log, int timeout)
0195: throws IOException, FTPException {
0196:
0197: control = new FTPControlSocket(remoteHost,
0198: FTPControlSocket.CONTROL_PORT, log, timeout);
0199: }
0200:
0201: /**
0202: * Constructor. Creates the control
0203: * socket
0204: *
0205: * @param remoteHost the remote hostname
0206: * @param controlPort port for control stream
0207: * @param millis the length of the timeout, in milliseconds
0208: */
0209: public FTPClient(String remoteHost, int controlPort,
0210: PrintWriter log, int timeout) throws IOException,
0211: FTPException {
0212:
0213: control = new FTPControlSocket(remoteHost, controlPort, log,
0214: timeout);
0215: }
0216:
0217: /**
0218: * Constructor. Creates the control
0219: * socket
0220: *
0221: * @param remoteAddr the address of the
0222: * remote host
0223: * @param millis the length of the timeout, in milliseconds
0224: */
0225: public FTPClient(InetAddress remoteAddr, PrintWriter log,
0226: int timeout) throws IOException, FTPException {
0227:
0228: control = new FTPControlSocket(remoteAddr,
0229: FTPControlSocket.CONTROL_PORT, log, timeout);
0230: }
0231:
0232: /**
0233: * Constructor. Creates the control
0234: * socket. Allows setting of control port (normally
0235: * set by default to 21).
0236: *
0237: * @param remoteAddr the address of the
0238: * remote host
0239: * @param controlPort port for control stream
0240: * @param millis the length of the timeout, in milliseconds
0241: */
0242: public FTPClient(InetAddress remoteAddr, int controlPort,
0243: PrintWriter log, int timeout) throws IOException,
0244: FTPException {
0245:
0246: control = new FTPControlSocket(remoteAddr, controlPort, log,
0247: timeout);
0248: }
0249:
0250: /**
0251: * Set the TCP timeout on the underlying socket.
0252: *
0253: * If a timeout is set, then any operation which
0254: * takes longer than the timeout value will be
0255: * killed with a java.io.InterruptedException. We
0256: * set both the control and data connections
0257: *
0258: * @param millis The length of the timeout, in milliseconds
0259: */
0260: public void setTimeout(int millis) throws IOException {
0261:
0262: this .timeout = millis;
0263: control.setTimeout(millis);
0264: }
0265:
0266: /**
0267: * Set the connect mode
0268: *
0269: * @param mode ACTIVE or PASV mode
0270: */
0271: public void setConnectMode(FTPConnectMode mode) {
0272: connectMode = mode;
0273: }
0274:
0275: /**
0276: * Login into an account on the FTP server. This
0277: * call completes the entire login process
0278: *
0279: * @param user user name
0280: * @param password user's password
0281: */
0282: public void login(String user, String password) throws IOException,
0283: FTPException {
0284:
0285: String response = control.sendCommand("USER " + user);
0286: lastValidReply = control.validateReply(response, "331");
0287: response = control.sendCommand("PASS " + password);
0288: lastValidReply = control.validateReply(response, "230");
0289: }
0290:
0291: /**
0292: * Supply the user name to log into an account
0293: * on the FTP server. Must be followed by the
0294: * password() method - but we allow for
0295: *
0296: * @param user user name
0297: * @param password user's password
0298: */
0299: public void user(String user) throws IOException, FTPException {
0300:
0301: String reply = control.sendCommand("USER " + user);
0302:
0303: // we allow for a site with no password - 230 response
0304: String[] validCodes = { "230", "331" };
0305: lastValidReply = control.validateReply(reply, validCodes);
0306: }
0307:
0308: /**
0309: * Supplies the password for a previously supplied
0310: * username to log into the FTP server. Must be
0311: * preceeded by the user() method
0312: *
0313: * @param user user name
0314: * @param password user's password
0315: */
0316: public void password(String password) throws IOException,
0317: FTPException {
0318:
0319: String reply = control.sendCommand("PASS " + password);
0320:
0321: // we allow for a site with no passwords (202)
0322: String[] validCodes = { "230", "202" };
0323: lastValidReply = control.validateReply(reply, validCodes);
0324: }
0325:
0326: /**
0327: * Set up SOCKS v4/v5 proxy settings. This can be used if there
0328: * is a SOCKS proxy server in place that must be connected thru.
0329: * Note that setting these properties directs <b>all</b> TCP
0330: * sockets in this JVM to the SOCKS proxy
0331: *
0332: * @param port SOCKS proxy port
0333: * @param host SOCKS proxy hostname
0334: */
0335: public static void initSOCKS(String port, String host) {
0336: Properties props = System.getProperties();
0337: props.put("socksProxyPort", port);
0338: props.put("socksProxyHost", host);
0339: System.setProperties(props);
0340: }
0341:
0342: /**
0343: * Set up SOCKS username and password for SOCKS username/password
0344: * authentication. Often, no authentication will be required
0345: * but the SOCKS server may be configured to request these.
0346: *
0347: * @param username the SOCKS username
0348: * @param password the SOCKS password
0349: */
0350: public static void initSOCKSAuthentication(String username,
0351: String password) {
0352: Properties props = System.getProperties();
0353: props.put("java.net.socks.username", username);
0354: props.put("java.net.socks.password", password);
0355: System.setProperties(props);
0356: }
0357:
0358: /**
0359: * Get the name of the remote host
0360: *
0361: * @return remote host name
0362: */
0363: String getRemoteHostName() {
0364: return control.getRemoteHostName();
0365: }
0366:
0367: /**
0368: * Issue arbitrary ftp commands to the FTP server.
0369: *
0370: * @param command ftp command to be sent to server
0371: * @param validCodes valid return codes for this command
0372: */
0373: public void quote(String command, String[] validCodes)
0374: throws IOException, FTPException {
0375:
0376: String reply = control.sendCommand(command);
0377:
0378: // allow for no validation to be supplied
0379: if (validCodes != null && validCodes.length > 0)
0380: lastValidReply = control.validateReply(reply, validCodes);
0381: }
0382:
0383: /**
0384: * Put a local file onto the FTP server. It
0385: * is placed in the current directory.
0386: *
0387: * @param localPath path of the local file
0388: * @param remoteFile name of remote file in
0389: * current directory
0390: */
0391: public void put(String localPath, String remoteFile)
0392: throws IOException, FTPException {
0393:
0394: put(localPath, remoteFile, false);
0395: }
0396:
0397: /**
0398: * Put a stream of data onto the FTP server. It
0399: * is placed in the current directory.
0400: *
0401: * @param srcStream input stream of data to put
0402: * @param remoteFile name of remote file in
0403: * current directory
0404: */
0405: public void put(InputStream srcStream, String remoteFile)
0406: throws IOException, FTPException {
0407:
0408: put(srcStream, remoteFile, false);
0409: }
0410:
0411: /**
0412: * Put a local file onto the FTP server. It
0413: * is placed in the current directory. Allows appending
0414: * if current file exists
0415: *
0416: * @param localPath path of the local file
0417: * @param remoteFile name of remote file in
0418: * current directory
0419: * @param append true if appending, false otherwise
0420: */
0421: public void put(String localPath, String remoteFile, boolean append)
0422: throws IOException, FTPException {
0423:
0424: // get according to set type
0425: if (getType() == FTPTransferType.ASCII) {
0426: putASCII(localPath, remoteFile, append);
0427: } else {
0428: putBinary(localPath, remoteFile, append);
0429: }
0430: validateTransfer();
0431: }
0432:
0433: /**
0434: * Put a stream of data onto the FTP server. It
0435: * is placed in the current directory. Allows appending
0436: * if current file exists
0437: *
0438: * @param srcStream input stream of data to put
0439: * @param remoteFile name of remote file in
0440: * current directory
0441: * @param append true if appending, false otherwise
0442: */
0443: public void put(InputStream srcStream, String remoteFile,
0444: boolean append) throws IOException, FTPException {
0445:
0446: // get according to set type
0447: if (getType() == FTPTransferType.ASCII) {
0448: putASCII(srcStream, remoteFile, append);
0449: } else {
0450: putBinary(srcStream, remoteFile, append);
0451: }
0452: validateTransfer();
0453: }
0454:
0455: /**
0456: * Validate that the put() or get() was successful
0457: */
0458: private void validateTransfer() throws IOException, FTPException {
0459:
0460: // check the control response
0461: String[] validCodes = { "226", "250" };
0462: String reply = control.readReply();
0463: lastValidReply = control.validateReply(reply, validCodes);
0464: }
0465:
0466: /**
0467: * Request the server to set up the put
0468: *
0469: * @param remoteFile name of remote file in
0470: * current directory
0471: * @param append true if appending, false otherwise
0472: */
0473: private void initPut(String remoteFile, boolean append)
0474: throws IOException, FTPException {
0475:
0476: // set up data channel
0477: data = control.createDataSocket(connectMode);
0478: data.setTimeout(timeout);
0479:
0480: // send the command to store
0481: String cmd = append ? "APPE " : "STOR ";
0482: String reply = control.sendCommand(cmd + remoteFile);
0483:
0484: // Can get a 125 or a 150
0485: String[] validCodes = { "125", "150" };
0486: lastValidReply = control.validateReply(reply, validCodes);
0487: }
0488:
0489: /**
0490: * Put as ASCII, i.e. read a line at a time and write
0491: * inserting the correct FTP separator
0492: *
0493: * @param localPath full path of local file to read from
0494: * @param remoteFile name of remote file we are writing to
0495: * @param append true if appending, false otherwise
0496: */
0497: private void putASCII(String localPath, String remoteFile,
0498: boolean append) throws IOException, FTPException {
0499:
0500: // create an inputstream & pass to common method
0501: InputStream srcStream = new FileInputStream(localPath);
0502: putASCII(srcStream, remoteFile, append);
0503: }
0504:
0505: /**
0506: * Put as ASCII, i.e. read a line at a time and write
0507: * inserting the correct FTP separator
0508: *
0509: * @param srcStream input stream of data to put
0510: * @param remoteFile name of remote file we are writing to
0511: * @param append true if appending, false otherwise
0512: */
0513: private void putASCII(InputStream srcStream, String remoteFile,
0514: boolean append) throws IOException, FTPException {
0515:
0516: // need to read line by line ...
0517: LineNumberReader in = new LineNumberReader(
0518: new InputStreamReader(srcStream));
0519:
0520: initPut(remoteFile, append);
0521:
0522: // get an character output stream to write to ... AFTER we
0523: // have the ok to go ahead AND AFTER we've successfully opened a
0524: // stream for the local file
0525: BufferedWriter out = new BufferedWriter(new OutputStreamWriter(
0526: data.getOutputStream()));
0527:
0528: // write line by line, writing \r\n as required by RFC959 after
0529: // each line
0530: String line = null;
0531: while ((line = in.readLine()) != null) {
0532: out.write(line, 0, line.length());
0533: out.write(FTPControlSocket.EOL, 0, FTPControlSocket.EOL
0534: .length());
0535: }
0536: in.close();
0537: out.flush();
0538: out.close();
0539:
0540: // and close the data socket
0541: try {
0542: data.close();
0543: } catch (IOException ignore) {
0544: }
0545: }
0546:
0547: /**
0548: * Put as binary, i.e. read and write raw bytes
0549: *
0550: * @param localPath full path of local file to read from
0551: * @param remoteFile name of remote file we are writing to
0552: * @param append true if appending, false otherwise
0553: */
0554: private void putBinary(String localPath, String remoteFile,
0555: boolean append) throws IOException, FTPException {
0556:
0557: // open input stream to read source file ... do this
0558: // BEFORE opening output stream to server, so if file not
0559: // found, an exception is thrown
0560: InputStream srcStream = new FileInputStream(localPath);
0561: putBinary(srcStream, remoteFile, append);
0562: }
0563:
0564: /**
0565: * Put as binary, i.e. read and write raw bytes
0566: *
0567: * @param srcStream input stream of data to put
0568: * @param remoteFile name of remote file we are writing to
0569: * @param append true if appending, false otherwise
0570: */
0571: private void putBinary(InputStream srcStream, String remoteFile,
0572: boolean append) throws IOException, FTPException {
0573:
0574: BufferedInputStream in = new BufferedInputStream(srcStream);
0575:
0576: initPut(remoteFile, append);
0577:
0578: // get an output stream
0579: BufferedOutputStream out = new BufferedOutputStream(
0580: new DataOutputStream(data.getOutputStream()));
0581:
0582: byte[] buf = new byte[512];
0583:
0584: // read a chunk at a time and write to the data socket
0585: long size = 0;
0586: int count = 0;
0587: while ((count = in.read(buf)) > 0) {
0588: out.write(buf, 0, count);
0589: size += count;
0590: }
0591:
0592: in.close();
0593:
0594: // flush and clean up
0595: out.flush();
0596: out.close();
0597:
0598: // and close the data socket
0599: try {
0600: data.close();
0601: } catch (IOException ignore) {
0602: }
0603:
0604: // log bytes transferred
0605: control.log("Transferred " + size + " bytes to remote host");
0606: }
0607:
0608: /**
0609: * Put data onto the FTP server. It
0610: * is placed in the current directory.
0611: *
0612: * @param data array of bytes
0613: * @param remoteFile name of remote file in
0614: * current directory
0615: */
0616: public void put(byte[] bytes, String remoteFile)
0617: throws IOException, FTPException {
0618:
0619: put(bytes, remoteFile, false);
0620: }
0621:
0622: /**
0623: * Put data onto the FTP server. It
0624: * is placed in the current directory. Allows
0625: * appending if current file exists
0626: *
0627: * @param data array of bytes
0628: * @param remoteFile name of remote file in
0629: * current directory
0630: * @param append true if appending, false otherwise
0631: */
0632: public void put(byte[] bytes, String remoteFile, boolean append)
0633: throws IOException, FTPException {
0634:
0635: initPut(remoteFile, append);
0636:
0637: // get an output stream
0638: BufferedOutputStream out = new BufferedOutputStream(
0639: new DataOutputStream(data.getOutputStream()));
0640:
0641: // write array
0642: out.write(bytes, 0, bytes.length);
0643:
0644: // flush and clean up
0645: out.flush();
0646: out.close();
0647:
0648: // and close the data socket
0649: try {
0650: data.close();
0651: } catch (IOException ignore) {
0652: }
0653:
0654: validateTransfer();
0655: }
0656:
0657: /**
0658: * Get data from the FTP server. Uses the currently
0659: * set transfer mode.
0660: *
0661: * @param localPath local file to put data in
0662: * @param remoteFile name of remote file in
0663: * current directory
0664: */
0665: public void get(String localPath, String remoteFile)
0666: throws IOException, FTPException {
0667:
0668: // get according to set type
0669: if (getType() == FTPTransferType.ASCII) {
0670: getASCII(localPath, remoteFile);
0671: } else {
0672: getBinary(localPath, remoteFile);
0673: }
0674: validateTransfer();
0675: }
0676:
0677: /**
0678: * Get data from the FTP server. Uses the currently
0679: * set transfer mode.
0680: *
0681: * @param destStream data stream to write data to
0682: * @param remoteFile name of remote file in
0683: * current directory
0684: */
0685: public void get(OutputStream destStream, String remoteFile)
0686: throws IOException, FTPException {
0687:
0688: // get according to set type
0689: if (getType() == FTPTransferType.ASCII) {
0690: getASCII(destStream, remoteFile);
0691: } else {
0692: getBinary(destStream, remoteFile);
0693: }
0694: validateTransfer();
0695: }
0696:
0697: /**
0698: * Request to the server that the get is set up
0699: *
0700: * @param remoteFile name of remote file
0701: */
0702: private void initGet(String remoteFile) throws IOException,
0703: FTPException {
0704:
0705: // set up data channel
0706: data = control.createDataSocket(connectMode);
0707: data.setTimeout(timeout);
0708:
0709: // send the retrieve command
0710: String reply = control.sendCommand("RETR " + remoteFile);
0711:
0712: // Can get a 125 or a 150
0713: String[] validCodes1 = { "125", "150" };
0714: lastValidReply = control.validateReply(reply, validCodes1);
0715: }
0716:
0717: /**
0718: * Get as ASCII, i.e. read a line at a time and write
0719: * using the correct newline separator for the OS
0720: *
0721: * @param localPath full path of local file to write to
0722: * @param remoteFile name of remote file
0723: */
0724: private void getASCII(String localPath, String remoteFile)
0725: throws IOException, FTPException {
0726:
0727: // B.McKeown:
0728: // Call initGet() before creating the FileOutputStream.
0729: // This will prevent being left with an empty file if a FTPException
0730: // is thrown by initGet().
0731: initGet(remoteFile);
0732:
0733: // B. McKeown: Need to store the local file name so the file can be
0734: // deleted if necessary.
0735: File localFile = new File(localPath);
0736:
0737: // create the buffered stream for writing
0738: BufferedWriter out = new BufferedWriter(new FileWriter(
0739: localPath));
0740:
0741: // get an character input stream to read data from ... AFTER we
0742: // have the ok to go ahead AND AFTER we've successfully opened a
0743: // stream for the local file
0744: LineNumberReader in = new LineNumberReader(
0745: new InputStreamReader(data.getInputStream()));
0746:
0747: // B. McKeown:
0748: // If we are in active mode we have to set the timeout of the passive
0749: // socket. We can achieve this by calling setTimeout() again.
0750: // If we are in passive mode then we are merely setting the value twice
0751: // which does no harm anyway. Doing this simplifies any logic changes.
0752: data.setTimeout(timeout);
0753:
0754: // read/write a line at a time
0755: IOException storedEx = null;
0756: String line = null;
0757: try {
0758: while ((line = in.readLine()) != null) {
0759: out.write(line, 0, line.length());
0760: out.newLine();
0761: }
0762: } catch (IOException ex) {
0763: storedEx = ex;
0764: localFile.delete();
0765: } finally {
0766: out.close();
0767: }
0768:
0769: try {
0770: in.close();
0771: data.close();
0772: } catch (IOException ignore) {
0773: }
0774:
0775: // if we failed to write the file, rethrow the exception
0776: if (storedEx != null)
0777: throw storedEx;
0778: }
0779:
0780: /**
0781: * Get as ASCII, i.e. read a line at a time and write
0782: * using the correct newline separator for the OS
0783: *
0784: * @param destStream data stream to write data to
0785: * @param remoteFile name of remote file
0786: */
0787: private void getASCII(OutputStream destStream, String remoteFile)
0788: throws IOException, FTPException {
0789:
0790: initGet(remoteFile);
0791:
0792: // create the buffered stream for writing
0793: BufferedWriter out = new BufferedWriter(new OutputStreamWriter(
0794: destStream));
0795:
0796: // get an character input stream to read data from ... AFTER we
0797: // have the ok to go ahead
0798: LineNumberReader in = new LineNumberReader(
0799: new InputStreamReader(data.getInputStream()));
0800:
0801: // B. McKeown:
0802: // If we are in active mode we have to set the timeout of the passive
0803: // socket. We can achieve this by calling setTimeout() again.
0804: // If we are in passive mode then we are merely setting the value twice
0805: // which does no harm anyway. Doing this simplifies any logic changes.
0806: data.setTimeout(timeout);
0807:
0808: // read/write a line at a time
0809: IOException storedEx = null;
0810: String line = null;
0811: try {
0812: while ((line = in.readLine()) != null) {
0813: out.write(line, 0, line.length());
0814: out.newLine();
0815: }
0816: } catch (IOException ex) {
0817: storedEx = ex;
0818: } finally {
0819: out.close();
0820: }
0821:
0822: try {
0823: in.close();
0824: data.close();
0825: } catch (IOException ignore) {
0826: }
0827:
0828: // if we failed to write the file, rethrow the exception
0829: if (storedEx != null)
0830: throw storedEx;
0831: }
0832:
0833: /**
0834: * Get as binary file, i.e. straight transfer of data
0835: *
0836: * @param localPath full path of local file to write to
0837: * @param remoteFile name of remote file
0838: */
0839: private void getBinary(String localPath, String remoteFile)
0840: throws IOException, FTPException {
0841:
0842: // B.McKeown:
0843: // Call initGet() before creating the FileOutputStream.
0844: // This will prevent being left with an empty file if a FTPException
0845: // is thrown by initGet().
0846: initGet(remoteFile);
0847:
0848: // B. McKeown: Need to store the local file name so the file can be
0849: // deleted if necessary.
0850: File localFile = new File(localPath);
0851:
0852: // create the buffered output stream for writing the file
0853: BufferedOutputStream out = new BufferedOutputStream(
0854: new FileOutputStream(localPath, false));
0855:
0856: // get an input stream to read data from ... AFTER we have
0857: // the ok to go ahead AND AFTER we've successfully opened a
0858: // stream for the local file
0859: BufferedInputStream in = new BufferedInputStream(
0860: new DataInputStream(data.getInputStream()));
0861:
0862: // B. McKeown:
0863: // If we are in active mode we have to set the timeout of the passive
0864: // socket. We can achieve this by calling setTimeout() again.
0865: // If we are in passive mode then we are merely setting the value twice
0866: // which does no harm anyway. Doing this simplifies any logic changes.
0867: data.setTimeout(timeout);
0868:
0869: // do the retrieving
0870: long size = 0;
0871: int chunksize = 4096;
0872: byte[] chunk = new byte[chunksize];
0873: int count;
0874: IOException storedEx = null;
0875:
0876: // read from socket & write to file in chunks
0877: try {
0878: while ((count = in.read(chunk, 0, chunksize)) >= 0) {
0879: out.write(chunk, 0, count);
0880: size += count;
0881: }
0882: } catch (IOException ex) {
0883: storedEx = ex;
0884: localFile.delete();
0885: } finally {
0886: out.close();
0887: }
0888:
0889: // close streams
0890: try {
0891: in.close();
0892: data.close();
0893: } catch (IOException ignore) {
0894: }
0895:
0896: // if we failed to write the file, rethrow the exception
0897: if (storedEx != null)
0898: throw storedEx;
0899:
0900: // log bytes transferred
0901: control.log("Transferred " + size + " bytes from remote host");
0902: }
0903:
0904: /**
0905: * Get as binary file, i.e. straight transfer of data
0906: *
0907: * @param destStream stream to write to
0908: * @param remoteFile name of remote file
0909: */
0910: private void getBinary(OutputStream destStream, String remoteFile)
0911: throws IOException, FTPException {
0912:
0913: initGet(remoteFile);
0914:
0915: // create the buffered output stream for writing the file
0916: BufferedOutputStream out = new BufferedOutputStream(destStream);
0917:
0918: // get an input stream to read data from ... AFTER we have
0919: // the ok to go ahead AND AFTER we've successfully opened a
0920: // stream for the local file
0921: BufferedInputStream in = new BufferedInputStream(
0922: new DataInputStream(data.getInputStream()));
0923:
0924: // B. McKeown:
0925: // If we are in active mode we have to set the timeout of the passive
0926: // socket. We can achieve this by calling setTimeout() again.
0927: // If we are in passive mode then we are merely setting the value twice
0928: // which does no harm anyway. Doing this simplifies any logic changes.
0929: data.setTimeout(timeout);
0930:
0931: // do the retrieving
0932: long size = 0;
0933: int chunksize = 4096;
0934: byte[] chunk = new byte[chunksize];
0935: int count;
0936: IOException storedEx = null;
0937:
0938: // read from socket & write to file in chunks
0939: try {
0940: while ((count = in.read(chunk, 0, chunksize)) >= 0) {
0941: out.write(chunk, 0, count);
0942: size += count;
0943: }
0944: } catch (IOException ex) {
0945: storedEx = ex;
0946: } finally {
0947: out.close();
0948: }
0949:
0950: // close streams
0951: try {
0952: in.close();
0953: data.close();
0954: } catch (IOException ignore) {
0955: }
0956:
0957: // if we failed to write to the stream, rethrow the exception
0958: if (storedEx != null)
0959: throw storedEx;
0960:
0961: // log bytes transferred
0962: control.log("Transferred " + size + " bytes from remote host");
0963: }
0964:
0965: /**
0966: * Get data from the FTP server. Transfers in
0967: * whatever mode we are in. Retrieve as a byte array. Note
0968: * that we may experience memory limitations as the
0969: * entire file must be held in memory at one time.
0970: *
0971: * @param remoteFile name of remote file in
0972: * current directory
0973: */
0974: public byte[] get(String remoteFile) throws IOException,
0975: FTPException {
0976:
0977: initGet(remoteFile);
0978:
0979: // get an input stream to read data from
0980: BufferedInputStream in = new BufferedInputStream(
0981: new DataInputStream(data.getInputStream()));
0982:
0983: // B. McKeown:
0984: // If we are in active mode we have to set the timeout of the passive
0985: // socket. We can achieve this by calling setTimeout() again.
0986: // If we are in passive mode then we are merely setting the value twice
0987: // which does no harm anyway. Doing this simplifies any logic changes.
0988: data.setTimeout(timeout);
0989:
0990: // do the retrieving
0991: int chunksize = 4096;
0992: byte[] chunk = new byte[chunksize]; // read chunks into
0993: byte[] resultBuf = null; // where we place result
0994: ByteArrayOutputStream temp = new ByteArrayOutputStream(
0995: chunksize); // temp swap buffer
0996: int count; // size of chunk read
0997:
0998: // read from socket & write to file
0999: while ((count = in.read(chunk, 0, chunksize)) >= 0) {
1000: temp.write(chunk, 0, count);
1001: }
1002: temp.close();
1003:
1004: // get the bytes from the temp buffer
1005: resultBuf = temp.toByteArray();
1006:
1007: // close streams
1008: try {
1009: in.close();
1010: data.close();
1011: } catch (IOException ignore) {
1012: }
1013:
1014: validateTransfer();
1015:
1016: return resultBuf;
1017: }
1018:
1019: /**
1020: * Run a site-specific command on the
1021: * server. Support for commands is dependent
1022: * on the server
1023: *
1024: * @param command the site command to run
1025: * @return true if command ok, false if
1026: * command not implemented
1027: */
1028: public boolean site(String command) throws IOException,
1029: FTPException {
1030:
1031: // send the retrieve command
1032: String reply = control.sendCommand("SITE " + command);
1033:
1034: // Can get a 200 (ok) or 202 (not impl). Some
1035: // FTP servers return 502 (not impl)
1036: String[] validCodes = { "200", "202", "502" };
1037: lastValidReply = control.validateReply(reply, validCodes);
1038:
1039: // return true or false? 200 is ok, 202/502 not
1040: // implemented
1041: if (reply.substring(0, 3).equals("200"))
1042: return true;
1043: else
1044: return false;
1045: }
1046:
1047: /**
1048: * List a directory's contents
1049: *
1050: * @param dirname the name of the directory (<b>not</b> a file mask)
1051: * @return a string containing the line separated
1052: * directory listing
1053: * @deprecated As of FTP 1.1, replaced by {@link #dir(String)}
1054: */
1055: public String list(String dirname) throws IOException, FTPException {
1056:
1057: return list(dirname, false);
1058: }
1059:
1060: /**
1061: * List a directory's contents as one string. A detailed
1062: * listing is available, otherwise just filenames are provided.
1063: * The detailed listing varies in details depending on OS and
1064: * FTP server.
1065: *
1066: * @param dirname the name of the directory(<b>not</b> a file mask)
1067: * @param full true if detailed listing required
1068: * false otherwise
1069: * @return a string containing the line separated
1070: * directory listing
1071: * @deprecated As of FTP 1.1, replaced by {@link #dir(String,boolean)}
1072: */
1073: public String list(String dirname, boolean full)
1074: throws IOException, FTPException {
1075:
1076: String[] list = dir(dirname, full);
1077:
1078: StringBuffer result = new StringBuffer();
1079: String sep = System.getProperty("line.separator");
1080:
1081: // loop thru results and make into one string
1082: for (int i = 0; i < list.length; i++) {
1083: result.append(list[i]);
1084: result.append(sep);
1085: }
1086:
1087: return result.toString();
1088: }
1089:
1090: /**
1091: * List current directory's contents as an array of strings of
1092: * filenames.
1093: *
1094: * @return an array of current directory listing strings
1095: */
1096: public String[] dir() throws IOException, FTPException {
1097:
1098: return dir(null, false);
1099: }
1100:
1101: /**
1102: * List a directory's contents as an array of strings of filenames.
1103: *
1104: * @param dirname name of directory(<b>not</b> a file mask)
1105: * @return an array of directory listing strings
1106: */
1107: public String[] dir(String dirname) throws IOException,
1108: FTPException {
1109:
1110: return dir(dirname, false);
1111: }
1112:
1113: /**
1114: * List a directory's contents as an array of strings. A detailed
1115: * listing is available, otherwise just filenames are provided.
1116: * The detailed listing varies in details depending on OS and
1117: * FTP server. Note that a full listing can be used on a file
1118: * name to obtain information about a file
1119: *
1120: * @param dirname name of directory (<b>not</b> a file mask)
1121: * @param full true if detailed listing required
1122: * false otherwise
1123: * @return an array of directory listing strings
1124: */
1125: public String[] dir(String dirname, boolean full)
1126: throws IOException, FTPException {
1127:
1128: // set up data channel
1129: data = control.createDataSocket(connectMode);
1130: data.setTimeout(timeout);
1131:
1132: // send the retrieve command
1133: String command = full ? "LIST " : "NLST ";
1134: if (dirname != null)
1135: command += dirname;
1136:
1137: // some FTP servers bomb out if NLST has whitespace appended
1138: command = command.trim();
1139: String reply = control.sendCommand(command);
1140:
1141: // check the control response. wu-ftp returns 550 if the
1142: // directory is empty, so we handle 550 appropriately. Similarly
1143: // proFTPD returns 450
1144: String[] validCodes1 = { "125", "150", "450", "550" };
1145: lastValidReply = control.validateReply(reply, validCodes1);
1146:
1147: // an empty array of files for 450/550
1148: String[] result = new String[0];
1149:
1150: // a normal reply ... extract the file list
1151: String replyCode = lastValidReply.getReplyCode();
1152: if (!replyCode.equals("450") && !replyCode.equals("550")) {
1153: // get an character input stream to read data from .
1154: LineNumberReader in = new LineNumberReader(
1155: new InputStreamReader(data.getInputStream()));
1156:
1157: // read a line at a time
1158: Vector lines = new Vector();
1159: String line = null;
1160: while ((line = in.readLine()) != null) {
1161: lines.add(line);
1162: }
1163: try {
1164: in.close();
1165: data.close();
1166: } catch (IOException ignore) {
1167: }
1168:
1169: // check the control response
1170: String[] validCodes2 = { "226", "250" };
1171: reply = control.readReply();
1172: lastValidReply = control.validateReply(reply, validCodes2);
1173:
1174: // empty array is default
1175: if (!lines.isEmpty())
1176: result = (String[]) lines.toArray(result);
1177: }
1178: return result;
1179: }
1180:
1181: /**
1182: * Gets the latest valid reply from the server
1183: *
1184: * @return reply object encapsulating last valid server response
1185: */
1186: public FTPReply getLastValidReply() {
1187: return lastValidReply;
1188: }
1189:
1190: /**
1191: * Switch debug of responses on or off
1192: *
1193: * @param on true if you wish to have responses to
1194: * the log stream, false otherwise
1195: */
1196: public void debugResponses(boolean on) {
1197: control.debugResponses(on);
1198: }
1199:
1200: /**
1201: * Set the logging stream, replacing
1202: * stdout
1203: *
1204: * @param log the new logging stream
1205: */
1206: public void setLogStream(PrintWriter log) {
1207: control.setLogStream(log);
1208: }
1209:
1210: /**
1211: * Get the current transfer type
1212: *
1213: * @return the current type of the transfer,
1214: * i.e. BINARY or ASCII
1215: */
1216: public FTPTransferType getType() {
1217: return transferType;
1218: }
1219:
1220: /**
1221: * Set the transfer type
1222: *
1223: * @param type the transfer type to
1224: * set the server to
1225: */
1226: public void setType(FTPTransferType type) throws IOException,
1227: FTPException {
1228:
1229: // determine the character to send
1230: String typeStr = FTPTransferType.ASCII_CHAR;
1231: if (type.equals(FTPTransferType.BINARY))
1232: typeStr = FTPTransferType.BINARY_CHAR;
1233:
1234: // send the command
1235: String reply = control.sendCommand("TYPE " + typeStr);
1236: lastValidReply = control.validateReply(reply, "200");
1237:
1238: // record the type
1239: transferType = type;
1240: }
1241:
1242: /**
1243: * Delete the specified remote file
1244: *
1245: * @param remoteFile name of remote file to
1246: * delete
1247: */
1248: public void delete(String remoteFile) throws IOException,
1249: FTPException {
1250:
1251: String reply = control.sendCommand("DELE " + remoteFile);
1252: lastValidReply = control.validateReply(reply, "250");
1253: }
1254:
1255: /**
1256: * Rename a file or directory
1257: *
1258: * @param from name of file or directory to rename
1259: * @param to intended name
1260: */
1261: public void rename(String from, String to) throws IOException,
1262: FTPException {
1263:
1264: String reply = control.sendCommand("RNFR " + from);
1265: lastValidReply = control.validateReply(reply, "350");
1266:
1267: reply = control.sendCommand("RNTO " + to);
1268: lastValidReply = control.validateReply(reply, "250");
1269: }
1270:
1271: /**
1272: * Delete the specified remote working directory
1273: *
1274: * @param dir name of remote directory to
1275: * delete
1276: */
1277: public void rmdir(String dir) throws IOException, FTPException {
1278:
1279: String reply = control.sendCommand("RMD " + dir);
1280:
1281: // some servers return 257, technically incorrect but
1282: // we cater for it ...
1283: String[] validCodes = { "250", "257" };
1284: lastValidReply = control.validateReply(reply, validCodes);
1285: }
1286:
1287: /**
1288: * Create the specified remote working directory
1289: *
1290: * @param dir name of remote directory to
1291: * create
1292: */
1293: public void mkdir(String dir) throws IOException, FTPException {
1294:
1295: String reply = control.sendCommand("MKD " + dir);
1296: lastValidReply = control.validateReply(reply, "257");
1297: }
1298:
1299: /**
1300: * Change the remote working directory to
1301: * that supplied
1302: *
1303: * @param dir name of remote directory to
1304: * change to
1305: */
1306: public void chdir(String dir) throws IOException, FTPException {
1307:
1308: String reply = control.sendCommand("CWD " + dir);
1309: lastValidReply = control.validateReply(reply, "250");
1310: }
1311:
1312: /**
1313: * Get modification time for a remote file
1314: *
1315: * @param remoteFile name of remote file
1316: * @return modification time of file as a date
1317: */
1318: public Date modtime(String remoteFile) throws IOException,
1319: FTPException {
1320:
1321: String reply = control.sendCommand("MDTM " + remoteFile);
1322: lastValidReply = control.validateReply(reply, "213");
1323:
1324: // parse the reply string ...
1325: Date ts = tsFormat.parse(lastValidReply.getReplyText(),
1326: new ParsePosition(0));
1327: return ts;
1328: }
1329:
1330: /**
1331: * Get the current remote working directory
1332: *
1333: * @return the current working directory
1334: */
1335: public String pwd() throws IOException, FTPException {
1336:
1337: String reply = control.sendCommand("PWD");
1338: lastValidReply = control.validateReply(reply, "257");
1339:
1340: // get the reply text and extract the dir
1341: // listed in quotes, if we can find it. Otherwise
1342: // just return the whole reply string
1343: String text = lastValidReply.getReplyText();
1344: int start = text.indexOf('"');
1345: int end = text.lastIndexOf('"');
1346: if (start >= 0 && end > start)
1347: return text.substring(start + 1, end);
1348: else
1349: return text;
1350: }
1351:
1352: /**
1353: * Get the type of the OS at the server
1354: *
1355: * @return the type of server OS
1356: */
1357: public String system() throws IOException, FTPException {
1358:
1359: String reply = control.sendCommand("SYST");
1360: lastValidReply = control.validateReply(reply, "215");
1361: return lastValidReply.getReplyText();
1362: }
1363:
1364: /**
1365: * Get the help text for the specified command
1366: *
1367: * @param command name of the command to get help on
1368: * @return help text from the server for the supplied command
1369: */
1370: public String help(String command) throws IOException, FTPException {
1371:
1372: String reply = control.sendCommand("HELP " + command);
1373: String[] validCodes = { "211", "214" };
1374: lastValidReply = control.validateReply(reply, validCodes);
1375: return lastValidReply.getReplyText();
1376: }
1377:
1378: /**
1379: * Quit the FTP session
1380: *
1381: */
1382: public void quit() throws IOException, FTPException {
1383:
1384: try {
1385: String reply = control.sendCommand("QUIT");
1386: String[] validCodes = { "221", "226" };
1387: lastValidReply = control.validateReply(reply, validCodes);
1388: } finally { // ensure we clean up the connection
1389: control.logout();
1390: control = null;
1391: }
1392: }
1393:
1394: }
|