All Downloads are FREE. Search and download functionalities are using the official Maven repository.

panda.net.ftp.FTPHTTPClient Maven / Gradle / Ivy

Go to download

Panda Core is the core module of Panda Framework, it contains commonly used utility classes similar to apache-commons.

There is a newer version: 1.8.0
Show newest version
package panda.net.ftp;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.net.Inet6Address;
import java.net.Socket;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.List;

import panda.codec.binary.Base64;

/**
 * Experimental attempt at FTP client that tunnels over an HTTP proxy connection.
 * 
 */
public class FTPHTTPClient extends FTPClient {
	private final String proxyHost;
	private final int proxyPort;
	private final String proxyUsername;
	private final String proxyPassword;

	private static final byte[] CRLF = { '\r', '\n' };
	private final Base64 base64 = new Base64();

	private String tunnelHost; // Save the host when setting up a tunnel (needed for EPSV)

	public FTPHTTPClient(String proxyHost, int proxyPort, String proxyUser, String proxyPass) {
		this.proxyHost = proxyHost;
		this.proxyPort = proxyPort;
		this.proxyUsername = proxyUser;
		this.proxyPassword = proxyPass;
		this.tunnelHost = null;
	}

	public FTPHTTPClient(String proxyHost, int proxyPort) {
		this(proxyHost, proxyPort, null, null);
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @throws IllegalStateException if connection mode is not passive
	 */
	@Override
	protected Socket _openDataConnection_(String command, String arg) throws IOException {
		// Force local passive mode, active mode not supported by through proxy
		if (getDataConnectionMode() != PASSIVE_LOCAL_DATA_CONNECTION_MODE) {
			throw new IllegalStateException("Only passive connection mode supported");
		}

		final boolean isInet6Address = getRemoteAddress() instanceof Inet6Address;
		String passiveHost = null;

		boolean attemptEPSV = isUseEPSVwithIPv4() || isInet6Address;
		if (attemptEPSV && epsv() == FTPReply.ENTERING_EPSV_MODE) {
			_parseExtendedPassiveModeReply(_replyLines.get(0));
			passiveHost = this.tunnelHost;
		}
		else {
			if (isInet6Address) {
				return null; // Must use EPSV for IPV6
			}
			// If EPSV failed on IPV4, revert to PASV
			if (pasv() != FTPReply.ENTERING_PASSIVE_MODE) {
				return null;
			}
			_parsePassiveModeReply(_replyLines.get(0));
			passiveHost = this.getPassiveHost();
		}

		Socket socket = _socketFactory_.createSocket(proxyHost, proxyPort);
		InputStream is = socket.getInputStream();
		OutputStream os = socket.getOutputStream();
		tunnelHandshake(passiveHost, this.getPassivePort(), is, os);
		if ((getRestartOffset() > 0) && !restart(getRestartOffset())) {
			socket.close();
			return null;
		}

		if (!FTPReply.isPositivePreliminary(sendCommand(command, arg))) {
			socket.close();
			return null;
		}

		return socket;
	}

	@Override
	public void connect(String host, int port) throws SocketException, IOException {

		_socket_ = _socketFactory_.createSocket(proxyHost, proxyPort);
		_input_ = _socket_.getInputStream();
		_output_ = _socket_.getOutputStream();
		Reader socketIsReader;
		try {
			socketIsReader = tunnelHandshake(host, port, _input_, _output_);
		}
		catch (Exception e) {
			IOException ioe = new IOException("Could not connect to " + host + " using port " + port);
			ioe.initCause(e);
			throw ioe;
		}
		super._connectAction_(socketIsReader);
	}

	private BufferedReader tunnelHandshake(String host, int port, InputStream input, OutputStream output)
			throws IOException, UnsupportedEncodingException {
		final String connectString = "CONNECT " + host + ":" + port + " HTTP/1.1";
		final String hostString = "Host: " + host + ":" + port;

		this.tunnelHost = host;
		output.write(connectString.getBytes("UTF-8"));
		output.write(CRLF);
		output.write(hostString.getBytes("UTF-8"));
		output.write(CRLF);

		if (proxyUsername != null && proxyPassword != null) {
			final String auth = proxyUsername + ":" + proxyPassword;
			final String header = "Proxy-Authorization: Basic " + base64.encodeToString(auth.getBytes("UTF-8"));
			output.write(header.getBytes("UTF-8"));
		}
		output.write(CRLF);

		List response = new ArrayList();
		BufferedReader reader = new BufferedReader(new InputStreamReader(input, getCharsetName())); 

		for (String line = reader.readLine(); line != null && line.length() > 0; line = reader.readLine()) {
			response.add(line);
		}

		int size = response.size();
		if (size == 0) {
			throw new IOException("No response from proxy");
		}

		String code = null;
		String resp = response.get(0);
		if (resp.startsWith("HTTP/") && resp.length() >= 12) {
			code = resp.substring(9, 12);
		}
		else {
			throw new IOException("Invalid response from proxy: " + resp);
		}

		if (!"200".equals(code)) {
			StringBuilder msg = new StringBuilder();
			msg.append("HTTPTunnelConnector: connection failed\r\n");
			msg.append("Response received from the proxy:\r\n");
			for (String line : response) {
				msg.append(line);
				msg.append("\r\n");
			}
			throw new IOException(msg.toString());
		}
		return reader;
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy