blob: 0e5296c6a2641eb860ac98717dc10b266555ab06 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.tools.ant.taskdefs;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.filters.ChainableReader;
import org.apache.tools.ant.types.RedirectorElement;
import org.apache.tools.ant.types.FilterChain;
import org.apache.tools.ant.types.Path;
import org.apache.tools.ant.types.resources.FileResource;
import java.util.Iterator;
import java.io.File;
import java.io.Reader;
import java.io.IOException;
/**
* JAR verification task.
* For every JAR passed in, we fork jarsigner to verify
* that it is correctly signed. This is more rigorous than just checking for
* the existence of a signature; the entire certification chain is tested
* @since Ant 1.7
*/
public class VerifyJar extends AbstractJarSignerTask {
/**
* no file message {@value}
*/
public static final String ERROR_NO_FILE = "Not found :";
/**
* The string we look for in the text to indicate direct verification
*/
private static final String VERIFIED_TEXT = "jar verified.";
/**
* certification flag
*/
private boolean certificates = false;
private BufferingOutputFilter outputCache = new BufferingOutputFilter();
/** Error output if there is a failure to verify the jar. */
public static final String ERROR_NO_VERIFY = "Failed to verify ";
/**
* Ask for certificate information to be printed
* @param certificates if true print certificates.
*/
public void setCertificates(boolean certificates) {
this.certificates = certificates;
}
/**
* verify our jar files
* @throws BuildException on error.
*/
public void execute() throws BuildException {
//validation logic
final boolean hasJar = jar != null;
if (!hasJar && !hasResources()) {
throw new BuildException(ERROR_NO_SOURCE);
}
beginExecution();
//patch the redirector to save output to a file
RedirectorElement redirector = getRedirector();
redirector.setAlwaysLog(true);
FilterChain outputFilterChain = redirector.createOutputFilterChain();
outputFilterChain.add(outputCache);
try {
Path sources = createUnifiedSourcePath();
Iterator iter = sources.iterator();
while (iter.hasNext()) {
FileResource fr = (FileResource) iter.next();
verifyOneJar(fr.getFile());
}
} finally {
endExecution();
}
}
/**
* verify a JAR.
* @param jar the jar to verify.
* @throws BuildException if the file could not be verified
*/
private void verifyOneJar(File jar) {
if (!jar.exists()) {
throw new BuildException(ERROR_NO_FILE + jar);
}
final ExecTask cmd = createJarSigner();
setCommonOptions(cmd);
bindToKeystore(cmd);
//verify special operations
addValue(cmd, "-verify");
if (certificates) {
addValue(cmd, "-certs");
}
//JAR is required
addValue(cmd, jar.getPath());
log("Verifying JAR: " + jar.getAbsolutePath());
outputCache.clear();
BuildException ex = null;
try {
cmd.execute();
} catch (BuildException e) {
ex = e;
}
String results = outputCache.toString();
//deal with jdk1.4.2 bug:
if (ex != null) {
if (results.indexOf("zip file closed") >= 0) {
log("You are running " + JARSIGNER_COMMAND + " against a JVM with"
+ " a known bug that manifests as an IllegalStateException.",
Project.MSG_WARN);
} else {
throw ex;
}
}
if (results.indexOf(VERIFIED_TEXT) < 0) {
throw new BuildException(ERROR_NO_VERIFY + jar);
}
}
/**
* we are not thread safe here. Do not use on multiple threads at the same time.
*/
private static class BufferingOutputFilter implements ChainableReader {
private BufferingOutputFilterReader buffer;
public Reader chain(Reader rdr) {
buffer = new BufferingOutputFilterReader(rdr);
return buffer;
}
public String toString() {
return buffer.toString();
}
public void clear() {
if (buffer != null) {
buffer.clear();
}
}
}
/**
* catch the output of the buffer
*/
private static class BufferingOutputFilterReader extends Reader {
private Reader next;
private StringBuffer buffer = new StringBuffer();
public BufferingOutputFilterReader(Reader next) {
this.next = next;
}
public int read(char[] cbuf, int off, int len) throws IOException {
//hand down
int result = next.read(cbuf, off, len);
//cache
buffer.append(cbuf, off, len);
//return
return result;
}
public void close() throws IOException {
next.close();
}
public String toString() {
return buffer.toString();
}
public void clear() {
buffer = new StringBuffer();
}
}
}