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