package org.codehaus.plexus.archiver.zip; /* * The MIT License * * Copyright (c) 2004, The Codehaus * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in * the Software without restriction, including without limitation the rights to * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is furnished to do * so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ import org.apache.commons.compress.archivers.zip.ExtraFieldUtils; import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; import org.apache.commons.compress.archivers.zip.ZipExtraField; import org.apache.commons.compress.archivers.zip.ZipFile; import org.apache.commons.compress.utils.BoundedInputStream; import org.codehaus.plexus.archiver.Archiver; import org.codehaus.plexus.archiver.ArchiverException; import org.codehaus.plexus.archiver.BasePlexusArchiverTest; import org.codehaus.plexus.archiver.UnArchiver; import org.codehaus.plexus.archiver.UnixStat; import org.codehaus.plexus.archiver.util.ArchiveEntryUtils; import org.codehaus.plexus.archiver.util.DefaultArchivedFileSet; import org.codehaus.plexus.archiver.util.DefaultFileSet; import org.codehaus.plexus.archiver.util.Streams; import org.codehaus.plexus.components.io.attributes.Java7FileAttributes; import org.codehaus.plexus.components.io.attributes.PlexusIoResourceAttributeUtils; import org.codehaus.plexus.components.io.attributes.PlexusIoResourceAttributes; import org.codehaus.plexus.components.io.functions.InputStreamTransformer; import org.codehaus.plexus.components.io.resources.PlexusIoFileResourceCollection; import org.codehaus.plexus.components.io.resources.PlexusIoResource; import org.codehaus.plexus.logging.Logger; import org.codehaus.plexus.logging.console.ConsoleLogger; import org.codehaus.plexus.util.FileUtils; import org.codehaus.plexus.util.IOUtil; import org.codehaus.plexus.util.Os; import javax.annotation.Nonnull; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.util.Arrays; import java.util.Date; import java.util.Enumeration; import java.util.Map; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import java.util.zip.ZipOutputStream; /** * @author Emmanuel Venisse * @version $Id$ */ public class ZipArchiverTest extends BasePlexusArchiverTest { private Logger logger; public void setUp() throws Exception { super.setUp(); logger = new ConsoleLogger( Logger.LEVEL_DEBUG, "test" ); } public void testImplicitPermissions() throws IOException { File zipFile = getTestFile( "target/output/zip-with-implicit-dirmode.zip" ); ZipArchiver archiver = getZipArchiver( zipFile ); archiver.setDefaultDirectoryMode( 0777 ); archiver.setDirectoryMode( 0641 ); archiver.setFileMode( 0222 ); archiver.addFile( new File( "pom.xml" ), "fizz/buzz/pom.xml" ); archiver.setDefaultDirectoryMode( 0530 ); archiver.setDirectoryMode( -1 ); // Not forced mode archiver.setFileMode( 0111 ); archiver.addFile( new File( "pom.xml" ), "fazz/bazz/pam.xml" ); archiver.createArchive(); assertTrue( zipFile.exists() ); ZipFile zf = new ZipFile( zipFile ); ZipArchiveEntry fizz = zf.getEntry( "fizz/" ); assertEquals( 040641, fizz.getUnixMode() ); ZipArchiveEntry pom = zf.getEntry( "fizz/buzz/pom.xml" ); assertEquals( 0100222, pom.getUnixMode() ); ZipArchiveEntry fazz = zf.getEntry( "fazz/" ); assertEquals( 040530, fazz.getUnixMode() ); ZipArchiveEntry pam = zf.getEntry( "fazz/bazz/pam.xml" ); assertEquals( 0100111, pam.getUnixMode() ); } public void testOverddidenPermissions() throws IOException { File zipFile = getTestFile( "target/output/zip-with-overriden-modes.zip" ); ZipArchiver archiver = getZipArchiver( zipFile ); archiver.setDefaultDirectoryMode( 0777 ); archiver.setDirectoryMode( 0641 ); archiver.setFileMode( 0777 ); archiver.addDirectory( new File( "src/test/resources/symlinks/src" ) ); archiver.createArchive(); assertTrue( zipFile.exists() ); ZipFile zf = new ZipFile( zipFile ); ZipArchiveEntry fizz = zf.getEntry( "symDir" ); assertTrue( fizz.isUnixSymlink() ); ZipArchiveEntry symR = zf.getEntry( "symR" ); assertTrue( symR.isUnixSymlink() ); } public void testCreateArchiveWithDetectedModes() throws Exception { String[] executablePaths = { "path/to/executable", "path/to/executable.bat" }; String[] confPaths = { "path/to/etc/file", "path/to/etc/file2" }; String[] logPaths = { "path/to/logs/log.txt" }; int exeMode = 0777; int confMode = 0600; int logMode = 0640; if ( Os.isFamily( Os.FAMILY_WINDOWS ) ) { StackTraceElement e = new Throwable().getStackTrace()[0]; System.out.println( "Cannot execute test: " + e.getMethodName() + " on " + System.getProperty( "os.name" ) ); return; } File tmpDir = null; try { tmpDir = File.createTempFile( "zip-with-chmod.", ".dir" ); tmpDir.delete(); tmpDir.mkdirs(); for ( String executablePath : executablePaths ) { writeFile( tmpDir, executablePath, exeMode ); } for ( String confPath : confPaths ) { writeFile( tmpDir, confPath, confMode ); } for ( String logPath : logPaths ) { writeFile( tmpDir, logPath, logMode ); } { Map<String, PlexusIoResourceAttributes> attributesByPath = PlexusIoResourceAttributeUtils.getFileAttributesByPath( tmpDir ); for ( String path : executablePaths ) { PlexusIoResourceAttributes attrs = attributesByPath.get( path ); if ( attrs == null ) { attrs = attributesByPath.get( new File( tmpDir, path ).getAbsolutePath() ); } assertNotNull( attrs ); assertEquals( "Wrong mode for: " + path + "; expected: " + exeMode, exeMode, attrs.getOctalMode() ); } for ( String path : confPaths ) { PlexusIoResourceAttributes attrs = attributesByPath.get( path ); if ( attrs == null ) { attrs = attributesByPath.get( new File( tmpDir, path ).getAbsolutePath() ); } assertNotNull( attrs ); assertEquals( "Wrong mode for: " + path + "; expected: " + confMode, confMode, attrs.getOctalMode() ); } for ( String path : logPaths ) { PlexusIoResourceAttributes attrs = attributesByPath.get( path ); if ( attrs == null ) { attrs = attributesByPath.get( new File( tmpDir, path ).getAbsolutePath() ); } assertNotNull( attrs ); assertEquals( "Wrong mode for: " + path + "; expected: " + logMode, logMode, attrs.getOctalMode() ); } } File zipFile = getTestFile( "target/output/zip-with-modes.zip" ); ZipArchiver archiver = getZipArchiver( zipFile ); archiver.addDirectory( tmpDir ); archiver.createArchive(); assertTrue( zipFile.exists() ); File zipFile2 = getTestFile( "target/output/zip-with-modes-L2.zip" ); archiver = getZipArchiver(); archiver.setDestFile( zipFile2 ); archiver.addArchivedFileSet( zipFile ); archiver.createArchive(); ZipFile zf = new ZipFile( zipFile2 ); for ( String path : executablePaths ) { ZipArchiveEntry ze = zf.getEntry( path ); int mode = ze.getUnixMode() & UnixStat.PERM_MASK; assertEquals( "Wrong mode for: " + path + "; expected: " + exeMode, exeMode, mode ); } for ( String path : confPaths ) { ZipArchiveEntry ze = zf.getEntry( path ); int mode = ze.getUnixMode() & UnixStat.PERM_MASK; assertEquals( "Wrong mode for: " + path + "; expected: " + confMode, confMode, mode ); } for ( String path : logPaths ) { ZipArchiveEntry ze = zf.getEntry( path ); int mode = ze.getUnixMode() & UnixStat.PERM_MASK; assertEquals( "Wrong mode for: " + path + "; expected: " + logMode, logMode, mode ); } } finally { if ( tmpDir != null && tmpDir.exists() ) { try { FileUtils.forceDelete( tmpDir ); } catch ( IOException e ) { e.printStackTrace(); } } } } private ZipArchiver getZipArchiver() { try { return (ZipArchiver) lookup( Archiver.ROLE, "zip" ); } catch ( Exception e ) { throw new RuntimeException( e ); } } private ZipArchiver getZipArchiver( File destFile ) { final ZipArchiver zipArchiver = getZipArchiver(); zipArchiver.setDestFile( destFile ); return zipArchiver; } private ZipUnArchiver getZipUnArchiver( File testJar ) throws Exception { ZipUnArchiver zu = (ZipUnArchiver) lookup( UnArchiver.ROLE, "zip" ); zu.setSourceFile( testJar ); return zu; } private void writeFile( File dir, String fname, int mode ) throws IOException, ArchiverException { File file = new File( dir, fname ); FileWriter writer = null; try { if ( file.getParentFile() != null ) { file.getParentFile().mkdirs(); } writer = new FileWriter( file ); writer.write( "This is a test file." ); } finally { IOUtil.close( writer ); } ArchiveEntryUtils.chmod( file, mode, logger, false ); } public void testCreateArchive() throws Exception { ZipArchiver archiver = newArchiver( "archive1.zip" ); createArchive( archiver ); } public void testAddArchivedFileSet() throws Exception { File toBeAdded = new File( "src/test/resources/test.zip" ); DefaultArchivedFileSet sfd = DefaultArchivedFileSet.archivedFileSet( toBeAdded ); File zipFIle = getTestFile( "target/output/withZip.zip" ); final ZipArchiver zipArchiver = getZipArchiver( zipFIle ); InputStreamTransformer is = new InputStreamTransformer() { @Nonnull public InputStream transform( @Nonnull PlexusIoResource resource, @Nonnull InputStream inputStream ) throws IOException { return new BoundedInputStream( inputStream, 3 ); } }; sfd.setStreamTransformer( is ); zipArchiver.addArchivedFileSet( sfd ); zipArchiver.createArchive(); final ZipUnArchiver zipUnArchiver = getZipUnArchiver( zipFIle ); File destFile = new File( "target/output/withZip" ); destFile.mkdirs(); zipUnArchiver.setDestFile( destFile ); zipUnArchiver.extract(); File a3byteFile = new File( destFile, "Users/kristian/lsrc/plexus/plexus-archiver/src/main/java/org/codehaus/plexus/archiver/zip/ZipArchiver.java" ); assertTrue( a3byteFile.exists() ); assertTrue( a3byteFile.length() == 3 ); } public void testCreateArchiveWithStreamTransformer() throws IOException { InputStreamTransformer is = new InputStreamTransformer() { @Nonnull public InputStream transform( @Nonnull PlexusIoResource resource, @Nonnull InputStream inputStream ) throws IOException { return new BoundedInputStream( inputStream, 3 ); } }; final ZipArchiver zipArchiver = getZipArchiver( getTestFile( "target/output/all3bytes.zip" ) ); File zipFIle = new File( "src/test/resources/test.zip" ); DefaultArchivedFileSet afs = new DefaultArchivedFileSet( zipFIle ); afs.setStreamTransformer( is ); afs.setPrefix( "azip/" ); zipArchiver.addArchivedFileSet( afs ); DefaultFileSet dfs = new DefaultFileSet( new File( "src/test/resources/mjar179" ) ); dfs.setStreamTransformer( is ); dfs.setPrefix( "mj179/" ); zipArchiver.addFileSet( dfs ); PlexusIoFileResourceCollection files = new PlexusIoFileResourceCollection(); files.setBaseDir( new File( "src/test/resources" ) ); files.setStreamTransformer( is ); files.setPrefix( "plexus/" ); zipArchiver.addResources( files ); zipArchiver.createArchive(); } private ZipArchiver newArchiver( String name ) throws Exception { ZipArchiver archiver = getZipArchiver( getTestFile( "target/output/" + name ) ); archiver.setFileMode( 0640 ); archiver.addFile( getTestFile( "src/test/resources/manifests/manifest1.mf" ), "one.txt" ); archiver.addFile( getTestFile( "src/test/resources/manifests/manifest2.mf" ), "two.txt", 0664 ); // reset default file mode for files included from now on archiver.setFileMode( 0400 ); archiver.setDirectoryMode( 0777 ); archiver.addDirectory( getTestFile( "src/test/resources/world-writable/" ), "worldwritable/" ); archiver.setDirectoryMode( 0070 ); archiver.addDirectory( getTestFile( "src/test/resources/group-writable/" ), "groupwritable/" ); archiver.setDirectoryMode( 0500 ); archiver.setFileMode( 0400 ); archiver.addDirectory( getTestFile( "src" ) ); return archiver; } private void createArchive( ZipArchiver archiver ) throws ArchiverException, IOException { archiver.createArchive(); ZipFile zf = new ZipFile( archiver.getDestFile() ); Enumeration e = zf.getEntries(); while ( e.hasMoreElements() ) { ZipArchiveEntry ze = (ZipArchiveEntry) e.nextElement(); if ( ze.isDirectory() ) { if ( ze.getName().startsWith( "worldwritable" ) ) { assertEquals( 0777, UnixStat.PERM_MASK & ze.getUnixMode() ); } else if ( ze.getName().startsWith( "groupwritable" ) ) { assertEquals( 0070, UnixStat.PERM_MASK & ze.getUnixMode() ); } else { assertEquals( 0500, UnixStat.PERM_MASK & ze.getUnixMode() ); } } else { if ( ze.getName().equals( "one.txt" ) ) { assertEquals( 0640, UnixStat.PERM_MASK & ze.getUnixMode() ); } else if ( ze.getName().equals( "two.txt" ) ) { assertEquals( 0664, UnixStat.PERM_MASK & ze.getUnixMode() ); } else if ( ze.isUnixSymlink() ) { // assertEquals( ze.getName(), 0500, UnixStat.PERM_MASK & ze.getUnixMode() ); } else { assertEquals( 0400, UnixStat.PERM_MASK & ze.getUnixMode() ); } } } } public void testSymlinkZip() throws Exception { final File zipFile = getTestFile( "target/output/pasymlinks.zip" ); final ZipArchiver zipArchiver = getZipArchiver( zipFile ); PlexusIoFileResourceCollection files = new PlexusIoFileResourceCollection(); files.setFollowingSymLinks( false ); files.setBaseDir( new File( "src/test/resources/symlinks" ) ); files.setPrefix( "plexus/" ); zipArchiver.addResources( files ); zipArchiver.createArchive(); final File output = getTestFile( "target/output/unzipped" ); output.mkdirs(); final ZipUnArchiver zipUnArchiver = getZipUnArchiver( zipFile ); zipUnArchiver.setDestFile( output ); zipUnArchiver.extract(); File symDir = new File( "target/output/unzipped/plexus/src/symDir" ); PlexusIoResourceAttributes fa = Java7FileAttributes.uncached( symDir ); assertTrue( fa.isSymbolicLink() ); } @SuppressWarnings( "ResultOfMethodCallIgnored" ) public void testSymlinkFileSet() throws Exception { final File zipFile = getTestFile( "target/output/pasymlinks-fileset.zip" ); final ZipArchiver zipArchiver = getZipArchiver( zipFile ); final DefaultFileSet fs = new DefaultFileSet(); fs.setPrefix( "bzz/" ); fs.setDirectory( new File( "src/test/resources/symlinks/src" ) ); zipArchiver.addFileSet( fs ); zipArchiver.createArchive(); final File output = getTestFile( "target/output/unzipped/symlFs" ); output.mkdirs(); final ZipUnArchiver zipUnArchiver = getZipUnArchiver( zipFile ); zipUnArchiver.setDestFile( output ); zipUnArchiver.extract(); File symDir = new File( output, "bzz/symDir" ); PlexusIoResourceAttributes fa = Java7FileAttributes.uncached( symDir ); assertTrue( fa.isSymbolicLink() ); } /* */ public void testForced() throws Exception { ZipArchiver archiver = newArchiver( "archive2.zip" ); assertTrue( archiver.isForced() ); File f = archiver.getDestFile(); if ( f.exists() ) { FileUtils.fileDelete( f.getPath() ); } assertFalse( f.exists() ); createArchive( archiver ); long l1 = f.lastModified(); assertTrue( f.exists() ); archiver = newArchiver( "archive2.zip" ); waitUntilNewTimestamp( archiver.getDestFile(), l1 ); createArchive( archiver ); long l2 = f.lastModified(); assertTrue( f.exists() ); assertTrue( l2 > l1 ); archiver = newArchiver( "archive2.zip" ); assertTrue( archiver.isSupportingForced() ); archiver.setForced( false ); assertFalse( archiver.isForced() ); createArchive( archiver ); long l3 = f.lastModified(); assertTrue( f.exists() ); assertEquals( l2, l3 ); } // Used to investigate extrafields public void testLookAtExtraZipFields_from_macos() throws IOException { InputStream fis = Streams.fileInputStream( new File( "src/test/resources/zip-timestamp/macOsZipFile.zip" ) ); ZipInputStream zis = new ZipInputStream( fis ); final java.util.zip.ZipEntry evenEntry = zis.getNextEntry(); final ZipExtraField[] parse = ExtraFieldUtils.parse( evenEntry.getExtra() ); System.out.println( Arrays.asList( parse ) ); final java.util.zip.ZipEntry oddEntry = zis.getNextEntry(); System.out.println( Arrays.asList( ExtraFieldUtils.parse( oddEntry.getExtra() ) ) ); System.out.println( "oddEntry.getTime() = " + new Date( oddEntry.getTime() ).toString() ); System.out.println( "oddEntry.getName() = " + oddEntry.getName() ); System.out.println( "new String(oddEntry.getExtra()) = " + new String( oddEntry.getExtra() ) ); System.out.println( "evenEntry.getName() = " + evenEntry.getName() ); System.out.println( "evenEntry.getTime() = " + new Date( evenEntry.getTime() ).toString() ); System.out.println( "new String(evenEntry.getExtra()) = " + new String( evenEntry.getExtra() ) ); } // Used to investigate date roundtrip behaviour across zip versions public void testZipStuff() throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ZipOutputStream zos = new ZipOutputStream( baos ); // name the file inside the zip file final File oddFile = new File( "src/test/resources/zip-timestamp/file-with-odd-time.txt" ); final File evenFile = new File( "src/test/resources/zip-timestamp/file-with-even-time.txt" ); final ZipEntry oddZe = new ZipEntry( oddFile.getName() ); oddZe.setTime( oddFile.lastModified() ); zos.putNextEntry( oddZe ); final ZipEntry evenZe = new ZipEntry( evenFile.getName() ); evenZe.setTime( evenFile.lastModified() ); zos.putNextEntry( evenZe ); zos.close(); ByteArrayInputStream bais = new ByteArrayInputStream( baos.toByteArray() ); ZipInputStream zipInputStream = new ZipInputStream( bais ); final java.util.zip.ZipEntry oddEntry = zipInputStream.getNextEntry(); System.out.println( "oddEntry.getTime() = " + new Date( oddEntry.getTime() ).toString() ); System.out.println( "oddEntry.getName() = " + oddEntry.getName() ); final java.util.zip.ZipEntry evenEntry = zipInputStream.getNextEntry(); System.out.println( "evenEntry.getName() = " + evenEntry.getName() ); System.out.println( "evenEntry.getTime() = " + new Date( evenEntry.getTime() ).toString() ); } public void notestJustThatOne() throws Exception { final File srcDir = new File( "src" ); String[] inc = { "test/java/org/codehaus/plexus/archiver/zip/ZipShortTest.java" }; final File zipFile = new File( "target/output/zz1.zip" ); final File zipFile2 = new File( "target/output/zz2.zip" ); ZipArchiver zipArchiver2 = getZipArchiver( zipFile2 ); // Bugbug: This does not work on 1.8....? zipArchiver2.addArchivedFileSet( zipFile ); FileUtils.removePath( zipFile2.getPath() ); zipArchiver2.createArchive(); } public void testCreateResourceCollection() throws Exception { final File srcDir = new File( "src" ); final File zipFile = new File( "target/output/src.zip" ); ZipArchiver zipArchiver = getZipArchiver( zipFile ); zipArchiver.addDirectory( srcDir, null, FileUtils.getDefaultExcludes() ); zipArchiver.setEncoding( "UTF-8" ); FileUtils.removePath( zipFile.getPath() ); zipArchiver.createArchive(); final File zipFile2 = new File( "target/output/src2.zip" ); ZipArchiver zipArchiver2 = getZipArchiver( zipFile2 ); zipArchiver2.addArchivedFileSet( zipFile, "prfx/" ); zipArchiver2.setEncoding( "UTF-8" ); FileUtils.removePath( zipFile2.getPath() ); zipArchiver2.createArchive(); final ZipFile cmp1 = new ZipFile( zipFile ); final ZipFile cmp2 = new ZipFile( zipFile2 ); ArchiveFileComparator.assertEquals( cmp1, cmp2, "prfx/" ); cmp1.close(); cmp2.close(); } public void testDefaultUTF8() throws IOException { final ZipArchiver zipArchiver = getZipArchiver( new File( "target/output/utf8-default.zip" ) ); zipArchiver.addDirectory( new File( "src/test/resources/miscUtf8" ) ); zipArchiver.createArchive(); } public void testDefaultUTF8withUTF8() throws IOException { final ZipArchiver zipArchiver = getZipArchiver( new File( "target/output/utf8-with_utf.zip" ) ); zipArchiver.setEncoding( "UTF-8" ); zipArchiver.addDirectory( new File( "src/test/resources/miscUtf8" ) ); zipArchiver.createArchive(); } }