/*
* @(#)FtpClient.java 1.55 06/10/10
*
* Copyright 1990-2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version
* 2 only, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License version 2 for more details (a copy is
* included at /legal/license.txt).
*
* You should have received a copy of the GNU General Public License
* version 2 along with this work; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
* Clara, CA 95054 or visit www.sun.com if you need additional
* information or have any questions.
*
*/
package sun.net.ftp;
import java.util.StringTokenizer;
import java.io.*;
import java.net.*;
import sun.net.TransferProtocolClient;
import sun.net.TelnetInputStream;
import sun.net.TelnetOutputStream;
/**
* This class implements the FTP client.
*
* @version 1.49, 08/19/02
* @author Jonathan Payne
*/
public class FtpClient extends TransferProtocolClient {
public static final int FTP_PORT = 21;
static int FTP_SUCCESS = 1;
static int FTP_TRY_AGAIN = 2;
static int FTP_ERROR = 3;
/** socket for data transfer */
private Socket dataSocket = null;
private boolean replyPending = false;
private boolean binaryMode = false;
/** user name for login */
String user = null;
/** password for login */
String password = null;
/** last command issued */
String command;
/** The last reply code from the ftp daemon. */
int lastReplyCode;
/** Welcome message from the server, if any. */
public String welcomeMsg;
/* The following three data members are left in for binary */
/* backwards-compatibility. Unfortunately, HotJava sets them directly */
/* when it wants to change the settings. The new design has us not */
/* cache these, so this is unnecessary, but eliminating the data members */
/* would break HJB 1.1 under JDK 1.2. */
/* */
/* These data members are not used, and their values are meaningless. */
/* TODO: Take them out for JDK 2.0! */
/**
* @deprecated
*/
public static boolean useFtpProxy;
/**
* @deprecated
*/
public static String ftpProxyHost;
/**
* @deprecated
*/
public static int ftpProxyPort;
/* these methods are used to determine whether ftp urls are sent to */
/* an http server instead of using a direct connection to the */
/* host. They aren't used directly here. */
/**
* @return if the networking layer should send ftp connections through
* a proxy
*/
public static boolean getUseFtpProxy() {
// if the ftp.proxyHost is set, use it!
return (getFtpProxyHost() != null);
}
/**
* @return the host to use, or null if none has been specified
*/
public static String getFtpProxyHost() {
return (String) java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction() {
public Object run() {
String result = System.getProperty("ftp.proxyHost");
if (result == null) {
result = System.getProperty("ftpProxyHost");
}
if (result == null) {
// as a last resort we use the general one if ftp.useProxy
// is true
if (Boolean.getBoolean("ftp.useProxy")) {
result = System.getProperty("proxyHost");
}
}
return result;
}
}
);
}
/**
* @return the proxy port to use. Will default reasonably if not set.
*/
public static int getFtpProxyPort() {
final int result[] = {80};
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction() {
public Object run() {
String tmp = System.getProperty("ftp.proxyPort");
if (tmp == null) {
// for compatibility with 1.0.2
tmp = System.getProperty("ftpProxyPort");
}
if (tmp == null) {
// as a last resort we use the general one if ftp.useProxy
// is true
if (Boolean.getBoolean("ftp.useProxy")) {
tmp = System.getProperty("proxyPort");
}
}
if (tmp != null) {
result[0] = Integer.parseInt(tmp);
}
return null;
}
}
);
return result[0];
}
/**
* issue the QUIT command to the FTP server and close the connection.
*/
public void closeServer() throws IOException {
if (serverIsOpen()) {
issueCommand("QUIT");
super.closeServer();
}
}
protected int issueCommand(String cmd) throws IOException {
command = cmd;
int reply;
if (replyPending) {
if (readReply() == FTP_ERROR)
System.out.print("Error reading FTP pending reply\n");
}
replyPending = false;
do {
sendServer(cmd + "\r\n");
reply = readReply();
}
while (reply == FTP_TRY_AGAIN);
return reply;
}
protected void issueCommandCheck(String cmd) throws IOException {
if (issueCommand(cmd) != FTP_SUCCESS)
throw new FtpProtocolException(cmd);
}
protected int readReply() throws IOException {
lastReplyCode = readServerResponse();
switch (lastReplyCode / 100) {
case 1:
replyPending = true;
/* falls into ... */
case 2:
case 3:
return FTP_SUCCESS;
case 5:
if (lastReplyCode == 530) {
if (user == null) {
throw new FtpLoginException("Not logged in");
}
return FTP_ERROR;
}
if (lastReplyCode == 550) {
throw new FileNotFoundException(command + ": " + getResponseString());
}
}
/* this statement is not reached */
return FTP_ERROR;
}
protected Socket openDataConnection(String cmd) throws IOException {
ServerSocket portSocket;
String portCmd;
InetAddress myAddress = InetAddress.getLocalHost();
byte addr[] = myAddress.getAddress();
int shift;
IOException e;
portSocket = new ServerSocket(0, 1);
portCmd = "PORT ";
/* append host addr */
for (int i = 0; i < addr.length; i++) {
portCmd = portCmd + (addr[i] & 0xFF) + ",";
}
/* append port number */
portCmd = portCmd + ((portSocket.getLocalPort() >>> 8) & 0xff) + ","
+ (portSocket.getLocalPort() & 0xff);
if (issueCommand(portCmd) == FTP_ERROR) {
e = new FtpProtocolException("PORT");
portSocket.close();
throw e;
}
if (issueCommand(cmd) == FTP_ERROR) {
e = new FtpProtocolException(cmd);
portSocket.close();
throw e;
}
dataSocket = portSocket.accept();
portSocket.close();
return dataSocket;
}
/* public methods */
/** open a FTP connection to host <i>host</i>. */
public void openServer(String host) throws IOException {
int port = FTP_PORT;
/*
String source = Firewall.verifyAccess(host, port);
if (source != null) {
Firewall.securityError("Applet at " +
source +
" tried to open FTP connection to "
+ host + ":" + port);
return;
}
*/
openServer(host, port);
}
/** open a FTP connection to host <i>host</i> on port <i>port</i>. */
public void openServer(String host, int port) throws IOException {
/*
String source = Firewall.verifyAccess(host, port);
if (source != null) {
Firewall.securityError("Applet at " +
source +
" tried to open FTP connection to "
+ host + ":" + port);
return;
}
*/
super.openServer(host, port);
if (readReply() == FTP_ERROR)
throw new FtpProtocolException("Welcome message");
}
/**
* login user to a host with username <i>user</i> and password
* <i>password</i>
*/
public void login(String user, String password) throws IOException {
/* It shouldn't send a password unless it
needs to. */
if (!serverIsOpen())
throw new FtpLoginException("not connected to host");
this.user = user;
this.password = password;
if (issueCommand("USER " + user) == FTP_ERROR)
throw new FtpLoginException("user");
if (password != null && issueCommand("PASS " + password) == FTP_ERROR)
throw new FtpLoginException("password");
// keep the welcome message around so we can
// put it in the resulting HTML page.
String l;
for (int i = 0; i < serverResponse.size(); i++) {
l = (String) serverResponse.elementAt(i);
if (l != null) {
if (l.charAt(3) != '-') {
break;
}
// get rid of the "230-" prefix
l = l.substring(4);
if (welcomeMsg == null) {
welcomeMsg = l;
} else {
welcomeMsg += l;
}
}
}
}
/** GET a file from the FTP server */
public TelnetInputStream get(String filename) throws IOException {
Socket s;
try {
s = openDataConnection("RETR " + filename);
} catch (FileNotFoundException fileException) {
/* Well, "/" might not be the file delimitor for this
particular ftp server, so let's try a series of
"cd" commands to get to the right place. */
StringTokenizer t = new StringTokenizer(filename, "/");
String pathElement = null;
while (t.hasMoreElements()) {
pathElement = t.nextToken();
if (!t.hasMoreElements()) {
/* This is the file component. Look it up now. */
break;
}
try {
cd(pathElement);
} catch (FtpProtocolException e) {
/* Giving up. */
throw fileException;
}
}
if (pathElement != null) {
s = openDataConnection("RETR " + pathElement);
} else {
throw fileException;
}
}
return new FtpInputStream(this, s.getInputStream(), binaryMode);
}
/** PUT a file to the FTP server */
public TelnetOutputStream put(String filename) throws IOException {
Socket s = openDataConnection("STOR " + filename);
return new TelnetOutputStream(s.getOutputStream(), binaryMode);
}
/** LIST files on a remote FTP server */
public TelnetInputStream list() throws IOException {
Socket s = openDataConnection("LIST");
return new TelnetInputStream(s.getInputStream(), binaryMode);
}
/** CD to a specific directory on a remote FTP server */
public void cd(String remoteDirectory) throws IOException {
issueCommandCheck("CWD " + remoteDirectory);
}
/** Set transfer type to 'I' */
public void binary() throws IOException {
issueCommandCheck("TYPE I");
binaryMode = true;
}
/** Set transfer type to 'A' */
public void ascii() throws IOException {
issueCommandCheck("TYPE A");
binaryMode = false;
}
/** New FTP client connected to host <i>host</i>. */
public FtpClient(String host) throws IOException {
super();
openServer(host, FTP_PORT);
}
/** New FTP client connected to host <i>host</i>, port <i>port</i>. */
public FtpClient(String host, int port) throws IOException {
super();
openServer(host, port);
}
/** Create an uninitialized FTP client. */
public FtpClient() {}
}