Print this page
Split |
Close |
Expand all |
Collapse all |
--- old/src/share/classes/sun/net/www/protocol/ftp/FtpURLConnection.java
+++ new/src/share/classes/sun/net/www/protocol/ftp/FtpURLConnection.java
1 1 /*
2 2 * Copyright (c) 1994, 2009, Oracle and/or its affiliates. All rights reserved.
3 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 4 *
5 5 * This code is free software; you can redistribute it and/or modify it
6 6 * under the terms of the GNU General Public License version 2 only, as
7 7 * published by the Free Software Foundation. Oracle designates this
8 8 * particular file as subject to the "Classpath" exception as provided
9 9 * by Oracle in the LICENSE file that accompanied this code.
10 10 *
11 11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 14 * version 2 for more details (a copy is included in the LICENSE file that
15 15 * accompanied this code).
16 16 *
17 17 * You should have received a copy of the GNU General Public License version
18 18 * 2 along with this work; if not, write to the Free Software Foundation,
19 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 20 *
21 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 22 * or visit www.oracle.com if you need additional information or have any
23 23 * questions.
24 24 */
25 25
26 26 /**
27 27 * FTP stream opener.
28 28 */
29 29
30 30 package sun.net.www.protocol.ftp;
31 31
32 32 import java.io.IOException;
33 33 import java.io.InputStream;
34 34 import java.io.OutputStream;
35 35 import java.io.BufferedInputStream;
36 36 import java.io.FilterInputStream;
37 37 import java.io.FilterOutputStream;
38 38 import java.io.FileNotFoundException;
↓ open down ↓ |
38 lines elided |
↑ open up ↑ |
39 39 import java.net.URL;
40 40 import java.net.SocketPermission;
41 41 import java.net.UnknownHostException;
42 42 import java.net.InetSocketAddress;
43 43 import java.net.URI;
44 44 import java.net.Proxy;
45 45 import java.net.ProxySelector;
46 46 import java.util.StringTokenizer;
47 47 import java.util.Iterator;
48 48 import java.security.Permission;
49 +import sun.net.NetworkClient;
49 50 import sun.net.www.MessageHeader;
50 51 import sun.net.www.MeteredStream;
51 52 import sun.net.www.URLConnection;
52 53 import sun.net.www.protocol.http.HttpURLConnection;
53 54 import sun.net.ftp.FtpClient;
54 55 import sun.net.ftp.FtpProtocolException;
55 56 import sun.net.ProgressSource;
56 57 import sun.net.ProgressMonitor;
57 58 import sun.net.www.ParseUtil;
58 59 import sun.security.action.GetPropertyAction;
59 60
60 61
61 62 /**
62 63 * This class Opens an FTP input (or output) stream given a URL.
63 64 * It works as a one shot FTP transfer :
64 65 * <UL>
65 66 * <LI>Login</LI>
66 67 * <LI>Get (or Put) the file</LI>
67 68 * <LI>Disconnect</LI>
68 69 * </UL>
69 70 * You should not have to use it directly in most cases because all will be handled
70 71 * in a abstract layer. Here is an example of how to use the class :
71 72 * <P>
72 73 * <code>URL url = new URL("ftp://ftp.sun.com/pub/test.txt");<p>
73 74 * UrlConnection con = url.openConnection();<p>
74 75 * InputStream is = con.getInputStream();<p>
75 76 * ...<p>
76 77 * is.close();</code>
77 78 *
78 79 * @see sun.net.ftp.FtpClient
79 80 */
80 81 public class FtpURLConnection extends URLConnection {
81 82
82 83 // In case we have to use proxies, we use HttpURLConnection
83 84 HttpURLConnection http = null;
84 85 private Proxy instProxy;
85 86
86 87 InputStream is = null;
87 88 OutputStream os = null;
88 89
89 90 FtpClient ftp = null;
90 91 Permission permission;
91 92
92 93 String password;
93 94 String user;
94 95
↓ open down ↓ |
36 lines elided |
↑ open up ↑ |
95 96 String host;
96 97 String pathname;
97 98 String filename;
98 99 String fullpath;
99 100 int port;
100 101 static final int NONE = 0;
101 102 static final int ASCII = 1;
102 103 static final int BIN = 2;
103 104 static final int DIR = 3;
104 105 int type = NONE;
105 - /* Redefine timeouts from java.net.URLConnection as we nee -1 to mean
106 + /* Redefine timeouts from java.net.URLConnection as we need -1 to mean
106 107 * not set. This is to ensure backward compatibility.
107 108 */
108 - private int connectTimeout = -1;
109 - private int readTimeout = -1;
109 + private int connectTimeout = NetworkClient.DEFAULT_CONNECT_TIMEOUT;;
110 + private int readTimeout = NetworkClient.DEFAULT_READ_TIMEOUT;;
110 111
111 112 /**
112 113 * For FTP URLs we need to have a special InputStream because we
113 114 * need to close 2 sockets after we're done with it :
114 115 * - The Data socket (for the file).
115 116 * - The command socket (FtpClient).
116 117 * Since that's the only class that needs to see that, it is an inner class.
117 118 */
118 119 protected class FtpInputStream extends FilterInputStream {
119 120 FtpClient ftp;
120 121 FtpInputStream(FtpClient cl, InputStream fd) {
121 122 super(new BufferedInputStream(fd));
122 123 ftp = cl;
123 124 }
124 125
125 126 @Override
126 127 public void close() throws IOException {
127 128 super.close();
128 129 if (ftp != null) {
129 130 ftp.close();
130 131 }
131 132 }
132 133 }
133 134
134 135 /**
135 136 * For FTP URLs we need to have a special OutputStream because we
136 137 * need to close 2 sockets after we're done with it :
137 138 * - The Data socket (for the file).
138 139 * - The command socket (FtpClient).
139 140 * Since that's the only class that needs to see that, it is an inner class.
140 141 */
141 142 protected class FtpOutputStream extends FilterOutputStream {
142 143 FtpClient ftp;
143 144 FtpOutputStream(FtpClient cl, OutputStream fd) {
144 145 super(fd);
145 146 ftp = cl;
146 147 }
147 148
148 149 @Override
149 150 public void close() throws IOException {
150 151 super.close();
151 152 if (ftp != null) {
152 153 ftp.close();
153 154 }
154 155 }
155 156 }
156 157
157 158 /**
158 159 * Creates an FtpURLConnection from a URL.
159 160 *
160 161 * @param url The <code>URL</code> to retrieve or store.
161 162 */
162 163 public FtpURLConnection(URL url) {
163 164 this(url, null);
164 165 }
165 166
166 167 /**
167 168 * Same as FtpURLconnection(URL) with a per connection proxy specified
168 169 */
169 170 FtpURLConnection(URL url, Proxy p) {
170 171 super(url);
171 172 instProxy = p;
172 173 host = url.getHost();
173 174 port = url.getPort();
174 175 String userInfo = url.getUserInfo();
175 176
176 177 if (userInfo != null) { // get the user and password
177 178 int delimiter = userInfo.indexOf(':');
178 179 if (delimiter == -1) {
179 180 user = ParseUtil.decode(userInfo);
180 181 password = null;
181 182 } else {
182 183 user = ParseUtil.decode(userInfo.substring(0, delimiter++));
183 184 password = ParseUtil.decode(userInfo.substring(delimiter));
184 185 }
185 186 }
186 187 }
187 188
188 189 private void setTimeouts() {
189 190 if (ftp != null) {
190 191 if (connectTimeout >= 0) {
191 192 ftp.setConnectTimeout(connectTimeout);
192 193 }
193 194 if (readTimeout >= 0) {
194 195 ftp.setReadTimeout(readTimeout);
195 196 }
196 197 }
197 198 }
198 199
199 200 /**
200 201 * Connects to the FTP server and logs in.
201 202 *
202 203 * @throws FtpLoginException if the login is unsuccessful
203 204 * @throws FtpProtocolException if an error occurs
204 205 * @throws UnknownHostException if trying to connect to an unknown host
205 206 */
206 207
207 208 public synchronized void connect() throws IOException {
208 209 if (connected) {
209 210 return;
210 211 }
211 212
212 213 Proxy p = null;
213 214 if (instProxy == null) { // no per connection proxy specified
214 215 /**
215 216 * Do we have to use a proxy?
216 217 */
217 218 ProxySelector sel = java.security.AccessController.doPrivileged(
218 219 new java.security.PrivilegedAction<ProxySelector>() {
219 220 public ProxySelector run() {
220 221 return ProxySelector.getDefault();
221 222 }
222 223 });
223 224 if (sel != null) {
224 225 URI uri = sun.net.www.ParseUtil.toURI(url);
225 226 Iterator<Proxy> it = sel.select(uri).iterator();
226 227 while (it.hasNext()) {
227 228 p = it.next();
228 229 if (p == null || p == Proxy.NO_PROXY ||
229 230 p.type() == Proxy.Type.SOCKS) {
230 231 break;
231 232 }
232 233 if (p.type() != Proxy.Type.HTTP ||
233 234 !(p.address() instanceof InetSocketAddress)) {
234 235 sel.connectFailed(uri, p.address(), new IOException("Wrong proxy type"));
235 236 continue;
236 237 }
237 238 // OK, we have an http proxy
238 239 InetSocketAddress paddr = (InetSocketAddress) p.address();
239 240 try {
240 241 http = new HttpURLConnection(url, p);
241 242 http.setDoInput(getDoInput());
242 243 http.setDoOutput(getDoOutput());
243 244 if (connectTimeout >= 0) {
244 245 http.setConnectTimeout(connectTimeout);
245 246 }
246 247 if (readTimeout >= 0) {
247 248 http.setReadTimeout(readTimeout);
248 249 }
249 250 http.connect();
250 251 connected = true;
251 252 return;
252 253 } catch (IOException ioe) {
253 254 sel.connectFailed(uri, paddr, ioe);
254 255 http = null;
255 256 }
256 257 }
257 258 }
258 259 } else { // per connection proxy specified
259 260 p = instProxy;
260 261 if (p.type() == Proxy.Type.HTTP) {
261 262 http = new HttpURLConnection(url, instProxy);
262 263 http.setDoInput(getDoInput());
263 264 http.setDoOutput(getDoOutput());
264 265 if (connectTimeout >= 0) {
265 266 http.setConnectTimeout(connectTimeout);
266 267 }
267 268 if (readTimeout >= 0) {
268 269 http.setReadTimeout(readTimeout);
269 270 }
270 271 http.connect();
271 272 connected = true;
272 273 return;
273 274 }
274 275 }
275 276
276 277 if (user == null) {
277 278 user = "anonymous";
278 279 String vers = java.security.AccessController.doPrivileged(
279 280 new GetPropertyAction("java.version"));
280 281 password = java.security.AccessController.doPrivileged(
281 282 new GetPropertyAction("ftp.protocol.user",
282 283 "Java" + vers + "@"));
283 284 }
284 285 try {
285 286 ftp = FtpClient.create();
286 287 if (p != null) {
287 288 ftp.setProxy(p);
288 289 }
289 290 setTimeouts();
290 291 if (port != -1) {
291 292 ftp.connect(new InetSocketAddress(host, port));
292 293 } else {
293 294 ftp.connect(new InetSocketAddress(host, FtpClient.defaultPort()));
294 295 }
295 296 } catch (UnknownHostException e) {
296 297 // Maybe do something smart here, like use a proxy like iftp.
297 298 // Just keep throwing for now.
298 299 throw e;
299 300 } catch (FtpProtocolException fe) {
300 301 throw new IOException(fe);
301 302 }
302 303 try {
303 304 ftp.login(user, password.toCharArray());
304 305 } catch (sun.net.ftp.FtpProtocolException e) {
305 306 ftp.close();
306 307 // Backward compatibility
307 308 throw new sun.net.ftp.FtpLoginException("Invalid username/password");
308 309 }
309 310 connected = true;
310 311 }
311 312
312 313
313 314 /*
314 315 * Decodes the path as per the RFC-1738 specifications.
315 316 */
316 317 private void decodePath(String path) {
317 318 int i = path.indexOf(";type=");
318 319 if (i >= 0) {
319 320 String s1 = path.substring(i + 6, path.length());
320 321 if ("i".equalsIgnoreCase(s1)) {
321 322 type = BIN;
322 323 }
323 324 if ("a".equalsIgnoreCase(s1)) {
324 325 type = ASCII;
325 326 }
326 327 if ("d".equalsIgnoreCase(s1)) {
327 328 type = DIR;
328 329 }
329 330 path = path.substring(0, i);
330 331 }
331 332 if (path != null && path.length() > 1 &&
332 333 path.charAt(0) == '/') {
333 334 path = path.substring(1);
334 335 }
335 336 if (path == null || path.length() == 0) {
336 337 path = "./";
337 338 }
338 339 if (!path.endsWith("/")) {
339 340 i = path.lastIndexOf('/');
340 341 if (i > 0) {
341 342 filename = path.substring(i + 1, path.length());
342 343 filename = ParseUtil.decode(filename);
343 344 pathname = path.substring(0, i);
344 345 } else {
345 346 filename = ParseUtil.decode(path);
346 347 pathname = null;
347 348 }
348 349 } else {
349 350 pathname = path.substring(0, path.length() - 1);
350 351 filename = null;
351 352 }
352 353 if (pathname != null) {
353 354 fullpath = pathname + "/" + (filename != null ? filename : "");
354 355 } else {
355 356 fullpath = filename;
356 357 }
357 358 }
358 359
359 360 /*
360 361 * As part of RFC-1738 it is specified that the path should be
361 362 * interpreted as a series of FTP CWD commands.
362 363 * This is because, '/' is not necessarly the directory delimiter
363 364 * on every systems.
364 365 */
365 366 private void cd(String path) throws FtpProtocolException, IOException {
366 367 if (path == null || path.isEmpty()) {
367 368 return;
368 369 }
369 370 if (path.indexOf('/') == -1) {
370 371 ftp.changeDirectory(ParseUtil.decode(path));
371 372 return;
372 373 }
373 374
374 375 StringTokenizer token = new StringTokenizer(path, "/");
375 376 while (token.hasMoreTokens()) {
376 377 ftp.changeDirectory(ParseUtil.decode(token.nextToken()));
377 378 }
378 379 }
379 380
380 381 /**
381 382 * Get the InputStream to retreive the remote file. It will issue the
382 383 * "get" (or "dir") command to the ftp server.
383 384 *
384 385 * @return the <code>InputStream</code> to the connection.
385 386 *
386 387 * @throws IOException if already opened for output
387 388 * @throws FtpProtocolException if errors occur during the transfert.
388 389 */
389 390 @Override
390 391 public InputStream getInputStream() throws IOException {
391 392 if (!connected) {
392 393 connect();
393 394 }
394 395
395 396 if (http != null) {
396 397 return http.getInputStream();
397 398 }
398 399
399 400 if (os != null) {
400 401 throw new IOException("Already opened for output");
401 402 }
402 403
403 404 if (is != null) {
404 405 return is;
405 406 }
406 407
407 408 MessageHeader msgh = new MessageHeader();
408 409
409 410 boolean isAdir = false;
410 411 try {
411 412 decodePath(url.getPath());
412 413 if (filename == null || type == DIR) {
413 414 ftp.setAsciiType();
414 415 cd(pathname);
415 416 if (filename == null) {
416 417 is = new FtpInputStream(ftp, ftp.list(null));
417 418 } else {
418 419 is = new FtpInputStream(ftp, ftp.nameList(filename));
419 420 }
420 421 } else {
421 422 if (type == ASCII) {
422 423 ftp.setAsciiType();
423 424 } else {
424 425 ftp.setBinaryType();
425 426 }
426 427 cd(pathname);
427 428 is = new FtpInputStream(ftp, ftp.getFileStream(filename));
428 429 }
429 430
430 431 /* Try to get the size of the file in bytes. If that is
431 432 successful, then create a MeteredStream. */
432 433 try {
433 434 long l = ftp.getLastTransferSize();
434 435 msgh.add("content-length", Long.toString(l));
435 436 if (l > 0) {
436 437
437 438 // Wrap input stream with MeteredStream to ensure read() will always return -1
438 439 // at expected length.
439 440
440 441 // Check if URL should be metered
441 442 boolean meteredInput = ProgressMonitor.getDefault().shouldMeterInput(url, "GET");
442 443 ProgressSource pi = null;
443 444
444 445 if (meteredInput) {
445 446 pi = new ProgressSource(url, "GET", l);
446 447 pi.beginTracking();
447 448 }
448 449
449 450 is = new MeteredStream(is, pi, l);
450 451 }
451 452 } catch (Exception e) {
452 453 e.printStackTrace();
453 454 /* do nothing, since all we were doing was trying to
454 455 get the size in bytes of the file */
455 456 }
456 457
457 458 if (isAdir) {
458 459 msgh.add("content-type", "text/plain");
459 460 msgh.add("access-type", "directory");
460 461 } else {
461 462 msgh.add("access-type", "file");
462 463 String ftype = guessContentTypeFromName(fullpath);
463 464 if (ftype == null && is.markSupported()) {
464 465 ftype = guessContentTypeFromStream(is);
465 466 }
466 467 if (ftype != null) {
467 468 msgh.add("content-type", ftype);
468 469 }
469 470 }
470 471 } catch (FileNotFoundException e) {
471 472 try {
472 473 cd(fullpath);
473 474 /* if that worked, then make a directory listing
474 475 and build an html stream with all the files in
475 476 the directory */
476 477 ftp.setAsciiType();
477 478
478 479 is = new FtpInputStream(ftp, ftp.list(null));
479 480 msgh.add("content-type", "text/plain");
480 481 msgh.add("access-type", "directory");
481 482 } catch (IOException ex) {
482 483 throw new FileNotFoundException(fullpath);
483 484 } catch (FtpProtocolException ex2) {
484 485 throw new FileNotFoundException(fullpath);
485 486 }
486 487 } catch (FtpProtocolException ftpe) {
487 488 throw new IOException(ftpe);
488 489 }
489 490 setProperties(msgh);
490 491 return is;
491 492 }
492 493
493 494 /**
494 495 * Get the OutputStream to store the remote file. It will issue the
495 496 * "put" command to the ftp server.
496 497 *
497 498 * @return the <code>OutputStream</code> to the connection.
498 499 *
499 500 * @throws IOException if already opened for input or the URL
500 501 * points to a directory
501 502 * @throws FtpProtocolException if errors occur during the transfert.
502 503 */
503 504 @Override
504 505 public OutputStream getOutputStream() throws IOException {
505 506 if (!connected) {
506 507 connect();
507 508 }
508 509
509 510 if (http != null) {
510 511 OutputStream out = http.getOutputStream();
511 512 // getInputStream() is neccessary to force a writeRequests()
512 513 // on the http client.
513 514 http.getInputStream();
514 515 return out;
515 516 }
516 517
517 518 if (is != null) {
518 519 throw new IOException("Already opened for input");
519 520 }
520 521
521 522 if (os != null) {
522 523 return os;
523 524 }
524 525
525 526 decodePath(url.getPath());
526 527 if (filename == null || filename.length() == 0) {
527 528 throw new IOException("illegal filename for a PUT");
528 529 }
529 530 try {
530 531 if (pathname != null) {
531 532 cd(pathname);
532 533 }
533 534 if (type == ASCII) {
534 535 ftp.setAsciiType();
535 536 } else {
536 537 ftp.setBinaryType();
537 538 }
538 539 os = new FtpOutputStream(ftp, ftp.putFileStream(filename, false));
539 540 } catch (FtpProtocolException e) {
540 541 throw new IOException(e);
541 542 }
542 543 return os;
543 544 }
544 545
545 546 String guessContentTypeFromFilename(String fname) {
546 547 return guessContentTypeFromName(fname);
547 548 }
548 549
549 550 /**
550 551 * Gets the <code>Permission</code> associated with the host & port.
551 552 *
552 553 * @return The <code>Permission</code> object.
553 554 */
554 555 @Override
555 556 public Permission getPermission() {
556 557 if (permission == null) {
557 558 int urlport = url.getPort();
558 559 urlport = urlport < 0 ? FtpClient.defaultPort() : urlport;
559 560 String urlhost = this.host + ":" + urlport;
560 561 permission = new SocketPermission(urlhost, "connect");
561 562 }
562 563 return permission;
563 564 }
564 565
565 566 /**
566 567 * Sets the general request property. If a property with the key already
567 568 * exists, overwrite its value with the new value.
568 569 *
569 570 * @param key the keyword by which the request is known
570 571 * (e.g., "<code>accept</code>").
571 572 * @param value the value associated with it.
572 573 * @throws IllegalStateException if already connected
573 574 * @see #getRequestProperty(java.lang.String)
574 575 */
575 576 @Override
576 577 public void setRequestProperty(String key, String value) {
577 578 super.setRequestProperty(key, value);
578 579 if ("type".equals(key)) {
579 580 if ("i".equalsIgnoreCase(value)) {
580 581 type = BIN;
581 582 } else if ("a".equalsIgnoreCase(value)) {
582 583 type = ASCII;
583 584 } else if ("d".equalsIgnoreCase(value)) {
584 585 type = DIR;
585 586 } else {
586 587 throw new IllegalArgumentException(
587 588 "Value of '" + key +
588 589 "' request property was '" + value +
589 590 "' when it must be either 'i', 'a' or 'd'");
590 591 }
591 592 }
592 593 }
593 594
594 595 /**
595 596 * Returns the value of the named general request property for this
596 597 * connection.
597 598 *
598 599 * @param key the keyword by which the request is known (e.g., "accept").
599 600 * @return the value of the named general request property for this
600 601 * connection.
601 602 * @throws IllegalStateException if already connected
602 603 * @see #setRequestProperty(java.lang.String, java.lang.String)
603 604 */
604 605 @Override
605 606 public String getRequestProperty(String key) {
606 607 String value = super.getRequestProperty(key);
607 608
608 609 if (value == null) {
609 610 if ("type".equals(key)) {
610 611 value = (type == ASCII ? "a" : type == DIR ? "d" : "i");
611 612 }
612 613 }
613 614
614 615 return value;
615 616 }
616 617
617 618 @Override
618 619 public void setConnectTimeout(int timeout) {
619 620 if (timeout < 0) {
620 621 throw new IllegalArgumentException("timeouts can't be negative");
621 622 }
622 623 connectTimeout = timeout;
623 624 }
624 625
625 626 @Override
626 627 public int getConnectTimeout() {
627 628 return (connectTimeout < 0 ? 0 : connectTimeout);
628 629 }
629 630
630 631 @Override
631 632 public void setReadTimeout(int timeout) {
632 633 if (timeout < 0) {
633 634 throw new IllegalArgumentException("timeouts can't be negative");
634 635 }
635 636 readTimeout = timeout;
636 637 }
637 638
638 639 @Override
639 640 public int getReadTimeout() {
640 641 return readTimeout < 0 ? 0 : readTimeout;
641 642 }
642 643 }
↓ open down ↓ |
523 lines elided |
↑ open up ↑ |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX