Lines 18-29
Link Here
|
18 |
|
18 |
|
19 |
package org.apache.tools.ant.taskdefs; |
19 |
package org.apache.tools.ant.taskdefs; |
20 |
|
20 |
|
21 |
import org.apache.tools.ant.BuildException; |
|
|
22 |
import org.apache.tools.ant.Project; |
23 |
import org.apache.tools.ant.Task; |
24 |
import org.apache.tools.ant.util.FileUtils; |
25 |
|
26 |
import java.io.File; |
21 |
import java.io.File; |
|
|
22 |
import java.io.FileNotFoundException; |
27 |
import java.io.FileOutputStream; |
23 |
import java.io.FileOutputStream; |
28 |
import java.io.IOException; |
24 |
import java.io.IOException; |
29 |
import java.io.InputStream; |
25 |
import java.io.InputStream; |
Lines 33-38
Link Here
|
33 |
import java.net.URL; |
29 |
import java.net.URL; |
34 |
import java.net.URLConnection; |
30 |
import java.net.URLConnection; |
35 |
import java.util.Date; |
31 |
import java.util.Date; |
|
|
32 |
import java.util.HashSet; |
33 |
import java.util.Set; |
34 |
|
35 |
import org.apache.tools.ant.BuildException; |
36 |
import org.apache.tools.ant.Project; |
37 |
import org.apache.tools.ant.Task; |
38 |
import org.apache.tools.ant.util.FileUtils; |
36 |
|
39 |
|
37 |
/** |
40 |
/** |
38 |
* Gets a particular file from a URL source. |
41 |
* Gets a particular file from a URL source. |
Lines 49-54
Link Here
|
49 |
private static final int DOTS_PER_LINE = 50; |
52 |
private static final int DOTS_PER_LINE = 50; |
50 |
private static final int BIG_BUFFER_SIZE = 100 * 1024; |
53 |
private static final int BIG_BUFFER_SIZE = 100 * 1024; |
51 |
private static final FileUtils FILE_UTILS = FileUtils.getFileUtils(); |
54 |
private static final FileUtils FILE_UTILS = FileUtils.getFileUtils(); |
|
|
55 |
private static final int REDIRECT_LIMIT = 25; |
56 |
|
57 |
private static final String HTTP = "http"; |
58 |
private static final String HTTPS = "https"; |
52 |
|
59 |
|
53 |
private URL source; // required |
60 |
private URL source; // required |
54 |
private File dest; // required |
61 |
private File dest; // required |
Lines 361-366
Link Here
|
361 |
} |
368 |
} |
362 |
|
369 |
|
363 |
private class GetThread extends Thread { |
370 |
private class GetThread extends Thread { |
|
|
371 |
|
364 |
private final boolean hasTimestamp; |
372 |
private final boolean hasTimestamp; |
365 |
private final long timestamp; |
373 |
private final long timestamp; |
366 |
private final DownloadProgress progress; |
374 |
private final DownloadProgress progress; |
Lines 371-376
Link Here
|
371 |
private BuildException exception = null; |
379 |
private BuildException exception = null; |
372 |
private InputStream is = null; |
380 |
private InputStream is = null; |
373 |
private OutputStream os = null; |
381 |
private OutputStream os = null; |
|
|
382 |
private Set triedSources = new HashSet(); |
383 |
private URLConnection connection; |
374 |
|
384 |
|
375 |
GetThread(boolean h, long t, DownloadProgress p, int l) { |
385 |
GetThread(boolean h, long t, DownloadProgress p, int l) { |
376 |
hasTimestamp = h; |
386 |
hasTimestamp = h; |
Lines 390-399
Link Here
|
390 |
} |
400 |
} |
391 |
|
401 |
|
392 |
private boolean get() throws IOException, BuildException { |
402 |
private boolean get() throws IOException, BuildException { |
393 |
//set up the URL connection |
403 |
|
394 |
URLConnection connection = source.openConnection(); |
404 |
connection = openConnection(source); |
395 |
//modify the headers |
405 |
|
396 |
//NB: things like user authentication could go in here too. |
406 |
if (connection == null) |
|
|
407 |
{ |
408 |
return false; |
409 |
} |
410 |
|
411 |
boolean downloadSucceeded = downloadFile(); |
412 |
|
413 |
//if (and only if) the use file time option is set, then |
414 |
//the saved file now has its timestamp set to that of the |
415 |
//downloaded file |
416 |
if (downloadSucceeded && useTimestamp) { |
417 |
updateTimeStamp(); |
418 |
} |
419 |
|
420 |
return downloadSucceeded; |
421 |
} |
422 |
|
423 |
|
424 |
private boolean redirectionAllowed(URL aSource, URL aDest) { |
425 |
if (!(aSource.getProtocol().equals(aDest.getProtocol()) || (HTTP |
426 |
.equals(aSource.getProtocol()) && HTTPS.equals(aDest |
427 |
.getProtocol())))) { |
428 |
String message = "Redirection detected from " |
429 |
+ aSource.getProtocol() + " to " + aDest.getProtocol() |
430 |
+ ". Protocol switch unsafe, not allowed."; |
431 |
if (ignoreErrors) { |
432 |
log(message, logLevel); |
433 |
return false; |
434 |
} else { |
435 |
throw new BuildException(message); |
436 |
} |
437 |
} |
438 |
if (triedSources.contains(aDest.toString())) { |
439 |
String message = "Cyclic redirection detected: " + aDest; |
440 |
if (ignoreErrors) { |
441 |
log(message, logLevel); |
442 |
return false; |
443 |
} else { |
444 |
throw new BuildException(message); |
445 |
} |
446 |
} |
447 |
|
448 |
if (triedSources.size() > REDIRECT_LIMIT) { |
449 |
String message = "More than " + REDIRECT_LIMIT |
450 |
+ " times redirected, giving up"; |
451 |
if (ignoreErrors) { |
452 |
log(message, logLevel); |
453 |
return false; |
454 |
} else { |
455 |
throw new BuildException(message); |
456 |
} |
457 |
} |
458 |
|
459 |
triedSources.add(aDest); |
460 |
return true; |
461 |
} |
462 |
|
463 |
private URLConnection openConnection(URL aSource) throws IOException { |
464 |
|
465 |
// set up the URL connection |
466 |
URLConnection connection = aSource.openConnection(); |
467 |
// modify the headers |
468 |
// NB: things like user authentication could go in here too. |
397 |
if (hasTimestamp) { |
469 |
if (hasTimestamp) { |
398 |
connection.setIfModifiedSince(timestamp); |
470 |
connection.setIfModifiedSince(timestamp); |
399 |
} |
471 |
} |
Lines 401-445
Link Here
|
401 |
if (uname != null || pword != null) { |
473 |
if (uname != null || pword != null) { |
402 |
String up = uname + ":" + pword; |
474 |
String up = uname + ":" + pword; |
403 |
String encoding; |
475 |
String encoding; |
404 |
//we do not use the sun impl for portability, |
476 |
// we do not use the sun impl for portability, |
405 |
//and always use our own implementation for consistent |
477 |
// and always use our own implementation for consistent |
406 |
//testing |
478 |
// testing |
407 |
Base64Converter encoder = new Base64Converter(); |
479 |
Base64Converter encoder = new Base64Converter(); |
408 |
encoding = encoder.encode(up.getBytes()); |
480 |
encoding = encoder.encode(up.getBytes()); |
409 |
connection.setRequestProperty ("Authorization", |
481 |
connection.setRequestProperty("Authorization", "Basic " |
410 |
"Basic " + encoding); |
482 |
+ encoding); |
411 |
} |
483 |
} |
412 |
|
484 |
|
413 |
//connect to the remote site (may take some time) |
485 |
if (connection instanceof HttpURLConnection) { |
|
|
486 |
((HttpURLConnection) connection) |
487 |
.setInstanceFollowRedirects(false); |
488 |
} |
489 |
// connect to the remote site (may take some time) |
414 |
connection.connect(); |
490 |
connection.connect(); |
415 |
//next test for a 304 result (HTTP only) |
491 |
|
|
|
492 |
// First check on a 301 / 302 (moved) response |
416 |
if (connection instanceof HttpURLConnection) { |
493 |
if (connection instanceof HttpURLConnection) { |
417 |
HttpURLConnection httpConnection |
494 |
HttpURLConnection httpConnection = (HttpURLConnection) connection; |
418 |
= (HttpURLConnection) connection; |
495 |
// httpConnection.setInstanceFollowRedirects(false); |
|
|
496 |
// httpConnection.setUseCaches(false); |
497 |
int responseCode = httpConnection.getResponseCode(); |
498 |
if (responseCode == HttpURLConnection.HTTP_MOVED_PERM || |
499 |
responseCode == HttpURLConnection.HTTP_MOVED_TEMP) |
500 |
{ |
501 |
String newLocation = httpConnection.getHeaderField("Location"); |
502 |
String message = aSource |
503 |
+ (responseCode == HttpURLConnection.HTTP_MOVED_PERM ? " permanently" |
504 |
: "") + " moved to " + newLocation; |
505 |
log(message, logLevel); |
506 |
URL newURL = new URL(newLocation); |
507 |
if (!redirectionAllowed(aSource, newURL)) |
508 |
{ |
509 |
return null; |
510 |
} |
511 |
return openConnection(newURL); |
512 |
} |
513 |
// next test for a 304 result (HTTP only) |
419 |
long lastModified = httpConnection.getLastModified(); |
514 |
long lastModified = httpConnection.getLastModified(); |
420 |
if (httpConnection.getResponseCode() |
515 |
if (responseCode == HttpURLConnection.HTTP_NOT_MODIFIED |
421 |
== HttpURLConnection.HTTP_NOT_MODIFIED |
516 |
|| (lastModified != 0 && hasTimestamp && timestamp >= lastModified)) { |
422 |
|| (lastModified != 0 && hasTimestamp |
517 |
// not modified so no file download. just return |
423 |
&& timestamp >= lastModified)) { |
518 |
// instead and trace out something so the user |
424 |
//not modified so no file download. just return |
519 |
// doesn't think that the download happened when it |
425 |
//instead and trace out something so the user |
520 |
// didn't |
426 |
//doesn't think that the download happened when it |
|
|
427 |
//didn't |
428 |
log("Not modified - so not downloaded", logLevel); |
521 |
log("Not modified - so not downloaded", logLevel); |
429 |
return false; |
522 |
return null; |
430 |
} |
523 |
} |
431 |
// test for 401 result (HTTP only) |
524 |
// test for 401 result (HTTP only) |
432 |
if (httpConnection.getResponseCode() |
525 |
if (responseCode == HttpURLConnection.HTTP_UNAUTHORIZED) { |
433 |
== HttpURLConnection.HTTP_UNAUTHORIZED) { |
|
|
434 |
String message = "HTTP Authorization failure"; |
526 |
String message = "HTTP Authorization failure"; |
435 |
if (ignoreErrors) { |
527 |
if (ignoreErrors) { |
436 |
log(message, logLevel); |
528 |
log(message, logLevel); |
437 |
return false; |
529 |
return null; |
438 |
} else { |
530 |
} else { |
439 |
throw new BuildException(message); |
531 |
throw new BuildException(message); |
440 |
} |
532 |
} |
441 |
} |
533 |
} |
442 |
|
|
|
443 |
} |
534 |
} |
444 |
|
535 |
|
445 |
//REVISIT: at this point even non HTTP connections may |
536 |
//REVISIT: at this point even non HTTP connections may |
Lines 447-457
Link Here
|
447 |
//the date of the content and skip the write if it is not |
538 |
//the date of the content and skip the write if it is not |
448 |
//newer. Some protocols (FTP) don't include dates, of |
539 |
//newer. Some protocols (FTP) don't include dates, of |
449 |
//course. |
540 |
//course. |
|
|
541 |
return connection; |
542 |
} |
450 |
|
543 |
|
|
|
544 |
private boolean downloadFile() |
545 |
throws FileNotFoundException, IOException { |
451 |
for (int i = 0; i < NUMBER_RETRIES; i++) { |
546 |
for (int i = 0; i < NUMBER_RETRIES; i++) { |
452 |
//this three attempt trick is to get round quirks in different |
547 |
// this three attempt trick is to get round quirks in different |
453 |
//Java implementations. Some of them take a few goes to bind |
548 |
// Java implementations. Some of them take a few goes to bind |
454 |
//property; we ignore the first couple of such failures. |
549 |
// property; we ignore the first couple of such failures. |
455 |
try { |
550 |
try { |
456 |
is = connection.getInputStream(); |
551 |
is = connection.getInputStream(); |
457 |
break; |
552 |
break; |
Lines 464-471
Link Here
|
464 |
if (ignoreErrors) { |
559 |
if (ignoreErrors) { |
465 |
return false; |
560 |
return false; |
466 |
} |
561 |
} |
467 |
throw new BuildException("Can't get " + source + " to " |
562 |
throw new BuildException("Can't get " + source + " to " + dest, |
468 |
+ dest, getLocation()); |
563 |
getLocation()); |
469 |
} |
564 |
} |
470 |
|
565 |
|
471 |
os = new FileOutputStream(dest); |
566 |
os = new FileOutputStream(dest); |
Lines 491-516
Link Here
|
491 |
} |
586 |
} |
492 |
} |
587 |
} |
493 |
progress.endDownload(); |
588 |
progress.endDownload(); |
494 |
|
|
|
495 |
//if (and only if) the use file time option is set, then |
496 |
//the saved file now has its timestamp set to that of the |
497 |
//downloaded file |
498 |
if (useTimestamp) { |
499 |
long remoteTimestamp = connection.getLastModified(); |
500 |
if (verbose) { |
501 |
Date t = new Date(remoteTimestamp); |
502 |
log("last modified = " + t.toString() |
503 |
+ ((remoteTimestamp == 0) |
504 |
? " - using current time instead" |
505 |
: ""), logLevel); |
506 |
} |
507 |
if (remoteTimestamp != 0) { |
508 |
FILE_UTILS.setFileLastModified(dest, remoteTimestamp); |
509 |
} |
510 |
} |
511 |
return true; |
589 |
return true; |
512 |
} |
590 |
} |
513 |
|
591 |
|
|
|
592 |
private void updateTimeStamp() { |
593 |
long remoteTimestamp = connection.getLastModified(); |
594 |
if (verbose) { |
595 |
Date t = new Date(remoteTimestamp); |
596 |
log("last modified = " + t.toString() |
597 |
+ ((remoteTimestamp == 0) |
598 |
? " - using current time instead" |
599 |
: ""), logLevel); |
600 |
} |
601 |
if (remoteTimestamp != 0) { |
602 |
FILE_UTILS.setFileLastModified(dest, remoteTimestamp); |
603 |
} |
604 |
} |
605 |
|
514 |
/** |
606 |
/** |
515 |
* Has the download completed successfully? |
607 |
* Has the download completed successfully? |
516 |
* |
608 |
* |