/*
* Zed Attack Proxy (ZAP) and its related class files.
*
* ZAP is an HTTP/HTTPS proxy for assessing web application security.
*
* Copyright 2012 ZAP development team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.zaproxy.zap.extension.autoupdate;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.Proxy;
import java.net.URL;
import java.util.Date;
import org.apache.log4j.Logger;
import org.zaproxy.zap.utils.HashUtils;
public class Downloader extends Thread {
private URL url;
private Proxy proxy;
private File targetFile;
private Exception exception = null;
private long size = 0;
private boolean complete = false;
private Date started = null;
private Date finished = null;
private boolean cancelDownload = false;
private String hash = null;
private boolean validated = false;
private static final Logger logger = Logger.getLogger(Downloader.class);
public Downloader(URL url, Proxy proxy, File targetFile, String hash) {
this (url, proxy, targetFile, 0, hash);
}
public Downloader(URL url, Proxy proxy, File targetFile, long size, String hash) {
super();
this.url = url;
this.proxy = proxy;
this.targetFile = targetFile;
this.size = size;
this.hash = hash;
}
@Override
public void run() {
this.started = new Date();
if (hash != null) {
if (hash.indexOf(":") > 0) {
downloadFile();
if (!cancelDownload) {
validateHashDownload();
}
} else {
logger.debug("Not downloading file, hash field does not have valid content (\"<ALGORITHM>:<HASH>\"): " + hash);
}
} else {
logger.debug("Not downloading file, does not have a hash: " + url);
}
this.complete = true;
this.finished = new Date();
if (cancelDownload) {
this.targetFile.delete();
}
}
private void downloadFile() {
BufferedInputStream in = null;
FileOutputStream out = null;
try {
/*
* The following code may be more efficient, but doesnt give us the option
* of cancelling downloads.
*
* ReadableByteChannel rbc = Channels.newChannel(url.openStream());
* FileOutputStream fos = new FileOutputStream(targetFile);
* fos.getChannel().transferFrom(rbc, 0, 1 << 24);
*/
// XXX Change to use HttpClient instead of URL to download the file. The java.net.Authenticator is shared by all
// the URLConnection, it may be changed by 3rd party add-ons/libraries and it can't be set on a single connection
// (see bug 4941958 [1]) in which case the authentication will not succeed (hence the file will not be downloaded).
//
// [1] http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4941958
in = new BufferedInputStream(url.openConnection(proxy).getInputStream());
out = new FileOutputStream(this.targetFile);
byte[] data = new byte[1024];
int count;
while(! cancelDownload && (count = in.read(data,0,1024)) != -1) {
out.write(data, 0, count);
}
} catch (Exception e) {
this.exception = e;
} finally {
try {
if (in != null) {
in.close();
}
if (out != null) {
out.close();
}
} catch (IOException e) {
// Ignore
}
}
}
private void validateHashDownload() {
try {
String algorithm = hash.substring(0, hash.indexOf(":"));
String hashValue = hash.substring(hash.indexOf(":") + 1);
String realHash = HashUtils.getHash(targetFile, algorithm);
if (realHash.equalsIgnoreCase(hashValue)) {
validated = true;
} else {
logger.debug("Wrong hash - expected " + hashValue + " got " + realHash);
}
} catch (Exception e) {
// Ignore - we default to unvalidated
logger.debug("Error checking hash", e);
}
}
public void cancelDownload() {
this.cancelDownload = true;
if (complete && this.targetFile.exists()) {
this.targetFile.delete();
}
}
public Exception getException() {
return exception;
}
public URL getUrl() {
return url;
}
public File getTargetFile() {
return targetFile;
}
public int getProgressPercent() {
if (complete) {
return 100;
}
if (this.size == 0) {
return 0;
}
return (int)(this.targetFile.length() * 100 / this.size);
}
public Date getStarted() {
return started;
}
public Date getFinished() {
return finished;
}
public String getHash() {
return hash;
}
public boolean isValidated() {
return validated;
}
}