001    /*
002     * ftp4j - A pure Java FTP client library
003     * 
004     * Copyright (C) 2008-2009 Carlo Pelliccia (www.sauronsoftware.it)
005     * 
006     * This program is free software: you can redistribute it and/or modify
007     * it under the terms of the GNU Lesser General Public License version
008     * 2.1, as published by the Free Software Foundation.
009     *
010     * This program is distributed in the hope that it will be useful,
011     * but WITHOUT ANY WARRANTY; without even the implied warranty of
012     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
013     * GNU General Public License for more details.
014     *
015     * You should have received a copy of the GNU Lesser General Public
016     * License version 2.1 along with this program.
017     * If not, see <http://www.gnu.org/licenses/>.
018     */
019    package it.sauronsoftware.ftp4j.connectors;
020    
021    import it.sauronsoftware.ftp4j.FTPConnection;
022    import it.sauronsoftware.ftp4j.FTPConnector;
023    
024    import java.io.BufferedReader;
025    import java.io.IOException;
026    import java.io.InputStream;
027    import java.io.InputStreamReader;
028    import java.io.OutputStream;
029    import java.net.Socket;
030    import java.util.ArrayList;
031    
032    /**
033     * This one connects a remote ftp host via a HTTP 1.1 proxy which allows
034     * tunneling through the HTTP CONNECT method.
035     * 
036     * @author Carlo Pelliccia
037     */
038    public class HTTPTunnelConnector implements FTPConnector {
039    
040            /**
041             * The proxy host name.
042             */
043            private String proxyHost;
044    
045            /**
046             * The proxy port.
047             */
048            private int proxyPort;
049    
050            /**
051             * The proxyUser for proxy authentication.
052             */
053            private String proxyUser;
054    
055            /**
056             * The proxyPass for proxy authentication.
057             */
058            private String proxyPass;
059    
060            /**
061             * Builds the connector.
062             * 
063             * @param proxyHost
064             *            The proxy host name.
065             * @param proxyPort
066             *            The proxy port.
067             * @param proxyUser
068             *            The username for proxy authentication.
069             * @param proxyPass
070             *            The password for proxy authentication.
071             */
072            public HTTPTunnelConnector(String proxyHost, int proxyPort,
073                            String proxyUser, String proxyPass) {
074                    this.proxyHost = proxyHost;
075                    this.proxyPort = proxyPort;
076                    this.proxyUser = proxyUser;
077                    this.proxyPass = proxyPass;
078            }
079    
080            /**
081             * Builds the connector.
082             * 
083             * @param proxyHost
084             *            The proxy host name.
085             * @param proxyPort
086             *            The proxy port.
087             */
088            public HTTPTunnelConnector(String proxyHost, int proxyPort) {
089                    this(proxyHost, proxyPort, null, null);
090            }
091    
092            private FTPConnection connect(String host, int port) throws IOException {
093                    // The CRLF sequence.
094                    byte[] CRLF = "\r\n".getBytes("UTF-8");
095                    // The connect command line.
096                    String connect = "CONNECT " + host + ":" + port + " HTTP/1.1";
097                    // A connection status flag.
098                    boolean connected = false;
099                    // The socket for the connection with the proxy.
100                    Socket socket = null;
101                    InputStream in = null;
102                    OutputStream out = null;
103                    // FTPConnection routine.
104                    try {
105                            socket = new Socket(proxyHost, proxyPort);
106                            in = socket.getInputStream();
107                            out = socket.getOutputStream();
108                            // Send the CONNECT request.
109                            out.write(connect.getBytes("UTF-8"));
110                            out.write(CRLF);
111                            // Auth headers
112                            if (proxyUser != null && proxyPass != null) {
113                                    String header = "Proxy-Authorization: Basic "
114                                                    + Base64.encode(proxyUser + ":" + proxyPass) + "\r\n";
115                                    out.write(header.getBytes("UTF-8"));
116                            }
117                            out.write(CRLF);
118                            // Get the proxy response.
119                            ArrayList responseLines = new ArrayList();
120                            BufferedReader reader = new BufferedReader(
121                                            new InputStreamReader(in));
122                            for (String line = reader.readLine(); line != null
123                                            && line.length() > 0; line = reader.readLine()) {
124                                    responseLines.add(line);
125                            }
126                            // Parse the response.
127                            int size = responseLines.size();
128                            if (size < 1) {
129                                    throw new IOException(
130                                                    "HTTPTunnelConnector: invalid proxy response");
131                            }
132                            String code = null;
133                            String response = (String) responseLines.get(0);
134                            if (response.startsWith("HTTP/") && response.length() >= 12) {
135                                    code = response.substring(9, 12);
136                            } else {
137                                    throw new IOException(
138                                                    "HTTPTunnelConnector: invalid proxy response");
139                            }
140                            if (!"200".equals(code)) {
141                                    StringBuffer msg = new StringBuffer();
142                                    msg.append("HTTPTunnelConnector: connection failed\r\n");
143                                    msg.append("Response received from the proxy:\r\n");
144                                    for (int i = 0; i < size; i++) {
145                                            String line = (String) responseLines.get(i);
146                                            msg.append(line);
147                                            msg.append("\r\n");
148                                    }
149                                    throw new IOException(msg.toString());
150                            }
151                            connected = true;
152                    } catch (IOException e) {
153                            throw e;
154                    } finally {
155                            if (!connected) {
156                                    if (out != null) {
157                                            try {
158                                                    out.close();
159                                            } catch (Throwable t) {
160                                                    ;
161                                            }
162                                    }
163                                    if (in != null) {
164                                            try {
165                                                    in.close();
166                                            } catch (Throwable t) {
167                                                    ;
168                                            }
169                                    }
170                                    if (socket != null) {
171                                            try {
172                                                    socket.close();
173                                            } catch (Throwable t) {
174                                                    ;
175                                            }
176                                    }
177                            }
178                    }
179                    return new SocketConnection(socket, in, out);
180            }
181    
182            public FTPConnection connectForCommunicationChannel(String host, int port)
183                            throws IOException {
184                    return connect(host, port);
185            }
186    
187            public FTPConnection connectForDataTransferChannel(String host, int port)
188                            throws IOException {
189                    return connect(host, port);
190            }
191    
192    }