/*
* Copyright 2010 netling project <http://netling.org>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.netling.ftp;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.Socket;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.List;
import org.netling.util.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 String host;
private int port;
private final byte[] CRLF;
public FTPHTTPClient(String proxyHost, int proxyPort, String proxyUser, String proxyPass) {
this.proxyHost = proxyHost;
this.proxyPort = proxyPort;
this.proxyUsername = proxyUser;
this.proxyPassword = proxyPass;
try {
CRLF = "\r\n".getBytes(getControlEncoding());
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
public FTPHTTPClient(String proxyHost, int proxyPort) {
this(proxyHost, proxyPort, null, null);
}
@Override
protected Socket openDataConnection(FTPCommand command, String arg)
throws IOException {
Socket socket = new Socket(host, port);
InputStream is = socket.getInputStream();
OutputStream os = socket.getOutputStream();
tunnelHandshake(host, port, is, os);
return socket;
}
@Override
public void connect(String host, int port) throws SocketException, IOException {
this.host = host;
this.port = port;
socket = new Socket(proxyHost, proxyPort);
input = socket.getInputStream();
output = socket.getOutputStream();
try {
tunnelHandshake(host, port, input, output);
}
catch (Exception e) {
throw new IOException("Could not connect to " + host, e);
}
}
private void tunnelHandshake(String host, int port, InputStream input, OutputStream output) throws IOException {
final String connectString = "CONNECT " + host + ":" + port + " HTTP/1.1";
output.write(connectString.getBytes(getControlEncoding()));
output.write(CRLF);
if (proxyUsername != null && proxyPassword != null) {
final String header = "Proxy-Authorization: Basic "
+ Base64.encodeBytes(new String(proxyUsername + ":" + proxyPassword).getBytes()) + "\r\n";
output.write(header.getBytes("UTF-8"));
output.write(CRLF);
List<String> response = new ArrayList<String>();
BufferedReader reader = new BufferedReader(
new InputStreamReader(input));
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());
}
}
}
}