    001    /*
    002     * Licensed to the Apache Software Foundation (ASF) under one or more
    003     * contributor license agreements.  See the NOTICE file distributed with
    004     * this work for additional information regarding copyright ownership.
    005     * The ASF licenses this file to You under the Apache License, Version 2.0
    006     * (the "License"); you may not use this file except in compliance with
    007     * the License.  You may obtain a copy of the License at
    008     *
    009     *      http://www.apache.org/licenses/LICENSE-2.0
    010     *
    011     * Unless required by applicable law or agreed to in writing, software
    012     * distributed under the License is distributed on an "AS IS" BASIS,
    013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014     * See the License for the specific language governing permissions and
    015     * limitations under the License.
    016     */
    017
    018    package org.apache.commons.net.ftp;
    019
    020    import java.io.BufferedReader;
    021    import java.io.IOException;
    022    import java.io.InputStream;
    023    import java.io.InputStreamReader;
    024    import java.io.OutputStream;
    025    import java.io.UnsupportedEncodingException;
    026    import java.net.Socket;
    027    import java.net.SocketException;
    028    import java.util.ArrayList;
    029    import java.util.List;
    030
    031    import org.apache.commons.net.util.Base64;
    032
    033    /**
    034     * Experimental attempt at FTP client that tunnels over an HTTP proxy connection.
    035     *
    036     * @author rory
    037     * @since 2.2
    038     */
    039    public class FTPHTTPClient extends FTPClient {
    040        private final String proxyHost;
    041        private final int proxyPort;
    042        private final String proxyUsername;
    043        private final String proxyPassword;
    044        private String host;
    045        private int port;
    046
    047        private final byte[] CRLF;
    048        private final Base64 base64 = new Base64();
    049
    050        public FTPHTTPClient(String proxyHost, int proxyPort, String proxyUser, String proxyPass) {
    051            this.proxyHost = proxyHost;
    052            this.proxyPort = proxyPort;
    053            this.proxyUsername = proxyUser;
    054            this.proxyPassword = proxyPass;
    055
    056            try {
    057                CRLF = "\r\n".getBytes(getControlEncoding());
    058            } catch (UnsupportedEncodingException e) {
    059                throw new RuntimeException(e);
    060            }
    061        }
    062
    063        public FTPHTTPClient(String proxyHost, int proxyPort) {
    064            this(proxyHost, proxyPort, null, null);
    065        }
    066
    067
    068        @Override
    069        protected Socket _openDataConnection_(int command, String arg)
    070        throws IOException {
    071            Socket socket = new Socket(host, port);
    072            InputStream is = socket.getInputStream();
    073            OutputStream os = socket.getOutputStream();
    074
    075            tunnelHandshake(host, port, is, os);
    076
    077            return socket;
    078        }
    079
    080        @Override
    081        public void connect(String host, int port) throws SocketException,
    082        IOException {
    083            this.host = host;
    084            this.port = port;
    085
    086            _socket_ = new Socket(proxyHost, proxyPort);
    087            _input_ = _socket_.getInputStream();
    088            _output_ = _socket_.getOutputStream();
    089            try {
    090                tunnelHandshake(host, port, _input_, _output_);
    091            }
    092            catch (Exception e) {
    093                IOException ioe = new IOException("Could not connect to " + host);
    094                ioe.initCause(e);
    095                throw ioe;
    096            }
    097        }
    098
    099        private void tunnelHandshake(String host, int port, InputStream input, OutputStream output) throws IOException,
    100        UnsupportedEncodingException {
    101            final String connectString = "CONNECT "  + host + ":" + port  + " HTTP/1.1";
    102
    103            _output_.write(connectString.getBytes(getControlEncoding()));
    104            _output_.write(CRLF);
    105
    106            if (proxyUsername != null && proxyPassword != null) {
    107                final String header = "Proxy-Authorization: Basic "
    108                    + base64.encode(proxyUsername + ":" + proxyPassword) + "\r\n";
    109                _output_.write(header.getBytes("UTF-8"));
    110                _output_.write(CRLF);
    111
    112                List<String> response = new ArrayList<String>();
    113                BufferedReader reader = new BufferedReader(
    114                        new InputStreamReader(_input_));
    115
    116                for (String line = reader.readLine(); line != null
    117                && line.length() > 0; line = reader.readLine()) {
    118                    response.add(line);
    119                }
    120
    121                int size = response.size();
    122                if (size == 0) {
    123                    throw new IOException("No response from proxy");
    124                }
    125
    126                String code = null;
    127                String resp = response.get(0);
    128                if (resp.startsWith("HTTP/") && resp.length() >= 12) {
    129                    code = resp.substring(9, 12);
    130                } else {
    131                    throw new IOException("Invalid response from proxy: " + resp);
    132                }
    133
    134                if (!"200".equals(code)) {
    135                    StringBuilder msg = new StringBuilder();
    136                    msg.append("HTTPTunnelConnector: connection failed\r\n");
    137                    msg.append("Response received from the proxy:\r\n");
    138                    for (String line : response) {
    139                        msg.append(line);
    140                        msg.append("\r\n");
    141                    }
    142                    throw new IOException(msg.toString());
    143                }
    144            }
    145        }
    146    }
    147
    148