package org.codehaus.mojo.webstart; /* * 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. */ import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.project.MavenProjectHelper; import org.codehaus.plexus.archiver.ArchiverException; import org.codehaus.plexus.archiver.UnArchiver; import org.codehaus.plexus.archiver.jar.JarArchiver; import org.codehaus.plexus.archiver.manager.ArchiverManager; import org.codehaus.plexus.archiver.manager.NoSuchArchiverException; import org.codehaus.plexus.util.FileUtils; import java.io.File; import java.io.FileFilter; import java.io.IOException; import java.util.Arrays; import java.util.List; /** * Unsigns a JAR, removing signatures. * * This code will hopefully be moved into the jar plugin when stable enough. * * @author <a href="mailto:jerome@coffeebreaks.org">Jerome Lacoste</a> * @author <a href="mailto:andrius@pivotcapital.com">Andrius Ĺ abanas</a> * @version $Id$ * @goal unsign * @phase package * @requiresProject * @todo refactor the common code with javadoc plugin */ public class JarUnsignMojo extends AbstractMojo { /** * Set this to <code>true</code> to disable signing. * Useful to speed up build process in development environment. * * @parameter expression="${maven.jar.unsign.skip}" default-value="false" */ private boolean skip; /** * The directory location used for temporary storage of files used by this mojo. * @parameter expression="${tempdir}" default-value="${basedir}" * @required */ private File tempDirectory; /** * Path of the jar to unsign. When specified, the finalName is ignored. * * @parameter alias="jarpath" * default-value="${project.build.directory}/${project.build.finalName}.${project.packaging}" */ private File jarPath; /** * See <a href="http://java.sun.com/j2se/1.4.2/docs/tooldocs/windows/jarsigner.html#Options">options</a>. * * Not specifying this argument will unsign the jar in-place (your original jar is going to be overwritten). * * @parameter expression="${unsignedjar}" */ // private File unsignedjar; /** * Automatically verify a jar after unsigning it. * <p/> * See <a href="http://java.sun.com/j2se/1.4.2/docs/tooldocs/windows/jarsigner.html#Options">options</a>. * * @parameter expression="${verify}" default-value="false" */ // private boolean verify; /** * Enable verbose * See <a href="http://java.sun.com/j2se/1.4.2/docs/tooldocs/windows/jarsigner.html#Options">options</a>. * * @parameter expression="${verbose}" default-value="false" */ private boolean verbose; /** * @component */ private MavenProjectHelper projectHelper; /** * To look up Archiver/UnArchiver implementations * * @component * @required */ protected ArchiverManager archiverManager; private static final String[] EXT_ARRAY = { "DSA", "RSA", "SF" }; private FileFilter removeSignatureFileFilter = new FileFilter() { private final List extToRemove = Arrays.asList( EXT_ARRAY ); public boolean accept( File file ) { String extension = FileUtils.getExtension( file.getAbsolutePath() ); return ( extToRemove.contains( extension ) ); } }; public void execute() throws MojoExecutionException { if ( skip ) { getLog().info( "Skipping JAR unsigning for file: " + jarPath.getAbsolutePath() ); return; } File jarFile = this.jarPath; File tempDirParent = this.tempDirectory; String archiveExt = FileUtils.getExtension( jarFile.getAbsolutePath() ).toLowerCase(); // create temp dir File tempDir = new File( tempDirParent, jarFile.getName() ); if ( !tempDir.mkdirs() ) { throw new MojoExecutionException( "Error creating temporary directory: " + tempDir ); } // FIXME we probably want to be more security conservative here. // it's very easy to guess where the directory will be and possible // to access/change its contents before the file is rejared.. // extract jar into temporary directory try { UnArchiver unArchiver = this.archiverManager.getUnArchiver( archiveExt ); unArchiver.setSourceFile( jarFile ); unArchiver.setDestDirectory( tempDir ); unArchiver.extract(); } catch ( ArchiverException ex ) { throw new MojoExecutionException( "Error unpacking file: " + jarFile + "to: " + tempDir, ex ); } catch ( NoSuchArchiverException ex ) { throw new MojoExecutionException( "Error acquiring unarchiver for extension: " + archiveExt, ex ); } // create and check META-INF directory File metaInf = new File( tempDir, "META-INF" ); if ( !metaInf.isDirectory() ) { verboseLog( "META-INT dir not found : nothing to do for file: " + jarPath.getAbsolutePath() ); return; } // filter signature files and remove them File[] filesToRemove = metaInf.listFiles( this.removeSignatureFileFilter ); if ( filesToRemove.length == 0 ) { verboseLog( "no files match " + toString( EXT_ARRAY ) + " : nothing to do for file: " + jarPath.getAbsolutePath() ); return; } for ( int i = 0; i < filesToRemove.length; i++ ) { if ( !filesToRemove[i].delete() ) { throw new MojoExecutionException( "Error removing signature file: " + filesToRemove[i] ); } verboseLog( "remove file :" + filesToRemove[i] ); } // recreate archive try { JarArchiver jarArchiver = (JarArchiver) this.archiverManager.getArchiver( "jar" ); jarArchiver.setUpdateMode( false ); jarArchiver.addDirectory( tempDir ); jarArchiver.setDestFile( jarFile ); jarArchiver.createArchive(); } catch ( ArchiverException ex ) { throw new MojoExecutionException( "Error packing directory: " + tempDir + "to: " + jarFile, ex ); } catch ( IOException ex ) { throw new MojoExecutionException( "Error packing directory: " + tempDir + "to: " + jarFile, ex ); } catch ( NoSuchArchiverException ex ) { throw new MojoExecutionException( "Error acquiring archiver for extension: jar", ex ); } try { FileUtils.deleteDirectory( tempDir ); } catch ( IOException ex ) { throw new MojoExecutionException( "Error cleaning up temporary directory file: " + tempDir, ex ); } } /** * Log as info when verbose or info is enabled, as debug otherwise. */ protected void verboseLog( String msg ) { infoOrDebug( isVerbose() || getLog().isInfoEnabled(), msg ); } /** if info is true, log as info(), otherwise as debug() */ private void infoOrDebug( boolean info , String msg ) { if ( info ) { getLog().info( msg ); } else { getLog().debug( msg ); } } public void setTempDir( File tempDirectory ) { this.tempDirectory = tempDirectory; } /* public void setBasedir( File basedir ) { this.basedir = basedir; } */ public void setJarPath( File jarPath ) { this.jarPath = jarPath; } /* public void setUnsignedJar( File unsignedjar ) { this.unsignedjar = unsignedjar; } */ public boolean isVerbose() { return verbose; } public void setVerbose( boolean verbose ) { this.verbose = verbose; } public void setArchiverManager( ArchiverManager archiverManager ) { this.archiverManager = archiverManager; } /* public void setVerify( boolean verify ) { this.verify = verify; } */ public String toString( String[] items ) { StringBuffer back = new StringBuffer( "{" ); for ( int i = 0; i < items.length; i++ ) { if ( i != 0 ) { back.append( ", " ); } back.append( '"' ).append( items[i] ).append( '"' ); } back.append( "}" ); return back.toString(); } }