1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.commons.net.ftp;
19
20 import java.io.BufferedReader;
21 import java.io.IOException;
22 import java.io.InputStream;
23 import java.io.InputStreamReader;
24 import java.io.OutputStream;
25 import java.io.Reader;
26 import java.io.UnsupportedEncodingException;
27 import java.net.Inet6Address;
28 import java.net.Socket;
29 import java.net.SocketException;
30 import java.nio.charset.Charset;
31 import java.nio.charset.StandardCharsets;
32 import java.util.ArrayList;
33 import java.util.Base64;
34 import java.util.List;
35
36
37
38
39
40
41 public class FTPHTTPClient extends FTPClient {
42
43 private static final byte[] CRLF = { '\r', '\n' };
44 private final String proxyHost;
45 private final int proxyPort;
46 private final String proxyUserName;
47 private final String proxyPassword;
48 private final Charset charset;
49 private String tunnelHost;
50
51
52
53
54
55
56
57 public FTPHTTPClient(final String proxyHost, final int proxyPort) {
58 this(proxyHost, proxyPort, null, null);
59 }
60
61
62
63
64
65
66
67
68 public FTPHTTPClient(final String proxyHost, final int proxyPort, final Charset encoding) {
69 this(proxyHost, proxyPort, null, null, encoding);
70 }
71
72
73
74
75
76
77
78
79
80 public FTPHTTPClient(final String proxyHost, final int proxyPort, final String proxyUser, final String proxyPass) {
81 this(proxyHost, proxyPort, proxyUser, proxyPass, StandardCharsets.UTF_8);
82 }
83
84
85
86
87
88
89
90
91
92
93 public FTPHTTPClient(final String proxyHost, final int proxyPort, final String proxyUser, final String proxyPass, final Charset encoding) {
94 this.proxyHost = proxyHost;
95 this.proxyPort = proxyPort;
96 this.proxyUserName = proxyUser;
97 this.proxyPassword = proxyPass;
98 this.charset = encoding;
99 }
100
101
102
103
104
105
106
107
108
109 @Override
110 @Deprecated
111 protected Socket _openDataConnection_(final int command, final String arg) throws IOException {
112 return super._openDataConnection_(command, arg);
113 }
114
115
116
117
118
119
120
121 @Override
122 protected Socket _openDataConnection_(final String command, final String arg) throws IOException {
123
124 if (getDataConnectionMode() != PASSIVE_LOCAL_DATA_CONNECTION_MODE) {
125 throw new IllegalStateException("Only passive connection mode supported");
126 }
127
128 final boolean isInet6Address = getRemoteAddress() instanceof Inet6Address;
129 final String passiveHost;
130
131 final boolean attemptEPSV = isUseEPSVwithIPv4() || isInet6Address;
132 if (attemptEPSV && epsv() == FTPReply.ENTERING_EPSV_MODE) {
133 _parseExtendedPassiveModeReply(_replyLines.get(0));
134 passiveHost = tunnelHost;
135 } else {
136
137 if (isInet6Address || pasv() != FTPReply.ENTERING_PASSIVE_MODE) {
138 return null;
139 }
140 _parsePassiveModeReply(_replyLines.get(0));
141 passiveHost = getPassiveHost();
142 }
143
144 final Socket socket = _socketFactory_.createSocket(proxyHost, proxyPort);
145 final InputStream is = socket.getInputStream();
146 final OutputStream os = socket.getOutputStream();
147 tunnelHandshake(passiveHost, getPassivePort(), is, os);
148 if (getRestartOffset() > 0 && !restart(getRestartOffset()) || !FTPReply.isPositivePreliminary(sendCommand(command, arg))) {
149 socket.close();
150 return null;
151 }
152
153 return socket;
154 }
155
156 @Override
157 public void connect(final String host, final int port) throws SocketException, IOException {
158
159 _socket_ = _socketFactory_.createSocket(proxyHost, proxyPort);
160 _input_ = _socket_.getInputStream();
161 _output_ = _socket_.getOutputStream();
162 final Reader socketIsReader;
163 try {
164 socketIsReader = tunnelHandshake(host, port, _input_, _output_);
165 } catch (final Exception e) {
166 throw new IOException("Could not connect to " + host + " using port " + port, e);
167 }
168 super._connectAction_(socketIsReader);
169 }
170
171 private BufferedReader tunnelHandshake(final String host, final int port, final InputStream input, final OutputStream output)
172 throws IOException, UnsupportedEncodingException {
173 final String connectString = "CONNECT " + host + ":" + port + " HTTP/1.1";
174 final String hostString = "Host: " + host + ":" + port;
175
176 this.tunnelHost = host;
177 output.write(connectString.getBytes(charset));
178 output.write(CRLF);
179 output.write(hostString.getBytes(charset));
180 output.write(CRLF);
181
182 if (proxyUserName != null && proxyPassword != null) {
183 final String auth = proxyUserName + ":" + proxyPassword;
184 final String header = "Proxy-Authorization: Basic " + Base64.getEncoder().encodeToString(auth.getBytes(charset));
185 output.write(header.getBytes(charset));
186 output.write(CRLF);
187 }
188 output.write(CRLF);
189
190 final List<String> response = new ArrayList<>();
191 final BufferedReader reader = new BufferedReader(new InputStreamReader(input, getCharset()));
192
193 for (String line = reader.readLine(); line != null && !line.isEmpty(); line = reader.readLine()) {
194 response.add(line);
195 }
196
197 final int size = response.size();
198 if (size == 0) {
199 throw new IOException("No response from proxy");
200 }
201
202 final String code;
203 final String resp = response.get(0);
204 if (!resp.startsWith("HTTP/") || resp.length() < 12) {
205 throw new IOException("Invalid response from proxy: " + resp);
206 }
207 code = resp.substring(9, 12);
208
209 if (!"200".equals(code)) {
210 final StringBuilder msg = new StringBuilder(256);
211 msg.append("HTTPTunnelConnector: connection failed\r\n");
212 msg.append("Response received from the proxy:\r\n");
213 for (final String line : response) {
214 msg.append(line);
215 msg.append("\r\n");
216 }
217 throw new IOException(msg.toString());
218 }
219 return reader;
220 }
221 }