first commit

This commit is contained in:
MaddoScientisto 2026-03-14 20:04:39 +01:00
commit 4d332ef662
27586 changed files with 3281783 additions and 0 deletions

View file

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
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.

View file

@ -0,0 +1,52 @@
Manifest-Version: 1.0
Export-Package: org.apache.commons.compress.archivers;version="1.8.1",
org.apache.commons.compress.archivers.ar;version="1.8.1",org.apache.c
ommons.compress.archivers.arj;version="1.8.1",org.apache.commons.comp
ress.archivers.cpio;version="1.8.1",org.apache.commons.compress.archi
vers.dump;version="1.8.1",org.apache.commons.compress.archivers.jar;v
ersion="1.8.1",org.apache.commons.compress.archivers.sevenz;version="
1.8.1",org.apache.commons.compress.archivers.tar;version="1.8.1",org.
apache.commons.compress.archivers.zip;version="1.8.1",org.apache.comm
ons.compress.changes;version="1.8.1",org.apache.commons.compress.comp
ressors;version="1.8.1",org.apache.commons.compress.compressors.bzip2
;version="1.8.1",org.apache.commons.compress.compressors.gzip;version
="1.8.1",org.apache.commons.compress.compressors.lzma;version="1.8.1"
,org.apache.commons.compress.compressors.pack200;version="1.8.1",org.
apache.commons.compress.compressors.snappy;version="1.8.1",org.apache
.commons.compress.compressors.xz;version="1.8.1",org.apache.commons.c
ompress.compressors.z;version="1.8.1",org.apache.commons.compress.com
pressors.z._internal_;version="1.8.1",org.apache.commons.compress.uti
ls;version="1.8.1"
Implementation-Title: Apache Commons Compress
Built-By: bodewig
Tool: Bnd-2.1.0.20130426-122213
Implementation-Vendor: The Apache Software Foundation
Implementation-Vendor-Id: org.apache
Specification-Title: Apache Commons Compress
Bundle-License: http://www.apache.org/licenses/LICENSE-2.0.txt
Bundle-SymbolicName: org.apache.commons.compress
Extension-Name: org.apache.commons.compress
X-Compile-Target-JDK: 1.5
Implementation-Version: 1.8.1
Specification-Vendor: The Apache Software Foundation
Bundle-Name: Apache Commons Compress
Created-By: Apache Maven Bundle Plugin
X-Compile-Source-JDK: 1.5
Bundle-Vendor: The Apache Software Foundation
Build-Jdk: 1.6.0_31
Bundle-Version: 1.8.1
Bnd-LastModified: 1399661128482
Bundle-ManifestVersion: 2
Bundle-Description: Apache Commons Compress software defines an API fo
r working with compression and archive formats.These include: bzip2,
gzip, pack200, lzma, xz, Snappy, traditional Unix Compress and ar, cp
io, jar, tar, zip, dump, 7z, arj.
Bundle-DocURL: http://commons.apache.org/proper/commons-compress/
Import-Package: org.tukaani.xz;resolution:=optional
Include-Resource: META-INF/LICENSE.txt=LICENSE.txt,META-INF/NOTICE.txt
=NOTICE.txt
Specification-Version: 1.8.1
Implementation-Build: trunk@r1593589; 2014-05-09 20:44:50+0200
Main-Class: org.apache.commons.compress.archivers.Lister
Archiver-Version: Plexus Archiver

View file

@ -0,0 +1,11 @@
Apache Commons Compress
Copyright 2002-2014 The Apache Software Foundation
This product includes software developed at
The Apache Software Foundation (http://www.apache.org/).
The files in the package org.apache.commons.compress.archivers.sevenz
were derived from the LZMA SDK, version 9.20 (C/ and CPP/7zip/),
which has been placed in the public domain:
"LZMA SDK is placed in the public domain." (http://www.7-zip.org/sdk.html)

View file

@ -0,0 +1,5 @@
#Generated by Maven
#Fri May 09 20:47:40 CEST 2014
version=1.8.1
groupId=org.apache.commons
artifactId=commons-compress

View file

@ -0,0 +1,342 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.apache.commons</groupId>
<artifactId>commons-parent</artifactId>
<version>33</version>
</parent>
<groupId>org.apache.commons</groupId>
<artifactId>commons-compress</artifactId>
<version>1.8.1</version>
<name>Apache Commons Compress</name>
<url>http://commons.apache.org/proper/commons-compress/</url>
<!-- The description is not indented to make it look better in the release notes -->
<description>
Apache Commons Compress software defines an API for working with compression and archive formats.
These include: bzip2, gzip, pack200, lzma, xz, Snappy, traditional Unix Compress and ar, cpio, jar, tar, zip, dump, 7z, arj.
</description>
<properties>
<maven.compiler.source>1.5</maven.compiler.source>
<maven.compiler.target>1.5</maven.compiler.target>
<commons.componentid>compress</commons.componentid>
<commons.jira.id>COMPRESS</commons.jira.id>
<commons.jira.pid>12310904</commons.jira.pid>
<!-- configuration bits for cutting a release candidate -->
<commons.release.version>${project.version}</commons.release.version>
<commons.rc.version>RC1</commons.rc.version>
</properties>
<issueManagement>
<system>jira</system>
<url>http://issues.apache.org/jira/browse/COMPRESS</url>
</issueManagement>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.tukaani</groupId>
<artifactId>xz</artifactId>
<version>1.5</version>
<optional>true</optional>
</dependency>
</dependencies>
<developers>
<developer>
<name>Torsten Curdt</name>
<id>tcurdt</id>
<email>tcurdt at apache.org</email>
</developer>
<developer>
<name>Stefan Bodewig</name>
<id>bodewig</id>
<email>bodewig at apache.org</email>
</developer>
<developer>
<name>Sebastian Bazley</name>
<id>sebb</id>
<email>sebb at apache.org</email>
</developer>
<developer>
<name>Christian Grobmeier</name>
<id>grobmeier</id>
<email>grobmeier at apache.org</email>
</developer>
<developer>
<name>Julius Davies</name>
<id>julius</id>
<email>julius at apache.org</email>
</developer>
<developer>
<name>Damjan Jovanovic</name>
<id>damjan</id>
<email>damjan at apache.org</email>
</developer>
<developer>
<name>Emmanuel Bourg</name>
<id>ebourg</id>
<email>ebourg at apache.org</email>
</developer>
</developers>
<contributors>
<contributor>
<name>Wolfgang Glas</name>
<email>wolfgang.glas at ev-i.at</email>
</contributor>
<contributor>
<name>Christian Kohlschütte</name>
<email>ck@newsclub.de</email>
</contributor>
<contributor>
<name>Bear Giles</name>
<email>bgiles@coyotesong.com</email>
</contributor>
<contributor>
<name>Michael Kuss</name>
<email>mail at michael minus kuss.de</email>
</contributor>
<contributor>
<name>Lasse Collin</name>
<email>lasse.collin@tukaani.org</email>
</contributor>
<contributor>
<name>John Kodis</name>
</contributor>
<contributor>
<name>BELUGA BEHR</name>
</contributor>
</contributors>
<scm>
<connection>scm:svn:http://svn.apache.org/repos/asf/commons/proper/compress/tags/COMPRESS-1.8.1</connection>
<developerConnection>scm:svn:https://svn.apache.org/repos/asf/commons/proper/compress/tags/COMPRESS-1.8.1</developerConnection>
<url>http://svn.apache.org/repos/asf/commons/proper/compress/tags/COMPRESS-1.8.1</url>
</scm>
<build>
<plugins>
<plugin>
<!-- create the source and binary assemblies -->
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<descriptors>
<descriptor>src/main/assembly/bin.xml</descriptor>
<descriptor>src/main/assembly/src.xml</descriptor>
</descriptors>
<tarLongFileMode>gnu</tarLongFileMode>
</configuration>
</plugin>
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifestEntries>
<Main-Class>org.apache.commons.compress.archivers.Lister</Main-Class>
<Extension-Name>org.apache.commons.compress</Extension-Name>
</manifestEntries>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<configuration>
<instructions>
<Import-Package>org.tukaani.xz;resolution:=optional</Import-Package>
</instructions>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-scm-publish-plugin</artifactId>
<configuration>
<ignorePathsToDelete>
<ignorePathToDelete>javadocs</ignorePathToDelete>
</ignorePathsToDelete>
</configuration>
</plugin>
</plugins>
</build>
<reporting>
<plugins>
<plugin>
<!-- generate the changes report from changes.xml and link to JIRA -->
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-changes-plugin</artifactId>
<version>${commons.changes.version}</version>
<configuration>
<issueLinkTemplatePerSystem>
<default>%URL%/%ISSUE%</default>
</issueLinkTemplatePerSystem>
</configuration>
<reportSets>
<reportSet>
<reports>
<report>changes-report</report>
<report>jira-report</report>
</reports>
</reportSet>
</reportSets>
</plugin>
<plugin>
<!-- generate a code coverage report -->
<groupId>org.codehaus.mojo</groupId>
<artifactId>cobertura-maven-plugin</artifactId>
<version>2.6</version>
</plugin>
<plugin>
<!-- generate the PMD reports -->
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-pmd-plugin</artifactId>
<version>2.5</version>
<configuration>
<minimumTokens>200</minimumTokens>
<targetJdk>${maven.compiler.source}</targetJdk>
<rulesets>
<ruleset>${basedir}/pmd-ruleset.xml</ruleset>
</rulesets>
</configuration>
</plugin>
<!-- Override Javadoc config in parent pom to add JCIP tags -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<configuration>
<quiet>true</quiet>
<source>${maven.compiler.source}</source>
<encoding>${commons.encoding}</encoding>
<docEncoding>${commons.docEncoding}</docEncoding>
<linksource>true</linksource>
<links>
<link>${commons.javadoc.java.link}</link>
<link>${commons.javadoc.javaee.link}</link>
</links>
<tags>
<tag>
<name>Immutable</name>
<placement>a</placement>
<head>This class is immutable</head>
</tag>
<tag>
<name>NotThreadSafe</name>
<placement>a</placement>
<head>This class is not thread-safe</head>
</tag>
<tag>
<name>ThreadSafe</name>
<placement>a</placement>
<head>This class is thread-safe</head>
</tag>
</tags>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>findbugs-maven-plugin</artifactId>
<version>2.5.3</version>
<configuration>
<threshold>Normal</threshold>
<effort>Default</effort>
<excludeFilterFile>${basedir}/findbugs-exclude-filter.xml</excludeFilterFile>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.rat</groupId>
<artifactId>apache-rat-plugin</artifactId>
<version>${commons.rat.version}</version>
<configuration>
<excludes>
<!-- files used during tests -->
<exclude>src/test/resources/**</exclude>
<!-- proposal text without license -->
<exclude>PROPOSAL.txt</exclude>
<exclude>.pmd</exclude>
<exclude>.gitignore</exclude>
<exclude>.gitattributes</exclude>
<exclude>.projectile</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</reporting>
<profiles>
<!-- Add long running tests as **/*IT.java -->
<profile>
<id>run-zipit</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<executions>
<execution>
<phase>process-test-resources</phase>
<configuration>
<target>
<untar src="${basedir}/src/test/resources/zip64support.tar.bz2"
dest="${project.build.testOutputDirectory}"
compression="bzip2"/>
</target>
</configuration>
<goals>
<goal>run</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<includes>
<include>**/zip/*IT.java</include>
</includes>
</configuration>
</plugin>
</plugins>
</build>
</profile>
<profile>
<id>run-tarit</id>
<build>
<plugins>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<includes>
<include>**/tar/*IT.java</include>
</includes>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>

View file

@ -0,0 +1,15 @@
package org.apache.commons.compress.archivers;
import java.util.Date;
public interface ArchiveEntry {
public static final long SIZE_UNKNOWN = -1L;
String getName();
long getSize();
boolean isDirectory();
Date getLastModifiedDate();
}

View file

@ -0,0 +1,14 @@
package org.apache.commons.compress.archivers;
public class ArchiveException extends Exception {
private static final long serialVersionUID = 2772690708123267100L;
public ArchiveException(String message) {
super(message);
}
public ArchiveException(String message, Exception cause) {
super(message);
initCause(cause);
}
}

View file

@ -0,0 +1,45 @@
package org.apache.commons.compress.archivers;
import java.io.IOException;
import java.io.InputStream;
public abstract class ArchiveInputStream extends InputStream {
private final byte[] SINGLE = new byte[1];
private static final int BYTE_MASK = 255;
private long bytesRead = 0L;
public abstract ArchiveEntry getNextEntry() throws IOException;
public int read() throws IOException {
int num = read(this.SINGLE, 0, 1);
return (num == -1) ? -1 : (this.SINGLE[0] & 0xFF);
}
protected void count(int read) {
count((long)read);
}
protected void count(long read) {
if (read != -1L)
this.bytesRead += read;
}
protected void pushedBackBytes(long pushedBack) {
this.bytesRead -= pushedBack;
}
@Deprecated
public int getCount() {
return (int)this.bytesRead;
}
public long getBytesRead() {
return this.bytesRead;
}
public boolean canReadEntryData(ArchiveEntry archiveEntry) {
return true;
}
}

View file

@ -0,0 +1,48 @@
package org.apache.commons.compress.archivers;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
public abstract class ArchiveOutputStream extends OutputStream {
private final byte[] oneByte = new byte[1];
static final int BYTE_MASK = 255;
private long bytesWritten = 0L;
public abstract void putArchiveEntry(ArchiveEntry paramArchiveEntry) throws IOException;
public abstract void closeArchiveEntry() throws IOException;
public abstract void finish() throws IOException;
public abstract ArchiveEntry createArchiveEntry(File paramFile, String paramString) throws IOException;
public void write(int b) throws IOException {
this.oneByte[0] = (byte)(b & 0xFF);
write(this.oneByte, 0, 1);
}
protected void count(int written) {
count((long)written);
}
protected void count(long written) {
if (written != -1L)
this.bytesWritten += written;
}
@Deprecated
public int getCount() {
return (int)this.bytesWritten;
}
public long getBytesWritten() {
return this.bytesWritten;
}
public boolean canWriteEntryData(ArchiveEntry archiveEntry) {
return true;
}
}

View file

@ -0,0 +1,175 @@
package org.apache.commons.compress.archivers;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import org.apache.commons.compress.archivers.ar.ArArchiveInputStream;
import org.apache.commons.compress.archivers.ar.ArArchiveOutputStream;
import org.apache.commons.compress.archivers.arj.ArjArchiveInputStream;
import org.apache.commons.compress.archivers.cpio.CpioArchiveInputStream;
import org.apache.commons.compress.archivers.cpio.CpioArchiveOutputStream;
import org.apache.commons.compress.archivers.dump.DumpArchiveInputStream;
import org.apache.commons.compress.archivers.jar.JarArchiveInputStream;
import org.apache.commons.compress.archivers.jar.JarArchiveOutputStream;
import org.apache.commons.compress.archivers.sevenz.SevenZFile;
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream;
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
import org.apache.commons.compress.utils.IOUtils;
public class ArchiveStreamFactory {
public static final String AR = "ar";
public static final String ARJ = "arj";
public static final String CPIO = "cpio";
public static final String DUMP = "dump";
public static final String JAR = "jar";
public static final String TAR = "tar";
public static final String ZIP = "zip";
public static final String SEVEN_Z = "7z";
private String entryEncoding = null;
public String getEntryEncoding() {
return this.entryEncoding;
}
public void setEntryEncoding(String entryEncoding) {
this.entryEncoding = entryEncoding;
}
public ArchiveInputStream createArchiveInputStream(String archiverName, InputStream in) throws ArchiveException {
if (archiverName == null)
throw new IllegalArgumentException("Archivername must not be null.");
if (in == null)
throw new IllegalArgumentException("InputStream must not be null.");
if ("ar".equalsIgnoreCase(archiverName))
return new ArArchiveInputStream(in);
if ("arj".equalsIgnoreCase(archiverName)) {
if (this.entryEncoding != null)
return new ArjArchiveInputStream(in, this.entryEncoding);
return new ArjArchiveInputStream(in);
}
if ("zip".equalsIgnoreCase(archiverName)) {
if (this.entryEncoding != null)
return new ZipArchiveInputStream(in, this.entryEncoding);
return new ZipArchiveInputStream(in);
}
if ("tar".equalsIgnoreCase(archiverName)) {
if (this.entryEncoding != null)
return new TarArchiveInputStream(in, this.entryEncoding);
return new TarArchiveInputStream(in);
}
if ("jar".equalsIgnoreCase(archiverName))
return new JarArchiveInputStream(in);
if ("cpio".equalsIgnoreCase(archiverName)) {
if (this.entryEncoding != null)
return new CpioArchiveInputStream(in, this.entryEncoding);
return new CpioArchiveInputStream(in);
}
if ("dump".equalsIgnoreCase(archiverName)) {
if (this.entryEncoding != null)
return new DumpArchiveInputStream(in, this.entryEncoding);
return new DumpArchiveInputStream(in);
}
if ("7z".equalsIgnoreCase(archiverName))
throw new StreamingNotSupportedException("7z");
throw new ArchiveException("Archiver: " + archiverName + " not found.");
}
public ArchiveOutputStream createArchiveOutputStream(String archiverName, OutputStream out) throws ArchiveException {
if (archiverName == null)
throw new IllegalArgumentException("Archivername must not be null.");
if (out == null)
throw new IllegalArgumentException("OutputStream must not be null.");
if ("ar".equalsIgnoreCase(archiverName))
return new ArArchiveOutputStream(out);
if ("zip".equalsIgnoreCase(archiverName)) {
ZipArchiveOutputStream zip = new ZipArchiveOutputStream(out);
if (this.entryEncoding != null)
zip.setEncoding(this.entryEncoding);
return zip;
}
if ("tar".equalsIgnoreCase(archiverName)) {
if (this.entryEncoding != null)
return new TarArchiveOutputStream(out, this.entryEncoding);
return new TarArchiveOutputStream(out);
}
if ("jar".equalsIgnoreCase(archiverName))
return new JarArchiveOutputStream(out);
if ("cpio".equalsIgnoreCase(archiverName)) {
if (this.entryEncoding != null)
return new CpioArchiveOutputStream(out, this.entryEncoding);
return new CpioArchiveOutputStream(out);
}
if ("7z".equalsIgnoreCase(archiverName))
throw new StreamingNotSupportedException("7z");
throw new ArchiveException("Archiver: " + archiverName + " not found.");
}
public ArchiveInputStream createArchiveInputStream(InputStream in) throws ArchiveException {
if (in == null)
throw new IllegalArgumentException("Stream must not be null.");
if (!in.markSupported())
throw new IllegalArgumentException("Mark is not supported.");
byte[] signature = new byte[12];
in.mark(signature.length);
try {
int signatureLength = IOUtils.readFully(in, signature);
in.reset();
if (ZipArchiveInputStream.matches(signature, signatureLength)) {
if (this.entryEncoding != null)
return new ZipArchiveInputStream(in, this.entryEncoding);
return new ZipArchiveInputStream(in);
}
if (JarArchiveInputStream.matches(signature, signatureLength))
return new JarArchiveInputStream(in);
if (ArArchiveInputStream.matches(signature, signatureLength))
return new ArArchiveInputStream(in);
if (CpioArchiveInputStream.matches(signature, signatureLength))
return new CpioArchiveInputStream(in);
if (ArjArchiveInputStream.matches(signature, signatureLength))
return new ArjArchiveInputStream(in);
if (SevenZFile.matches(signature, signatureLength))
throw new StreamingNotSupportedException("7z");
byte[] dumpsig = new byte[32];
in.mark(dumpsig.length);
signatureLength = IOUtils.readFully(in, dumpsig);
in.reset();
if (DumpArchiveInputStream.matches(dumpsig, signatureLength))
return new DumpArchiveInputStream(in);
byte[] tarheader = new byte[512];
in.mark(tarheader.length);
signatureLength = IOUtils.readFully(in, tarheader);
in.reset();
if (TarArchiveInputStream.matches(tarheader, signatureLength)) {
if (this.entryEncoding != null)
return new TarArchiveInputStream(in, this.entryEncoding);
return new TarArchiveInputStream(in);
}
if (signatureLength >= 512) {
TarArchiveInputStream tais = null;
try {
tais = new TarArchiveInputStream(new ByteArrayInputStream(tarheader));
if (tais.getNextTarEntry().isCheckSumOK())
return new TarArchiveInputStream(in);
} catch (Exception e) {
} finally {
IOUtils.closeQuietly(tais);
}
}
} catch (IOException e) {
throw new ArchiveException("Could not use reset and mark operations.", e);
}
throw new ArchiveException("No Archiver found for the stream signature");
}
}

View file

@ -0,0 +1,38 @@
package org.apache.commons.compress.archivers;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
public final class Lister {
private static final ArchiveStreamFactory factory = new ArchiveStreamFactory();
public static void main(String[] args) throws Exception {
ArchiveInputStream ais;
if (args.length == 0) {
usage();
return;
}
System.out.println("Analysing " + args[0]);
File f = new File(args[0]);
if (!f.isFile())
System.err.println(f + " doesn't exist or is a directory");
InputStream fis = new BufferedInputStream(new FileInputStream(f));
if (args.length > 1) {
ais = factory.createArchiveInputStream(args[1], fis);
} else {
ais = factory.createArchiveInputStream(fis);
}
System.out.println("Created " + ais.toString());
ArchiveEntry ae;
while ((ae = ais.getNextEntry()) != null)
System.out.println(ae.getName());
ais.close();
fis.close();
}
private static void usage() {
System.out.println("Parameters: archive-name [archive-type]");
}
}

View file

@ -0,0 +1,16 @@
package org.apache.commons.compress.archivers;
public class StreamingNotSupportedException extends ArchiveException {
private static final long serialVersionUID = 1L;
private final String format;
public StreamingNotSupportedException(String format) {
super("The " + format + " doesn't support streaming.");
this.format = format;
}
public String getFormat() {
return this.format;
}
}

View file

@ -0,0 +1,100 @@
package org.apache.commons.compress.archivers.ar;
import java.io.File;
import java.util.Date;
import org.apache.commons.compress.archivers.ArchiveEntry;
public class ArArchiveEntry implements ArchiveEntry {
public static final String HEADER = "!<arch>\n";
public static final String TRAILER = "`\n";
private final String name;
private final int userId;
private final int groupId;
private final int mode;
private static final int DEFAULT_MODE = 33188;
private final long lastModified;
private final long length;
public ArArchiveEntry(String name, long length) {
this(name, length, 0, 0, 33188, System.currentTimeMillis() / 1000L);
}
public ArArchiveEntry(String name, long length, int userId, int groupId, int mode, long lastModified) {
this.name = name;
this.length = length;
this.userId = userId;
this.groupId = groupId;
this.mode = mode;
this.lastModified = lastModified;
}
public ArArchiveEntry(File inputFile, String entryName) {
this(entryName, inputFile.isFile() ? inputFile.length() : 0L, 0, 0, 33188, inputFile.lastModified() / 1000L);
}
public long getSize() {
return getLength();
}
public String getName() {
return this.name;
}
public int getUserId() {
return this.userId;
}
public int getGroupId() {
return this.groupId;
}
public int getMode() {
return this.mode;
}
public long getLastModified() {
return this.lastModified;
}
public Date getLastModifiedDate() {
return new Date(1000L * getLastModified());
}
public long getLength() {
return this.length;
}
public boolean isDirectory() {
return false;
}
public int hashCode() {
int prime = 31;
int result = 1;
result = 31 * result + ((this.name == null) ? 0 : this.name.hashCode());
return result;
}
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null || getClass() != obj.getClass())
return false;
ArArchiveEntry other = (ArArchiveEntry)obj;
if (this.name == null) {
if (other.name != null)
return false;
} else if (!this.name.equals(other.name)) {
return false;
}
return true;
}
}

View file

@ -0,0 +1,224 @@
package org.apache.commons.compress.archivers.ar;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import org.apache.commons.compress.archivers.ArchiveEntry;
import org.apache.commons.compress.archivers.ArchiveInputStream;
import org.apache.commons.compress.utils.ArchiveUtils;
import org.apache.commons.compress.utils.IOUtils;
public class ArArchiveInputStream extends ArchiveInputStream {
private final InputStream input;
private long offset = 0L;
private boolean closed;
private ArArchiveEntry currentEntry = null;
private byte[] namebuffer = null;
private long entryOffset = -1L;
private final byte[] NAME_BUF = new byte[16];
private final byte[] LAST_MODIFIED_BUF = new byte[12];
private final byte[] ID_BUF = new byte[6];
private final byte[] FILE_MODE_BUF = new byte[8];
private final byte[] LENGTH_BUF = new byte[10];
static final String BSD_LONGNAME_PREFIX = "#1/";
public ArArchiveInputStream(InputStream pInput) {
this.input = pInput;
this.closed = false;
}
public ArArchiveEntry getNextArEntry() throws IOException {
if (this.currentEntry != null) {
long entryEnd = this.entryOffset + this.currentEntry.getLength();
IOUtils.skip(this, entryEnd - this.offset);
this.currentEntry = null;
}
if (this.offset == 0L) {
byte[] arrayOfByte1 = ArchiveUtils.toAsciiBytes("!<arch>\n");
byte[] arrayOfByte2 = new byte[arrayOfByte1.length];
int j = IOUtils.readFully(this, arrayOfByte2);
if (j != arrayOfByte1.length)
throw new IOException("failed to read header. Occured at byte: " + getBytesRead());
for (int k = 0; k < arrayOfByte1.length; k++) {
if (arrayOfByte1[k] != arrayOfByte2[k])
throw new IOException("invalid header " + ArchiveUtils.toAsciiString(arrayOfByte2));
}
}
if (this.offset % 2L != 0L && read() < 0)
return null;
if (this.input.available() == 0)
return null;
IOUtils.readFully(this, this.NAME_BUF);
IOUtils.readFully(this, this.LAST_MODIFIED_BUF);
IOUtils.readFully(this, this.ID_BUF);
int userId = asInt(this.ID_BUF, true);
IOUtils.readFully(this, this.ID_BUF);
IOUtils.readFully(this, this.FILE_MODE_BUF);
IOUtils.readFully(this, this.LENGTH_BUF);
byte[] expected = ArchiveUtils.toAsciiBytes("`\n");
byte[] realized = new byte[expected.length];
int read = IOUtils.readFully(this, realized);
if (read != expected.length)
throw new IOException("failed to read entry trailer. Occured at byte: " + getBytesRead());
for (int i = 0; i < expected.length; i++) {
if (expected[i] != realized[i])
throw new IOException("invalid entry trailer. not read the content? Occured at byte: " + getBytesRead());
}
this.entryOffset = this.offset;
String temp = ArchiveUtils.toAsciiString(this.NAME_BUF).trim();
if (isGNUStringTable(temp)) {
this.currentEntry = readGNUStringTable(this.LENGTH_BUF);
return getNextArEntry();
}
long len = asLong(this.LENGTH_BUF);
if (temp.endsWith("/")) {
temp = temp.substring(0, temp.length() - 1);
} else if (isGNULongName(temp)) {
int off = Integer.parseInt(temp.substring(1));
temp = getExtendedName(off);
} else if (isBSDLongName(temp)) {
temp = getBSDLongName(temp);
int nameLen = temp.length();
len -= (long)nameLen;
this.entryOffset += (long)nameLen;
}
this.currentEntry = new ArArchiveEntry(temp, len, userId, asInt(this.ID_BUF, true), asInt(this.FILE_MODE_BUF, 8), asLong(this.LAST_MODIFIED_BUF));
return this.currentEntry;
}
private String getExtendedName(int offset) throws IOException {
if (this.namebuffer == null)
throw new IOException("Cannot process GNU long filename as no // record was found");
for (int i = offset; i < this.namebuffer.length; i++) {
if (this.namebuffer[i] == 10) {
if (this.namebuffer[i - 1] == 47)
i--;
return ArchiveUtils.toAsciiString(this.namebuffer, offset, i - offset);
}
}
throw new IOException("Failed to read entry: " + offset);
}
private long asLong(byte[] input) {
return Long.parseLong(ArchiveUtils.toAsciiString(input).trim());
}
private int asInt(byte[] input) {
return asInt(input, 10, false);
}
private int asInt(byte[] input, boolean treatBlankAsZero) {
return asInt(input, 10, treatBlankAsZero);
}
private int asInt(byte[] input, int base) {
return asInt(input, base, false);
}
private int asInt(byte[] input, int base, boolean treatBlankAsZero) {
String string = ArchiveUtils.toAsciiString(input).trim();
if (string.length() == 0 && treatBlankAsZero)
return 0;
return Integer.parseInt(string, base);
}
public ArchiveEntry getNextEntry() throws IOException {
return getNextArEntry();
}
public void close() throws IOException {
if (!this.closed) {
this.closed = true;
this.input.close();
}
this.currentEntry = null;
}
public int read(byte[] b, int off, int len) throws IOException {
int toRead = len;
if (this.currentEntry != null) {
long entryEnd = this.entryOffset + this.currentEntry.getLength();
if (len > 0 && entryEnd > this.offset) {
toRead = (int)Math.min((long)len, entryEnd - this.offset);
} else {
return -1;
}
}
int ret = this.input.read(b, off, toRead);
count(ret);
this.offset += (ret > 0) ? (long)ret : 0L;
return ret;
}
public static boolean matches(byte[] signature, int length) {
if (length < 8)
return false;
if (signature[0] != 33)
return false;
if (signature[1] != 60)
return false;
if (signature[2] != 97)
return false;
if (signature[3] != 114)
return false;
if (signature[4] != 99)
return false;
if (signature[5] != 104)
return false;
if (signature[6] != 62)
return false;
if (signature[7] != 10)
return false;
return true;
}
private static final int BSD_LONGNAME_PREFIX_LEN = "#1/".length();
private static final String BSD_LONGNAME_PATTERN = "^#1/\\d+";
private static final String GNU_STRING_TABLE_NAME = "//";
private static final String GNU_LONGNAME_PATTERN = "^/\\d+";
private static boolean isBSDLongName(String name) {
return (name != null && name.matches("^#1/\\d+"));
}
private String getBSDLongName(String bsdLongName) throws IOException {
int nameLen = Integer.parseInt(bsdLongName.substring(BSD_LONGNAME_PREFIX_LEN));
byte[] name = new byte[nameLen];
int read = IOUtils.readFully(this.input, name);
count(read);
if (read != nameLen)
throw new EOFException();
return ArchiveUtils.toAsciiString(name);
}
private static boolean isGNUStringTable(String name) {
return "//".equals(name);
}
private ArArchiveEntry readGNUStringTable(byte[] length) throws IOException {
int bufflen = asInt(length);
this.namebuffer = new byte[bufflen];
int read = IOUtils.readFully(this, this.namebuffer, 0, bufflen);
if (read != bufflen)
throw new IOException("Failed to read complete // record: expected=" + bufflen + " read=" + read);
return new ArArchiveEntry("//", (long)bufflen);
}
private boolean isGNULongName(String name) {
return (name != null && name.matches("^/\\d+"));
}
}

View file

@ -0,0 +1,153 @@
package org.apache.commons.compress.archivers.ar;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import org.apache.commons.compress.archivers.ArchiveEntry;
import org.apache.commons.compress.archivers.ArchiveOutputStream;
import org.apache.commons.compress.utils.ArchiveUtils;
public class ArArchiveOutputStream extends ArchiveOutputStream {
public static final int LONGFILE_ERROR = 0;
public static final int LONGFILE_BSD = 1;
private final OutputStream out;
private long entryOffset = 0L;
private ArArchiveEntry prevEntry;
private boolean haveUnclosedEntry = false;
private int longFileMode = 0;
private boolean finished = false;
public ArArchiveOutputStream(OutputStream pOut) {
this.out = pOut;
}
public void setLongFileMode(int longFileMode) {
this.longFileMode = longFileMode;
}
private long writeArchiveHeader() throws IOException {
byte[] header = ArchiveUtils.toAsciiBytes("!<arch>\n");
this.out.write(header);
return (long)header.length;
}
public void closeArchiveEntry() throws IOException {
if (this.finished)
throw new IOException("Stream has already been finished");
if (this.prevEntry == null || !this.haveUnclosedEntry)
throw new IOException("No current entry to close");
if (this.entryOffset % 2L != 0L)
this.out.write(10);
this.haveUnclosedEntry = false;
}
public void putArchiveEntry(ArchiveEntry pEntry) throws IOException {
if (this.finished)
throw new IOException("Stream has already been finished");
ArArchiveEntry pArEntry = (ArArchiveEntry)pEntry;
if (this.prevEntry == null) {
writeArchiveHeader();
} else {
if (this.prevEntry.getLength() != this.entryOffset)
throw new IOException("length does not match entry (" + this.prevEntry.getLength() + " != " + this.entryOffset);
if (this.haveUnclosedEntry)
closeArchiveEntry();
}
this.prevEntry = pArEntry;
writeEntryHeader(pArEntry);
this.entryOffset = 0L;
this.haveUnclosedEntry = true;
}
private long fill(long pOffset, long pNewOffset, char pFill) throws IOException {
long diff = pNewOffset - pOffset;
if (diff > 0L)
for (int i = 0; (long)i < diff; i++)
write(pFill);
return pNewOffset;
}
private long write(String data) throws IOException {
byte[] bytes = data.getBytes("ascii");
write(bytes);
return (long)bytes.length;
}
private long writeEntryHeader(ArArchiveEntry pEntry) throws IOException {
long offset = 0L;
boolean mustAppendName = false;
String n = pEntry.getName();
if (0 == this.longFileMode && n.length() > 16)
throw new IOException("filename too long, > 16 chars: " + n);
if (1 == this.longFileMode && (n.length() > 16 || n.indexOf(" ") > -1)) {
mustAppendName = true;
offset += write("#1/" + String.valueOf(n.length()));
} else {
offset += write(n);
}
offset = fill(offset, 16L, ' ');
String m = "" + pEntry.getLastModified();
if (m.length() > 12)
throw new IOException("modified too long");
offset += write(m);
offset = fill(offset, 28L, ' ');
String u = "" + pEntry.getUserId();
if (u.length() > 6)
throw new IOException("userid too long");
offset += write(u);
offset = fill(offset, 34L, ' ');
String g = "" + pEntry.getGroupId();
if (g.length() > 6)
throw new IOException("groupid too long");
offset += write(g);
offset = fill(offset, 40L, ' ');
String fm = "" + Integer.toString(pEntry.getMode(), 8);
if (fm.length() > 8)
throw new IOException("filemode too long");
offset += write(fm);
offset = fill(offset, 48L, ' ');
String s = String.valueOf(pEntry.getLength() + (long)(mustAppendName ? n.length() : 0L));
if (s.length() > 10)
throw new IOException("size too long");
offset += write(s);
offset = fill(offset, 58L, ' ');
offset += write("`\n");
if (mustAppendName)
offset += write(n);
return offset;
}
public void write(byte[] b, int off, int len) throws IOException {
this.out.write(b, off, len);
count(len);
this.entryOffset += (long)len;
}
public void close() throws IOException {
if (!this.finished)
finish();
this.out.close();
this.prevEntry = null;
}
public ArchiveEntry createArchiveEntry(File inputFile, String entryName) throws IOException {
if (this.finished)
throw new IOException("Stream has already been finished");
return new ArArchiveEntry(inputFile, entryName);
}
public void finish() throws IOException {
if (this.haveUnclosedEntry)
throw new IOException("This archive contains unclosed entries.");
if (this.finished)
throw new IOException("This archive has already been finished");
this.finished = true;
}
}

View file

@ -0,0 +1,84 @@
package org.apache.commons.compress.archivers.arj;
import java.io.File;
import java.util.Date;
import java.util.regex.Matcher;
import org.apache.commons.compress.archivers.ArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipUtil;
public class ArjArchiveEntry implements ArchiveEntry {
private final LocalFileHeader localFileHeader;
public ArjArchiveEntry() {
this.localFileHeader = new LocalFileHeader();
}
ArjArchiveEntry(LocalFileHeader localFileHeader) {
this.localFileHeader = localFileHeader;
}
public String getName() {
if ((this.localFileHeader.arjFlags & 0x10) != 0)
return this.localFileHeader.name.replaceAll("/", Matcher.quoteReplacement(File.separator));
return this.localFileHeader.name;
}
public long getSize() {
return this.localFileHeader.originalSize;
}
public boolean isDirectory() {
return (this.localFileHeader.fileType == 3);
}
public Date getLastModifiedDate() {
long ts = isHostOsUnix() ? ((long)this.localFileHeader.dateTimeModified * 1000L) : ZipUtil.dosToJavaTime(0xFFFFFFFFL & (long)this.localFileHeader.dateTimeModified);
return new Date(ts);
}
public int getMode() {
return this.localFileHeader.fileAccessMode;
}
public int getUnixMode() {
return isHostOsUnix() ? getMode() : 0;
}
public int getHostOs() {
return this.localFileHeader.hostOS;
}
public boolean isHostOsUnix() {
return (getHostOs() == 2 || getHostOs() == 8);
}
int getMethod() {
return this.localFileHeader.method;
}
public static class HostOs {
public static final int DOS = 0;
public static final int PRIMOS = 1;
public static final int UNIX = 2;
public static final int AMIGA = 3;
public static final int MAC_OS = 4;
public static final int OS_2 = 5;
public static final int APPLE_GS = 6;
public static final int ATARI_ST = 7;
public static final int NEXT = 8;
public static final int VAX_VMS = 9;
public static final int WIN95 = 10;
public static final int WIN32 = 11;
}
}

View file

@ -0,0 +1,260 @@
package org.apache.commons.compress.archivers.arj;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.zip.CRC32;
import org.apache.commons.compress.archivers.ArchiveEntry;
import org.apache.commons.compress.archivers.ArchiveException;
import org.apache.commons.compress.archivers.ArchiveInputStream;
import org.apache.commons.compress.utils.BoundedInputStream;
import org.apache.commons.compress.utils.CRC32VerifyingInputStream;
import org.apache.commons.compress.utils.IOUtils;
public class ArjArchiveInputStream extends ArchiveInputStream {
private static final int ARJ_MAGIC_1 = 96;
private static final int ARJ_MAGIC_2 = 234;
private final DataInputStream in;
private final String charsetName;
private final MainHeader mainHeader;
private LocalFileHeader currentLocalFileHeader = null;
private InputStream currentInputStream = null;
public ArjArchiveInputStream(InputStream inputStream, String charsetName) throws ArchiveException {
this.in = new DataInputStream(inputStream);
this.charsetName = charsetName;
try {
this.mainHeader = readMainHeader();
if ((this.mainHeader.arjFlags & 0x1) != 0)
throw new ArchiveException("Encrypted ARJ files are unsupported");
if ((this.mainHeader.arjFlags & 0x4) != 0)
throw new ArchiveException("Multi-volume ARJ files are unsupported");
} catch (IOException ioException) {
throw new ArchiveException(ioException.getMessage(), ioException);
}
}
public ArjArchiveInputStream(InputStream inputStream) throws ArchiveException {
this(inputStream, "CP437");
}
public void close() throws IOException {
this.in.close();
}
private int read8(DataInputStream dataIn) throws IOException {
int value = dataIn.readUnsignedByte();
count(1);
return value;
}
private int read16(DataInputStream dataIn) throws IOException {
int value = dataIn.readUnsignedShort();
count(2);
return Integer.reverseBytes(value) >>> 16;
}
private int read32(DataInputStream dataIn) throws IOException {
int value = dataIn.readInt();
count(4);
return Integer.reverseBytes(value);
}
private String readString(DataInputStream dataIn) throws IOException {
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
int nextByte;
while ((nextByte = dataIn.readUnsignedByte()) != 0)
buffer.write(nextByte);
if (this.charsetName != null)
return new String(buffer.toByteArray(), this.charsetName);
return new String(buffer.toByteArray());
}
private void readFully(DataInputStream dataIn, byte[] b) throws IOException {
dataIn.readFully(b);
count(b.length);
}
private byte[] readHeader() throws IOException {
boolean found = false;
byte[] basicHeaderBytes = null;
while (true) {
int first = 0;
int second = read8(this.in);
do {
first = second;
second = read8(this.in);
} while (first != 96 && second != 234);
int basicHeaderSize = read16(this.in);
if (basicHeaderSize == 0)
return null;
if (basicHeaderSize <= 2600) {
basicHeaderBytes = new byte[basicHeaderSize];
readFully(this.in, basicHeaderBytes);
long basicHeaderCrc32 = (long)read32(this.in) & 0xFFFFFFFFL;
CRC32 crc32 = new CRC32();
crc32.update(basicHeaderBytes);
if (basicHeaderCrc32 == crc32.getValue())
found = true;
}
if (found)
return basicHeaderBytes;
}
}
private MainHeader readMainHeader() throws IOException {
byte[] basicHeaderBytes = readHeader();
if (basicHeaderBytes == null)
throw new IOException("Archive ends without any headers");
DataInputStream basicHeader = new DataInputStream(new ByteArrayInputStream(basicHeaderBytes));
int firstHeaderSize = basicHeader.readUnsignedByte();
byte[] firstHeaderBytes = new byte[firstHeaderSize - 1];
basicHeader.readFully(firstHeaderBytes);
DataInputStream firstHeader = new DataInputStream(new ByteArrayInputStream(firstHeaderBytes));
MainHeader hdr = new MainHeader();
hdr.archiverVersionNumber = firstHeader.readUnsignedByte();
hdr.minVersionToExtract = firstHeader.readUnsignedByte();
hdr.hostOS = firstHeader.readUnsignedByte();
hdr.arjFlags = firstHeader.readUnsignedByte();
hdr.securityVersion = firstHeader.readUnsignedByte();
hdr.fileType = firstHeader.readUnsignedByte();
hdr.reserved = firstHeader.readUnsignedByte();
hdr.dateTimeCreated = read32(firstHeader);
hdr.dateTimeModified = read32(firstHeader);
hdr.archiveSize = 0xFFFFFFFFL & (long)read32(firstHeader);
hdr.securityEnvelopeFilePosition = read32(firstHeader);
hdr.fileSpecPosition = read16(firstHeader);
hdr.securityEnvelopeLength = read16(firstHeader);
pushedBackBytes(20L);
hdr.encryptionVersion = firstHeader.readUnsignedByte();
hdr.lastChapter = firstHeader.readUnsignedByte();
if (firstHeaderSize >= 33) {
hdr.arjProtectionFactor = firstHeader.readUnsignedByte();
hdr.arjFlags2 = firstHeader.readUnsignedByte();
firstHeader.readUnsignedByte();
firstHeader.readUnsignedByte();
}
hdr.name = readString(basicHeader);
hdr.comment = readString(basicHeader);
int extendedHeaderSize = read16(this.in);
if (extendedHeaderSize > 0) {
hdr.extendedHeaderBytes = new byte[extendedHeaderSize];
readFully(this.in, hdr.extendedHeaderBytes);
long extendedHeaderCrc32 = 0xFFFFFFFFL & (long)read32(this.in);
CRC32 crc32 = new CRC32();
crc32.update(hdr.extendedHeaderBytes);
if (extendedHeaderCrc32 != crc32.getValue())
throw new IOException("Extended header CRC32 verification failure");
}
return hdr;
}
private LocalFileHeader readLocalFileHeader() throws IOException {
byte[] basicHeaderBytes = readHeader();
if (basicHeaderBytes == null)
return null;
DataInputStream basicHeader = new DataInputStream(new ByteArrayInputStream(basicHeaderBytes));
int firstHeaderSize = basicHeader.readUnsignedByte();
byte[] firstHeaderBytes = new byte[firstHeaderSize - 1];
basicHeader.readFully(firstHeaderBytes);
DataInputStream firstHeader = new DataInputStream(new ByteArrayInputStream(firstHeaderBytes));
LocalFileHeader localFileHeader = new LocalFileHeader();
localFileHeader.archiverVersionNumber = firstHeader.readUnsignedByte();
localFileHeader.minVersionToExtract = firstHeader.readUnsignedByte();
localFileHeader.hostOS = firstHeader.readUnsignedByte();
localFileHeader.arjFlags = firstHeader.readUnsignedByte();
localFileHeader.method = firstHeader.readUnsignedByte();
localFileHeader.fileType = firstHeader.readUnsignedByte();
localFileHeader.reserved = firstHeader.readUnsignedByte();
localFileHeader.dateTimeModified = read32(firstHeader);
localFileHeader.compressedSize = 0xFFFFFFFFL & (long)read32(firstHeader);
localFileHeader.originalSize = 0xFFFFFFFFL & (long)read32(firstHeader);
localFileHeader.originalCrc32 = 0xFFFFFFFFL & (long)read32(firstHeader);
localFileHeader.fileSpecPosition = read16(firstHeader);
localFileHeader.fileAccessMode = read16(firstHeader);
pushedBackBytes(20L);
localFileHeader.firstChapter = firstHeader.readUnsignedByte();
localFileHeader.lastChapter = firstHeader.readUnsignedByte();
readExtraData(firstHeaderSize, firstHeader, localFileHeader);
localFileHeader.name = readString(basicHeader);
localFileHeader.comment = readString(basicHeader);
ArrayList<byte[]> extendedHeaders = new ArrayList<byte[]>();
int extendedHeaderSize;
while ((extendedHeaderSize = read16(this.in)) > 0) {
byte[] extendedHeaderBytes = new byte[extendedHeaderSize];
readFully(this.in, extendedHeaderBytes);
long extendedHeaderCrc32 = 0xFFFFFFFFL & (long)read32(this.in);
CRC32 crc32 = new CRC32();
crc32.update(extendedHeaderBytes);
if (extendedHeaderCrc32 != crc32.getValue())
throw new IOException("Extended header CRC32 verification failure");
extendedHeaders.add(extendedHeaderBytes);
}
localFileHeader.extendedHeaders = extendedHeaders.<byte[]>toArray(new byte[extendedHeaders.size()][]);
return localFileHeader;
}
private void readExtraData(int firstHeaderSize, DataInputStream firstHeader, LocalFileHeader localFileHeader) throws IOException {
if (firstHeaderSize >= 33) {
localFileHeader.extendedFilePosition = read32(firstHeader);
if (firstHeaderSize >= 45) {
localFileHeader.dateTimeAccessed = read32(firstHeader);
localFileHeader.dateTimeCreated = read32(firstHeader);
localFileHeader.originalSizeEvenForVolumes = read32(firstHeader);
pushedBackBytes(12L);
}
pushedBackBytes(4L);
}
}
public static boolean matches(byte[] signature, int length) {
return (length >= 2 && (0xFF & signature[0]) == 96 && (0xFF & signature[1]) == 234);
}
public String getArchiveName() {
return this.mainHeader.name;
}
public String getArchiveComment() {
return this.mainHeader.comment;
}
public ArjArchiveEntry getNextEntry() throws IOException {
if (this.currentInputStream != null) {
IOUtils.skip(this.currentInputStream, Long.MAX_VALUE);
this.currentInputStream.close();
this.currentLocalFileHeader = null;
this.currentInputStream = null;
}
this.currentLocalFileHeader = readLocalFileHeader();
if (this.currentLocalFileHeader != null) {
this.currentInputStream = new BoundedInputStream(this.in, this.currentLocalFileHeader.compressedSize);
if (this.currentLocalFileHeader.method == 0)
this.currentInputStream = new CRC32VerifyingInputStream(this.currentInputStream, this.currentLocalFileHeader.originalSize, this.currentLocalFileHeader.originalCrc32);
return new ArjArchiveEntry(this.currentLocalFileHeader);
}
this.currentInputStream = null;
return null;
}
public boolean canReadEntryData(ArchiveEntry ae) {
return (ae instanceof ArjArchiveEntry && ((ArjArchiveEntry)ae).getMethod() == 0);
}
public int read(byte[] b, int off, int len) throws IOException {
if (this.currentLocalFileHeader == null)
throw new IllegalStateException("No current arj entry");
if (this.currentLocalFileHeader.method != 0)
throw new IOException("Unsupported compression method " + this.currentLocalFileHeader.method);
return this.currentInputStream.read(b, off, len);
}
}

View file

@ -0,0 +1,135 @@
package org.apache.commons.compress.archivers.arj;
import java.util.Arrays;
class LocalFileHeader {
int archiverVersionNumber;
int minVersionToExtract;
int hostOS;
int arjFlags;
int method;
int fileType;
int reserved;
int dateTimeModified;
long compressedSize;
long originalSize;
long originalCrc32;
int fileSpecPosition;
int fileAccessMode;
int firstChapter;
int lastChapter;
int extendedFilePosition;
int dateTimeAccessed;
int dateTimeCreated;
int originalSizeEvenForVolumes;
String name;
String comment;
byte[][] extendedHeaders = null;
static class Flags {
static final int GARBLED = 1;
static final int VOLUME = 4;
static final int EXTFILE = 8;
static final int PATHSYM = 16;
static final int BACKUP = 32;
}
static class FileTypes {
static final int BINARY = 0;
static final int SEVEN_BIT_TEXT = 1;
static final int DIRECTORY = 3;
static final int VOLUME_LABEL = 4;
static final int CHAPTER_LABEL = 5;
}
static class Methods {
static final int STORED = 0;
static final int COMPRESSED_MOST = 1;
static final int COMPRESSED_FASTEST = 4;
static final int NO_DATA_NO_CRC = 8;
static final int NO_DATA = 9;
}
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("LocalFileHeader [archiverVersionNumber=");
builder.append(this.archiverVersionNumber);
builder.append(", minVersionToExtract=");
builder.append(this.minVersionToExtract);
builder.append(", hostOS=");
builder.append(this.hostOS);
builder.append(", arjFlags=");
builder.append(this.arjFlags);
builder.append(", method=");
builder.append(this.method);
builder.append(", fileType=");
builder.append(this.fileType);
builder.append(", reserved=");
builder.append(this.reserved);
builder.append(", dateTimeModified=");
builder.append(this.dateTimeModified);
builder.append(", compressedSize=");
builder.append(this.compressedSize);
builder.append(", originalSize=");
builder.append(this.originalSize);
builder.append(", originalCrc32=");
builder.append(this.originalCrc32);
builder.append(", fileSpecPosition=");
builder.append(this.fileSpecPosition);
builder.append(", fileAccessMode=");
builder.append(this.fileAccessMode);
builder.append(", firstChapter=");
builder.append(this.firstChapter);
builder.append(", lastChapter=");
builder.append(this.lastChapter);
builder.append(", extendedFilePosition=");
builder.append(this.extendedFilePosition);
builder.append(", dateTimeAccessed=");
builder.append(this.dateTimeAccessed);
builder.append(", dateTimeCreated=");
builder.append(this.dateTimeCreated);
builder.append(", originalSizeEvenForVolumes=");
builder.append(this.originalSizeEvenForVolumes);
builder.append(", name=");
builder.append(this.name);
builder.append(", comment=");
builder.append(this.comment);
builder.append(", extendedHeaders=");
builder.append(Arrays.toString((Object[])this.extendedHeaders));
builder.append("]");
return builder.toString();
}
}

View file

@ -0,0 +1,109 @@
package org.apache.commons.compress.archivers.arj;
import java.util.Arrays;
class MainHeader {
int archiverVersionNumber;
int minVersionToExtract;
int hostOS;
int arjFlags;
int securityVersion;
int fileType;
int reserved;
int dateTimeCreated;
int dateTimeModified;
long archiveSize;
int securityEnvelopeFilePosition;
int fileSpecPosition;
int securityEnvelopeLength;
int encryptionVersion;
int lastChapter;
int arjProtectionFactor;
int arjFlags2;
String name;
String comment;
byte[] extendedHeaderBytes = null;
static class Flags {
static final int GARBLED = 1;
static final int OLD_SECURED_NEW_ANSI_PAGE = 2;
static final int VOLUME = 4;
static final int ARJPROT = 8;
static final int PATHSYM = 16;
static final int BACKUP = 32;
static final int SECURED = 64;
static final int ALTNAME = 128;
}
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("MainHeader [archiverVersionNumber=");
builder.append(this.archiverVersionNumber);
builder.append(", minVersionToExtract=");
builder.append(this.minVersionToExtract);
builder.append(", hostOS=");
builder.append(this.hostOS);
builder.append(", arjFlags=");
builder.append(this.arjFlags);
builder.append(", securityVersion=");
builder.append(this.securityVersion);
builder.append(", fileType=");
builder.append(this.fileType);
builder.append(", reserved=");
builder.append(this.reserved);
builder.append(", dateTimeCreated=");
builder.append(this.dateTimeCreated);
builder.append(", dateTimeModified=");
builder.append(this.dateTimeModified);
builder.append(", archiveSize=");
builder.append(this.archiveSize);
builder.append(", securityEnvelopeFilePosition=");
builder.append(this.securityEnvelopeFilePosition);
builder.append(", fileSpecPosition=");
builder.append(this.fileSpecPosition);
builder.append(", securityEnvelopeLength=");
builder.append(this.securityEnvelopeLength);
builder.append(", encryptionVersion=");
builder.append(this.encryptionVersion);
builder.append(", lastChapter=");
builder.append(this.lastChapter);
builder.append(", arjProtectionFactor=");
builder.append(this.arjProtectionFactor);
builder.append(", arjFlags2=");
builder.append(this.arjFlags2);
builder.append(", name=");
builder.append(this.name);
builder.append(", comment=");
builder.append(this.comment);
builder.append(", extendedHeaderBytes=");
builder.append(Arrays.toString(this.extendedHeaderBytes));
builder.append("]");
return builder.toString();
}
}

View file

@ -0,0 +1,350 @@
package org.apache.commons.compress.archivers.cpio;
import java.io.File;
import java.util.Date;
import org.apache.commons.compress.archivers.ArchiveEntry;
public class CpioArchiveEntry implements CpioConstants, ArchiveEntry {
private final short fileFormat;
private final int headerSize;
private final int alignmentBoundary;
private long chksum = 0L;
private long filesize = 0L;
private long gid = 0L;
private long inode = 0L;
private long maj = 0L;
private long min = 0L;
private long mode = 0L;
private long mtime = 0L;
private String name;
private long nlink = 0L;
private long rmaj = 0L;
private long rmin = 0L;
private long uid = 0L;
public CpioArchiveEntry(short format) {
switch (format) {
case 1:
this.headerSize = 110;
this.alignmentBoundary = 4;
break;
case 2:
this.headerSize = 110;
this.alignmentBoundary = 4;
break;
case 4:
this.headerSize = 76;
this.alignmentBoundary = 0;
break;
case 8:
this.headerSize = 26;
this.alignmentBoundary = 2;
break;
default:
throw new IllegalArgumentException("Unknown header type");
}
this.fileFormat = format;
}
public CpioArchiveEntry(String name) {
this((short)1, name);
}
public CpioArchiveEntry(short format, String name) {
this(format);
this.name = name;
}
public CpioArchiveEntry(String name, long size) {
this(name);
setSize(size);
}
public CpioArchiveEntry(short format, String name, long size) {
this(format, name);
setSize(size);
}
public CpioArchiveEntry(File inputFile, String entryName) {
this((short)1, inputFile, entryName);
}
public CpioArchiveEntry(short format, File inputFile, String entryName) {
this(format, entryName, inputFile.isFile() ? inputFile.length() : 0L);
if (inputFile.isDirectory()) {
setMode(16384L);
} else if (inputFile.isFile()) {
setMode(32768L);
} else {
throw new IllegalArgumentException("Cannot determine type of file " + inputFile.getName());
}
setTime(inputFile.lastModified() / 1000L);
}
private void checkNewFormat() {
if ((this.fileFormat & 0x3) == 0)
throw new UnsupportedOperationException();
}
private void checkOldFormat() {
if ((this.fileFormat & 0xC) == 0)
throw new UnsupportedOperationException();
}
public long getChksum() {
checkNewFormat();
return this.chksum;
}
public long getDevice() {
checkOldFormat();
return this.min;
}
public long getDeviceMaj() {
checkNewFormat();
return this.maj;
}
public long getDeviceMin() {
checkNewFormat();
return this.min;
}
public long getSize() {
return this.filesize;
}
public short getFormat() {
return this.fileFormat;
}
public long getGID() {
return this.gid;
}
public int getHeaderSize() {
return this.headerSize;
}
public int getAlignmentBoundary() {
return this.alignmentBoundary;
}
public int getHeaderPadCount() {
if (this.alignmentBoundary == 0)
return 0;
int size = this.headerSize + 1;
if (this.name != null)
size += this.name.length();
int remain = size % this.alignmentBoundary;
if (remain > 0)
return this.alignmentBoundary - remain;
return 0;
}
public int getDataPadCount() {
if (this.alignmentBoundary == 0)
return 0;
long size = this.filesize;
int remain = (int)(size % (long)this.alignmentBoundary);
if (remain > 0)
return this.alignmentBoundary - remain;
return 0;
}
public long getInode() {
return this.inode;
}
public long getMode() {
return (this.mode == 0L && !"TRAILER!!!".equals(this.name)) ? 32768L : this.mode;
}
public String getName() {
return this.name;
}
public long getNumberOfLinks() {
return (this.nlink == 0L) ? (isDirectory() ? 2L : 1L) : this.nlink;
}
public long getRemoteDevice() {
checkOldFormat();
return this.rmin;
}
public long getRemoteDeviceMaj() {
checkNewFormat();
return this.rmaj;
}
public long getRemoteDeviceMin() {
checkNewFormat();
return this.rmin;
}
public long getTime() {
return this.mtime;
}
public Date getLastModifiedDate() {
return new Date(1000L * getTime());
}
public long getUID() {
return this.uid;
}
public boolean isBlockDevice() {
return (CpioUtil.fileType(this.mode) == 24576L);
}
public boolean isCharacterDevice() {
return (CpioUtil.fileType(this.mode) == 8192L);
}
public boolean isDirectory() {
return (CpioUtil.fileType(this.mode) == 16384L);
}
public boolean isNetwork() {
return (CpioUtil.fileType(this.mode) == 36864L);
}
public boolean isPipe() {
return (CpioUtil.fileType(this.mode) == 4096L);
}
public boolean isRegularFile() {
return (CpioUtil.fileType(this.mode) == 32768L);
}
public boolean isSocket() {
return (CpioUtil.fileType(this.mode) == 49152L);
}
public boolean isSymbolicLink() {
return (CpioUtil.fileType(this.mode) == 40960L);
}
public void setChksum(long chksum) {
checkNewFormat();
this.chksum = chksum;
}
public void setDevice(long device) {
checkOldFormat();
this.min = device;
}
public void setDeviceMaj(long maj) {
checkNewFormat();
this.maj = maj;
}
public void setDeviceMin(long min) {
checkNewFormat();
this.min = min;
}
public void setSize(long size) {
if (size < 0L || size > 4294967295L)
throw new IllegalArgumentException("invalid entry size <" + size + ">");
this.filesize = size;
}
public void setGID(long gid) {
this.gid = gid;
}
public void setInode(long inode) {
this.inode = inode;
}
public void setMode(long mode) {
long maskedMode = mode & 0xF000L;
switch ((int)maskedMode) {
case 4096:
case 8192:
case 16384:
case 24576:
case 32768:
case 36864:
case 40960:
case 49152:
break;
default:
throw new IllegalArgumentException("Unknown mode. Full: " + Long.toHexString(mode) + " Masked: " + Long.toHexString(maskedMode));
}
this.mode = mode;
}
public void setName(String name) {
this.name = name;
}
public void setNumberOfLinks(long nlink) {
this.nlink = nlink;
}
public void setRemoteDevice(long device) {
checkOldFormat();
this.rmin = device;
}
public void setRemoteDeviceMaj(long rmaj) {
checkNewFormat();
this.rmaj = rmaj;
}
public void setRemoteDeviceMin(long rmin) {
checkNewFormat();
this.rmin = rmin;
}
public void setTime(long time) {
this.mtime = time;
}
public void setUID(long uid) {
this.uid = uid;
}
public int hashCode() {
int prime = 31;
int result = 1;
result = 31 * result + ((this.name == null) ? 0 : this.name.hashCode());
return result;
}
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null || getClass() != obj.getClass())
return false;
CpioArchiveEntry other = (CpioArchiveEntry)obj;
if (this.name == null) {
if (other.name != null)
return false;
} else if (!this.name.equals(other.name)) {
return false;
}
return true;
}
}

View file

@ -0,0 +1,304 @@
package org.apache.commons.compress.archivers.cpio;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import org.apache.commons.compress.archivers.ArchiveEntry;
import org.apache.commons.compress.archivers.ArchiveInputStream;
import org.apache.commons.compress.archivers.zip.ZipEncoding;
import org.apache.commons.compress.archivers.zip.ZipEncodingHelper;
import org.apache.commons.compress.utils.ArchiveUtils;
import org.apache.commons.compress.utils.IOUtils;
public class CpioArchiveInputStream extends ArchiveInputStream implements CpioConstants {
private boolean closed = false;
private CpioArchiveEntry entry;
private long entryBytesRead = 0L;
private boolean entryEOF = false;
private final byte[] tmpbuf = new byte[4096];
private long crc = 0L;
private final InputStream in;
private final byte[] TWO_BYTES_BUF = new byte[2];
private final byte[] FOUR_BYTES_BUF = new byte[4];
private final byte[] SIX_BYTES_BUF = new byte[6];
private final int blockSize;
private final ZipEncoding encoding;
public CpioArchiveInputStream(InputStream in) {
this(in, 512, "US-ASCII");
}
public CpioArchiveInputStream(InputStream in, String encoding) {
this(in, 512, encoding);
}
public CpioArchiveInputStream(InputStream in, int blockSize) {
this(in, blockSize, "US-ASCII");
}
public CpioArchiveInputStream(InputStream in, int blockSize, String encoding) {
this.in = in;
this.blockSize = blockSize;
this.encoding = ZipEncodingHelper.getZipEncoding(encoding);
}
public int available() throws IOException {
ensureOpen();
if (this.entryEOF)
return 0;
return 1;
}
public void close() throws IOException {
if (!this.closed) {
this.in.close();
this.closed = true;
}
}
private void closeEntry() throws IOException {
while (skip(Integer.MAX_VALUE) == Integer.MAX_VALUE);
}
private void ensureOpen() throws IOException {
if (this.closed)
throw new IOException("Stream closed");
}
public CpioArchiveEntry getNextCPIOEntry() throws IOException {
ensureOpen();
if (this.entry != null)
closeEntry();
readFully(this.TWO_BYTES_BUF, 0, this.TWO_BYTES_BUF.length);
if (CpioUtil.byteArray2long(this.TWO_BYTES_BUF, false) == 29127L) {
this.entry = readOldBinaryEntry(false);
} else if (CpioUtil.byteArray2long(this.TWO_BYTES_BUF, true) == 29127L) {
this.entry = readOldBinaryEntry(true);
} else {
System.arraycopy(this.TWO_BYTES_BUF, 0, this.SIX_BYTES_BUF, 0, this.TWO_BYTES_BUF.length);
readFully(this.SIX_BYTES_BUF, this.TWO_BYTES_BUF.length, this.FOUR_BYTES_BUF.length);
String magicString = ArchiveUtils.toAsciiString(this.SIX_BYTES_BUF);
if (magicString.equals("070701")) {
this.entry = readNewEntry(false);
} else if (magicString.equals("070702")) {
this.entry = readNewEntry(true);
} else if (magicString.equals("070707")) {
this.entry = readOldAsciiEntry();
} else {
throw new IOException("Unknown magic [" + magicString + "]. Occured at byte: " + getBytesRead());
}
}
this.entryBytesRead = 0L;
this.entryEOF = false;
this.crc = 0L;
if (this.entry.getName().equals("TRAILER!!!")) {
this.entryEOF = true;
skipRemainderOfLastBlock();
return null;
}
return this.entry;
}
private void skip(int bytes) throws IOException {
if (bytes > 0)
readFully(this.FOUR_BYTES_BUF, 0, bytes);
}
public int read(byte[] b, int off, int len) throws IOException {
ensureOpen();
if (off < 0 || len < 0 || off > b.length - len)
throw new IndexOutOfBoundsException();
if (len == 0)
return 0;
if (this.entry == null || this.entryEOF)
return -1;
if (this.entryBytesRead == this.entry.getSize()) {
skip(this.entry.getDataPadCount());
this.entryEOF = true;
if (this.entry.getFormat() == 2 && this.crc != this.entry.getChksum())
throw new IOException("CRC Error. Occured at byte: " + getBytesRead());
return -1;
}
int tmplength = (int)Math.min((long)len, this.entry.getSize() - this.entryBytesRead);
if (tmplength < 0)
return -1;
int tmpread = readFully(b, off, tmplength);
if (this.entry.getFormat() == 2)
for (int pos = 0; pos < tmpread; pos++)
this.crc += (long)(b[pos] & 0xFF);
this.entryBytesRead += (long)tmpread;
return tmpread;
}
private final int readFully(byte[] b, int off, int len) throws IOException {
int count = IOUtils.readFully(this.in, b, off, len);
count(count);
if (count < len)
throw new EOFException();
return count;
}
private long readBinaryLong(int length, boolean swapHalfWord) throws IOException {
byte[] tmp = new byte[length];
readFully(tmp, 0, tmp.length);
return CpioUtil.byteArray2long(tmp, swapHalfWord);
}
private long readAsciiLong(int length, int radix) throws IOException {
byte[] tmpBuffer = new byte[length];
readFully(tmpBuffer, 0, tmpBuffer.length);
return Long.parseLong(ArchiveUtils.toAsciiString(tmpBuffer), radix);
}
private CpioArchiveEntry readNewEntry(boolean hasCrc) throws IOException {
CpioArchiveEntry ret;
if (hasCrc) {
ret = new CpioArchiveEntry((short)2);
} else {
ret = new CpioArchiveEntry((short)1);
}
ret.setInode(readAsciiLong(8, 16));
long mode = readAsciiLong(8, 16);
if (CpioUtil.fileType(mode) != 0L)
ret.setMode(mode);
ret.setUID(readAsciiLong(8, 16));
ret.setGID(readAsciiLong(8, 16));
ret.setNumberOfLinks(readAsciiLong(8, 16));
ret.setTime(readAsciiLong(8, 16));
ret.setSize(readAsciiLong(8, 16));
ret.setDeviceMaj(readAsciiLong(8, 16));
ret.setDeviceMin(readAsciiLong(8, 16));
ret.setRemoteDeviceMaj(readAsciiLong(8, 16));
ret.setRemoteDeviceMin(readAsciiLong(8, 16));
long namesize = readAsciiLong(8, 16);
ret.setChksum(readAsciiLong(8, 16));
String name = readCString((int)namesize);
ret.setName(name);
if (CpioUtil.fileType(mode) == 0L && !name.equals("TRAILER!!!"))
throw new IOException("Mode 0 only allowed in the trailer. Found entry name: " + name + " Occured at byte: " + getBytesRead());
skip(ret.getHeaderPadCount());
return ret;
}
private CpioArchiveEntry readOldAsciiEntry() throws IOException {
CpioArchiveEntry ret = new CpioArchiveEntry((short)4);
ret.setDevice(readAsciiLong(6, 8));
ret.setInode(readAsciiLong(6, 8));
long mode = readAsciiLong(6, 8);
if (CpioUtil.fileType(mode) != 0L)
ret.setMode(mode);
ret.setUID(readAsciiLong(6, 8));
ret.setGID(readAsciiLong(6, 8));
ret.setNumberOfLinks(readAsciiLong(6, 8));
ret.setRemoteDevice(readAsciiLong(6, 8));
ret.setTime(readAsciiLong(11, 8));
long namesize = readAsciiLong(6, 8);
ret.setSize(readAsciiLong(11, 8));
String name = readCString((int)namesize);
ret.setName(name);
if (CpioUtil.fileType(mode) == 0L && !name.equals("TRAILER!!!"))
throw new IOException("Mode 0 only allowed in the trailer. Found entry: " + name + " Occured at byte: " + getBytesRead());
return ret;
}
private CpioArchiveEntry readOldBinaryEntry(boolean swapHalfWord) throws IOException {
CpioArchiveEntry ret = new CpioArchiveEntry((short)8);
ret.setDevice(readBinaryLong(2, swapHalfWord));
ret.setInode(readBinaryLong(2, swapHalfWord));
long mode = readBinaryLong(2, swapHalfWord);
if (CpioUtil.fileType(mode) != 0L)
ret.setMode(mode);
ret.setUID(readBinaryLong(2, swapHalfWord));
ret.setGID(readBinaryLong(2, swapHalfWord));
ret.setNumberOfLinks(readBinaryLong(2, swapHalfWord));
ret.setRemoteDevice(readBinaryLong(2, swapHalfWord));
ret.setTime(readBinaryLong(4, swapHalfWord));
long namesize = readBinaryLong(2, swapHalfWord);
ret.setSize(readBinaryLong(4, swapHalfWord));
String name = readCString((int)namesize);
ret.setName(name);
if (CpioUtil.fileType(mode) == 0L && !name.equals("TRAILER!!!"))
throw new IOException("Mode 0 only allowed in the trailer. Found entry: " + name + "Occured at byte: " + getBytesRead());
skip(ret.getHeaderPadCount());
return ret;
}
private String readCString(int length) throws IOException {
byte[] tmpBuffer = new byte[length - 1];
readFully(tmpBuffer, 0, tmpBuffer.length);
this.in.read();
return this.encoding.decode(tmpBuffer);
}
public long skip(long n) throws IOException {
if (n < 0L)
throw new IllegalArgumentException("negative skip length");
ensureOpen();
int max = (int)Math.min(n, Integer.MAX_VALUE);
int total = 0;
while (total < max) {
int len = max - total;
if (len > this.tmpbuf.length)
len = this.tmpbuf.length;
len = read(this.tmpbuf, 0, len);
if (len == -1) {
this.entryEOF = true;
break;
}
total += len;
}
return (long)total;
}
public ArchiveEntry getNextEntry() throws IOException {
return getNextCPIOEntry();
}
private void skipRemainderOfLastBlock() throws IOException {
long readFromLastBlock = getBytesRead() % (long)this.blockSize;
long remainingBytes = (readFromLastBlock == 0L) ? 0L : ((long)this.blockSize - readFromLastBlock);
while (remainingBytes > 0L) {
long skipped = skip((long)this.blockSize - readFromLastBlock);
if (skipped <= 0L)
break;
remainingBytes -= skipped;
}
}
public static boolean matches(byte[] signature, int length) {
if (length < 6)
return false;
if (signature[0] == 113 && (signature[1] & 0xFF) == 199)
return true;
if (signature[1] == 113 && (signature[0] & 0xFF) == 199)
return true;
if (signature[0] != 48)
return false;
if (signature[1] != 55)
return false;
if (signature[2] != 48)
return false;
if (signature[3] != 55)
return false;
if (signature[4] != 48)
return false;
if (signature[5] == 49)
return true;
if (signature[5] == 50)
return true;
if (signature[5] == 55)
return true;
return false;
}
}

View file

@ -0,0 +1,296 @@
package org.apache.commons.compress.archivers.cpio;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.util.HashMap;
import org.apache.commons.compress.archivers.ArchiveEntry;
import org.apache.commons.compress.archivers.ArchiveOutputStream;
import org.apache.commons.compress.archivers.zip.ZipEncoding;
import org.apache.commons.compress.archivers.zip.ZipEncodingHelper;
import org.apache.commons.compress.utils.ArchiveUtils;
public class CpioArchiveOutputStream extends ArchiveOutputStream implements CpioConstants {
private CpioArchiveEntry entry;
private boolean closed = false;
private boolean finished;
private final short entryFormat;
private final HashMap<String, CpioArchiveEntry> names = new HashMap<String, CpioArchiveEntry>();
private long crc = 0L;
private long written;
private final OutputStream out;
private final int blockSize;
private long nextArtificalDeviceAndInode = 1L;
private final ZipEncoding encoding;
public CpioArchiveOutputStream(OutputStream out, short format) {
this(out, format, 512, "US-ASCII");
}
public CpioArchiveOutputStream(OutputStream out, short format, int blockSize) {
this(out, format, blockSize, "US-ASCII");
}
public CpioArchiveOutputStream(OutputStream out, short format, int blockSize, String encoding) {
this.out = out;
switch (format) {
case 1:
case 2:
case 4:
case 8:
break;
default:
throw new IllegalArgumentException("Unknown format: " + format);
}
this.entryFormat = format;
this.blockSize = blockSize;
this.encoding = ZipEncodingHelper.getZipEncoding(encoding);
}
public CpioArchiveOutputStream(OutputStream out) {
this(out, (short)1);
}
public CpioArchiveOutputStream(OutputStream out, String encoding) {
this(out, (short)1, 512, encoding);
}
private void ensureOpen() throws IOException {
if (this.closed)
throw new IOException("Stream closed");
}
public void putArchiveEntry(ArchiveEntry entry) throws IOException {
if (this.finished)
throw new IOException("Stream has already been finished");
CpioArchiveEntry e = (CpioArchiveEntry)entry;
ensureOpen();
if (this.entry != null)
closeArchiveEntry();
if (e.getTime() == -1L)
e.setTime(System.currentTimeMillis() / 1000L);
short format = e.getFormat();
if (format != this.entryFormat)
throw new IOException("Header format: " + format + " does not match existing format: " + this.entryFormat);
if (this.names.put(e.getName(), e) != null)
throw new IOException("duplicate entry: " + e.getName());
writeHeader(e);
this.entry = e;
this.written = 0L;
}
private void writeHeader(CpioArchiveEntry e) throws IOException {
boolean swapHalfWord;
switch (e.getFormat()) {
case 1:
this.out.write(ArchiveUtils.toAsciiBytes("070701"));
count(6);
writeNewEntry(e);
break;
case 2:
this.out.write(ArchiveUtils.toAsciiBytes("070702"));
count(6);
writeNewEntry(e);
break;
case 4:
this.out.write(ArchiveUtils.toAsciiBytes("070707"));
count(6);
writeOldAsciiEntry(e);
break;
case 8:
swapHalfWord = true;
writeBinaryLong(29127L, 2, swapHalfWord);
writeOldBinaryEntry(e, swapHalfWord);
break;
default:
throw new IOException("unknown format " + e.getFormat());
}
}
private void writeNewEntry(CpioArchiveEntry entry) throws IOException {
long inode = entry.getInode();
long devMin = entry.getDeviceMin();
inode = devMin = 0L;
inode = this.nextArtificalDeviceAndInode & 0xFFFFFFFFFFFFFFFFL;
devMin = this.nextArtificalDeviceAndInode++ >> 32L & 0xFFFFFFFFFFFFFFFFL;
this.nextArtificalDeviceAndInode = Math.max(this.nextArtificalDeviceAndInode, inode + 4294967296L * devMin) + 1L;
writeAsciiLong(inode, 8, 16);
writeAsciiLong(entry.getMode(), 8, 16);
writeAsciiLong(entry.getUID(), 8, 16);
writeAsciiLong(entry.getGID(), 8, 16);
writeAsciiLong(entry.getNumberOfLinks(), 8, 16);
writeAsciiLong(entry.getTime(), 8, 16);
writeAsciiLong(entry.getSize(), 8, 16);
writeAsciiLong(entry.getDeviceMaj(), 8, 16);
writeAsciiLong(devMin, 8, 16);
writeAsciiLong(entry.getRemoteDeviceMaj(), 8, 16);
writeAsciiLong(entry.getRemoteDeviceMin(), 8, 16);
writeAsciiLong((long)(entry.getName().length() + 1), 8, 16);
writeAsciiLong(entry.getChksum(), 8, 16);
writeCString(entry.getName());
pad(entry.getHeaderPadCount());
}
private void writeOldAsciiEntry(CpioArchiveEntry entry) throws IOException {
long inode = entry.getInode();
long device = entry.getDevice();
inode = device = 0L;
inode = this.nextArtificalDeviceAndInode & 0x3FFFFL;
device = this.nextArtificalDeviceAndInode++ >> 18L & 0x3FFFFL;
this.nextArtificalDeviceAndInode = Math.max(this.nextArtificalDeviceAndInode, inode + 262144L * device) + 1L;
writeAsciiLong(device, 6, 8);
writeAsciiLong(inode, 6, 8);
writeAsciiLong(entry.getMode(), 6, 8);
writeAsciiLong(entry.getUID(), 6, 8);
writeAsciiLong(entry.getGID(), 6, 8);
writeAsciiLong(entry.getNumberOfLinks(), 6, 8);
writeAsciiLong(entry.getRemoteDevice(), 6, 8);
writeAsciiLong(entry.getTime(), 11, 8);
writeAsciiLong((long)(entry.getName().length() + 1), 6, 8);
writeAsciiLong(entry.getSize(), 11, 8);
writeCString(entry.getName());
}
private void writeOldBinaryEntry(CpioArchiveEntry entry, boolean swapHalfWord) throws IOException {
long inode = entry.getInode();
long device = entry.getDevice();
inode = device = 0L;
inode = this.nextArtificalDeviceAndInode & 0xFFFFL;
device = this.nextArtificalDeviceAndInode++ >> 16L & 0xFFFFL;
this.nextArtificalDeviceAndInode = Math.max(this.nextArtificalDeviceAndInode, inode + 65536L * device) + 1L;
writeBinaryLong(device, 2, swapHalfWord);
writeBinaryLong(inode, 2, swapHalfWord);
writeBinaryLong(entry.getMode(), 2, swapHalfWord);
writeBinaryLong(entry.getUID(), 2, swapHalfWord);
writeBinaryLong(entry.getGID(), 2, swapHalfWord);
writeBinaryLong(entry.getNumberOfLinks(), 2, swapHalfWord);
writeBinaryLong(entry.getRemoteDevice(), 2, swapHalfWord);
writeBinaryLong(entry.getTime(), 4, swapHalfWord);
writeBinaryLong((long)(entry.getName().length() + 1), 2, swapHalfWord);
writeBinaryLong(entry.getSize(), 4, swapHalfWord);
writeCString(entry.getName());
pad(entry.getHeaderPadCount());
}
public void closeArchiveEntry() throws IOException {
if (this.finished)
throw new IOException("Stream has already been finished");
ensureOpen();
if (this.entry == null)
throw new IOException("Trying to close non-existent entry");
if (this.entry.getSize() != this.written)
throw new IOException("invalid entry size (expected " + this.entry.getSize() + " but got " + this.written + " bytes)");
pad(this.entry.getDataPadCount());
if (this.entry.getFormat() == 2 && this.crc != this.entry.getChksum())
throw new IOException("CRC Error");
this.entry = null;
this.crc = 0L;
this.written = 0L;
}
public void write(byte[] b, int off, int len) throws IOException {
ensureOpen();
if (off < 0 || len < 0 || off > b.length - len)
throw new IndexOutOfBoundsException();
if (len == 0)
return;
if (this.entry == null)
throw new IOException("no current CPIO entry");
if (this.written + (long)len > this.entry.getSize())
throw new IOException("attempt to write past end of STORED entry");
this.out.write(b, off, len);
this.written += (long)len;
if (this.entry.getFormat() == 2)
for (int pos = 0; pos < len; pos++)
this.crc += (long)(b[pos] & 0xFF);
count(len);
}
public void finish() throws IOException {
ensureOpen();
if (this.finished)
throw new IOException("This archive has already been finished");
if (this.entry != null)
throw new IOException("This archive contains unclosed entries.");
this.entry = new CpioArchiveEntry(this.entryFormat);
this.entry.setName("TRAILER!!!");
this.entry.setNumberOfLinks(1L);
writeHeader(this.entry);
closeArchiveEntry();
int lengthOfLastBlock = (int)(getBytesWritten() % (long)this.blockSize);
if (lengthOfLastBlock != 0)
pad(this.blockSize - lengthOfLastBlock);
this.finished = true;
}
public void close() throws IOException {
if (!this.finished)
finish();
if (!this.closed) {
this.out.close();
this.closed = true;
}
}
private void pad(int count) throws IOException {
if (count > 0) {
byte[] buff = new byte[count];
this.out.write(buff);
count(count);
}
}
private void writeBinaryLong(long number, int length, boolean swapHalfWord) throws IOException {
byte[] tmp = CpioUtil.long2byteArray(number, length, swapHalfWord);
this.out.write(tmp);
count(tmp.length);
}
private void writeAsciiLong(long number, int length, int radix) throws IOException {
String tmpStr;
StringBuilder tmp = new StringBuilder();
if (radix == 16) {
tmp.append(Long.toHexString(number));
} else if (radix == 8) {
tmp.append(Long.toOctalString(number));
} else {
tmp.append(Long.toString(number));
}
if (tmp.length() <= length) {
long insertLength = (long)(length - tmp.length());
for (int pos = 0; (long)pos < insertLength; pos++)
tmp.insert(0, "0");
tmpStr = tmp.toString();
} else {
tmpStr = tmp.substring(tmp.length() - length);
}
byte[] b = ArchiveUtils.toAsciiBytes(tmpStr);
this.out.write(b);
count(b.length);
}
private void writeCString(String str) throws IOException {
ByteBuffer buf = this.encoding.encode(str);
int len = buf.limit() - buf.position();
this.out.write(buf.array(), buf.arrayOffset(), len);
this.out.write(0);
count(len + 1);
}
public ArchiveEntry createArchiveEntry(File inputFile, String entryName) throws IOException {
if (this.finished)
throw new IOException("Stream has already been finished");
return new CpioArchiveEntry(inputFile, entryName);
}
}

View file

@ -0,0 +1,69 @@
package org.apache.commons.compress.archivers.cpio;
public interface CpioConstants {
public static final String MAGIC_NEW = "070701";
public static final String MAGIC_NEW_CRC = "070702";
public static final String MAGIC_OLD_ASCII = "070707";
public static final int MAGIC_OLD_BINARY = 29127;
public static final short FORMAT_NEW = 1;
public static final short FORMAT_NEW_CRC = 2;
public static final short FORMAT_OLD_ASCII = 4;
public static final short FORMAT_OLD_BINARY = 8;
public static final short FORMAT_NEW_MASK = 3;
public static final short FORMAT_OLD_MASK = 12;
public static final int S_IFMT = 61440;
public static final int C_ISSOCK = 49152;
public static final int C_ISLNK = 40960;
public static final int C_ISNWK = 36864;
public static final int C_ISREG = 32768;
public static final int C_ISBLK = 24576;
public static final int C_ISDIR = 16384;
public static final int C_ISCHR = 8192;
public static final int C_ISFIFO = 4096;
public static final int C_ISUID = 2048;
public static final int C_ISGID = 1024;
public static final int C_ISVTX = 512;
public static final int C_IRUSR = 256;
public static final int C_IWUSR = 128;
public static final int C_IXUSR = 64;
public static final int C_IRGRP = 32;
public static final int C_IWGRP = 16;
public static final int C_IXGRP = 8;
public static final int C_IROTH = 4;
public static final int C_IWOTH = 2;
public static final int C_IXOTH = 1;
public static final String CPIO_TRAILER = "TRAILER!!!";
public static final int BLOCK_SIZE = 512;
}

View file

@ -0,0 +1,52 @@
package org.apache.commons.compress.archivers.cpio;
class CpioUtil {
static long fileType(long mode) {
return mode & 0xF000L;
}
static long byteArray2long(byte[] number, boolean swapHalfWord) {
if (number.length % 2 != 0)
throw new UnsupportedOperationException();
long ret = 0L;
int pos = 0;
byte[] tmp_number = new byte[number.length];
System.arraycopy(number, 0, tmp_number, 0, number.length);
if (!swapHalfWord) {
byte tmp = 0;
for (int i = 0; i < tmp_number.length; i++) {
tmp = tmp_number[i];
tmp_number[i++] = tmp_number[i];
tmp_number[i] = tmp;
}
}
ret = (long)(tmp_number[0] & 0xFF);
for (pos = 1; pos < tmp_number.length; pos++) {
ret <<= 8L;
ret |= (long)(tmp_number[pos] & 0xFF);
}
return ret;
}
static byte[] long2byteArray(long number, int length, boolean swapHalfWord) {
byte[] ret = new byte[length];
int pos = 0;
long tmp_number = 0L;
if (length % 2 != 0 || length < 2)
throw new UnsupportedOperationException();
tmp_number = number;
for (pos = length - 1; pos >= 0; pos--) {
ret[pos] = (byte)(int)(tmp_number & 0xFFL);
tmp_number >>= 8L;
}
if (!swapHalfWord) {
byte tmp = 0;
for (int i = 0; i < length; i++) {
tmp = ret[i];
ret[i++] = ret[i];
ret[i] = tmp;
}
}
return ret;
}
}

View file

@ -0,0 +1,38 @@
package org.apache.commons.compress.archivers.dump;
class Dirent {
private final int ino;
private final int parentIno;
private final int type;
private final String name;
Dirent(int ino, int parentIno, int type, String name) {
this.ino = ino;
this.parentIno = parentIno;
this.type = type;
this.name = name;
}
int getIno() {
return this.ino;
}
int getParentIno() {
return this.parentIno;
}
int getType() {
return this.type;
}
String getName() {
return this.name;
}
public String toString() {
return String.format("[%d]: %s", this.ino, this.name);
}
}

View file

@ -0,0 +1,64 @@
package org.apache.commons.compress.archivers.dump;
public final class DumpArchiveConstants {
public static final int TP_SIZE = 1024;
public static final int NTREC = 10;
public static final int HIGH_DENSITY_NTREC = 32;
public static final int OFS_MAGIC = 60011;
public static final int NFS_MAGIC = 60012;
public static final int FS_UFS2_MAGIC = 424935705;
public static final int CHECKSUM = 84446;
public static final int LBLSIZE = 16;
public static final int NAMELEN = 64;
public enum SEGMENT_TYPE {
TAPE(1),
INODE(2),
BITS(3),
ADDR(4),
END(5),
CLRI(6);
int code;
SEGMENT_TYPE(int code) {
this.code = code;
}
public static SEGMENT_TYPE find(int code) {
for (SEGMENT_TYPE t : values()) {
if (t.code == code)
return t;
}
return null;
}
}
public enum COMPRESSION_TYPE {
ZLIB(0),
BZLIB(1),
LZO(2);
int code;
COMPRESSION_TYPE(int code) {
this.code = code;
}
public static COMPRESSION_TYPE find(int code) {
for (COMPRESSION_TYPE t : values()) {
if (t.code == code)
return t;
}
return null;
}
}
}

View file

@ -0,0 +1,416 @@
package org.apache.commons.compress.archivers.dump;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.Set;
import org.apache.commons.compress.archivers.ArchiveEntry;
public class DumpArchiveEntry implements ArchiveEntry {
private String name;
private TYPE type = TYPE.UNKNOWN;
private int mode;
private Set<PERMISSION> permissions = Collections.<PERMISSION>emptySet();
private long size;
private long atime;
private long mtime;
private int uid;
private int gid;
private final DumpArchiveSummary summary = null;
private final TapeSegmentHeader header = new TapeSegmentHeader();
private String simpleName;
private String originalName;
private int volume;
private long offset;
private int ino;
private int nlink;
private long ctime;
private int generation;
private boolean isDeleted;
public DumpArchiveEntry() {}
public DumpArchiveEntry(String name, String simpleName) {
setName(name);
this.simpleName = simpleName;
}
protected DumpArchiveEntry(String name, String simpleName, int ino, TYPE type) {
setType(type);
setName(name);
this.simpleName = simpleName;
this.ino = ino;
this.offset = 0L;
}
public String getSimpleName() {
return this.simpleName;
}
protected void setSimpleName(String simpleName) {
this.simpleName = simpleName;
}
public int getIno() {
return this.header.getIno();
}
public int getNlink() {
return this.nlink;
}
public void setNlink(int nlink) {
this.nlink = nlink;
}
public Date getCreationTime() {
return new Date(this.ctime);
}
public void setCreationTime(Date ctime) {
this.ctime = ctime.getTime();
}
public int getGeneration() {
return this.generation;
}
public void setGeneration(int generation) {
this.generation = generation;
}
public boolean isDeleted() {
return this.isDeleted;
}
public void setDeleted(boolean isDeleted) {
this.isDeleted = isDeleted;
}
public long getOffset() {
return this.offset;
}
public void setOffset(long offset) {
this.offset = offset;
}
public int getVolume() {
return this.volume;
}
public void setVolume(int volume) {
this.volume = volume;
}
public DumpArchiveConstants.SEGMENT_TYPE getHeaderType() {
return this.header.getType();
}
public int getHeaderCount() {
return this.header.getCount();
}
public int getHeaderHoles() {
return this.header.getHoles();
}
public boolean isSparseRecord(int idx) {
return ((this.header.getCdata(idx) & 0x1) == 0);
}
public int hashCode() {
return this.ino;
}
public boolean equals(Object o) {
if (o == this)
return true;
if (o == null || !o.getClass().equals(getClass()))
return false;
DumpArchiveEntry rhs = (DumpArchiveEntry)o;
if (this.header == null || rhs.header == null)
return false;
if (this.ino != rhs.ino)
return false;
if ((this.summary == null && rhs.summary != null) || (this.summary != null && !this.summary.equals(rhs.summary)))
return false;
return true;
}
public String toString() {
return getName();
}
static DumpArchiveEntry parse(byte[] buffer) {
DumpArchiveEntry entry = new DumpArchiveEntry();
TapeSegmentHeader header = entry.header;
header.type = DumpArchiveConstants.SEGMENT_TYPE.find(DumpArchiveUtil.convert32(buffer, 0));
header.volume = DumpArchiveUtil.convert32(buffer, 12);
entry.ino = header.ino = DumpArchiveUtil.convert32(buffer, 20);
int m = DumpArchiveUtil.convert16(buffer, 32);
entry.setType(TYPE.find(m >> 12 & 0xF));
entry.setMode(m);
entry.nlink = DumpArchiveUtil.convert16(buffer, 34);
entry.setSize(DumpArchiveUtil.convert64(buffer, 40));
long t = 1000L * (long)DumpArchiveUtil.convert32(buffer, 48) + (long)(DumpArchiveUtil.convert32(buffer, 52) / 1000);
entry.setAccessTime(new Date(t));
t = 1000L * (long)DumpArchiveUtil.convert32(buffer, 56) + (long)(DumpArchiveUtil.convert32(buffer, 60) / 1000);
entry.setLastModifiedDate(new Date(t));
t = 1000L * (long)DumpArchiveUtil.convert32(buffer, 64) + (long)(DumpArchiveUtil.convert32(buffer, 68) / 1000);
entry.ctime = t;
entry.generation = DumpArchiveUtil.convert32(buffer, 140);
entry.setUserId(DumpArchiveUtil.convert32(buffer, 144));
entry.setGroupId(DumpArchiveUtil.convert32(buffer, 148));
header.count = DumpArchiveUtil.convert32(buffer, 160);
header.holes = 0;
for (int i = 0; i < 512 && i < header.count; i++) {
if (buffer[164 + i] == 0)
header.holes++;
}
System.arraycopy(buffer, 164, header.cdata, 0, 512);
entry.volume = header.getVolume();
return entry;
}
void update(byte[] buffer) {
this.header.volume = DumpArchiveUtil.convert32(buffer, 16);
this.header.count = DumpArchiveUtil.convert32(buffer, 160);
this.header.holes = 0;
for (int i = 0; i < 512 && i < this.header.count; i++) {
if (buffer[164 + i] == 0)
this.header.holes++;
}
System.arraycopy(buffer, 164, this.header.cdata, 0, 512);
}
static class TapeSegmentHeader {
private DumpArchiveConstants.SEGMENT_TYPE type;
private int volume;
private int ino;
private int count;
private int holes;
private final byte[] cdata = new byte[512];
public DumpArchiveConstants.SEGMENT_TYPE getType() {
return this.type;
}
public int getVolume() {
return this.volume;
}
public int getIno() {
return this.ino;
}
void setIno(int ino) {
this.ino = ino;
}
public int getCount() {
return this.count;
}
public int getHoles() {
return this.holes;
}
public int getCdata(int idx) {
return this.cdata[idx];
}
}
public String getName() {
return this.name;
}
String getOriginalName() {
return this.originalName;
}
public final void setName(String name) {
this.originalName = name;
if (name != null) {
if (isDirectory() && !name.endsWith("/"))
name = name + "/";
if (name.startsWith("./"))
name = name.substring(2);
}
this.name = name;
}
public Date getLastModifiedDate() {
return new Date(this.mtime);
}
public boolean isDirectory() {
return (this.type == TYPE.DIRECTORY);
}
public boolean isFile() {
return (this.type == TYPE.FILE);
}
public boolean isSocket() {
return (this.type == TYPE.SOCKET);
}
public boolean isChrDev() {
return (this.type == TYPE.CHRDEV);
}
public boolean isBlkDev() {
return (this.type == TYPE.BLKDEV);
}
public boolean isFifo() {
return (this.type == TYPE.FIFO);
}
public TYPE getType() {
return this.type;
}
public void setType(TYPE type) {
this.type = type;
}
public int getMode() {
return this.mode;
}
public void setMode(int mode) {
this.mode = mode & 0xFFF;
this.permissions = PERMISSION.find(mode);
}
public Set<PERMISSION> getPermissions() {
return this.permissions;
}
public long getSize() {
return isDirectory() ? -1L : this.size;
}
long getEntrySize() {
return this.size;
}
public void setSize(long size) {
this.size = size;
}
public void setLastModifiedDate(Date mtime) {
this.mtime = mtime.getTime();
}
public Date getAccessTime() {
return new Date(this.atime);
}
public void setAccessTime(Date atime) {
this.atime = atime.getTime();
}
public int getUserId() {
return this.uid;
}
public void setUserId(int uid) {
this.uid = uid;
}
public int getGroupId() {
return this.gid;
}
public void setGroupId(int gid) {
this.gid = gid;
}
public enum TYPE {
WHITEOUT(14),
SOCKET(12),
LINK(10),
FILE(8),
BLKDEV(6),
DIRECTORY(4),
CHRDEV(2),
FIFO(1),
UNKNOWN(15);
private int code;
TYPE(int code) {
this.code = code;
}
public static TYPE find(int code) {
TYPE type = UNKNOWN;
for (TYPE t : values()) {
if (code == t.code)
type = t;
}
return type;
}
}
public enum PERMISSION {
SETUID(2048),
SETGUI(1024),
STICKY(512),
USER_READ(256),
USER_WRITE(128),
USER_EXEC(64),
GROUP_READ(32),
GROUP_WRITE(16),
GROUP_EXEC(8),
WORLD_READ(4),
WORLD_WRITE(2),
WORLD_EXEC(1);
private int code;
PERMISSION(int code) {
this.code = code;
}
public static Set<PERMISSION> find(int code) {
Set<PERMISSION> set = new HashSet<PERMISSION>();
for (PERMISSION p : values()) {
if ((code & p.code) == p.code)
set.add(p);
}
if (set.isEmpty())
return Collections.<PERMISSION>emptySet();
return EnumSet.<PERMISSION>copyOf((Collection)set);
}
}
}

View file

@ -0,0 +1,22 @@
package org.apache.commons.compress.archivers.dump;
import java.io.IOException;
public class DumpArchiveException extends IOException {
private static final long serialVersionUID = 1L;
public DumpArchiveException() {}
public DumpArchiveException(String msg) {
super(msg);
}
public DumpArchiveException(Throwable cause) {
initCause(cause);
}
public DumpArchiveException(String msg, Throwable cause) {
super(msg);
initCause(cause);
}
}

View file

@ -0,0 +1,299 @@
package org.apache.commons.compress.archivers.dump;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.PriorityQueue;
import java.util.Queue;
import java.util.Stack;
import org.apache.commons.compress.archivers.ArchiveEntry;
import org.apache.commons.compress.archivers.ArchiveException;
import org.apache.commons.compress.archivers.ArchiveInputStream;
import org.apache.commons.compress.archivers.zip.ZipEncoding;
import org.apache.commons.compress.archivers.zip.ZipEncodingHelper;
public class DumpArchiveInputStream extends ArchiveInputStream {
private DumpArchiveSummary summary;
private DumpArchiveEntry active;
private boolean isClosed;
private boolean hasHitEOF;
private long entrySize;
private long entryOffset;
private int readIdx;
private final byte[] readBuf = new byte[1024];
private byte[] blockBuffer;
private int recordOffset;
private long filepos;
protected TapeInputStream raw;
private final Map<Integer, Dirent> names = new HashMap<Integer, Dirent>();
private final Map<Integer, DumpArchiveEntry> pending = new HashMap<Integer, DumpArchiveEntry>();
private Queue<DumpArchiveEntry> queue;
private final ZipEncoding encoding;
public DumpArchiveInputStream(InputStream is) throws ArchiveException {
this(is, null);
}
public DumpArchiveInputStream(InputStream is, String encoding) throws ArchiveException {
this.raw = new TapeInputStream(is);
this.hasHitEOF = false;
this.encoding = ZipEncodingHelper.getZipEncoding(encoding);
try {
byte[] headerBytes = this.raw.readRecord();
if (!DumpArchiveUtil.verify(headerBytes))
throw new UnrecognizedFormatException();
this.summary = new DumpArchiveSummary(headerBytes, this.encoding);
this.raw.resetBlockSize(this.summary.getNTRec(), this.summary.isCompressed());
this.blockBuffer = new byte[4096];
readCLRI();
readBITS();
} catch (IOException ex) {
throw new ArchiveException(ex.getMessage(), ex);
}
Dirent root = new Dirent(2, 2, 4, ".");
this.names.put(Integer.valueOf(2), root);
this.queue = new PriorityQueue<DumpArchiveEntry>(10, new Comparator<DumpArchiveEntry>() {
public int compare(DumpArchiveEntry p, DumpArchiveEntry q) {
if (p.getOriginalName() == null || q.getOriginalName() == null)
return Integer.MAX_VALUE;
return p.getOriginalName().compareTo(q.getOriginalName());
}
});
}
@Deprecated
public int getCount() {
return (int)getBytesRead();
}
public long getBytesRead() {
return this.raw.getBytesRead();
}
public DumpArchiveSummary getSummary() {
return this.summary;
}
private void readCLRI() throws IOException {
byte[] buffer = this.raw.readRecord();
if (!DumpArchiveUtil.verify(buffer))
throw new InvalidFormatException();
this.active = DumpArchiveEntry.parse(buffer);
if (DumpArchiveConstants.SEGMENT_TYPE.CLRI != this.active.getHeaderType())
throw new InvalidFormatException();
if (this.raw.skip((long)(1024 * this.active.getHeaderCount())) == -1L)
throw new EOFException();
this.readIdx = this.active.getHeaderCount();
}
private void readBITS() throws IOException {
byte[] buffer = this.raw.readRecord();
if (!DumpArchiveUtil.verify(buffer))
throw new InvalidFormatException();
this.active = DumpArchiveEntry.parse(buffer);
if (DumpArchiveConstants.SEGMENT_TYPE.BITS != this.active.getHeaderType())
throw new InvalidFormatException();
if (this.raw.skip((long)(1024 * this.active.getHeaderCount())) == -1L)
throw new EOFException();
this.readIdx = this.active.getHeaderCount();
}
public DumpArchiveEntry getNextDumpEntry() throws IOException {
return getNextEntry();
}
public DumpArchiveEntry getNextEntry() throws IOException {
DumpArchiveEntry entry = null;
String path = null;
if (!this.queue.isEmpty())
return this.queue.remove();
while (entry == null) {
if (this.hasHitEOF)
return null;
while (this.readIdx < this.active.getHeaderCount()) {
if (!this.active.isSparseRecord(this.readIdx++) && this.raw.skip(1024L) == -1L)
throw new EOFException();
}
this.readIdx = 0;
this.filepos = this.raw.getBytesRead();
byte[] headerBytes = this.raw.readRecord();
if (!DumpArchiveUtil.verify(headerBytes))
throw new InvalidFormatException();
this.active = DumpArchiveEntry.parse(headerBytes);
while (DumpArchiveConstants.SEGMENT_TYPE.ADDR == this.active.getHeaderType()) {
if (this.raw.skip((long)(1024 * (this.active.getHeaderCount() - this.active.getHeaderHoles()))) == -1L)
throw new EOFException();
this.filepos = this.raw.getBytesRead();
headerBytes = this.raw.readRecord();
if (!DumpArchiveUtil.verify(headerBytes))
throw new InvalidFormatException();
this.active = DumpArchiveEntry.parse(headerBytes);
}
if (DumpArchiveConstants.SEGMENT_TYPE.END == this.active.getHeaderType()) {
this.hasHitEOF = true;
return null;
}
entry = this.active;
if (entry.isDirectory()) {
readDirectoryEntry(this.active);
this.entryOffset = 0L;
this.entrySize = 0L;
this.readIdx = this.active.getHeaderCount();
} else {
this.entryOffset = 0L;
this.entrySize = this.active.getEntrySize();
this.readIdx = 0;
}
this.recordOffset = this.readBuf.length;
path = getPath(entry);
if (path == null)
entry = null;
}
entry.setName(path);
entry.setSimpleName(this.names.get(Integer.valueOf(entry.getIno())).getName());
entry.setOffset(this.filepos);
return entry;
}
private void readDirectoryEntry(DumpArchiveEntry entry) throws IOException {
long size = entry.getEntrySize();
boolean first = true;
while (first || DumpArchiveConstants.SEGMENT_TYPE.ADDR == entry.getHeaderType()) {
if (!first)
this.raw.readRecord();
if (!this.names.containsKey(Integer.valueOf(entry.getIno())) && DumpArchiveConstants.SEGMENT_TYPE.INODE == entry.getHeaderType())
this.pending.put(Integer.valueOf(entry.getIno()), entry);
int datalen = 1024 * entry.getHeaderCount();
if (this.blockBuffer.length < datalen)
this.blockBuffer = new byte[datalen];
if (this.raw.read(this.blockBuffer, 0, datalen) != datalen)
throw new EOFException();
int reclen = 0;
for (int i = 0; i < datalen - 8 && (long)i < size - 8L;
i += reclen) {
int ino = DumpArchiveUtil.convert32(this.blockBuffer, i);
reclen = DumpArchiveUtil.convert16(this.blockBuffer, i + 4);
byte type = this.blockBuffer[i + 6];
String name = DumpArchiveUtil.decode(this.encoding, this.blockBuffer, i + 8, this.blockBuffer[i + 7]);
if (!".".equals(name) && !"..".equals(name)) {
Dirent d = new Dirent(ino, entry.getIno(), type, name);
this.names.put(Integer.valueOf(ino), d);
for (Map.Entry<Integer, DumpArchiveEntry> e : this.pending.entrySet()) {
String path = getPath(e.getValue());
if (path != null) {
e.getValue().setName(path);
e.getValue().setSimpleName(this.names.get(e.getKey()).getName());
this.queue.add(e.getValue());
}
}
for (DumpArchiveEntry e : this.queue)
this.pending.remove(Integer.valueOf(e.getIno()));
}
}
byte[] peekBytes = this.raw.peek();
if (!DumpArchiveUtil.verify(peekBytes))
throw new InvalidFormatException();
entry = DumpArchiveEntry.parse(peekBytes);
first = false;
size -= 1024L;
}
}
private String getPath(DumpArchiveEntry entry) {
Stack<String> elements = new Stack<String>();
Dirent dirent = null;
for (int i = entry.getIno();; i = dirent.getParentIno()) {
if (!this.names.containsKey(Integer.valueOf(i))) {
elements.clear();
break;
}
dirent = this.names.get(Integer.valueOf(i));
elements.push(dirent.getName());
if (dirent.getIno() == dirent.getParentIno())
break;
}
if (elements.isEmpty()) {
this.pending.put(Integer.valueOf(entry.getIno()), entry);
return null;
}
StringBuilder sb = new StringBuilder(elements.pop());
while (!elements.isEmpty()) {
sb.append('/');
sb.append(elements.pop());
}
return sb.toString();
}
public int read(byte[] buf, int off, int len) throws IOException {
int totalRead = 0;
if (this.hasHitEOF || this.isClosed || this.entryOffset >= this.entrySize)
return -1;
if (this.active == null)
throw new IllegalStateException("No current dump entry");
if ((long)len + this.entryOffset > this.entrySize)
len = (int)(this.entrySize - this.entryOffset);
while (len > 0) {
int sz = (len > this.readBuf.length - this.recordOffset) ? (this.readBuf.length - this.recordOffset) : len;
if (this.recordOffset + sz <= this.readBuf.length) {
System.arraycopy(this.readBuf, this.recordOffset, buf, off, sz);
totalRead += sz;
this.recordOffset += sz;
len -= sz;
off += sz;
}
if (len > 0) {
if (this.readIdx >= 512) {
byte[] headerBytes = this.raw.readRecord();
if (!DumpArchiveUtil.verify(headerBytes))
throw new InvalidFormatException();
this.active = DumpArchiveEntry.parse(headerBytes);
this.readIdx = 0;
}
if (!this.active.isSparseRecord(this.readIdx++)) {
int r = this.raw.read(this.readBuf, 0, this.readBuf.length);
if (r != this.readBuf.length)
throw new EOFException();
} else {
Arrays.fill(this.readBuf, (byte)0);
}
this.recordOffset = 0;
}
}
this.entryOffset += (long)totalRead;
return totalRead;
}
public void close() throws IOException {
if (!this.isClosed) {
this.isClosed = true;
this.raw.close();
}
}
public static boolean matches(byte[] buffer, int length) {
if (length < 32)
return false;
if (length >= 1024)
return DumpArchiveUtil.verify(buffer);
return (60012 == DumpArchiveUtil.convert32(buffer, 24));
}
}

View file

@ -0,0 +1,178 @@
package org.apache.commons.compress.archivers.dump;
import java.io.IOException;
import java.util.Date;
import org.apache.commons.compress.archivers.zip.ZipEncoding;
public class DumpArchiveSummary {
private long dumpDate;
private long previousDumpDate;
private int volume;
private String label;
private int level;
private String filesys;
private String devname;
private String hostname;
private int flags;
private int firstrec;
private int ntrec;
DumpArchiveSummary(byte[] buffer, ZipEncoding encoding) throws IOException {
this.dumpDate = 1000L * (long)DumpArchiveUtil.convert32(buffer, 4);
this.previousDumpDate = 1000L * (long)DumpArchiveUtil.convert32(buffer, 8);
this.volume = DumpArchiveUtil.convert32(buffer, 12);
this.label = DumpArchiveUtil.decode(encoding, buffer, 676, 16).trim();
this.level = DumpArchiveUtil.convert32(buffer, 692);
this.filesys = DumpArchiveUtil.decode(encoding, buffer, 696, 64).trim();
this.devname = DumpArchiveUtil.decode(encoding, buffer, 760, 64).trim();
this.hostname = DumpArchiveUtil.decode(encoding, buffer, 824, 64).trim();
this.flags = DumpArchiveUtil.convert32(buffer, 888);
this.firstrec = DumpArchiveUtil.convert32(buffer, 892);
this.ntrec = DumpArchiveUtil.convert32(buffer, 896);
}
public Date getDumpDate() {
return new Date(this.dumpDate);
}
public void setDumpDate(Date dumpDate) {
this.dumpDate = dumpDate.getTime();
}
public Date getPreviousDumpDate() {
return new Date(this.previousDumpDate);
}
public void setPreviousDumpDate(Date previousDumpDate) {
this.previousDumpDate = previousDumpDate.getTime();
}
public int getVolume() {
return this.volume;
}
public void setVolume(int volume) {
this.volume = volume;
}
public int getLevel() {
return this.level;
}
public void setLevel(int level) {
this.level = level;
}
public String getLabel() {
return this.label;
}
public void setLabel(String label) {
this.label = label;
}
public String getFilesystem() {
return this.filesys;
}
public void setFilesystem(String filesystem) {
this.filesys = filesystem;
}
public String getDevname() {
return this.devname;
}
public void setDevname(String devname) {
this.devname = devname;
}
public String getHostname() {
return this.hostname;
}
public void setHostname(String hostname) {
this.hostname = hostname;
}
public int getFlags() {
return this.flags;
}
public void setFlags(int flags) {
this.flags = flags;
}
public int getFirstRecord() {
return this.firstrec;
}
public void setFirstRecord(int firstrec) {
this.firstrec = firstrec;
}
public int getNTRec() {
return this.ntrec;
}
public void setNTRec(int ntrec) {
this.ntrec = ntrec;
}
public boolean isNewHeader() {
return ((this.flags & 0x1) == 1);
}
public boolean isNewInode() {
return ((this.flags & 0x2) == 2);
}
public boolean isCompressed() {
return ((this.flags & 0x80) == 128);
}
public boolean isMetaDataOnly() {
return ((this.flags & 0x100) == 256);
}
public boolean isExtendedAttributes() {
return ((this.flags & 0x8000) == 32768);
}
public int hashCode() {
int hash = 17;
if (this.label != null)
hash = this.label.hashCode();
hash = (int)((long)hash + 31L * this.dumpDate);
if (this.hostname != null)
hash = 31 * this.hostname.hashCode() + 17;
if (this.devname != null)
hash = 31 * this.devname.hashCode() + 17;
return hash;
}
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || !o.getClass().equals(getClass()))
return false;
DumpArchiveSummary rhs = (DumpArchiveSummary)o;
if (this.dumpDate != rhs.dumpDate)
return false;
if (getHostname() == null || !getHostname().equals(rhs.getHostname()))
return false;
if (getDevname() == null || !getDevname().equals(rhs.getDevname()))
return false;
return true;
}
}

View file

@ -0,0 +1,62 @@
package org.apache.commons.compress.archivers.dump;
import java.io.IOException;
import org.apache.commons.compress.archivers.zip.ZipEncoding;
class DumpArchiveUtil {
public static int calculateChecksum(byte[] buffer) {
int calc = 0;
for (int i = 0; i < 256; i++)
calc += convert32(buffer, 4 * i);
return 84446 - (calc - convert32(buffer, 28));
}
public static final boolean verify(byte[] buffer) {
int magic = convert32(buffer, 24);
if (magic != 60012)
return false;
int checksum = convert32(buffer, 28);
if (checksum != calculateChecksum(buffer))
return false;
return true;
}
public static final int getIno(byte[] buffer) {
return convert32(buffer, 20);
}
public static final long convert64(byte[] buffer, int offset) {
long i = 0L;
i += (long)buffer[offset + 7] << 56L;
i += (long)buffer[offset + 6] << 48L & 0xFF000000000000L;
i += (long)buffer[offset + 5] << 40L & 0xFF0000000000L;
i += (long)buffer[offset + 4] << 32L & 0xFF00000000L;
i += (long)buffer[offset + 3] << 24L & 0xFF000000L;
i += (long)buffer[offset + 2] << 16L & 0xFF0000L;
i += (long)buffer[offset + 1] << 8L & 0xFF00L;
i += (long)buffer[offset] & 0xFFL;
return i;
}
public static final int convert32(byte[] buffer, int offset) {
int i = 0;
i = buffer[offset + 3] << 24;
i += buffer[offset + 2] << 16 & 0xFF0000;
i += buffer[offset + 1] << 8 & 0xFF00;
i += buffer[offset] & 0xFF;
return i;
}
public static final int convert16(byte[] buffer, int offset) {
int i = 0;
i += buffer[offset + 1] << 8 & 0xFF00;
i += buffer[offset] & 0xFF;
return i;
}
static String decode(ZipEncoding encoding, byte[] b, int offset, int len) throws IOException {
byte[] copy = new byte[len];
System.arraycopy(b, offset, copy, 0, len);
return encoding.decode(copy);
}
}

View file

@ -0,0 +1,20 @@
package org.apache.commons.compress.archivers.dump;
public class InvalidFormatException extends DumpArchiveException {
private static final long serialVersionUID = 1L;
protected long offset;
public InvalidFormatException() {
super("there was an error decoding a tape segment");
}
public InvalidFormatException(long offset) {
super("there was an error decoding a tape segment header at offset " + offset + ".");
this.offset = offset;
}
public long getOffset() {
return this.offset;
}
}

View file

@ -0,0 +1,9 @@
package org.apache.commons.compress.archivers.dump;
public class ShortFileException extends DumpArchiveException {
private static final long serialVersionUID = 1L;
public ShortFileException() {
super("unexpected EOF");
}
}

View file

@ -0,0 +1,175 @@
package org.apache.commons.compress.archivers.dump;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.zip.DataFormatException;
import java.util.zip.Inflater;
import org.apache.commons.compress.utils.IOUtils;
class TapeInputStream extends FilterInputStream {
private byte[] blockBuffer = new byte[1024];
private int currBlkIdx = -1;
private int blockSize = 1024;
private static final int recordSize = 1024;
private int readOffset = 1024;
private boolean isCompressed = false;
private long bytesRead = 0L;
public TapeInputStream(InputStream in) {
super(in);
}
public void resetBlockSize(int recsPerBlock, boolean isCompressed) throws IOException {
this.isCompressed = isCompressed;
this.blockSize = 1024 * recsPerBlock;
byte[] oldBuffer = this.blockBuffer;
this.blockBuffer = new byte[this.blockSize];
System.arraycopy(oldBuffer, 0, this.blockBuffer, 0, 1024);
readFully(this.blockBuffer, 1024, this.blockSize - 1024);
this.currBlkIdx = 0;
this.readOffset = 1024;
}
public int available() throws IOException {
if (this.readOffset < this.blockSize)
return this.blockSize - this.readOffset;
return this.in.available();
}
public int read() throws IOException {
throw new IllegalArgumentException("all reads must be multiple of record size (1024 bytes.");
}
public int read(byte[] b, int off, int len) throws IOException {
if (len % 1024 != 0)
throw new IllegalArgumentException("all reads must be multiple of record size (1024 bytes.");
int bytes = 0;
while (bytes < len) {
if (this.readOffset == this.blockSize && !readBlock(true))
return -1;
int n = 0;
if (this.readOffset + len - bytes <= this.blockSize) {
n = len - bytes;
} else {
n = this.blockSize - this.readOffset;
}
System.arraycopy(this.blockBuffer, this.readOffset, b, off, n);
this.readOffset += n;
bytes += n;
off += n;
}
return bytes;
}
public long skip(long len) throws IOException {
if (len % 1024L != 0L)
throw new IllegalArgumentException("all reads must be multiple of record size (1024 bytes.");
long bytes = 0L;
while (bytes < len) {
if (this.readOffset == this.blockSize)
if (!readBlock((len - bytes < (long)this.blockSize)))
return -1L;
long n = 0L;
if ((long)this.readOffset + len - bytes <= (long)this.blockSize) {
n = len - bytes;
} else {
n = (long)(this.blockSize - this.readOffset);
}
this.readOffset = (int)((long)this.readOffset + n);
bytes += n;
}
return bytes;
}
public void close() throws IOException {
if (this.in != null && this.in != System.in)
this.in.close();
}
public byte[] peek() throws IOException {
if (this.readOffset == this.blockSize && !readBlock(true))
return null;
byte[] b = new byte[1024];
System.arraycopy(this.blockBuffer, this.readOffset, b, 0, b.length);
return b;
}
public byte[] readRecord() throws IOException {
byte[] result = new byte[1024];
if (-1 == read(result, 0, result.length))
throw new ShortFileException();
return result;
}
private boolean readBlock(boolean decompress) throws IOException {
boolean success = true;
if (this.in == null)
throw new IOException("input buffer is closed");
if (!this.isCompressed || this.currBlkIdx == -1) {
success = readFully(this.blockBuffer, 0, this.blockSize);
this.bytesRead += (long)this.blockSize;
} else {
if (!readFully(this.blockBuffer, 0, 4))
return false;
this.bytesRead += 4L;
int h = DumpArchiveUtil.convert32(this.blockBuffer, 0);
boolean compressed = ((h & 0x1) == 1);
if (!compressed) {
success = readFully(this.blockBuffer, 0, this.blockSize);
this.bytesRead += (long)this.blockSize;
} else {
int flags = h >> 1 & 0x7;
int length = h >> 4 & 0xFFFFFFF;
byte[] compBuffer = new byte[length];
success = readFully(compBuffer, 0, length);
this.bytesRead += (long)length;
if (!decompress) {
Arrays.fill(this.blockBuffer, (byte)0);
} else {
switch (DumpArchiveConstants.COMPRESSION_TYPE.find(flags & 0x3)) {
case ZLIB:
try {
Inflater inflator = new Inflater();
inflator.setInput(compBuffer, 0, compBuffer.length);
length = inflator.inflate(this.blockBuffer);
if (length != this.blockSize)
throw new ShortFileException();
inflator.end();
} catch (DataFormatException e) {
throw new DumpArchiveException("bad data", e);
}
break;
case BZLIB:
throw new UnsupportedCompressionAlgorithmException("BZLIB2");
case LZO:
throw new UnsupportedCompressionAlgorithmException("LZO");
default:
throw new UnsupportedCompressionAlgorithmException();
}
}
}
}
this.currBlkIdx++;
this.readOffset = 0;
return success;
}
private boolean readFully(byte[] b, int off, int len) throws IOException {
int count = IOUtils.readFully(this.in, b, off, len);
if (count < len)
throw new ShortFileException();
return true;
}
public long getBytesRead() {
return this.bytesRead;
}
}

View file

@ -0,0 +1,9 @@
package org.apache.commons.compress.archivers.dump;
public class UnrecognizedFormatException extends DumpArchiveException {
private static final long serialVersionUID = 1L;
public UnrecognizedFormatException() {
super("this is not a recognized format.");
}
}

View file

@ -0,0 +1,13 @@
package org.apache.commons.compress.archivers.dump;
public class UnsupportedCompressionAlgorithmException extends DumpArchiveException {
private static final long serialVersionUID = 1L;
public UnsupportedCompressionAlgorithmException() {
super("this file uses an unsupported compression algorithm.");
}
public UnsupportedCompressionAlgorithmException(String alg) {
super("this file uses an unsupported compression algorithm: " + alg + ".");
}
}

View file

@ -0,0 +1,45 @@
package org.apache.commons.compress.archivers.jar;
import java.security.cert.Certificate;
import java.util.jar.Attributes;
import java.util.jar.JarEntry;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
public class JarArchiveEntry extends ZipArchiveEntry {
private final Attributes manifestAttributes = null;
private final Certificate[] certificates = null;
public JarArchiveEntry(ZipEntry entry) throws ZipException {
super(entry);
}
public JarArchiveEntry(String name) {
super(name);
}
public JarArchiveEntry(ZipArchiveEntry entry) throws ZipException {
super(entry);
}
public JarArchiveEntry(JarEntry entry) throws ZipException {
super(entry);
}
@Deprecated
public Attributes getManifestAttributes() {
return this.manifestAttributes;
}
@Deprecated
public Certificate[] getCertificates() {
if (this.certificates != null) {
Certificate[] certs = new Certificate[this.certificates.length];
System.arraycopy(this.certificates, 0, certs, 0, certs.length);
return certs;
}
return null;
}
}

View file

@ -0,0 +1,26 @@
package org.apache.commons.compress.archivers.jar;
import java.io.IOException;
import java.io.InputStream;
import org.apache.commons.compress.archivers.ArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream;
public class JarArchiveInputStream extends ZipArchiveInputStream {
public JarArchiveInputStream(InputStream inputStream) {
super(inputStream);
}
public JarArchiveEntry getNextJarEntry() throws IOException {
ZipArchiveEntry entry = getNextZipEntry();
return (entry == null) ? null : new JarArchiveEntry(entry);
}
public ArchiveEntry getNextEntry() throws IOException {
return getNextJarEntry();
}
public static boolean matches(byte[] signature, int length) {
return ZipArchiveInputStream.matches(signature, length);
}
}

View file

@ -0,0 +1,24 @@
package org.apache.commons.compress.archivers.jar;
import java.io.IOException;
import java.io.OutputStream;
import org.apache.commons.compress.archivers.ArchiveEntry;
import org.apache.commons.compress.archivers.zip.JarMarker;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
public class JarArchiveOutputStream extends ZipArchiveOutputStream {
private boolean jarMarkerAdded = false;
public JarArchiveOutputStream(OutputStream out) {
super(out);
}
public void putArchiveEntry(ArchiveEntry ze) throws IOException {
if (!this.jarMarkerAdded) {
((ZipArchiveEntry)ze).addAsFirstExtraField(JarMarker.getInstance());
this.jarMarkerAdded = true;
}
super.putArchiveEntry(ze);
}
}

View file

@ -0,0 +1,93 @@
package org.apache.commons.compress.archivers.sevenz;
import java.io.IOException;
import java.io.InputStream;
import java.security.GeneralSecurityException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
class AES256SHA256Decoder extends CoderBase {
AES256SHA256Decoder() {
super(new Class<?>[0]);
}
InputStream decode(final InputStream in, final Coder coder, final byte[] passwordBytes) throws IOException {
return new InputStream() {
private boolean isInitialized = false;
private CipherInputStream cipherInputStream = null;
private CipherInputStream init() throws IOException {
byte[] aesKeyBytes;
if (this.isInitialized)
return this.cipherInputStream;
int byte0 = 0xFF & coder.properties[0];
int numCyclesPower = byte0 & 0x3F;
int byte1 = 0xFF & coder.properties[1];
int ivSize = (byte0 >> 6 & 0x1) + (byte1 & 0xF);
int saltSize = (byte0 >> 7 & 0x1) + (byte1 >> 4);
if (2 + saltSize + ivSize > coder.properties.length)
throw new IOException("Salt size + IV size too long");
byte[] salt = new byte[saltSize];
System.arraycopy(coder.properties, 2, salt, 0, saltSize);
byte[] iv = new byte[16];
System.arraycopy(coder.properties, 2 + saltSize, iv, 0, ivSize);
if (passwordBytes == null)
throw new IOException("Cannot read encrypted files without a password");
if (numCyclesPower == 63) {
aesKeyBytes = new byte[32];
System.arraycopy(salt, 0, aesKeyBytes, 0, saltSize);
System.arraycopy(passwordBytes, 0, aesKeyBytes, saltSize, Math.min(passwordBytes.length, aesKeyBytes.length - saltSize));
} else {
MessageDigest digest;
try {
digest = MessageDigest.getInstance("SHA-256");
} catch (NoSuchAlgorithmException noSuchAlgorithmException) {
IOException ioe = new IOException("SHA-256 is unsupported by your Java implementation");
ioe.initCause(noSuchAlgorithmException);
throw ioe;
}
byte[] extra = new byte[8];
for (long j = 0L; j < 1L << numCyclesPower; j++) {
digest.update(salt);
digest.update(passwordBytes);
digest.update(extra);
for (int k = 0; k < extra.length; k++) {
extra[k] = (byte)(extra[k] + 1);
if (extra[k] != 0)
break;
}
}
aesKeyBytes = digest.digest();
}
SecretKey aesKey = new SecretKeySpec(aesKeyBytes, "AES");
try {
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
cipher.init(2, aesKey, new IvParameterSpec(iv));
this.cipherInputStream = new CipherInputStream(in, cipher);
this.isInitialized = true;
return this.cipherInputStream;
} catch (GeneralSecurityException generalSecurityException) {
IOException ioe = new IOException("Decryption error (do you have the JCE Unlimited Strength Jurisdiction Policy Files installed?)");
ioe.initCause(generalSecurityException);
throw ioe;
}
}
public int read() throws IOException {
return init().read();
}
public int read(byte[] b, int off, int len) throws IOException {
return init().read(b, off, len);
}
public void close() {}
};
}
}

View file

@ -0,0 +1,21 @@
package org.apache.commons.compress.archivers.sevenz;
import java.util.BitSet;
class Archive {
long packPos;
long[] packSizes;
BitSet packCrcsDefined;
long[] packCrcs;
Folder[] folders;
SubStreamsInfo subStreamsInfo;
SevenZArchiveEntry[] files;
StreamMap streamMap;
}

View file

@ -0,0 +1,7 @@
package org.apache.commons.compress.archivers.sevenz;
class BindPair {
long inIndex;
long outIndex;
}

View file

@ -0,0 +1,38 @@
package org.apache.commons.compress.archivers.sevenz;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
class BoundedRandomAccessFileInputStream extends InputStream {
private final RandomAccessFile file;
private long bytesRemaining;
public BoundedRandomAccessFileInputStream(RandomAccessFile file, long size) {
this.file = file;
this.bytesRemaining = size;
}
public int read() throws IOException {
if (this.bytesRemaining > 0L) {
this.bytesRemaining--;
return this.file.read();
}
return -1;
}
public int read(byte[] b, int off, int len) throws IOException {
if (this.bytesRemaining == 0L)
return -1;
int bytesToRead = len;
if ((long)bytesToRead > this.bytesRemaining)
bytesToRead = (int)this.bytesRemaining;
int bytesRead = this.file.read(b, off, bytesToRead);
if (bytesRead >= 0)
this.bytesRemaining -= (long)bytesRead;
return bytesRead;
}
public void close() {}
}

View file

@ -0,0 +1,11 @@
package org.apache.commons.compress.archivers.sevenz;
class Coder {
byte[] decompressionMethodId;
long numInStreams;
long numOutStreams;
byte[] properties = null;
}

View file

@ -0,0 +1,41 @@
package org.apache.commons.compress.archivers.sevenz;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
abstract class CoderBase {
private final Class<?>[] acceptableOptions;
private static final byte[] NONE = new byte[0];
protected CoderBase(Class<?>... acceptableOptions) {
this.acceptableOptions = acceptableOptions;
}
boolean canAcceptOptions(Object opts) {
for (Class<?> c : this.acceptableOptions) {
if (c.isInstance(opts))
return true;
}
return false;
}
byte[] getOptionsAsProperties(Object options) {
return NONE;
}
Object getOptionsFromCoder(Coder coder, InputStream in) {
return null;
}
abstract InputStream decode(InputStream paramInputStream, Coder paramCoder, byte[] paramArrayOfbyte) throws IOException;
OutputStream encode(OutputStream out, Object options) throws IOException {
throw new UnsupportedOperationException("method doesn't support writing");
}
protected static int numberOptionOrDefault(Object options, int defaultValue) {
return (options instanceof Number) ? ((Number)options).intValue() : defaultValue;
}
}

View file

@ -0,0 +1,179 @@
package org.apache.commons.compress.archivers.sevenz;
import java.io.FilterInputStream;
import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.zip.Deflater;
import java.util.zip.DeflaterOutputStream;
import java.util.zip.Inflater;
import java.util.zip.InflaterInputStream;
import org.apache.commons.compress.compressors.bzip2.BZip2CompressorInputStream;
import org.apache.commons.compress.compressors.bzip2.BZip2CompressorOutputStream;
import org.tukaani.xz.ARMOptions;
import org.tukaani.xz.ARMThumbOptions;
import org.tukaani.xz.FilterOptions;
import org.tukaani.xz.FinishableOutputStream;
import org.tukaani.xz.FinishableWrapperOutputStream;
import org.tukaani.xz.IA64Options;
import org.tukaani.xz.LZMAInputStream;
import org.tukaani.xz.PowerPCOptions;
import org.tukaani.xz.SPARCOptions;
import org.tukaani.xz.X86Options;
class Coders {
private static final Map<SevenZMethod, CoderBase> CODER_MAP = new HashMap<SevenZMethod, CoderBase>() {
private static final long serialVersionUID = 1664829131806520867L;
{
put(SevenZMethod.COPY, new Coders.CopyDecoder());
put(SevenZMethod.LZMA, new Coders.LZMADecoder());
put(SevenZMethod.LZMA2, new LZMA2Decoder());
put(SevenZMethod.DEFLATE, new Coders.DeflateDecoder());
put(SevenZMethod.BZIP2, new Coders.BZIP2Decoder());
put(SevenZMethod.AES256SHA256, new AES256SHA256Decoder());
put(SevenZMethod.BCJ_X86_FILTER, new Coders.BCJDecoder(new X86Options()));
put(SevenZMethod.BCJ_PPC_FILTER, new Coders.BCJDecoder(new PowerPCOptions()));
put(SevenZMethod.BCJ_IA64_FILTER, new Coders.BCJDecoder(new IA64Options()));
put(SevenZMethod.BCJ_ARM_FILTER, new Coders.BCJDecoder(new ARMOptions()));
put(SevenZMethod.BCJ_ARM_THUMB_FILTER, new Coders.BCJDecoder(new ARMThumbOptions()));
put(SevenZMethod.BCJ_SPARC_FILTER, new Coders.BCJDecoder(new SPARCOptions()));
put(SevenZMethod.DELTA_FILTER, new DeltaDecoder());
}
};
static CoderBase findByMethod(SevenZMethod method) {
return CODER_MAP.get(method);
}
static InputStream addDecoder(InputStream is, Coder coder, byte[] password) throws IOException {
CoderBase cb = findByMethod(SevenZMethod.byId(coder.decompressionMethodId));
if (cb == null)
throw new IOException("Unsupported compression method " + Arrays.toString(coder.decompressionMethodId));
return cb.decode(is, coder, password);
}
static OutputStream addEncoder(OutputStream out, SevenZMethod method, Object options) throws IOException {
CoderBase cb = findByMethod(method);
if (cb == null)
throw new IOException("Unsupported compression method " + method);
return cb.encode(out, options);
}
static class CopyDecoder extends CoderBase {
CopyDecoder() {
super(new Class<?>[0]);
}
InputStream decode(InputStream in, Coder coder, byte[] password) throws IOException {
return in;
}
OutputStream encode(OutputStream out, Object options) {
return out;
}
}
static class LZMADecoder extends CoderBase {
LZMADecoder() {
super(new Class<?>[0]);
}
InputStream decode(InputStream in, Coder coder, byte[] password) throws IOException {
byte propsByte = coder.properties[0];
long dictSize = (long)coder.properties[1];
for (int i = 1; i < 4; i++)
dictSize |= ((long)coder.properties[i + 1] & 0xFFL) << 8 * i;
if (dictSize > 2147483632L)
throw new IOException("Dictionary larger than 4GiB maximum size");
return new LZMAInputStream(in, -1L, propsByte, (int)dictSize);
}
}
static class BCJDecoder extends CoderBase {
private final FilterOptions opts;
BCJDecoder(FilterOptions opts) {
super(new Class<?>[0]);
this.opts = opts;
}
InputStream decode(InputStream in, Coder coder, byte[] password) throws IOException {
try {
return this.opts.getInputStream(in);
} catch (AssertionError e) {
IOException ex = new IOException("BCJ filter needs XZ for Java > 1.4 - see http://commons.apache.org/proper/commons-compress/limitations.html#7Z");
ex.initCause(e);
throw ex;
}
}
OutputStream encode(OutputStream out, Object options) {
FinishableOutputStream fo = this.opts.getOutputStream(new FinishableWrapperOutputStream(out));
return new FilterOutputStream((OutputStream)fo) {
public void flush() {}
};
}
}
static class DeflateDecoder extends CoderBase {
DeflateDecoder() {
super(new Class<?>[] { Number.class });
}
InputStream decode(InputStream in, Coder coder, byte[] password) throws IOException {
return new InflaterInputStream(new Coders.DummyByteAddingInputStream(in), new Inflater(true));
}
OutputStream encode(OutputStream out, Object options) {
int level = numberOptionOrDefault(options, 9);
return new DeflaterOutputStream(out, new Deflater(level, true));
}
}
static class BZIP2Decoder extends CoderBase {
BZIP2Decoder() {
super(new Class<?>[] { Number.class });
}
InputStream decode(InputStream in, Coder coder, byte[] password) throws IOException {
return new BZip2CompressorInputStream(in);
}
OutputStream encode(OutputStream out, Object options) throws IOException {
int blockSize = numberOptionOrDefault(options, 9);
return new BZip2CompressorOutputStream(out, blockSize);
}
}
private static class DummyByteAddingInputStream extends FilterInputStream {
private boolean addDummyByte = true;
private DummyByteAddingInputStream(InputStream in) {
super(in);
}
public int read() throws IOException {
int result = super.read();
if (result == -1 && this.addDummyByte) {
this.addDummyByte = false;
result = 0;
}
return result;
}
public int read(byte[] b, int off, int len) throws IOException {
int result = super.read(b, off, len);
if (result == -1 && this.addDummyByte) {
this.addDummyByte = false;
b[off] = 0;
return 1;
}
return result;
}
}
}

View file

@ -0,0 +1,41 @@
package org.apache.commons.compress.archivers.sevenz;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import org.tukaani.xz.DeltaOptions;
import org.tukaani.xz.FinishableWrapperOutputStream;
import org.tukaani.xz.UnsupportedOptionsException;
class DeltaDecoder extends CoderBase {
DeltaDecoder() {
super(new Class<?>[] { Number.class });
}
InputStream decode(InputStream in, Coder coder, byte[] password) throws IOException {
return new DeltaOptions(getOptionsFromCoder(coder)).getInputStream(in);
}
OutputStream encode(OutputStream out, Object options) throws IOException {
int distance = numberOptionOrDefault(options, 1);
try {
return (OutputStream)new DeltaOptions(distance).getOutputStream(new FinishableWrapperOutputStream(out));
} catch (UnsupportedOptionsException ex) {
throw new IOException(ex.getMessage());
}
}
byte[] getOptionsAsProperties(Object options) {
return new byte[] { (byte)(numberOptionOrDefault(options, 1) - 1) };
}
Object getOptionsFromCoder(Coder coder, InputStream in) {
return getOptionsFromCoder(coder);
}
private int getOptionsFromCoder(Coder coder) {
if (coder.properties == null || coder.properties.length == 0)
return 1;
return (0xFF & coder.properties[0]) + 1;
}
}

View file

@ -0,0 +1,60 @@
package org.apache.commons.compress.archivers.sevenz;
import java.util.LinkedList;
class Folder {
Coder[] coders;
long totalInputStreams;
long totalOutputStreams;
BindPair[] bindPairs;
long[] packedStreams;
long[] unpackSizes;
boolean hasCrc;
long crc;
int numUnpackSubStreams;
Iterable<Coder> getOrderedCoders() {
LinkedList<Coder> l = new LinkedList<Coder>();
int current = (int)this.packedStreams[0];
while (current != -1) {
l.addLast(this.coders[current]);
int pair = findBindPairForOutStream(current);
current = (pair != -1) ? (int)(this.bindPairs[pair]).inIndex : -1;
}
return l;
}
int findBindPairForInStream(int index) {
for (int i = 0; i < this.bindPairs.length; i++) {
if ((this.bindPairs[i]).inIndex == (long)index)
return i;
}
return -1;
}
int findBindPairForOutStream(int index) {
for (int i = 0; i < this.bindPairs.length; i++) {
if ((this.bindPairs[i]).outIndex == (long)index)
return i;
}
return -1;
}
long getUnpackSize() {
if (this.totalOutputStreams == 0L)
return 0L;
for (int i = (int)this.totalOutputStreams - 1; i >= 0; i--) {
if (findBindPairForOutStream(i) < 0)
return this.unpackSizes[i];
}
return 0L;
}
}

View file

@ -0,0 +1,70 @@
package org.apache.commons.compress.archivers.sevenz;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import org.tukaani.xz.FinishableOutputStream;
import org.tukaani.xz.FinishableWrapperOutputStream;
import org.tukaani.xz.LZMA2InputStream;
import org.tukaani.xz.LZMA2Options;
class LZMA2Decoder extends CoderBase {
LZMA2Decoder() {
super(new Class<?>[] { LZMA2Options.class, Number.class });
}
InputStream decode(InputStream in, Coder coder, byte[] password) throws IOException {
try {
int dictionarySize = getDictionarySize(coder);
return new LZMA2InputStream(in, dictionarySize);
} catch (IllegalArgumentException ex) {
throw new IOException(ex.getMessage());
}
}
OutputStream encode(OutputStream out, Object opts) throws IOException {
LZMA2Options options = getOptions(opts);
FinishableWrapperOutputStream finishableWrapperOutputStream = new FinishableWrapperOutputStream(out);
return (OutputStream)options.getOutputStream((FinishableOutputStream)finishableWrapperOutputStream);
}
byte[] getOptionsAsProperties(Object opts) {
int dictSize = getDictSize(opts);
int lead = Integer.numberOfLeadingZeros(dictSize);
int secondBit = (dictSize >>> 30 - lead) - 2;
return new byte[] { (byte)((19 - lead) * 2 + secondBit) };
}
Object getOptionsFromCoder(Coder coder, InputStream in) {
return getDictionarySize(coder);
}
private int getDictSize(Object opts) {
if (opts instanceof LZMA2Options)
return ((LZMA2Options)opts).getDictSize();
return numberOptionOrDefault(opts);
}
private int getDictionarySize(Coder coder) throws IllegalArgumentException {
int dictionarySizeBits = 0xFF & coder.properties[0];
if ((dictionarySizeBits & 0xFFFFFFC0) != 0)
throw new IllegalArgumentException("Unsupported LZMA2 property bits");
if (dictionarySizeBits > 40)
throw new IllegalArgumentException("Dictionary larger than 4GiB maximum size");
if (dictionarySizeBits == 40)
return -1;
return (0x2 | dictionarySizeBits & 0x1) << dictionarySizeBits / 2 + 11;
}
private LZMA2Options getOptions(Object opts) throws IOException {
if (opts instanceof LZMA2Options)
return (LZMA2Options)opts;
LZMA2Options options = new LZMA2Options();
options.setDictSize(numberOptionOrDefault(opts));
return options;
}
private int numberOptionOrDefault(Object opts) {
return numberOptionOrDefault(opts, 8388608);
}
}

View file

@ -0,0 +1,55 @@
package org.apache.commons.compress.archivers.sevenz;
final class NID {
public static final int kEnd = 0;
public static final int kHeader = 1;
public static final int kArchiveProperties = 2;
public static final int kAdditionalStreamsInfo = 3;
public static final int kMainStreamsInfo = 4;
public static final int kFilesInfo = 5;
public static final int kPackInfo = 6;
public static final int kUnpackInfo = 7;
public static final int kSubStreamsInfo = 8;
public static final int kSize = 9;
public static final int kCRC = 10;
public static final int kFolder = 11;
public static final int kCodersUnpackSize = 12;
public static final int kNumUnpackStream = 13;
public static final int kEmptyStream = 14;
public static final int kEmptyFile = 15;
public static final int kAnti = 16;
public static final int kName = 17;
public static final int kCTime = 18;
public static final int kATime = 19;
public static final int kMTime = 20;
public static final int kWinAttributes = 21;
public static final int kComment = 22;
public static final int kEncodedHeader = 23;
public static final int kStartPos = 24;
public static final int kDummy = 25;
}

View file

@ -0,0 +1,258 @@
package org.apache.commons.compress.archivers.sevenz;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.LinkedList;
import java.util.TimeZone;
import org.apache.commons.compress.archivers.ArchiveEntry;
public class SevenZArchiveEntry implements ArchiveEntry {
private String name;
private boolean hasStream;
private boolean isDirectory;
private boolean isAntiItem;
private boolean hasCreationDate;
private boolean hasLastModifiedDate;
private boolean hasAccessDate;
private long creationDate;
private long lastModifiedDate;
private long accessDate;
private boolean hasWindowsAttributes;
private int windowsAttributes;
private boolean hasCrc;
private long crc;
private long compressedCrc;
private long size;
private long compressedSize;
private Iterable<? extends SevenZMethodConfiguration> contentMethods;
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public boolean hasStream() {
return this.hasStream;
}
public void setHasStream(boolean hasStream) {
this.hasStream = hasStream;
}
public boolean isDirectory() {
return this.isDirectory;
}
public void setDirectory(boolean isDirectory) {
this.isDirectory = isDirectory;
}
public boolean isAntiItem() {
return this.isAntiItem;
}
public void setAntiItem(boolean isAntiItem) {
this.isAntiItem = isAntiItem;
}
public boolean getHasCreationDate() {
return this.hasCreationDate;
}
public void setHasCreationDate(boolean hasCreationDate) {
this.hasCreationDate = hasCreationDate;
}
public Date getCreationDate() {
if (this.hasCreationDate)
return ntfsTimeToJavaTime(this.creationDate);
throw new UnsupportedOperationException("The entry doesn't have this timestamp");
}
public void setCreationDate(long ntfsCreationDate) {
this.creationDate = ntfsCreationDate;
}
public void setCreationDate(Date creationDate) {
this.hasCreationDate = (creationDate != null);
if (this.hasCreationDate)
this.creationDate = javaTimeToNtfsTime(creationDate);
}
public boolean getHasLastModifiedDate() {
return this.hasLastModifiedDate;
}
public void setHasLastModifiedDate(boolean hasLastModifiedDate) {
this.hasLastModifiedDate = hasLastModifiedDate;
}
public Date getLastModifiedDate() {
if (this.hasLastModifiedDate)
return ntfsTimeToJavaTime(this.lastModifiedDate);
throw new UnsupportedOperationException("The entry doesn't have this timestamp");
}
public void setLastModifiedDate(long ntfsLastModifiedDate) {
this.lastModifiedDate = ntfsLastModifiedDate;
}
public void setLastModifiedDate(Date lastModifiedDate) {
this.hasLastModifiedDate = (lastModifiedDate != null);
if (this.hasLastModifiedDate)
this.lastModifiedDate = javaTimeToNtfsTime(lastModifiedDate);
}
public boolean getHasAccessDate() {
return this.hasAccessDate;
}
public void setHasAccessDate(boolean hasAcessDate) {
this.hasAccessDate = hasAcessDate;
}
public Date getAccessDate() {
if (this.hasAccessDate)
return ntfsTimeToJavaTime(this.accessDate);
throw new UnsupportedOperationException("The entry doesn't have this timestamp");
}
public void setAccessDate(long ntfsAccessDate) {
this.accessDate = ntfsAccessDate;
}
public void setAccessDate(Date accessDate) {
this.hasAccessDate = (accessDate != null);
if (this.hasAccessDate)
this.accessDate = javaTimeToNtfsTime(accessDate);
}
public boolean getHasWindowsAttributes() {
return this.hasWindowsAttributes;
}
public void setHasWindowsAttributes(boolean hasWindowsAttributes) {
this.hasWindowsAttributes = hasWindowsAttributes;
}
public int getWindowsAttributes() {
return this.windowsAttributes;
}
public void setWindowsAttributes(int windowsAttributes) {
this.windowsAttributes = windowsAttributes;
}
public boolean getHasCrc() {
return this.hasCrc;
}
public void setHasCrc(boolean hasCrc) {
this.hasCrc = hasCrc;
}
@Deprecated
public int getCrc() {
return (int)this.crc;
}
@Deprecated
public void setCrc(int crc) {
this.crc = (long)crc;
}
public long getCrcValue() {
return this.crc;
}
public void setCrcValue(long crc) {
this.crc = crc;
}
@Deprecated
int getCompressedCrc() {
return (int)this.compressedCrc;
}
@Deprecated
void setCompressedCrc(int crc) {
this.compressedCrc = (long)crc;
}
long getCompressedCrcValue() {
return this.compressedCrc;
}
void setCompressedCrcValue(long crc) {
this.compressedCrc = crc;
}
public long getSize() {
return this.size;
}
public void setSize(long size) {
this.size = size;
}
long getCompressedSize() {
return this.compressedSize;
}
void setCompressedSize(long size) {
this.compressedSize = size;
}
public void setContentMethods(Iterable<? extends SevenZMethodConfiguration> methods) {
if (methods != null) {
LinkedList<SevenZMethodConfiguration> l = new LinkedList<SevenZMethodConfiguration>();
for (SevenZMethodConfiguration m : methods)
l.addLast(m);
this.contentMethods = Collections.<SevenZMethodConfiguration>unmodifiableList(l);
} else {
this.contentMethods = null;
}
}
public Iterable<? extends SevenZMethodConfiguration> getContentMethods() {
return this.contentMethods;
}
public static Date ntfsTimeToJavaTime(long ntfsTime) {
Calendar ntfsEpoch = Calendar.getInstance();
ntfsEpoch.setTimeZone(TimeZone.getTimeZone("GMT+0"));
ntfsEpoch.set(1601, 0, 1, 0, 0, 0);
ntfsEpoch.set(14, 0);
long realTime = ntfsEpoch.getTimeInMillis() + ntfsTime / 10000L;
return new Date(realTime);
}
public static long javaTimeToNtfsTime(Date date) {
Calendar ntfsEpoch = Calendar.getInstance();
ntfsEpoch.setTimeZone(TimeZone.getTimeZone("GMT+0"));
ntfsEpoch.set(1601, 0, 1, 0, 0, 0);
ntfsEpoch.set(14, 0);
return (date.getTime() - ntfsEpoch.getTimeInMillis()) * 1000L * 10L;
}
}

View file

@ -0,0 +1,692 @@
package org.apache.commons.compress.archivers.sevenz;
import java.io.ByteArrayInputStream;
import java.io.Closeable;
import java.io.DataInput;
import java.io.DataInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.util.Arrays;
import java.util.BitSet;
import java.util.LinkedList;
import java.util.zip.CRC32;
import org.apache.commons.compress.utils.BoundedInputStream;
import org.apache.commons.compress.utils.CRC32VerifyingInputStream;
import org.apache.commons.compress.utils.IOUtils;
public class SevenZFile implements Closeable {
static final int SIGNATURE_HEADER_SIZE = 32;
private RandomAccessFile file;
private final Archive archive;
private int currentEntryIndex = -1;
private int currentFolderIndex = -1;
private InputStream currentFolderInputStream = null;
private InputStream currentEntryInputStream = null;
private byte[] password;
static final byte[] sevenZSignature = new byte[] { 55, 122, -68, -81, 39, 28 };
public SevenZFile(File filename, byte[] password) throws IOException {
boolean succeeded = false;
this.file = new RandomAccessFile(filename, "r");
try {
this.archive = readHeaders(password);
if (password != null) {
this.password = new byte[password.length];
System.arraycopy(password, 0, this.password, 0, password.length);
} else {
this.password = null;
}
succeeded = true;
} finally {
if (!succeeded)
this.file.close();
}
}
public SevenZFile(File filename) throws IOException {
this(filename, null);
}
public void close() throws IOException {
if (this.file != null)
try {
this.file.close();
} finally {
this.file = null;
if (this.password != null)
Arrays.fill(this.password, (byte)0);
this.password = null;
}
}
public SevenZArchiveEntry getNextEntry() throws IOException {
if (this.currentEntryIndex >= this.archive.files.length - 1)
return null;
this.currentEntryIndex++;
SevenZArchiveEntry entry = this.archive.files[this.currentEntryIndex];
buildDecodingStream();
return entry;
}
private Archive readHeaders(byte[] password) throws IOException {
byte[] signature = new byte[6];
this.file.readFully(signature);
if (!Arrays.equals(signature, sevenZSignature))
throw new IOException("Bad 7z signature");
byte archiveVersionMajor = this.file.readByte();
byte archiveVersionMinor = this.file.readByte();
if (archiveVersionMajor != 0)
throw new IOException(String.format("Unsupported 7z version (%d,%d)", archiveVersionMajor, archiveVersionMinor));
long startHeaderCrc = 0xFFFFFFFFL & (long)Integer.reverseBytes(this.file.readInt());
StartHeader startHeader = readStartHeader(startHeaderCrc);
int nextHeaderSizeInt = (int)startHeader.nextHeaderSize;
if ((long)nextHeaderSizeInt != startHeader.nextHeaderSize)
throw new IOException("cannot handle nextHeaderSize " + startHeader.nextHeaderSize);
this.file.seek(32L + startHeader.nextHeaderOffset);
byte[] nextHeader = new byte[nextHeaderSizeInt];
this.file.readFully(nextHeader);
CRC32 crc = new CRC32();
crc.update(nextHeader);
if (startHeader.nextHeaderCrc != crc.getValue())
throw new IOException("NextHeader CRC mismatch");
ByteArrayInputStream byteStream = new ByteArrayInputStream(nextHeader);
DataInputStream nextHeaderInputStream = new DataInputStream(byteStream);
Archive archive = new Archive();
int nid = nextHeaderInputStream.readUnsignedByte();
if (nid == 23) {
nextHeaderInputStream = readEncodedHeader(nextHeaderInputStream, archive, password);
archive = new Archive();
nid = nextHeaderInputStream.readUnsignedByte();
}
if (nid == 1) {
readHeader(nextHeaderInputStream, archive);
nextHeaderInputStream.close();
} else {
throw new IOException("Broken or unsupported archive: no Header");
}
return archive;
}
private StartHeader readStartHeader(long startHeaderCrc) throws IOException {
StartHeader startHeader = new StartHeader();
DataInputStream dataInputStream = null;
try {
dataInputStream = new DataInputStream(new CRC32VerifyingInputStream((InputStream)new BoundedRandomAccessFileInputStream(this.file, 20L), 20L, startHeaderCrc));
startHeader.nextHeaderOffset = Long.reverseBytes(dataInputStream.readLong());
startHeader.nextHeaderSize = Long.reverseBytes(dataInputStream.readLong());
startHeader.nextHeaderCrc = 0xFFFFFFFFL & (long)Integer.reverseBytes(dataInputStream.readInt());
return startHeader;
} finally {
if (dataInputStream != null)
dataInputStream.close();
}
}
private void readHeader(DataInput header, Archive archive) throws IOException {
int nid = header.readUnsignedByte();
if (nid == 2) {
readArchiveProperties(header);
nid = header.readUnsignedByte();
}
if (nid == 3)
throw new IOException("Additional streams unsupported");
if (nid == 4) {
readStreamsInfo(header, archive);
nid = header.readUnsignedByte();
}
if (nid == 5) {
readFilesInfo(header, archive);
nid = header.readUnsignedByte();
}
if (nid != 0)
throw new IOException("Badly terminated header");
}
private void readArchiveProperties(DataInput input) throws IOException {
int nid = input.readUnsignedByte();
while (nid != 0) {
long propertySize = readUint64(input);
byte[] property = new byte[(int)propertySize];
input.readFully(property);
nid = input.readUnsignedByte();
}
}
private DataInputStream readEncodedHeader(DataInputStream header, Archive archive, byte[] password) throws IOException {
readStreamsInfo(header, archive);
Folder folder = archive.folders[0];
int firstPackStreamIndex = 0;
long folderOffset = 32L + archive.packPos + 0L;
this.file.seek(folderOffset);
InputStream inputStreamStack = new BoundedRandomAccessFileInputStream(this.file, archive.packSizes[0]);
for (Coder coder : folder.getOrderedCoders()) {
if (coder.numInStreams != 1L || coder.numOutStreams != 1L)
throw new IOException("Multi input/output stream coders are not yet supported");
inputStreamStack = Coders.addDecoder(inputStreamStack, coder, password);
}
if (folder.hasCrc)
inputStreamStack = new CRC32VerifyingInputStream(inputStreamStack, folder.getUnpackSize(), folder.crc);
byte[] nextHeader = new byte[(int)folder.getUnpackSize()];
DataInputStream nextHeaderInputStream = new DataInputStream(inputStreamStack);
try {
nextHeaderInputStream.readFully(nextHeader);
} finally {
nextHeaderInputStream.close();
}
return new DataInputStream(new ByteArrayInputStream(nextHeader));
}
private void readStreamsInfo(DataInput header, Archive archive) throws IOException {
int nid = header.readUnsignedByte();
if (nid == 6) {
readPackInfo(header, archive);
nid = header.readUnsignedByte();
}
if (nid == 7) {
readUnpackInfo(header, archive);
nid = header.readUnsignedByte();
} else {
archive.folders = new Folder[0];
}
if (nid == 8) {
readSubStreamsInfo(header, archive);
nid = header.readUnsignedByte();
}
if (nid != 0)
throw new IOException("Badly terminated StreamsInfo");
}
private void readPackInfo(DataInput header, Archive archive) throws IOException {
archive.packPos = readUint64(header);
long numPackStreams = readUint64(header);
int nid = header.readUnsignedByte();
if (nid == 9) {
archive.packSizes = new long[(int)numPackStreams];
for (int i = 0; i < archive.packSizes.length; i++)
archive.packSizes[i] = readUint64(header);
nid = header.readUnsignedByte();
}
if (nid == 10) {
archive.packCrcsDefined = readAllOrBits(header, (int)numPackStreams);
archive.packCrcs = new long[(int)numPackStreams];
for (int i = 0; i < (int)numPackStreams; i++) {
if (archive.packCrcsDefined.get(i))
archive.packCrcs[i] = 0xFFFFFFFFL & (long)Integer.reverseBytes(header.readInt());
}
nid = header.readUnsignedByte();
}
if (nid != 0)
throw new IOException("Badly terminated PackInfo (" + nid + ")");
}
private void readUnpackInfo(DataInput header, Archive archive) throws IOException {
int nid = header.readUnsignedByte();
if (nid != 11)
throw new IOException("Expected kFolder, got " + nid);
long numFolders = readUint64(header);
Folder[] folders = new Folder[(int)numFolders];
archive.folders = folders;
int external = header.readUnsignedByte();
if (external != 0)
throw new IOException("External unsupported");
for (int i = 0; i < (int)numFolders; i++)
folders[i] = readFolder(header);
nid = header.readUnsignedByte();
if (nid != 12)
throw new IOException("Expected kCodersUnpackSize, got " + nid);
for (Folder folder : folders) {
folder.unpackSizes = new long[(int)folder.totalOutputStreams];
for (int j = 0; (long)j < folder.totalOutputStreams; j++)
folder.unpackSizes[j] = readUint64(header);
}
nid = header.readUnsignedByte();
if (nid == 10) {
BitSet crcsDefined = readAllOrBits(header, (int)numFolders);
for (int j = 0; j < (int)numFolders; j++) {
if (crcsDefined.get(j)) {
(folders[j]).hasCrc = true;
(folders[j]).crc = 0xFFFFFFFFL & (long)Integer.reverseBytes(header.readInt());
} else {
(folders[j]).hasCrc = false;
}
}
nid = header.readUnsignedByte();
}
if (nid != 0)
throw new IOException("Badly terminated UnpackInfo");
}
private void readSubStreamsInfo(DataInput header, Archive archive) throws IOException {
for (Folder folder : archive.folders)
folder.numUnpackSubStreams = 1;
int totalUnpackStreams = archive.folders.length;
int nid = header.readUnsignedByte();
if (nid == 13) {
totalUnpackStreams = 0;
for (Folder folder : archive.folders) {
long numStreams = readUint64(header);
folder.numUnpackSubStreams = (int)numStreams;
totalUnpackStreams = (int)((long)totalUnpackStreams + numStreams);
}
nid = header.readUnsignedByte();
}
SubStreamsInfo subStreamsInfo = new SubStreamsInfo();
subStreamsInfo.unpackSizes = new long[totalUnpackStreams];
subStreamsInfo.hasCrc = new BitSet(totalUnpackStreams);
subStreamsInfo.crcs = new long[totalUnpackStreams];
int nextUnpackStream = 0;
for (Folder folder : archive.folders) {
if (folder.numUnpackSubStreams != 0) {
long sum = 0L;
if (nid == 9)
for (int i = 0; i < folder.numUnpackSubStreams - 1; i++) {
long size = readUint64(header);
subStreamsInfo.unpackSizes[nextUnpackStream++] = size;
sum += size;
}
subStreamsInfo.unpackSizes[nextUnpackStream++] = folder.getUnpackSize() - sum;
}
}
if (nid == 9)
nid = header.readUnsignedByte();
int numDigests = 0;
for (Folder folder : archive.folders) {
if (folder.numUnpackSubStreams != 1 || !folder.hasCrc)
numDigests += folder.numUnpackSubStreams;
}
if (nid == 10) {
BitSet hasMissingCrc = readAllOrBits(header, numDigests);
long[] missingCrcs = new long[numDigests];
for (int i = 0; i < numDigests; i++) {
if (hasMissingCrc.get(i))
missingCrcs[i] = 0xFFFFFFFFL & (long)Integer.reverseBytes(header.readInt());
}
int nextCrc = 0;
int nextMissingCrc = 0;
for (Folder folder : archive.folders) {
if (folder.numUnpackSubStreams == 1 && folder.hasCrc) {
subStreamsInfo.hasCrc.set(nextCrc, true);
subStreamsInfo.crcs[nextCrc] = folder.crc;
nextCrc++;
} else {
for (int j = 0; j < folder.numUnpackSubStreams; j++) {
subStreamsInfo.hasCrc.set(nextCrc, hasMissingCrc.get(nextMissingCrc));
subStreamsInfo.crcs[nextCrc] = missingCrcs[nextMissingCrc];
nextCrc++;
nextMissingCrc++;
}
}
}
nid = header.readUnsignedByte();
}
if (nid != 0)
throw new IOException("Badly terminated SubStreamsInfo");
archive.subStreamsInfo = subStreamsInfo;
}
private Folder readFolder(DataInput header) throws IOException {
Folder folder = new Folder();
long numCoders = readUint64(header);
Coder[] coders = new Coder[(int)numCoders];
long totalInStreams = 0L;
long totalOutStreams = 0L;
for (int i = 0; i < coders.length; i++) {
coders[i] = new Coder();
int bits = header.readUnsignedByte();
int idSize = bits & 0xF;
boolean isSimple = ((bits & 0x10) == 0);
boolean hasAttributes = ((bits & 0x20) != 0);
boolean moreAlternativeMethods = ((bits & 0x80) != 0);
(coders[i]).decompressionMethodId = new byte[idSize];
header.readFully((coders[i]).decompressionMethodId);
if (isSimple) {
(coders[i]).numInStreams = 1L;
(coders[i]).numOutStreams = 1L;
} else {
(coders[i]).numInStreams = readUint64(header);
(coders[i]).numOutStreams = readUint64(header);
}
totalInStreams += (coders[i]).numInStreams;
totalOutStreams += (coders[i]).numOutStreams;
if (hasAttributes) {
long propertiesSize = readUint64(header);
(coders[i]).properties = new byte[(int)propertiesSize];
header.readFully((coders[i]).properties);
}
if (moreAlternativeMethods)
throw new IOException("Alternative methods are unsupported, please report. The reference implementation doesn't support them either.");
}
folder.coders = coders;
folder.totalInputStreams = totalInStreams;
folder.totalOutputStreams = totalOutStreams;
if (totalOutStreams == 0L)
throw new IOException("Total output streams can't be 0");
long numBindPairs = totalOutStreams - 1L;
BindPair[] bindPairs = new BindPair[(int)numBindPairs];
for (int j = 0; j < bindPairs.length; j++) {
bindPairs[j] = new BindPair();
(bindPairs[j]).inIndex = readUint64(header);
(bindPairs[j]).outIndex = readUint64(header);
}
folder.bindPairs = bindPairs;
if (totalInStreams < numBindPairs)
throw new IOException("Total input streams can't be less than the number of bind pairs");
long numPackedStreams = totalInStreams - numBindPairs;
long[] packedStreams = new long[(int)numPackedStreams];
if (numPackedStreams == 1L) {
int k;
for (k = 0; k < (int)totalInStreams &&
folder.findBindPairForInStream(k) >= 0; k++);
if (k == (int)totalInStreams)
throw new IOException("Couldn't find stream's bind pair index");
packedStreams[0] = (long)k;
} else {
for (int k = 0; k < (int)numPackedStreams; k++)
packedStreams[k] = readUint64(header);
}
folder.packedStreams = packedStreams;
return folder;
}
private BitSet readAllOrBits(DataInput header, int size) throws IOException {
BitSet bits;
int areAllDefined = header.readUnsignedByte();
if (areAllDefined != 0) {
bits = new BitSet(size);
for (int i = 0; i < size; i++)
bits.set(i, true);
} else {
bits = readBits(header, size);
}
return bits;
}
private BitSet readBits(DataInput header, int size) throws IOException {
BitSet bits = new BitSet(size);
int mask = 0;
int cache = 0;
for (int i = 0; i < size; i++) {
if (mask == 0) {
mask = 128;
cache = header.readUnsignedByte();
}
bits.set(i, ((cache & mask) != 0));
mask >>>= 1;
}
return bits;
}
private void readFilesInfo(DataInput header, Archive archive) throws IOException {
long numFiles = readUint64(header);
SevenZArchiveEntry[] files = new SevenZArchiveEntry[(int)numFiles];
for (int i = 0; i < files.length; i++)
files[i] = new SevenZArchiveEntry();
BitSet isEmptyStream = null;
BitSet isEmptyFile = null;
BitSet isAnti = null;
while (true) {
int external;
BitSet timesDefined, attributesDefined;
byte[] names;
int k, nextFile, m, nextName, n;
int propertyType = header.readUnsignedByte();
if (propertyType == 0)
break;
long size = readUint64(header);
switch (propertyType) {
case 14:
isEmptyStream = readBits(header, files.length);
continue;
case 15:
if (isEmptyStream == null)
throw new IOException("Header format error: kEmptyStream must appear before kEmptyFile");
isEmptyFile = readBits(header, isEmptyStream.cardinality());
continue;
case 16:
if (isEmptyStream == null)
throw new IOException("Header format error: kEmptyStream must appear before kAnti");
isAnti = readBits(header, isEmptyStream.cardinality());
continue;
case 17:
external = header.readUnsignedByte();
if (external != 0)
throw new IOException("Not implemented");
if ((size - 1L & 0x1L) != 0L)
throw new IOException("File names length invalid");
names = new byte[(int)(size - 1L)];
header.readFully(names);
nextFile = 0;
nextName = 0;
for (n = 0; n < names.length; n += 2) {
if (names[n] == 0 && names[n + 1] == 0) {
files[nextFile++].setName(new String(names, nextName, n - nextName, "UTF-16LE"));
nextName = n + 2;
}
}
if (nextName != names.length || nextFile != files.length)
throw new IOException("Error parsing file names");
continue;
case 18:
timesDefined = readAllOrBits(header, files.length);
k = header.readUnsignedByte();
if (k != 0)
throw new IOException("Unimplemented");
for (m = 0; m < files.length; m++) {
files[m].setHasCreationDate(timesDefined.get(m));
if (files[m].getHasCreationDate())
files[m].setCreationDate(Long.reverseBytes(header.readLong()));
}
continue;
case 19:
timesDefined = readAllOrBits(header, files.length);
k = header.readUnsignedByte();
if (k != 0)
throw new IOException("Unimplemented");
for (m = 0; m < files.length; m++) {
files[m].setHasAccessDate(timesDefined.get(m));
if (files[m].getHasAccessDate())
files[m].setAccessDate(Long.reverseBytes(header.readLong()));
}
continue;
case 20:
timesDefined = readAllOrBits(header, files.length);
k = header.readUnsignedByte();
if (k != 0)
throw new IOException("Unimplemented");
for (m = 0; m < files.length; m++) {
files[m].setHasLastModifiedDate(timesDefined.get(m));
if (files[m].getHasLastModifiedDate())
files[m].setLastModifiedDate(Long.reverseBytes(header.readLong()));
}
continue;
case 21:
attributesDefined = readAllOrBits(header, files.length);
k = header.readUnsignedByte();
if (k != 0)
throw new IOException("Unimplemented");
for (m = 0; m < files.length; m++) {
files[m].setHasWindowsAttributes(attributesDefined.get(m));
if (files[m].getHasWindowsAttributes())
files[m].setWindowsAttributes(Integer.reverseBytes(header.readInt()));
}
continue;
case 24:
throw new IOException("kStartPos is unsupported, please report");
case 25:
throw new IOException("kDummy is unsupported, please report");
}
throw new IOException("Unknown property " + propertyType);
}
int nonEmptyFileCounter = 0;
int emptyFileCounter = 0;
for (int j = 0; j < files.length; j++) {
files[j].setHasStream((isEmptyStream == null) ? true : (!isEmptyStream.get(j)));
if (files[j].hasStream()) {
files[j].setDirectory(false);
files[j].setAntiItem(false);
files[j].setHasCrc(archive.subStreamsInfo.hasCrc.get(nonEmptyFileCounter));
files[j].setCrcValue(archive.subStreamsInfo.crcs[nonEmptyFileCounter]);
files[j].setSize(archive.subStreamsInfo.unpackSizes[nonEmptyFileCounter]);
nonEmptyFileCounter++;
} else {
files[j].setDirectory((isEmptyFile == null) ? true : (!isEmptyFile.get(emptyFileCounter)));
files[j].setAntiItem((isAnti == null) ? false : isAnti.get(emptyFileCounter));
files[j].setHasCrc(false);
files[j].setSize(0L);
emptyFileCounter++;
}
}
archive.files = files;
calculateStreamMap(archive);
}
private void calculateStreamMap(Archive archive) throws IOException {
StreamMap streamMap = new StreamMap();
int nextFolderPackStreamIndex = 0;
int numFolders = (archive.folders != null) ? archive.folders.length : 0;
streamMap.folderFirstPackStreamIndex = new int[numFolders];
for (int i = 0; i < numFolders; i++) {
streamMap.folderFirstPackStreamIndex[i] = nextFolderPackStreamIndex;
nextFolderPackStreamIndex += (archive.folders[i]).packedStreams.length;
}
long nextPackStreamOffset = 0L;
int numPackSizes = (archive.packSizes != null) ? archive.packSizes.length : 0;
streamMap.packStreamOffsets = new long[numPackSizes];
for (int j = 0; j < numPackSizes; j++) {
streamMap.packStreamOffsets[j] = nextPackStreamOffset;
nextPackStreamOffset += archive.packSizes[j];
}
streamMap.folderFirstFileIndex = new int[numFolders];
streamMap.fileFolderIndex = new int[archive.files.length];
int nextFolderIndex = 0;
int nextFolderUnpackStreamIndex = 0;
for (int k = 0; k < archive.files.length; k++) {
if (!archive.files[k].hasStream() && nextFolderUnpackStreamIndex == 0) {
streamMap.fileFolderIndex[k] = -1;
} else {
if (nextFolderUnpackStreamIndex == 0) {
for (; nextFolderIndex < archive.folders.length; nextFolderIndex++) {
streamMap.folderFirstFileIndex[nextFolderIndex] = k;
if ((archive.folders[nextFolderIndex]).numUnpackSubStreams > 0)
break;
}
if (nextFolderIndex >= archive.folders.length)
throw new IOException("Too few folders in archive");
}
streamMap.fileFolderIndex[k] = nextFolderIndex;
if (archive.files[k].hasStream()) {
nextFolderUnpackStreamIndex++;
if (nextFolderUnpackStreamIndex >= (archive.folders[nextFolderIndex]).numUnpackSubStreams) {
nextFolderIndex++;
nextFolderUnpackStreamIndex = 0;
}
}
}
}
archive.streamMap = streamMap;
}
private void buildDecodingStream() throws IOException {
int folderIndex = this.archive.streamMap.fileFolderIndex[this.currentEntryIndex];
if (folderIndex < 0) {
this.currentEntryInputStream = new BoundedInputStream(new ByteArrayInputStream(new byte[0]), 0L);
return;
}
SevenZArchiveEntry file = this.archive.files[this.currentEntryIndex];
if (this.currentFolderIndex == folderIndex) {
drainPreviousEntry();
file.setContentMethods(this.archive.files[this.currentEntryIndex - 1].getContentMethods());
} else {
this.currentFolderIndex = folderIndex;
if (this.currentFolderInputStream != null) {
this.currentFolderInputStream.close();
this.currentFolderInputStream = null;
}
Folder folder = this.archive.folders[folderIndex];
int firstPackStreamIndex = this.archive.streamMap.folderFirstPackStreamIndex[folderIndex];
long folderOffset = 32L + this.archive.packPos + this.archive.streamMap.packStreamOffsets[firstPackStreamIndex];
this.currentFolderInputStream = buildDecoderStack(folder, folderOffset, firstPackStreamIndex, file);
}
InputStream fileStream = new BoundedInputStream(this.currentFolderInputStream, file.getSize());
if (file.getHasCrc()) {
this.currentEntryInputStream = new CRC32VerifyingInputStream(fileStream, file.getSize(), file.getCrcValue());
} else {
this.currentEntryInputStream = fileStream;
}
}
private void drainPreviousEntry() throws IOException {
if (this.currentEntryInputStream != null) {
IOUtils.skip(this.currentEntryInputStream, Long.MAX_VALUE);
this.currentEntryInputStream.close();
this.currentEntryInputStream = null;
}
}
private InputStream buildDecoderStack(Folder folder, long folderOffset, int firstPackStreamIndex, SevenZArchiveEntry entry) throws IOException {
this.file.seek(folderOffset);
InputStream inputStreamStack = new BoundedRandomAccessFileInputStream(this.file, this.archive.packSizes[firstPackStreamIndex]);
LinkedList<SevenZMethodConfiguration> methods = new LinkedList<SevenZMethodConfiguration>();
for (Coder coder : folder.getOrderedCoders()) {
if (coder.numInStreams != 1L || coder.numOutStreams != 1L)
throw new IOException("Multi input/output stream coders are not yet supported");
SevenZMethod method = SevenZMethod.byId(coder.decompressionMethodId);
inputStreamStack = Coders.addDecoder(inputStreamStack, coder, this.password);
methods.addFirst(new SevenZMethodConfiguration(method, Coders.findByMethod(method).getOptionsFromCoder(coder, inputStreamStack)));
}
entry.setContentMethods(methods);
if (folder.hasCrc)
return new CRC32VerifyingInputStream(inputStreamStack, folder.getUnpackSize(), folder.crc);
return inputStreamStack;
}
public int read() throws IOException {
if (this.currentEntryInputStream == null)
throw new IllegalStateException("No current 7z entry");
return this.currentEntryInputStream.read();
}
public int read(byte[] b) throws IOException {
return read(b, 0, b.length);
}
public int read(byte[] b, int off, int len) throws IOException {
if (this.currentEntryInputStream == null)
throw new IllegalStateException("No current 7z entry");
return this.currentEntryInputStream.read(b, off, len);
}
private static long readUint64(DataInput in) throws IOException {
long firstByte = (long)in.readUnsignedByte();
int mask = 128;
long value = 0L;
for (int i = 0; i < 8; i++) {
if ((firstByte & (long)mask) == 0L)
return value | (firstByte & (long)(mask - 1)) << 8 * i;
long nextByte = (long)in.readUnsignedByte();
value |= nextByte << 8 * i;
mask >>>= 1;
}
return value;
}
public static boolean matches(byte[] signature, int length) {
if (length < sevenZSignature.length)
return false;
for (int i = 0; i < sevenZSignature.length; i++) {
if (signature[i] != sevenZSignature[i])
return false;
}
return true;
}
}

View file

@ -0,0 +1,39 @@
package org.apache.commons.compress.archivers.sevenz;
import java.util.Arrays;
public enum SevenZMethod {
COPY(new byte[] { (byte)0 }),
LZMA(new byte[] { (byte)3, (byte)1, (byte)1 }),
LZMA2(new byte[] { (byte)33 }),
DEFLATE(new byte[] { (byte)4, (byte)1, (byte)8 }),
BZIP2(new byte[] { (byte)4, (byte)2, (byte)2 }),
AES256SHA256(new byte[] { (byte)6, (byte)-15, (byte)7, (byte)1 }),
BCJ_X86_FILTER(new byte[] { (byte)3, (byte)3, (byte)1, (byte)3 }),
BCJ_PPC_FILTER(new byte[] { (byte)3, (byte)3, (byte)2, (byte)5 }),
BCJ_IA64_FILTER(new byte[] { (byte)3, (byte)3, (byte)4, (byte)1 }),
BCJ_ARM_FILTER(new byte[] { (byte)3, (byte)3, (byte)5, (byte)1 }),
BCJ_ARM_THUMB_FILTER(new byte[] { (byte)3, (byte)3, (byte)7, (byte)1 }),
BCJ_SPARC_FILTER(new byte[] { (byte)3, (byte)3, (byte)8, (byte)5 }),
DELTA_FILTER(new byte[] { (byte)3 });
private final byte[] id;
SevenZMethod(byte[] id) {
this.id = id;
}
byte[] getId() {
byte[] copy = new byte[this.id.length];
System.arraycopy(this.id, 0, copy, 0, this.id.length);
return copy;
}
static SevenZMethod byId(byte[] id) {
for (SevenZMethod m : SevenZMethod.class.getEnumConstants()) {
if (Arrays.equals(m.id, id))
return m;
}
return null;
}
}

View file

@ -0,0 +1,26 @@
package org.apache.commons.compress.archivers.sevenz;
public class SevenZMethodConfiguration {
private final SevenZMethod method;
private final Object options;
public SevenZMethodConfiguration(SevenZMethod method) {
this(method, null);
}
public SevenZMethodConfiguration(SevenZMethod method, Object options) {
this.method = method;
this.options = options;
if (options != null && !Coders.findByMethod(method).canAcceptOptions(options))
throw new IllegalArgumentException("The " + method + " method doesn't support options of type " + options.getClass());
}
public SevenZMethod getMethod() {
return this.method;
}
public Object getOptions() {
return this.options;
}
}

View file

@ -0,0 +1,584 @@
package org.apache.commons.compress.archivers.sevenz;
import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.DataOutput;
import java.io.DataOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.zip.CRC32;
import org.apache.commons.compress.archivers.ArchiveEntry;
import org.apache.commons.compress.utils.CountingOutputStream;
public class SevenZOutputFile implements Closeable {
private final RandomAccessFile file;
private final List<SevenZArchiveEntry> files = new ArrayList<SevenZArchiveEntry>();
private int numNonEmptyStreams = 0;
private final CRC32 crc32 = new CRC32();
private final CRC32 compressedCrc32 = new CRC32();
private long fileBytesWritten = 0L;
private boolean finished = false;
private CountingOutputStream currentOutputStream;
private CountingOutputStream[] additionalCountingStreams;
private Iterable<? extends SevenZMethodConfiguration> contentMethods = Collections.<SevenZMethodConfiguration>singletonList(new SevenZMethodConfiguration(SevenZMethod.LZMA2));
private final Map<SevenZArchiveEntry, long[]> additionalSizes = new HashMap<SevenZArchiveEntry, long[]>();
public SevenZOutputFile(File filename) throws IOException {
this.file = new RandomAccessFile(filename, "rw");
this.file.seek(32L);
}
public void setContentCompression(SevenZMethod method) {
setContentMethods(Collections.<SevenZMethodConfiguration>singletonList(new SevenZMethodConfiguration(method)));
}
public void setContentMethods(Iterable<? extends SevenZMethodConfiguration> methods) {
this.contentMethods = reverse(methods);
}
public void close() throws IOException {
if (!this.finished)
finish();
this.file.close();
}
public SevenZArchiveEntry createArchiveEntry(File inputFile, String entryName) throws IOException {
SevenZArchiveEntry entry = new SevenZArchiveEntry();
entry.setDirectory(inputFile.isDirectory());
entry.setName(entryName);
entry.setLastModifiedDate(new Date(inputFile.lastModified()));
return entry;
}
public void putArchiveEntry(ArchiveEntry archiveEntry) throws IOException {
SevenZArchiveEntry entry = (SevenZArchiveEntry)archiveEntry;
this.files.add(entry);
}
public void closeArchiveEntry() throws IOException {
if (this.currentOutputStream != null) {
this.currentOutputStream.flush();
this.currentOutputStream.close();
}
SevenZArchiveEntry entry = this.files.get(this.files.size() - 1);
if (this.fileBytesWritten > 0L) {
entry.setHasStream(true);
this.numNonEmptyStreams++;
entry.setSize(this.currentOutputStream.getBytesWritten());
entry.setCompressedSize(this.fileBytesWritten);
entry.setCrcValue(this.crc32.getValue());
entry.setCompressedCrcValue(this.compressedCrc32.getValue());
entry.setHasCrc(true);
if (this.additionalCountingStreams != null) {
long[] sizes = new long[this.additionalCountingStreams.length];
for (int i = 0; i < this.additionalCountingStreams.length; i++)
sizes[i] = this.additionalCountingStreams[i].getBytesWritten();
this.additionalSizes.put(entry, sizes);
}
} else {
entry.setHasStream(false);
entry.setSize(0L);
entry.setCompressedSize(0L);
entry.setHasCrc(false);
}
this.currentOutputStream = null;
this.additionalCountingStreams = null;
this.crc32.reset();
this.compressedCrc32.reset();
this.fileBytesWritten = 0L;
}
public void write(int b) throws IOException {
getCurrentOutputStream().write(b);
}
public void write(byte[] b) throws IOException {
write(b, 0, b.length);
}
public void write(byte[] b, int off, int len) throws IOException {
if (len > 0)
getCurrentOutputStream().write(b, off, len);
}
public void finish() throws IOException {
if (this.finished)
throw new IOException("This archive has already been finished");
this.finished = true;
long headerPosition = this.file.getFilePointer();
ByteArrayOutputStream headerBaos = new ByteArrayOutputStream();
DataOutputStream header = new DataOutputStream(headerBaos);
writeHeader(header);
header.flush();
byte[] headerBytes = headerBaos.toByteArray();
this.file.write(headerBytes);
CRC32 crc32 = new CRC32();
this.file.seek(0L);
this.file.write(SevenZFile.sevenZSignature);
this.file.write(0);
this.file.write(2);
ByteArrayOutputStream startHeaderBaos = new ByteArrayOutputStream();
DataOutputStream startHeaderStream = new DataOutputStream(startHeaderBaos);
startHeaderStream.writeLong(Long.reverseBytes(headerPosition - 32L));
startHeaderStream.writeLong(Long.reverseBytes(0xFFFFFFFFL & (long)headerBytes.length));
crc32.reset();
crc32.update(headerBytes);
startHeaderStream.writeInt(Integer.reverseBytes((int)crc32.getValue()));
startHeaderStream.flush();
byte[] startHeaderBytes = startHeaderBaos.toByteArray();
crc32.reset();
crc32.update(startHeaderBytes);
this.file.writeInt(Integer.reverseBytes((int)crc32.getValue()));
this.file.write(startHeaderBytes);
}
private OutputStream getCurrentOutputStream() throws IOException {
if (this.currentOutputStream == null)
this.currentOutputStream = setupFileOutputStream();
return this.currentOutputStream;
}
private CountingOutputStream setupFileOutputStream() throws IOException {
if (this.files.isEmpty())
throw new IllegalStateException("No current 7z entry");
OutputStream out = new OutputStreamWrapper();
ArrayList<CountingOutputStream> moreStreams = new ArrayList<CountingOutputStream>();
boolean first = true;
for (SevenZMethodConfiguration m : getContentMethods(this.files.get(this.files.size() - 1))) {
if (!first) {
CountingOutputStream cos = new CountingOutputStream(out);
moreStreams.add(cos);
out = cos;
}
out = Coders.addEncoder(out, m.getMethod(), m.getOptions());
first = false;
}
if (!moreStreams.isEmpty())
this.additionalCountingStreams = moreStreams.<CountingOutputStream>toArray(new CountingOutputStream[moreStreams.size()]);
return new CountingOutputStream(out) {
public void write(int b) throws IOException {
super.write(b);
SevenZOutputFile.this.crc32.update(b);
}
public void write(byte[] b) throws IOException {
super.write(b);
SevenZOutputFile.this.crc32.update(b);
}
public void write(byte[] b, int off, int len) throws IOException {
super.write(b, off, len);
SevenZOutputFile.this.crc32.update(b, off, len);
}
};
}
private Iterable<? extends SevenZMethodConfiguration> getContentMethods(SevenZArchiveEntry entry) {
Iterable<? extends SevenZMethodConfiguration> ms = entry.getContentMethods();
return (ms == null) ? this.contentMethods : ms;
}
private void writeHeader(DataOutput header) throws IOException {
header.write(1);
header.write(4);
writeStreamsInfo(header);
writeFilesInfo(header);
header.write(0);
}
private void writeStreamsInfo(DataOutput header) throws IOException {
if (this.numNonEmptyStreams > 0) {
writePackInfo(header);
writeUnpackInfo(header);
}
writeSubStreamsInfo(header);
header.write(0);
}
private void writePackInfo(DataOutput header) throws IOException {
header.write(6);
writeUint64(header, 0L);
writeUint64(header, 0xFFFFFFFFL & (long)this.numNonEmptyStreams);
header.write(9);
for (SevenZArchiveEntry entry : this.files) {
if (entry.hasStream())
writeUint64(header, entry.getCompressedSize());
}
header.write(10);
header.write(1);
for (SevenZArchiveEntry entry : this.files) {
if (entry.hasStream())
header.writeInt(Integer.reverseBytes((int)entry.getCompressedCrcValue()));
}
header.write(0);
}
private void writeUnpackInfo(DataOutput header) throws IOException {
header.write(7);
header.write(11);
writeUint64(header, (long)this.numNonEmptyStreams);
header.write(0);
for (SevenZArchiveEntry entry : this.files) {
if (entry.hasStream())
writeFolder(header, entry);
}
header.write(12);
for (SevenZArchiveEntry entry : this.files) {
if (entry.hasStream()) {
long[] moreSizes = this.additionalSizes.get(entry);
if (moreSizes != null)
for (long s : moreSizes)
writeUint64(header, s);
writeUint64(header, entry.getSize());
}
}
header.write(10);
header.write(1);
for (SevenZArchiveEntry entry : this.files) {
if (entry.hasStream())
header.writeInt(Integer.reverseBytes((int)entry.getCrcValue()));
}
header.write(0);
}
private void writeFolder(DataOutput header, SevenZArchiveEntry entry) throws IOException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
int numCoders = 0;
for (SevenZMethodConfiguration m : getContentMethods(entry)) {
numCoders++;
writeSingleCodec(m, bos);
}
writeUint64(header, (long)numCoders);
header.write(bos.toByteArray());
for (int i = 0; i < numCoders - 1; i++) {
writeUint64(header, (long)(i + 1));
writeUint64(header, (long)i);
}
}
private void writeSingleCodec(SevenZMethodConfiguration m, OutputStream bos) throws IOException {
byte[] id = m.getMethod().getId();
byte[] properties = Coders.findByMethod(m.getMethod()).getOptionsAsProperties(m.getOptions());
int codecFlags = id.length;
if (properties.length > 0)
codecFlags |= 0x20;
bos.write(codecFlags);
bos.write(id);
if (properties.length > 0) {
bos.write(properties.length);
bos.write(properties);
}
}
private void writeSubStreamsInfo(DataOutput header) throws IOException {
header.write(8);
header.write(0);
}
private void writeFilesInfo(DataOutput header) throws IOException {
header.write(5);
writeUint64(header, (long)this.files.size());
writeFileEmptyStreams(header);
writeFileEmptyFiles(header);
writeFileAntiItems(header);
writeFileNames(header);
writeFileCTimes(header);
writeFileATimes(header);
writeFileMTimes(header);
writeFileWindowsAttributes(header);
header.write(0);
}
private void writeFileEmptyStreams(DataOutput header) throws IOException {
boolean hasEmptyStreams = false;
for (SevenZArchiveEntry entry : this.files) {
if (!entry.hasStream()) {
hasEmptyStreams = true;
break;
}
}
if (hasEmptyStreams) {
header.write(14);
BitSet emptyStreams = new BitSet(this.files.size());
for (int i = 0; i < this.files.size(); i++)
emptyStreams.set(i, !this.files.get(i).hasStream());
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream out = new DataOutputStream(baos);
writeBits(out, emptyStreams, this.files.size());
out.flush();
byte[] contents = baos.toByteArray();
writeUint64(header, (long)contents.length);
header.write(contents);
}
}
private void writeFileEmptyFiles(DataOutput header) throws IOException {
boolean hasEmptyFiles = false;
int emptyStreamCounter = 0;
BitSet emptyFiles = new BitSet(0);
for (int i = 0; i < this.files.size(); i++) {
if (!this.files.get(i).hasStream()) {
boolean isDir = this.files.get(i).isDirectory();
emptyFiles.set(emptyStreamCounter++, !isDir);
hasEmptyFiles |= !isDir ? true : false;
}
}
if (hasEmptyFiles) {
header.write(15);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream out = new DataOutputStream(baos);
writeBits(out, emptyFiles, emptyStreamCounter);
out.flush();
byte[] contents = baos.toByteArray();
writeUint64(header, (long)contents.length);
header.write(contents);
}
}
private void writeFileAntiItems(DataOutput header) throws IOException {
boolean hasAntiItems = false;
BitSet antiItems = new BitSet(0);
int antiItemCounter = 0;
for (int i = 0; i < this.files.size(); i++) {
if (!this.files.get(i).hasStream()) {
boolean isAnti = this.files.get(i).isAntiItem();
antiItems.set(antiItemCounter++, isAnti);
hasAntiItems |= isAnti;
}
}
if (hasAntiItems) {
header.write(16);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream out = new DataOutputStream(baos);
writeBits(out, antiItems, antiItemCounter);
out.flush();
byte[] contents = baos.toByteArray();
writeUint64(header, (long)contents.length);
header.write(contents);
}
}
private void writeFileNames(DataOutput header) throws IOException {
header.write(17);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream out = new DataOutputStream(baos);
out.write(0);
for (SevenZArchiveEntry entry : this.files) {
out.write(entry.getName().getBytes("UTF-16LE"));
out.writeShort(0);
}
out.flush();
byte[] contents = baos.toByteArray();
writeUint64(header, (long)contents.length);
header.write(contents);
}
private void writeFileCTimes(DataOutput header) throws IOException {
int numCreationDates = 0;
for (SevenZArchiveEntry entry : this.files) {
if (entry.getHasCreationDate())
numCreationDates++;
}
if (numCreationDates > 0) {
header.write(18);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream out = new DataOutputStream(baos);
if (numCreationDates != this.files.size()) {
out.write(0);
BitSet cTimes = new BitSet(this.files.size());
for (int i = 0; i < this.files.size(); i++)
cTimes.set(i, this.files.get(i).getHasCreationDate());
writeBits(out, cTimes, this.files.size());
} else {
out.write(1);
}
out.write(0);
for (SevenZArchiveEntry entry : this.files) {
if (entry.getHasCreationDate())
out.writeLong(Long.reverseBytes(SevenZArchiveEntry.javaTimeToNtfsTime(entry.getCreationDate())));
}
out.flush();
byte[] contents = baos.toByteArray();
writeUint64(header, (long)contents.length);
header.write(contents);
}
}
private void writeFileATimes(DataOutput header) throws IOException {
int numAccessDates = 0;
for (SevenZArchiveEntry entry : this.files) {
if (entry.getHasAccessDate())
numAccessDates++;
}
if (numAccessDates > 0) {
header.write(19);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream out = new DataOutputStream(baos);
if (numAccessDates != this.files.size()) {
out.write(0);
BitSet aTimes = new BitSet(this.files.size());
for (int i = 0; i < this.files.size(); i++)
aTimes.set(i, this.files.get(i).getHasAccessDate());
writeBits(out, aTimes, this.files.size());
} else {
out.write(1);
}
out.write(0);
for (SevenZArchiveEntry entry : this.files) {
if (entry.getHasAccessDate())
out.writeLong(Long.reverseBytes(SevenZArchiveEntry.javaTimeToNtfsTime(entry.getAccessDate())));
}
out.flush();
byte[] contents = baos.toByteArray();
writeUint64(header, (long)contents.length);
header.write(contents);
}
}
private void writeFileMTimes(DataOutput header) throws IOException {
int numLastModifiedDates = 0;
for (SevenZArchiveEntry entry : this.files) {
if (entry.getHasLastModifiedDate())
numLastModifiedDates++;
}
if (numLastModifiedDates > 0) {
header.write(20);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream out = new DataOutputStream(baos);
if (numLastModifiedDates != this.files.size()) {
out.write(0);
BitSet mTimes = new BitSet(this.files.size());
for (int i = 0; i < this.files.size(); i++)
mTimes.set(i, this.files.get(i).getHasLastModifiedDate());
writeBits(out, mTimes, this.files.size());
} else {
out.write(1);
}
out.write(0);
for (SevenZArchiveEntry entry : this.files) {
if (entry.getHasLastModifiedDate())
out.writeLong(Long.reverseBytes(SevenZArchiveEntry.javaTimeToNtfsTime(entry.getLastModifiedDate())));
}
out.flush();
byte[] contents = baos.toByteArray();
writeUint64(header, (long)contents.length);
header.write(contents);
}
}
private void writeFileWindowsAttributes(DataOutput header) throws IOException {
int numWindowsAttributes = 0;
for (SevenZArchiveEntry entry : this.files) {
if (entry.getHasWindowsAttributes())
numWindowsAttributes++;
}
if (numWindowsAttributes > 0) {
header.write(21);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream out = new DataOutputStream(baos);
if (numWindowsAttributes != this.files.size()) {
out.write(0);
BitSet attributes = new BitSet(this.files.size());
for (int i = 0; i < this.files.size(); i++)
attributes.set(i, this.files.get(i).getHasWindowsAttributes());
writeBits(out, attributes, this.files.size());
} else {
out.write(1);
}
out.write(0);
for (SevenZArchiveEntry entry : this.files) {
if (entry.getHasWindowsAttributes())
out.writeInt(Integer.reverseBytes(entry.getWindowsAttributes()));
}
out.flush();
byte[] contents = baos.toByteArray();
writeUint64(header, (long)contents.length);
header.write(contents);
}
}
private void writeUint64(DataOutput header, long value) throws IOException {
int firstByte = 0;
int mask = 128;
int i;
for (i = 0; i < 8; i++) {
if (value < 1L << 7 * (i + 1)) {
firstByte = (int)((long)firstByte | value >>> 8 * i);
break;
}
firstByte |= mask;
mask >>>= 1;
}
header.write(firstByte);
for (; i > 0; i--) {
header.write((int)(0xFFL & value));
value >>>= 8L;
}
}
private void writeBits(DataOutput header, BitSet bits, int length) throws IOException {
int cache = 0;
int shift = 7;
for (int i = 0; i < length; i++) {
cache |= (bits.get(i) ? 1 : 0) << shift;
if (--shift < 0) {
header.write(cache);
shift = 7;
cache = 0;
}
}
if (shift != 7)
header.write(cache);
}
private static <T> Iterable<T> reverse(Iterable<T> i) {
LinkedList<T> l = new LinkedList<T>();
for (T t : (Iterable<T>)i)
l.addFirst(t);
return l;
}
private class OutputStreamWrapper extends OutputStream {
private OutputStreamWrapper() {}
public void write(int b) throws IOException {
SevenZOutputFile.this.file.write(b);
SevenZOutputFile.this.compressedCrc32.update(b);
SevenZOutputFile.this.fileBytesWritten++;
}
public void write(byte[] b) throws IOException {
write(b, 0, b.length);
}
public void write(byte[] b, int off, int len) throws IOException {
SevenZOutputFile.this.file.write(b, off, len);
SevenZOutputFile.this.compressedCrc32.update(b, off, len);
SevenZOutputFile.this.fileBytesWritten += (long)len;
}
public void flush() throws IOException {}
public void close() throws IOException {}
}
}

View file

@ -0,0 +1,9 @@
package org.apache.commons.compress.archivers.sevenz;
class StartHeader {
long nextHeaderOffset;
long nextHeaderSize;
long nextHeaderCrc;
}

View file

@ -0,0 +1,11 @@
package org.apache.commons.compress.archivers.sevenz;
class StreamMap {
int[] folderFirstPackStreamIndex;
long[] packStreamOffsets;
int[] folderFirstFileIndex;
int[] fileFolderIndex;
}

View file

@ -0,0 +1,11 @@
package org.apache.commons.compress.archivers.sevenz;
import java.util.BitSet;
class SubStreamsInfo {
long[] unpackSizes;
BitSet hasCrc;
long[] crcs;
}

View file

@ -0,0 +1,484 @@
package org.apache.commons.compress.archivers.tar;
import java.io.File;
import java.io.IOException;
import java.util.Date;
import java.util.Locale;
import org.apache.commons.compress.archivers.ArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipEncoding;
import org.apache.commons.compress.utils.ArchiveUtils;
public class TarArchiveEntry implements TarConstants, ArchiveEntry {
private String name = "";
private int mode;
private int userId = 0;
private int groupId = 0;
private long size = 0L;
private long modTime;
private boolean checkSumOK;
private byte linkFlag;
private String linkName = "";
private String magic = "ustar\000";
private String version = "00";
private String userName;
private String groupName = "";
private int devMajor = 0;
private int devMinor = 0;
private boolean isExtended;
private long realSize;
private final File file;
public static final int MAX_NAMELEN = 31;
public static final int DEFAULT_DIR_MODE = 16877;
public static final int DEFAULT_FILE_MODE = 33188;
public static final int MILLIS_PER_SECOND = 1000;
private TarArchiveEntry() {
String user = System.getProperty("user.name", "");
if (user.length() > 31)
user = user.substring(0, 31);
this.userName = user;
this.file = null;
}
public TarArchiveEntry(String name) {
this(name, false);
}
public TarArchiveEntry(String name, boolean preserveLeadingSlashes) {
this();
name = normalizeFileName(name, preserveLeadingSlashes);
boolean isDir = name.endsWith("/");
this.name = name;
this.mode = isDir ? 16877 : 33188;
this.linkFlag = isDir ? 53 : 48;
this.modTime = new Date().getTime() / 1000L;
this.userName = "";
}
public TarArchiveEntry(String name, byte linkFlag) {
this(name, linkFlag, false);
}
public TarArchiveEntry(String name, byte linkFlag, boolean preserveLeadingSlashes) {
this(name, preserveLeadingSlashes);
this.linkFlag = linkFlag;
if (linkFlag == 76) {
this.magic = "ustar ";
this.version = " \000";
}
}
public TarArchiveEntry(File file) {
this(file, normalizeFileName(file.getPath(), false));
}
public TarArchiveEntry(File file, String fileName) {
this.file = file;
if (file.isDirectory()) {
this.mode = 16877;
this.linkFlag = 53;
int nameLength = fileName.length();
if (nameLength == 0 || fileName.charAt(nameLength - 1) != '/') {
this.name = fileName + "/";
} else {
this.name = fileName;
}
} else {
this.mode = 33188;
this.linkFlag = 48;
this.size = file.length();
this.name = fileName;
}
this.modTime = file.lastModified() / 1000L;
this.userName = "";
}
public TarArchiveEntry(byte[] headerBuf) {
this();
parseTarHeader(headerBuf);
}
public TarArchiveEntry(byte[] headerBuf, ZipEncoding encoding) throws IOException {
this();
parseTarHeader(headerBuf, encoding);
}
public boolean equals(TarArchiveEntry it) {
return getName().equals(it.getName());
}
public boolean equals(Object it) {
if (it == null || getClass() != it.getClass())
return false;
return equals((TarArchiveEntry)it);
}
public int hashCode() {
return getName().hashCode();
}
public boolean isDescendent(TarArchiveEntry desc) {
return desc.getName().startsWith(getName());
}
public String getName() {
return this.name.toString();
}
public void setName(String name) {
this.name = normalizeFileName(name, false);
}
public void setMode(int mode) {
this.mode = mode;
}
public String getLinkName() {
return this.linkName.toString();
}
public void setLinkName(String link) {
this.linkName = link;
}
public int getUserId() {
return this.userId;
}
public void setUserId(int userId) {
this.userId = userId;
}
public int getGroupId() {
return this.groupId;
}
public void setGroupId(int groupId) {
this.groupId = groupId;
}
public String getUserName() {
return this.userName.toString();
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getGroupName() {
return this.groupName.toString();
}
public void setGroupName(String groupName) {
this.groupName = groupName;
}
public void setIds(int userId, int groupId) {
setUserId(userId);
setGroupId(groupId);
}
public void setNames(String userName, String groupName) {
setUserName(userName);
setGroupName(groupName);
}
public void setModTime(long time) {
this.modTime = time / 1000L;
}
public void setModTime(Date time) {
this.modTime = time.getTime() / 1000L;
}
public Date getModTime() {
return new Date(this.modTime * 1000L);
}
public Date getLastModifiedDate() {
return getModTime();
}
public boolean isCheckSumOK() {
return this.checkSumOK;
}
public File getFile() {
return this.file;
}
public int getMode() {
return this.mode;
}
public long getSize() {
return this.size;
}
public void setSize(long size) {
if (size < 0L)
throw new IllegalArgumentException("Size is out of range: " + size);
this.size = size;
}
public int getDevMajor() {
return this.devMajor;
}
public void setDevMajor(int devNo) {
if (devNo < 0)
throw new IllegalArgumentException("Major device number is out of range: " + devNo);
this.devMajor = devNo;
}
public int getDevMinor() {
return this.devMinor;
}
public void setDevMinor(int devNo) {
if (devNo < 0)
throw new IllegalArgumentException("Minor device number is out of range: " + devNo);
this.devMinor = devNo;
}
public boolean isExtended() {
return this.isExtended;
}
public long getRealSize() {
return this.realSize;
}
public boolean isGNUSparse() {
return (this.linkFlag == 83);
}
public boolean isGNULongLinkEntry() {
return (this.linkFlag == 75 && this.name.equals("././@LongLink"));
}
public boolean isGNULongNameEntry() {
return (this.linkFlag == 76 && this.name.equals("././@LongLink"));
}
public boolean isPaxHeader() {
return (this.linkFlag == 120 || this.linkFlag == 88);
}
public boolean isGlobalPaxHeader() {
return (this.linkFlag == 103);
}
public boolean isDirectory() {
if (this.file != null)
return this.file.isDirectory();
if (this.linkFlag == 53)
return true;
if (getName().endsWith("/"))
return true;
return false;
}
public boolean isFile() {
if (this.file != null)
return this.file.isFile();
if (this.linkFlag == 0 || this.linkFlag == 48)
return true;
return !getName().endsWith("/");
}
public boolean isSymbolicLink() {
return (this.linkFlag == 50);
}
public boolean isLink() {
return (this.linkFlag == 49);
}
public boolean isCharacterDevice() {
return (this.linkFlag == 51);
}
public boolean isBlockDevice() {
return (this.linkFlag == 52);
}
public boolean isFIFO() {
return (this.linkFlag == 54);
}
public TarArchiveEntry[] getDirectoryEntries() {
if (this.file == null || !this.file.isDirectory())
return new TarArchiveEntry[0];
String[] list = this.file.list();
TarArchiveEntry[] result = new TarArchiveEntry[list.length];
for (int i = 0; i < list.length; i++)
result[i] = new TarArchiveEntry(new File(this.file, list[i]));
return result;
}
public void writeEntryHeader(byte[] outbuf) {
try {
writeEntryHeader(outbuf, TarUtils.DEFAULT_ENCODING, false);
} catch (IOException ex) {
try {
writeEntryHeader(outbuf, TarUtils.FALLBACK_ENCODING, false);
} catch (IOException ex2) {
throw new RuntimeException(ex2);
}
}
}
public void writeEntryHeader(byte[] outbuf, ZipEncoding encoding, boolean starMode) throws IOException {
int offset = 0;
offset = TarUtils.formatNameBytes(this.name, outbuf, offset, 100, encoding);
offset = writeEntryHeaderField((long)this.mode, outbuf, offset, 8, starMode);
offset = writeEntryHeaderField((long)this.userId, outbuf, offset, 8, starMode);
offset = writeEntryHeaderField((long)this.groupId, outbuf, offset, 8, starMode);
offset = writeEntryHeaderField(this.size, outbuf, offset, 12, starMode);
offset = writeEntryHeaderField(this.modTime, outbuf, offset, 12, starMode);
int csOffset = offset;
for (int c = 0; c < 8; c++)
outbuf[offset++] = 32;
outbuf[offset++] = this.linkFlag;
offset = TarUtils.formatNameBytes(this.linkName, outbuf, offset, 100, encoding);
offset = TarUtils.formatNameBytes(this.magic, outbuf, offset, 6);
offset = TarUtils.formatNameBytes(this.version, outbuf, offset, 2);
offset = TarUtils.formatNameBytes(this.userName, outbuf, offset, 32, encoding);
offset = TarUtils.formatNameBytes(this.groupName, outbuf, offset, 32, encoding);
offset = writeEntryHeaderField((long)this.devMajor, outbuf, offset, 8, starMode);
offset = writeEntryHeaderField((long)this.devMinor, outbuf, offset, 8, starMode);
while (offset < outbuf.length)
outbuf[offset++] = 0;
long chk = TarUtils.computeCheckSum(outbuf);
TarUtils.formatCheckSumOctalBytes(chk, outbuf, csOffset, 8);
}
private int writeEntryHeaderField(long value, byte[] outbuf, int offset, int length, boolean starMode) {
if (!starMode && (value < 0L || value >= 1L << 3 * (length - 1)))
return TarUtils.formatLongOctalBytes(0L, outbuf, offset, length);
return TarUtils.formatLongOctalOrBinaryBytes(value, outbuf, offset, length);
}
public void parseTarHeader(byte[] header) {
try {
parseTarHeader(header, TarUtils.DEFAULT_ENCODING);
} catch (IOException ex) {
try {
parseTarHeader(header, TarUtils.DEFAULT_ENCODING, true);
} catch (IOException ex2) {
throw new RuntimeException(ex2);
}
}
}
public void parseTarHeader(byte[] header, ZipEncoding encoding) throws IOException {
parseTarHeader(header, encoding, false);
}
private void parseTarHeader(byte[] header, ZipEncoding encoding, boolean oldStyle) throws IOException {
String prefix;
int offset = 0;
this.name = oldStyle ? TarUtils.parseName(header, offset, 100) : TarUtils.parseName(header, offset, 100, encoding);
offset += 100;
this.mode = (int)TarUtils.parseOctalOrBinary(header, offset, 8);
offset += 8;
this.userId = (int)TarUtils.parseOctalOrBinary(header, offset, 8);
offset += 8;
this.groupId = (int)TarUtils.parseOctalOrBinary(header, offset, 8);
offset += 8;
this.size = TarUtils.parseOctalOrBinary(header, offset, 12);
offset += 12;
this.modTime = TarUtils.parseOctalOrBinary(header, offset, 12);
offset += 12;
this.checkSumOK = TarUtils.verifyCheckSum(header);
offset += 8;
this.linkFlag = header[offset++];
this.linkName = oldStyle ? TarUtils.parseName(header, offset, 100) : TarUtils.parseName(header, offset, 100, encoding);
offset += 100;
this.magic = TarUtils.parseName(header, offset, 6);
offset += 6;
this.version = TarUtils.parseName(header, offset, 2);
offset += 2;
this.userName = oldStyle ? TarUtils.parseName(header, offset, 32) : TarUtils.parseName(header, offset, 32, encoding);
offset += 32;
this.groupName = oldStyle ? TarUtils.parseName(header, offset, 32) : TarUtils.parseName(header, offset, 32, encoding);
offset += 32;
this.devMajor = (int)TarUtils.parseOctalOrBinary(header, offset, 8);
offset += 8;
this.devMinor = (int)TarUtils.parseOctalOrBinary(header, offset, 8);
offset += 8;
int type = evaluateType(header);
switch (type) {
case 2:
offset += 12;
offset += 12;
offset += 12;
offset += 4;
offset++;
offset += 96;
this.isExtended = TarUtils.parseBoolean(header, offset);
offset++;
this.realSize = TarUtils.parseOctal(header, offset, 12);
offset += 12;
break;
default:
prefix = oldStyle ? TarUtils.parseName(header, offset, 155) : TarUtils.parseName(header, offset, 155, encoding);
if (isDirectory() && !this.name.endsWith("/"))
this.name += "/";
if (prefix.length() > 0)
this.name = prefix + "/" + this.name;
break;
}
}
private static String normalizeFileName(String fileName, boolean preserveLeadingSlashes) {
String osname = System.getProperty("os.name").toLowerCase(Locale.ENGLISH);
if (osname != null)
if (osname.startsWith("windows")) {
if (fileName.length() > 2) {
char ch1 = fileName.charAt(0);
char ch2 = fileName.charAt(1);
if (ch2 == ':' && ((ch1 >= 'a' && ch1 <= 'z') || (ch1 >= 'A' && ch1 <= 'Z')))
fileName = fileName.substring(2);
}
} else if (osname.indexOf("netware") > -1) {
int colon = fileName.indexOf(':');
if (colon != -1)
fileName = fileName.substring(colon + 1);
}
fileName = fileName.replace(File.separatorChar, '/');
while (!preserveLeadingSlashes && fileName.startsWith("/"))
fileName = fileName.substring(1);
return fileName;
}
private int evaluateType(byte[] header) {
if (ArchiveUtils.matchAsciiBuffer("ustar ", header, 257, 6))
return 2;
if (ArchiveUtils.matchAsciiBuffer("ustar\000", header, 257, 6))
return 3;
return 0;
}
}

View file

@ -0,0 +1,363 @@
package org.apache.commons.compress.archivers.tar;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.compress.archivers.ArchiveEntry;
import org.apache.commons.compress.archivers.ArchiveInputStream;
import org.apache.commons.compress.archivers.zip.ZipEncoding;
import org.apache.commons.compress.archivers.zip.ZipEncodingHelper;
import org.apache.commons.compress.utils.ArchiveUtils;
import org.apache.commons.compress.utils.IOUtils;
public class TarArchiveInputStream extends ArchiveInputStream {
private static final int SMALL_BUFFER_SIZE = 256;
private final byte[] SMALL_BUF = new byte[256];
private final int recordSize;
private final int blockSize;
private boolean hasHitEOF;
private long entrySize;
private long entryOffset;
private final InputStream is;
private TarArchiveEntry currEntry;
private final ZipEncoding encoding;
public TarArchiveInputStream(InputStream is) {
this(is, 10240, 512);
}
public TarArchiveInputStream(InputStream is, String encoding) {
this(is, 10240, 512, encoding);
}
public TarArchiveInputStream(InputStream is, int blockSize) {
this(is, blockSize, 512);
}
public TarArchiveInputStream(InputStream is, int blockSize, String encoding) {
this(is, blockSize, 512, encoding);
}
public TarArchiveInputStream(InputStream is, int blockSize, int recordSize) {
this(is, blockSize, recordSize, null);
}
public TarArchiveInputStream(InputStream is, int blockSize, int recordSize, String encoding) {
this.is = is;
this.hasHitEOF = false;
this.encoding = ZipEncodingHelper.getZipEncoding(encoding);
this.recordSize = recordSize;
this.blockSize = blockSize;
}
public void close() throws IOException {
this.is.close();
}
public int getRecordSize() {
return this.recordSize;
}
public int available() throws IOException {
if (this.entrySize - this.entryOffset > Integer.MAX_VALUE)
return Integer.MAX_VALUE;
return (int)(this.entrySize - this.entryOffset);
}
public long skip(long n) throws IOException {
if (n <= 0L)
return 0L;
long available = this.entrySize - this.entryOffset;
long skipped = this.is.skip(Math.min(n, available));
count(skipped);
this.entryOffset += skipped;
return skipped;
}
public synchronized void reset() {}
public TarArchiveEntry getNextTarEntry() throws IOException {
if (this.hasHitEOF)
return null;
if (this.currEntry != null) {
IOUtils.skip(this, Long.MAX_VALUE);
skipRecordPadding();
}
byte[] headerBuf = getRecord();
if (headerBuf == null) {
this.currEntry = null;
return null;
}
try {
this.currEntry = new TarArchiveEntry(headerBuf, this.encoding);
} catch (IllegalArgumentException e) {
IOException ioe = new IOException("Error detected parsing the header");
ioe.initCause(e);
throw ioe;
}
this.entryOffset = 0L;
this.entrySize = this.currEntry.getSize();
if (this.currEntry.isGNULongLinkEntry()) {
byte[] longLinkData = getLongNameData();
if (longLinkData == null)
return null;
this.currEntry.setLinkName(this.encoding.decode(longLinkData));
}
if (this.currEntry.isGNULongNameEntry()) {
byte[] longNameData = getLongNameData();
if (longNameData == null)
return null;
this.currEntry.setName(this.encoding.decode(longNameData));
}
if (this.currEntry.isPaxHeader())
paxHeaders();
if (this.currEntry.isGNUSparse())
readGNUSparse();
this.entrySize = this.currEntry.getSize();
return this.currEntry;
}
private void skipRecordPadding() throws IOException {
if (this.entrySize > 0L && this.entrySize % (long)this.recordSize != 0L) {
long numRecords = this.entrySize / (long)this.recordSize + 1L;
long padding = numRecords * (long)this.recordSize - this.entrySize;
long skipped = IOUtils.skip(this.is, padding);
count(skipped);
}
}
protected byte[] getLongNameData() throws IOException {
ByteArrayOutputStream longName = new ByteArrayOutputStream();
int length = 0;
while ((length = read(this.SMALL_BUF)) >= 0)
longName.write(this.SMALL_BUF, 0, length);
getNextEntry();
if (this.currEntry == null)
return null;
byte[] longNameData = longName.toByteArray();
length = longNameData.length;
while (length > 0 && longNameData[length - 1] == 0)
length--;
if (length != longNameData.length) {
byte[] l = new byte[length];
System.arraycopy(longNameData, 0, l, 0, length);
longNameData = l;
}
return longNameData;
}
private byte[] getRecord() throws IOException {
byte[] headerBuf = readRecord();
this.hasHitEOF = isEOFRecord(headerBuf);
if (this.hasHitEOF && headerBuf != null) {
tryToConsumeSecondEOFRecord();
consumeRemainderOfLastBlock();
headerBuf = null;
}
return headerBuf;
}
protected boolean isEOFRecord(byte[] record) {
return (record == null || ArchiveUtils.isArrayZero(record, this.recordSize));
}
protected byte[] readRecord() throws IOException {
byte[] record = new byte[this.recordSize];
int readNow = IOUtils.readFully(this.is, record);
count(readNow);
if (readNow != this.recordSize)
return null;
return record;
}
private void paxHeaders() throws IOException {
Map<String, String> headers = parsePaxHeaders(this);
getNextEntry();
applyPaxHeadersToCurrentEntry(headers);
}
Map<String, String> parsePaxHeaders(InputStream i) throws IOException {
int ch;
Map<String, String> headers = new HashMap<String, String>();
do {
int len = 0;
int read = 0;
while ((ch = i.read()) != -1) {
read++;
if (ch == 32) {
ByteArrayOutputStream coll = new ByteArrayOutputStream();
while ((ch = i.read()) != -1) {
read++;
if (ch == 61) {
String keyword = coll.toString("UTF-8");
byte[] rest = new byte[len - read];
int got = IOUtils.readFully(i, rest);
if (got != len - read)
throw new IOException("Failed to read Paxheader. Expected " + (len - read) + " bytes, read " + got);
String value = new String(rest, 0, len - read - 1, "UTF-8");
headers.put(keyword, value);
break;
}
coll.write((byte)ch);
}
break;
}
len *= 10;
len += ch - 48;
}
} while (ch != -1);
return headers;
}
private void applyPaxHeadersToCurrentEntry(Map<String, String> headers) {
for (Map.Entry<String, String> ent : headers.entrySet()) {
String key = ent.getKey();
String val = ent.getValue();
if ("path".equals(key)) {
this.currEntry.setName(val);
continue;
}
if ("linkpath".equals(key)) {
this.currEntry.setLinkName(val);
continue;
}
if ("gid".equals(key)) {
this.currEntry.setGroupId(Integer.parseInt(val));
continue;
}
if ("gname".equals(key)) {
this.currEntry.setGroupName(val);
continue;
}
if ("uid".equals(key)) {
this.currEntry.setUserId(Integer.parseInt(val));
continue;
}
if ("uname".equals(key)) {
this.currEntry.setUserName(val);
continue;
}
if ("size".equals(key)) {
this.currEntry.setSize(Long.parseLong(val));
continue;
}
if ("mtime".equals(key)) {
this.currEntry.setModTime((long)(Double.parseDouble(val) * 1000.0D));
continue;
}
if ("SCHILY.devminor".equals(key)) {
this.currEntry.setDevMinor(Integer.parseInt(val));
continue;
}
if ("SCHILY.devmajor".equals(key))
this.currEntry.setDevMajor(Integer.parseInt(val));
}
}
private void readGNUSparse() throws IOException {
if (this.currEntry.isExtended()) {
TarArchiveSparseEntry entry;
do {
byte[] headerBuf = getRecord();
if (headerBuf == null) {
this.currEntry = null;
break;
}
entry = new TarArchiveSparseEntry(headerBuf);
} while (entry.isExtended());
}
}
public ArchiveEntry getNextEntry() throws IOException {
return getNextTarEntry();
}
private void tryToConsumeSecondEOFRecord() throws IOException {
boolean shouldReset = true;
boolean marked = this.is.markSupported();
if (marked)
this.is.mark(this.recordSize);
try {
shouldReset = !isEOFRecord(readRecord());
} finally {
if (shouldReset && marked) {
pushedBackBytes((long)this.recordSize);
this.is.reset();
}
}
}
public int read(byte[] buf, int offset, int numToRead) throws IOException {
int totalRead = 0;
if (this.hasHitEOF || this.entryOffset >= this.entrySize)
return -1;
if (this.currEntry == null)
throw new IllegalStateException("No current tar entry");
numToRead = Math.min(numToRead, available());
totalRead = this.is.read(buf, offset, numToRead);
if (totalRead == -1) {
if (numToRead > 0)
throw new IOException("Truncated TAR archive");
this.hasHitEOF = true;
} else {
count(totalRead);
this.entryOffset += (long)totalRead;
}
return totalRead;
}
public boolean canReadEntryData(ArchiveEntry ae) {
if (ae instanceof TarArchiveEntry) {
TarArchiveEntry te = (TarArchiveEntry)ae;
return !te.isGNUSparse();
}
return false;
}
public TarArchiveEntry getCurrentEntry() {
return this.currEntry;
}
protected final void setCurrentEntry(TarArchiveEntry e) {
this.currEntry = e;
}
protected final boolean isAtEOF() {
return this.hasHitEOF;
}
protected final void setAtEOF(boolean b) {
this.hasHitEOF = b;
}
private void consumeRemainderOfLastBlock() throws IOException {
long bytesReadOfLastBlock = getBytesRead() % (long)this.blockSize;
if (bytesReadOfLastBlock > 0L) {
long skipped = IOUtils.skip(this.is, (long)this.blockSize - bytesReadOfLastBlock);
count(skipped);
}
}
public static boolean matches(byte[] signature, int length) {
if (length < 265)
return false;
if (ArchiveUtils.matchAsciiBuffer("ustar\000", signature, 257, 6) && ArchiveUtils.matchAsciiBuffer("00", signature, 263, 2))
return true;
if (ArchiveUtils.matchAsciiBuffer("ustar ", signature, 257, 6) && (ArchiveUtils.matchAsciiBuffer(" \000", signature, 263, 2) || ArchiveUtils.matchAsciiBuffer("0\000", signature, 263, 2)))
return true;
if (ArchiveUtils.matchAsciiBuffer("ustar\000", signature, 257, 6) && ArchiveUtils.matchAsciiBuffer("\000\000", signature, 263, 2))
return true;
return false;
}
}

View file

@ -0,0 +1,359 @@
package org.apache.commons.compress.archivers.tar;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.io.StringWriter;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.compress.archivers.ArchiveEntry;
import org.apache.commons.compress.archivers.ArchiveOutputStream;
import org.apache.commons.compress.archivers.zip.ZipEncoding;
import org.apache.commons.compress.archivers.zip.ZipEncodingHelper;
import org.apache.commons.compress.utils.CountingOutputStream;
public class TarArchiveOutputStream extends ArchiveOutputStream {
public static final int LONGFILE_ERROR = 0;
public static final int LONGFILE_TRUNCATE = 1;
public static final int LONGFILE_GNU = 2;
public static final int LONGFILE_POSIX = 3;
public static final int BIGNUMBER_ERROR = 0;
public static final int BIGNUMBER_STAR = 1;
public static final int BIGNUMBER_POSIX = 2;
private long currSize;
private String currName;
private long currBytes;
private final byte[] recordBuf;
private int assemLen;
private final byte[] assemBuf;
private int longFileMode = 0;
private int bigNumberMode = 0;
private int recordsWritten;
private final int recordsPerBlock;
private final int recordSize;
private boolean closed = false;
private boolean haveUnclosedEntry = false;
private boolean finished = false;
private final OutputStream out;
private final ZipEncoding encoding;
private boolean addPaxHeadersForNonAsciiNames = false;
private static final ZipEncoding ASCII = ZipEncodingHelper.getZipEncoding("ASCII");
public TarArchiveOutputStream(OutputStream os) {
this(os, 10240, 512);
}
public TarArchiveOutputStream(OutputStream os, String encoding) {
this(os, 10240, 512, encoding);
}
public TarArchiveOutputStream(OutputStream os, int blockSize) {
this(os, blockSize, 512);
}
public TarArchiveOutputStream(OutputStream os, int blockSize, String encoding) {
this(os, blockSize, 512, encoding);
}
public TarArchiveOutputStream(OutputStream os, int blockSize, int recordSize) {
this(os, blockSize, recordSize, null);
}
public TarArchiveOutputStream(OutputStream os, int blockSize, int recordSize, String encoding) {
this.out = new CountingOutputStream(os);
this.encoding = ZipEncodingHelper.getZipEncoding(encoding);
this.assemLen = 0;
this.assemBuf = new byte[recordSize];
this.recordBuf = new byte[recordSize];
this.recordSize = recordSize;
this.recordsPerBlock = blockSize / recordSize;
}
public void setLongFileMode(int longFileMode) {
this.longFileMode = longFileMode;
}
public void setBigNumberMode(int bigNumberMode) {
this.bigNumberMode = bigNumberMode;
}
public void setAddPaxHeadersForNonAsciiNames(boolean b) {
this.addPaxHeadersForNonAsciiNames = b;
}
@Deprecated
public int getCount() {
return (int)getBytesWritten();
}
public long getBytesWritten() {
return ((CountingOutputStream)this.out).getBytesWritten();
}
public void finish() throws IOException {
if (this.finished)
throw new IOException("This archive has already been finished");
if (this.haveUnclosedEntry)
throw new IOException("This archives contains unclosed entries.");
writeEOFRecord();
writeEOFRecord();
padAsNeeded();
this.out.flush();
this.finished = true;
}
public void close() throws IOException {
if (!this.finished)
finish();
if (!this.closed) {
this.out.close();
this.closed = true;
}
}
public int getRecordSize() {
return this.recordSize;
}
public void putArchiveEntry(ArchiveEntry archiveEntry) throws IOException {
if (this.finished)
throw new IOException("Stream has already been finished");
TarArchiveEntry entry = (TarArchiveEntry)archiveEntry;
Map<String, String> paxHeaders = new HashMap<String, String>();
String entryName = entry.getName();
boolean paxHeaderContainsPath = handleLongName(entryName, paxHeaders, "path", (byte)76, "file name");
String linkName = entry.getLinkName();
boolean paxHeaderContainsLinkPath = (linkName != null && linkName.length() > 0 && handleLongName(linkName, paxHeaders, "linkpath", (byte)75, "link name"));
if (this.bigNumberMode == 2) {
addPaxHeadersForBigNumbers(paxHeaders, entry);
} else if (this.bigNumberMode != 1) {
failForBigNumbers(entry);
}
if (this.addPaxHeadersForNonAsciiNames && !paxHeaderContainsPath && !ASCII.canEncode(entryName))
paxHeaders.put("path", entryName);
if (this.addPaxHeadersForNonAsciiNames && !paxHeaderContainsLinkPath && (entry.isLink() || entry.isSymbolicLink()) && !ASCII.canEncode(linkName))
paxHeaders.put("linkpath", linkName);
if (paxHeaders.size() > 0)
writePaxHeaders(entryName, paxHeaders);
entry.writeEntryHeader(this.recordBuf, this.encoding, (this.bigNumberMode == 1));
writeRecord(this.recordBuf);
this.currBytes = 0L;
if (entry.isDirectory()) {
this.currSize = 0L;
} else {
this.currSize = entry.getSize();
}
this.currName = entryName;
this.haveUnclosedEntry = true;
}
public void closeArchiveEntry() throws IOException {
if (this.finished)
throw new IOException("Stream has already been finished");
if (!this.haveUnclosedEntry)
throw new IOException("No current entry to close");
if (this.assemLen > 0) {
for (int i = this.assemLen; i < this.assemBuf.length; i++)
this.assemBuf[i] = 0;
writeRecord(this.assemBuf);
this.currBytes += (long)this.assemLen;
this.assemLen = 0;
}
if (this.currBytes < this.currSize)
throw new IOException("entry '" + this.currName + "' closed at '" + this.currBytes + "' before the '" + this.currSize + "' bytes specified in the header were written");
this.haveUnclosedEntry = false;
}
public void write(byte[] wBuf, int wOffset, int numToWrite) throws IOException {
if (!this.haveUnclosedEntry)
throw new IllegalStateException("No current tar entry");
if (this.currBytes + (long)numToWrite > this.currSize)
throw new IOException("request to write '" + numToWrite + "' bytes exceeds size in header of '" + this.currSize + "' bytes for entry '" + this.currName + "'");
if (this.assemLen > 0)
if (this.assemLen + numToWrite >= this.recordBuf.length) {
int aLen = this.recordBuf.length - this.assemLen;
System.arraycopy(this.assemBuf, 0, this.recordBuf, 0, this.assemLen);
System.arraycopy(wBuf, wOffset, this.recordBuf, this.assemLen, aLen);
writeRecord(this.recordBuf);
this.currBytes += (long)this.recordBuf.length;
wOffset += aLen;
numToWrite -= aLen;
this.assemLen = 0;
} else {
System.arraycopy(wBuf, wOffset, this.assemBuf, this.assemLen, numToWrite);
wOffset += numToWrite;
this.assemLen += numToWrite;
numToWrite = 0;
}
while (numToWrite > 0) {
if (numToWrite < this.recordBuf.length) {
System.arraycopy(wBuf, wOffset, this.assemBuf, this.assemLen, numToWrite);
this.assemLen += numToWrite;
break;
}
writeRecord(wBuf, wOffset);
int num = this.recordBuf.length;
this.currBytes += (long)num;
numToWrite -= num;
wOffset += num;
}
}
void writePaxHeaders(String entryName, Map<String, String> headers) throws IOException {
String name = "./PaxHeaders.X/" + stripTo7Bits(entryName);
if (name.length() >= 100)
name = name.substring(0, 99);
TarArchiveEntry pex = new TarArchiveEntry(name, (byte)120);
StringWriter w = new StringWriter();
for (Map.Entry<String, String> h : headers.entrySet()) {
String key = h.getKey();
String value = h.getValue();
int len = key.length() + value.length() + 3 + 2;
String line = len + " " + key + "=" + value + "\n";
int actualLength = (line.getBytes("UTF-8")).length;
while (len != actualLength) {
len = actualLength;
line = len + " " + key + "=" + value + "\n";
actualLength = (line.getBytes("UTF-8")).length;
}
w.write(line);
}
byte[] data = w.toString().getBytes("UTF-8");
pex.setSize((long)data.length);
putArchiveEntry(pex);
write(data);
closeArchiveEntry();
}
private String stripTo7Bits(String name) {
int length = name.length();
StringBuilder result = new StringBuilder(length);
for (int i = 0; i < length; i++) {
char stripped = (char)(name.charAt(i) & 0x7F);
if (shouldBeReplaced(stripped)) {
result.append("_");
} else {
result.append(stripped);
}
}
return result.toString();
}
private boolean shouldBeReplaced(char c) {
return (c == '\000' || c == '/' || c == '\\');
}
private void writeEOFRecord() throws IOException {
Arrays.fill(this.recordBuf, (byte)0);
writeRecord(this.recordBuf);
}
public void flush() throws IOException {
this.out.flush();
}
public ArchiveEntry createArchiveEntry(File inputFile, String entryName) throws IOException {
if (this.finished)
throw new IOException("Stream has already been finished");
return new TarArchiveEntry(inputFile, entryName);
}
private void writeRecord(byte[] record) throws IOException {
if (record.length != this.recordSize)
throw new IOException("record to write has length '" + record.length + "' which is not the record size of '" + this.recordSize + "'");
this.out.write(record);
this.recordsWritten++;
}
private void writeRecord(byte[] buf, int offset) throws IOException {
if (offset + this.recordSize > buf.length)
throw new IOException("record has length '" + buf.length + "' with offset '" + offset + "' which is less than the record size of '" + this.recordSize + "'");
this.out.write(buf, offset, this.recordSize);
this.recordsWritten++;
}
private void padAsNeeded() throws IOException {
int start = this.recordsWritten % this.recordsPerBlock;
if (start != 0)
for (int i = start; i < this.recordsPerBlock; i++)
writeEOFRecord();
}
private void addPaxHeadersForBigNumbers(Map<String, String> paxHeaders, TarArchiveEntry entry) {
addPaxHeaderForBigNumber(paxHeaders, "size", entry.getSize(), 8589934591L);
addPaxHeaderForBigNumber(paxHeaders, "gid", (long)entry.getGroupId(), 2097151L);
addPaxHeaderForBigNumber(paxHeaders, "mtime", entry.getModTime().getTime() / 1000L, 8589934591L);
addPaxHeaderForBigNumber(paxHeaders, "uid", (long)entry.getUserId(), 2097151L);
addPaxHeaderForBigNumber(paxHeaders, "SCHILY.devmajor", (long)entry.getDevMajor(), 2097151L);
addPaxHeaderForBigNumber(paxHeaders, "SCHILY.devminor", (long)entry.getDevMinor(), 2097151L);
failForBigNumber("mode", (long)entry.getMode(), 2097151L);
}
private void addPaxHeaderForBigNumber(Map<String, String> paxHeaders, String header, long value, long maxValue) {
if (value < 0L || value > maxValue)
paxHeaders.put(header, String.valueOf(value));
}
private void failForBigNumbers(TarArchiveEntry entry) {
failForBigNumber("entry size", entry.getSize(), 8589934591L);
failForBigNumber("group id", (long)entry.getGroupId(), 2097151L);
failForBigNumber("last modification time", entry.getModTime().getTime() / 1000L, 8589934591L);
failForBigNumber("user id", (long)entry.getUserId(), 2097151L);
failForBigNumber("mode", (long)entry.getMode(), 2097151L);
failForBigNumber("major device number", (long)entry.getDevMajor(), 2097151L);
failForBigNumber("minor device number", (long)entry.getDevMinor(), 2097151L);
}
private void failForBigNumber(String field, long value, long maxValue) {
if (value < 0L || value > maxValue)
throw new RuntimeException(field + " '" + value + "' is too big ( > " + maxValue + " )");
}
private boolean handleLongName(String name, Map<String, String> paxHeaders, String paxHeaderName, byte linkType, String fieldName) throws IOException {
ByteBuffer encodedName = this.encoding.encode(name);
int len = encodedName.limit() - encodedName.position();
if (len >= 100) {
if (this.longFileMode == 3) {
paxHeaders.put(paxHeaderName, name);
return true;
}
if (this.longFileMode == 2) {
TarArchiveEntry longLinkEntry = new TarArchiveEntry("././@LongLink", linkType);
longLinkEntry.setSize((long)(len + 1));
putArchiveEntry(longLinkEntry);
write(encodedName.array(), encodedName.arrayOffset(), len);
write(0);
closeArchiveEntry();
} else if (this.longFileMode != 1) {
throw new RuntimeException(fieldName + " '" + name + "' is too long ( > " + 'd' + " bytes)");
}
}
return false;
}
}

View file

@ -0,0 +1,17 @@
package org.apache.commons.compress.archivers.tar;
import java.io.IOException;
public class TarArchiveSparseEntry implements TarConstants {
private final boolean isExtended;
public TarArchiveSparseEntry(byte[] headerBuf) throws IOException {
int offset = 0;
offset += 504;
this.isExtended = TarUtils.parseBoolean(headerBuf, offset);
}
public boolean isExtended() {
return this.isExtended;
}
}

View file

@ -0,0 +1,113 @@
package org.apache.commons.compress.archivers.tar;
public interface TarConstants {
public static final int DEFAULT_RCDSIZE = 512;
public static final int DEFAULT_BLKSIZE = 10240;
public static final int FORMAT_OLDGNU = 2;
public static final int FORMAT_POSIX = 3;
public static final int NAMELEN = 100;
public static final int MODELEN = 8;
public static final int UIDLEN = 8;
public static final int GIDLEN = 8;
public static final long MAXID = 2097151L;
public static final int CHKSUMLEN = 8;
public static final int CHKSUM_OFFSET = 148;
public static final int SIZELEN = 12;
public static final long MAXSIZE = 8589934591L;
public static final int MAGIC_OFFSET = 257;
public static final int MAGICLEN = 6;
public static final int VERSION_OFFSET = 263;
public static final int VERSIONLEN = 2;
public static final int MODTIMELEN = 12;
public static final int UNAMELEN = 32;
public static final int GNAMELEN = 32;
public static final int DEVLEN = 8;
public static final int PREFIXLEN = 155;
public static final int ATIMELEN_GNU = 12;
public static final int CTIMELEN_GNU = 12;
public static final int OFFSETLEN_GNU = 12;
public static final int LONGNAMESLEN_GNU = 4;
public static final int PAD2LEN_GNU = 1;
public static final int SPARSELEN_GNU = 96;
public static final int ISEXTENDEDLEN_GNU = 1;
public static final int REALSIZELEN_GNU = 12;
public static final int SPARSELEN_GNU_SPARSE = 504;
public static final int ISEXTENDEDLEN_GNU_SPARSE = 1;
public static final byte LF_OLDNORM = 0;
public static final byte LF_NORMAL = 48;
public static final byte LF_LINK = 49;
public static final byte LF_SYMLINK = 50;
public static final byte LF_CHR = 51;
public static final byte LF_BLK = 52;
public static final byte LF_DIR = 53;
public static final byte LF_FIFO = 54;
public static final byte LF_CONTIG = 55;
public static final byte LF_GNUTYPE_LONGLINK = 75;
public static final byte LF_GNUTYPE_LONGNAME = 76;
public static final byte LF_GNUTYPE_SPARSE = 83;
public static final byte LF_PAX_EXTENDED_HEADER_LC = 120;
public static final byte LF_PAX_EXTENDED_HEADER_UC = 88;
public static final byte LF_PAX_GLOBAL_EXTENDED_HEADER = 103;
public static final String MAGIC_POSIX = "ustar\000";
public static final String VERSION_POSIX = "00";
public static final String MAGIC_GNU = "ustar ";
public static final String VERSION_GNU_SPACE = " \000";
public static final String VERSION_GNU_ZERO = "0\000";
public static final String MAGIC_ANT = "ustar\000";
public static final String VERSION_ANT = "\000\000";
public static final String GNU_LONGLINK = "././@LongLink";
}

View file

@ -0,0 +1,265 @@
package org.apache.commons.compress.archivers.tar;
import java.io.IOException;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import org.apache.commons.compress.archivers.zip.ZipEncoding;
import org.apache.commons.compress.archivers.zip.ZipEncodingHelper;
public class TarUtils {
private static final int BYTE_MASK = 255;
static final ZipEncoding DEFAULT_ENCODING = ZipEncodingHelper.getZipEncoding(null);
static final ZipEncoding FALLBACK_ENCODING = new ZipEncoding() {
public boolean canEncode(String name) {
return true;
}
public ByteBuffer encode(String name) {
int length = name.length();
byte[] buf = new byte[length];
for (int i = 0; i < length; i++)
buf[i] = (byte)name.charAt(i);
return ByteBuffer.wrap(buf);
}
public String decode(byte[] buffer) {
int length = buffer.length;
StringBuilder result = new StringBuilder(length);
for (int i = 0; i < length; i++) {
byte b = buffer[i];
if (b == 0)
break;
result.append((char)(b & 0xFF));
}
return result.toString();
}
};
public static long parseOctal(byte[] buffer, int offset, int length) {
long result = 0L;
int end = offset + length;
int start = offset;
if (length < 2)
throw new IllegalArgumentException("Length " + length + " must be at least 2");
if (buffer[start] == 0)
return 0L;
while (start < end &&
buffer[start] == 32)
start++;
byte trailer = buffer[end - 1];
while (start < end && (trailer == 0 || trailer == 32)) {
end--;
trailer = buffer[end - 1];
}
for (; start < end; start++) {
byte currentByte = buffer[start];
if (currentByte < 48 || currentByte > 55)
throw new IllegalArgumentException(exceptionMessage(buffer, offset, length, start, currentByte));
result = (result << 3L) + (long)(currentByte - 48);
}
return result;
}
public static long parseOctalOrBinary(byte[] buffer, int offset, int length) {
if ((buffer[offset] & 0x80) == 0)
return parseOctal(buffer, offset, length);
boolean negative = (buffer[offset] == -1);
if (length < 9)
return parseBinaryLong(buffer, offset, length, negative);
return parseBinaryBigInteger(buffer, offset, length, negative);
}
private static long parseBinaryLong(byte[] buffer, int offset, int length, boolean negative) {
if (length >= 9)
throw new IllegalArgumentException("At offset " + offset + ", " + length + " byte binary number" + " exceeds maximum signed long" + " value");
long val = 0L;
for (int i = 1; i < length; i++)
val = (val << 8L) + (long)(buffer[offset + i] & 0xFF);
if (negative) {
val--;
val ^= (long)Math.pow(2.0D, (double)((length - 1) * 8)) - 1L;
}
return negative ? -val : val;
}
private static long parseBinaryBigInteger(byte[] buffer, int offset, int length, boolean negative) {
byte[] remainder = new byte[length - 1];
System.arraycopy(buffer, offset + 1, remainder, 0, length - 1);
BigInteger val = new BigInteger(remainder);
if (negative)
val = val.add(BigInteger.valueOf(-1L)).not();
if (val.bitLength() > 63)
throw new IllegalArgumentException("At offset " + offset + ", " + length + " byte binary number" + " exceeds maximum signed long" + " value");
return negative ? -val.longValue() : val.longValue();
}
public static boolean parseBoolean(byte[] buffer, int offset) {
return (buffer[offset] == 1);
}
private static String exceptionMessage(byte[] buffer, int offset, int length, int current, byte currentByte) {
String string = new String(buffer, offset, length);
string = string.replaceAll("\000", "{NUL}");
String s = "Invalid byte " + currentByte + " at offset " + (current - offset) + " in '" + string + "' len=" + length;
return s;
}
public static String parseName(byte[] buffer, int offset, int length) {
try {
return parseName(buffer, offset, length, DEFAULT_ENCODING);
} catch (IOException ex) {
try {
return parseName(buffer, offset, length, FALLBACK_ENCODING);
} catch (IOException ex2) {
throw new RuntimeException(ex2);
}
}
}
public static String parseName(byte[] buffer, int offset, int length, ZipEncoding encoding) throws IOException {
int len = length;
for (; len > 0 &&
buffer[offset + len - 1] == 0; len--);
if (len > 0) {
byte[] b = new byte[len];
System.arraycopy(buffer, offset, b, 0, len);
return encoding.decode(b);
}
return "";
}
public static int formatNameBytes(String name, byte[] buf, int offset, int length) {
try {
return formatNameBytes(name, buf, offset, length, DEFAULT_ENCODING);
} catch (IOException ex) {
try {
return formatNameBytes(name, buf, offset, length, FALLBACK_ENCODING);
} catch (IOException ex2) {
throw new RuntimeException(ex2);
}
}
}
public static int formatNameBytes(String name, byte[] buf, int offset, int length, ZipEncoding encoding) throws IOException {
int len = name.length();
ByteBuffer b = encoding.encode(name);
while (b.limit() > length && len > 0)
b = encoding.encode(name.substring(0, --len));
int limit = b.limit() - b.position();
System.arraycopy(b.array(), b.arrayOffset(), buf, offset, limit);
for (int i = limit; i < length; i++)
buf[offset + i] = 0;
return offset + length;
}
public static void formatUnsignedOctalString(long value, byte[] buffer, int offset, int length) {
int remaining = length;
remaining--;
if (value == 0L) {
buffer[offset + remaining--] = 48;
} else {
long val = value;
for (; remaining >= 0 && val != 0L; remaining--) {
buffer[offset + remaining] = (byte)(48 + (byte)(int)(val & 0x7L));
val >>>= 3L;
}
if (val != 0L)
throw new IllegalArgumentException(value + "=" + Long.toOctalString(value) + " will not fit in octal number buffer of length " + length);
}
for (; remaining >= 0; remaining--)
buffer[offset + remaining] = 48;
}
public static int formatOctalBytes(long value, byte[] buf, int offset, int length) {
int idx = length - 2;
formatUnsignedOctalString(value, buf, offset, idx);
buf[offset + idx++] = 32;
buf[offset + idx] = 0;
return offset + length;
}
public static int formatLongOctalBytes(long value, byte[] buf, int offset, int length) {
int idx = length - 1;
formatUnsignedOctalString(value, buf, offset, idx);
buf[offset + idx] = 32;
return offset + length;
}
public static int formatLongOctalOrBinaryBytes(long value, byte[] buf, int offset, int length) {
long maxAsOctalChar = (length == 8) ? 2097151L : 8589934591L;
boolean negative = (value < 0L);
if (!negative && value <= maxAsOctalChar)
return formatLongOctalBytes(value, buf, offset, length);
if (length < 9)
formatLongBinary(value, buf, offset, length, negative);
formatBigIntegerBinary(value, buf, offset, length, negative);
buf[offset] = (byte)(negative ? 255 : 128);
return offset + length;
}
private static void formatLongBinary(long value, byte[] buf, int offset, int length, boolean negative) {
int bits = (length - 1) * 8;
long max = 1L << bits;
long val = Math.abs(value);
if (val >= max)
throw new IllegalArgumentException("Value " + value + " is too large for " + length + " byte field.");
if (negative) {
val ^= max - 1L;
val |= (long)(255 << bits);
val++;
}
for (int i = offset + length - 1; i >= offset; i--) {
buf[i] = (byte)(int)val;
val >>= 8L;
}
}
private static void formatBigIntegerBinary(long value, byte[] buf, int offset, int length, boolean negative) {
BigInteger val = BigInteger.valueOf(value);
byte[] b = val.toByteArray();
int len = b.length;
int off = offset + length - len;
System.arraycopy(b, 0, buf, off, len);
byte fill = (byte)(negative ? 255 : 0);
for (int i = offset + 1; i < off; i++)
buf[i] = fill;
}
public static int formatCheckSumOctalBytes(long value, byte[] buf, int offset, int length) {
int idx = length - 2;
formatUnsignedOctalString(value, buf, offset, idx);
buf[offset + idx++] = 0;
buf[offset + idx] = 32;
return offset + length;
}
public static long computeCheckSum(byte[] buf) {
long sum = 0L;
for (byte element : buf)
sum += (long)(0xFF & element);
return sum;
}
public static boolean verifyCheckSum(byte[] header) {
long storedSum = 0L;
long unsignedSum = 0L;
long signedSum = 0L;
int digits = 0;
for (int i = 0; i < header.length; i++) {
byte b = header[i];
if (148 <= i && i < 156) {
if (48 <= b && b <= 55 && digits++ < 6) {
storedSum = storedSum * 8L + (long)b - 48L;
} else if (digits > 0) {
digits = 6;
}
b = 32;
}
unsignedSum += (long)(0xFF & b);
signedSum += (long)b;
}
return (storedSum == unsignedSum || storedSum == signedSum || storedSum > unsignedSum);
}
}

View file

@ -0,0 +1,108 @@
package org.apache.commons.compress.archivers.zip;
import java.io.UnsupportedEncodingException;
import java.util.zip.CRC32;
import java.util.zip.ZipException;
public abstract class AbstractUnicodeExtraField implements ZipExtraField {
private long nameCRC32;
private byte[] unicodeName;
private byte[] data;
protected AbstractUnicodeExtraField() {}
protected AbstractUnicodeExtraField(String text, byte[] bytes, int off, int len) {
CRC32 crc32 = new CRC32();
crc32.update(bytes, off, len);
this.nameCRC32 = crc32.getValue();
try {
this.unicodeName = text.getBytes("UTF-8");
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("FATAL: UTF-8 encoding not supported.", e);
}
}
protected AbstractUnicodeExtraField(String text, byte[] bytes) {
this(text, bytes, 0, bytes.length);
}
private void assembleData() {
if (this.unicodeName == null)
return;
this.data = new byte[5 + this.unicodeName.length];
this.data[0] = 1;
System.arraycopy(ZipLong.getBytes(this.nameCRC32), 0, this.data, 1, 4);
System.arraycopy(this.unicodeName, 0, this.data, 5, this.unicodeName.length);
}
public long getNameCRC32() {
return this.nameCRC32;
}
public void setNameCRC32(long nameCRC32) {
this.nameCRC32 = nameCRC32;
this.data = null;
}
public byte[] getUnicodeName() {
byte[] b = null;
if (this.unicodeName != null) {
b = new byte[this.unicodeName.length];
System.arraycopy(this.unicodeName, 0, b, 0, b.length);
}
return b;
}
public void setUnicodeName(byte[] unicodeName) {
if (unicodeName != null) {
this.unicodeName = new byte[unicodeName.length];
System.arraycopy(unicodeName, 0, this.unicodeName, 0, unicodeName.length);
} else {
this.unicodeName = null;
}
this.data = null;
}
public byte[] getCentralDirectoryData() {
if (this.data == null)
assembleData();
byte[] b = null;
if (this.data != null) {
b = new byte[this.data.length];
System.arraycopy(this.data, 0, b, 0, b.length);
}
return b;
}
public ZipShort getCentralDirectoryLength() {
if (this.data == null)
assembleData();
return new ZipShort((this.data != null) ? this.data.length : 0);
}
public byte[] getLocalFileDataData() {
return getCentralDirectoryData();
}
public ZipShort getLocalFileDataLength() {
return getCentralDirectoryLength();
}
public void parseFromLocalFileData(byte[] buffer, int offset, int length) throws ZipException {
if (length < 5)
throw new ZipException("UniCode path extra data must have at least 5 bytes.");
int version = buffer[offset];
if (version != 1)
throw new ZipException("Unsupported version [" + version + "] for UniCode path extra data.");
this.nameCRC32 = ZipLong.getValue(buffer, offset + 1);
this.unicodeName = new byte[length - 5];
System.arraycopy(buffer, offset + 5, this.unicodeName, 0, length - 5);
this.data = null;
}
public void parseFromCentralDirectoryData(byte[] buffer, int offset, int length) throws ZipException {
parseFromLocalFileData(buffer, offset, length);
}
}

View file

@ -0,0 +1,148 @@
package org.apache.commons.compress.archivers.zip;
import java.util.zip.CRC32;
import java.util.zip.ZipException;
public class AsiExtraField implements ZipExtraField, UnixStat, Cloneable {
private static final ZipShort HEADER_ID = new ZipShort(30062);
private static final int WORD = 4;
private int mode = 0;
private int uid = 0;
private int gid = 0;
private String link = "";
private boolean dirFlag = false;
private CRC32 crc = new CRC32();
public ZipShort getHeaderId() {
return HEADER_ID;
}
public ZipShort getLocalFileDataLength() {
return new ZipShort(14 + (getLinkedFile().getBytes()).length);
}
public ZipShort getCentralDirectoryLength() {
return getLocalFileDataLength();
}
public byte[] getLocalFileDataData() {
byte[] data = new byte[getLocalFileDataLength().getValue() - 4];
System.arraycopy(ZipShort.getBytes(getMode()), 0, data, 0, 2);
byte[] linkArray = getLinkedFile().getBytes();
System.arraycopy(ZipLong.getBytes((long)linkArray.length), 0, data, 2, 4);
System.arraycopy(ZipShort.getBytes(getUserId()), 0, data, 6, 2);
System.arraycopy(ZipShort.getBytes(getGroupId()), 0, data, 8, 2);
System.arraycopy(linkArray, 0, data, 10, linkArray.length);
this.crc.reset();
this.crc.update(data);
long checksum = this.crc.getValue();
byte[] result = new byte[data.length + 4];
System.arraycopy(ZipLong.getBytes(checksum), 0, result, 0, 4);
System.arraycopy(data, 0, result, 4, data.length);
return result;
}
public byte[] getCentralDirectoryData() {
return getLocalFileDataData();
}
public void setUserId(int uid) {
this.uid = uid;
}
public int getUserId() {
return this.uid;
}
public void setGroupId(int gid) {
this.gid = gid;
}
public int getGroupId() {
return this.gid;
}
public void setLinkedFile(String name) {
this.link = name;
this.mode = getMode(this.mode);
}
public String getLinkedFile() {
return this.link;
}
public boolean isLink() {
return (getLinkedFile().length() != 0);
}
public void setMode(int mode) {
this.mode = getMode(mode);
}
public int getMode() {
return this.mode;
}
public void setDirectory(boolean dirFlag) {
this.dirFlag = dirFlag;
this.mode = getMode(this.mode);
}
public boolean isDirectory() {
return (this.dirFlag && !isLink());
}
public void parseFromLocalFileData(byte[] data, int offset, int length) throws ZipException {
long givenChecksum = ZipLong.getValue(data, offset);
byte[] tmp = new byte[length - 4];
System.arraycopy(data, offset + 4, tmp, 0, length - 4);
this.crc.reset();
this.crc.update(tmp);
long realChecksum = this.crc.getValue();
if (givenChecksum != realChecksum)
throw new ZipException("bad CRC checksum " + Long.toHexString(givenChecksum) + " instead of " + Long.toHexString(realChecksum));
int newMode = ZipShort.getValue(tmp, 0);
byte[] linkArray = new byte[(int)ZipLong.getValue(tmp, 2)];
this.uid = ZipShort.getValue(tmp, 6);
this.gid = ZipShort.getValue(tmp, 8);
if (linkArray.length == 0) {
this.link = "";
} else {
System.arraycopy(tmp, 10, linkArray, 0, linkArray.length);
this.link = new String(linkArray);
}
setDirectory(((newMode & 0x4000) != 0));
setMode(newMode);
}
public void parseFromCentralDirectoryData(byte[] buffer, int offset, int length) throws ZipException {
parseFromLocalFileData(buffer, offset, length);
}
protected int getMode(int mode) {
int type = 32768;
if (isLink()) {
type = 40960;
} else if (isDirectory()) {
type = 16384;
}
return type | mode & 0xFFF;
}
public Object clone() {
try {
AsiExtraField cloned = (AsiExtraField)super.clone();
cloned.crc = new CRC32();
return cloned;
} catch (CloneNotSupportedException cnfe) {
throw new RuntimeException(cnfe);
}
}
}

View file

@ -0,0 +1,104 @@
package org.apache.commons.compress.archivers.zip;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
class BinaryTree {
private static final int UNDEFINED = -1;
private static final int NODE = -2;
private final int[] tree;
public BinaryTree(int depth) {
this.tree = new int[(1 << depth + 1) - 1];
Arrays.fill(this.tree, -1);
}
public void addLeaf(int node, int path, int depth, int value) {
if (depth == 0) {
if (this.tree[node] == -1) {
this.tree[node] = value;
} else {
throw new IllegalArgumentException("Tree value at index " + node + " has already been assigned (" + this.tree[node] + ")");
}
} else {
this.tree[node] = -2;
int nextChild = 2 * node + 1 + (path & 0x1);
addLeaf(nextChild, path >>> 1, depth - 1, value);
}
}
public int read(BitStream stream) throws IOException {
int bit, value;
int currentIndex = 0;
while (true) {
bit = stream.nextBit();
if (bit == -1)
return -1;
int childIndex = 2 * currentIndex + 1 + bit;
value = this.tree[childIndex];
if (value == -2) {
currentIndex = childIndex;
continue;
}
break;
}
if (value != -1)
return value;
throw new IOException("The child " + bit + " of node at index " + currentIndex + " is not defined");
}
static BinaryTree decode(InputStream in, int totalNumberOfValues) throws IOException {
int size = in.read() + 1;
if (size == 0)
throw new IOException("Cannot read the size of the encoded tree, unexpected end of stream");
byte[] encodedTree = new byte[size];
new DataInputStream(in).readFully(encodedTree);
int maxLength = 0;
int[] originalBitLengths = new int[totalNumberOfValues];
int pos = 0;
for (byte b : encodedTree) {
int numberOfValues = ((b & 0xF0) >> 4) + 1;
int bitLength = (b & 0xF) + 1;
for (int n = 0; n < numberOfValues; n++)
originalBitLengths[pos++] = bitLength;
maxLength = Math.max(maxLength, bitLength);
}
int[] permutation = new int[originalBitLengths.length];
for (int k = 0; k < permutation.length; k++)
permutation[k] = k;
int c = 0;
int[] sortedBitLengths = new int[originalBitLengths.length];
for (int j = 0; j < originalBitLengths.length; j++) {
for (int l = 0; l < originalBitLengths.length; l++) {
if (originalBitLengths[l] == j) {
sortedBitLengths[c] = j;
permutation[c] = l;
c++;
}
}
}
int code = 0;
int codeIncrement = 0;
int lastBitLength = 0;
int[] codes = new int[totalNumberOfValues];
for (int i = totalNumberOfValues - 1; i >= 0; i--) {
code += codeIncrement;
if (sortedBitLengths[i] != lastBitLength) {
lastBitLength = sortedBitLengths[i];
codeIncrement = 1 << 16 - lastBitLength;
}
codes[permutation[i]] = code;
}
BinaryTree tree = new BinaryTree(maxLength);
for (int m = 0; m < codes.length; m++) {
int bitLength = originalBitLengths[m];
if (bitLength > 0)
tree.addLeaf(0, Integer.reverse(codes[m] << 16), bitLength, m);
}
return tree;
}
}

View file

@ -0,0 +1,53 @@
package org.apache.commons.compress.archivers.zip;
import java.io.IOException;
import java.io.InputStream;
class BitStream {
private final InputStream in;
private long bitCache;
private int bitCacheSize;
private static final int[] MASKS = new int[] { 0, 1, 3, 7, 15, 31, 63, 127, 255 };
BitStream(InputStream in) {
this.in = in;
}
private boolean fillCache() throws IOException {
boolean filled = false;
while (this.bitCacheSize <= 56) {
long nextByte = (long)this.in.read();
if (nextByte == -1L)
break;
filled = true;
this.bitCache |= nextByte << this.bitCacheSize;
this.bitCacheSize += 8;
}
return filled;
}
int nextBit() throws IOException {
if (this.bitCacheSize == 0 && !fillCache())
return -1;
int bit = (int)(this.bitCache & 0x1L);
this.bitCache >>>= 1L;
this.bitCacheSize--;
return bit;
}
int nextBits(int n) throws IOException {
if (this.bitCacheSize < n && !fillCache())
return -1;
int bits = (int)(this.bitCache & (long)MASKS[n]);
this.bitCache >>>= n;
this.bitCacheSize -= n;
return bits;
}
int nextByte() throws IOException {
return nextBits(8);
}
}

View file

@ -0,0 +1,43 @@
package org.apache.commons.compress.archivers.zip;
class CircularBuffer {
private final int size;
private final byte[] buffer;
private int readIndex;
private int writeIndex;
CircularBuffer(int size) {
this.size = size;
this.buffer = new byte[size];
}
public boolean available() {
return (this.readIndex != this.writeIndex);
}
public void put(int value) {
this.buffer[this.writeIndex] = (byte)value;
this.writeIndex = (this.writeIndex + 1) % this.size;
}
public int get() {
if (available()) {
int value = this.buffer[this.readIndex];
this.readIndex = (this.readIndex + 1) % this.size;
return value & 0xFF;
}
return -1;
}
public void copy(int distance, int length) {
int pos1 = this.writeIndex - distance;
int pos2 = pos1 + length;
for (int i = pos1; i < pos2; i++) {
this.buffer[this.writeIndex] = this.buffer[(i + this.size) % this.size];
this.writeIndex = (this.writeIndex + 1) % this.size;
}
}
}

View file

@ -0,0 +1,79 @@
package org.apache.commons.compress.archivers.zip;
import java.io.IOException;
import java.io.InputStream;
class ExplodingInputStream extends InputStream {
private final InputStream in;
private BitStream bits;
private final int dictionarySize;
private final int numberOfTrees;
private final int minimumMatchLength;
private BinaryTree literalTree;
private BinaryTree lengthTree;
private BinaryTree distanceTree;
private final CircularBuffer buffer = new CircularBuffer(32768);
public ExplodingInputStream(int dictionarySize, int numberOfTrees, InputStream in) {
if (dictionarySize != 4096 && dictionarySize != 8192)
throw new IllegalArgumentException("The dictionary size must be 4096 or 8192");
if (numberOfTrees != 2 && numberOfTrees != 3)
throw new IllegalArgumentException("The number of trees must be 2 or 3");
this.dictionarySize = dictionarySize;
this.numberOfTrees = numberOfTrees;
this.minimumMatchLength = numberOfTrees;
this.in = in;
}
private void init() throws IOException {
if (this.bits == null) {
if (this.numberOfTrees == 3)
this.literalTree = BinaryTree.decode(this.in, 256);
this.lengthTree = BinaryTree.decode(this.in, 64);
this.distanceTree = BinaryTree.decode(this.in, 64);
this.bits = new BitStream(this.in);
}
}
public int read() throws IOException {
if (!this.buffer.available())
fillBuffer();
return this.buffer.get();
}
private void fillBuffer() throws IOException {
init();
int bit = this.bits.nextBit();
if (bit == 1) {
int literal;
if (this.literalTree != null) {
literal = this.literalTree.read(this.bits);
} else {
literal = this.bits.nextBits(8);
}
if (literal == -1)
return;
this.buffer.put(literal);
} else if (bit == 0) {
int distanceLowSize = (this.dictionarySize == 4096) ? 6 : 7;
int distanceLow = this.bits.nextBits(distanceLowSize);
int distanceHigh = this.distanceTree.read(this.bits);
if (distanceHigh == -1 && distanceLow <= 0)
return;
int distance = distanceHigh << distanceLowSize | distanceLow;
int length = this.lengthTree.read(this.bits);
if (length == 63)
length += this.bits.nextBits(8);
length += this.minimumMatchLength;
this.buffer.copy(distance + 1, length);
}
}
}

View file

@ -0,0 +1,173 @@
package org.apache.commons.compress.archivers.zip;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.zip.ZipException;
public class ExtraFieldUtils {
private static final int WORD = 4;
private static final Map<ZipShort, Class<?>> implementations = new ConcurrentHashMap<ZipShort, Class<?>>();
static {
register(AsiExtraField.class);
register(X5455_ExtendedTimestamp.class);
register(X7875_NewUnix.class);
register(JarMarker.class);
register(UnicodePathExtraField.class);
register(UnicodeCommentExtraField.class);
register(Zip64ExtendedInformationExtraField.class);
}
public static void register(Class<?> c) {
try {
ZipExtraField ze = (ZipExtraField)c.newInstance();
implementations.put(ze.getHeaderId(), c);
} catch (ClassCastException cc) {
throw new RuntimeException(c + " doesn't implement ZipExtraField");
} catch (InstantiationException ie) {
throw new RuntimeException(c + " is not a concrete class");
} catch (IllegalAccessException ie) {
throw new RuntimeException(c + "'s no-arg constructor is not public");
}
}
public static ZipExtraField createExtraField(ZipShort headerId) throws InstantiationException, IllegalAccessException {
Class<?> c = implementations.get(headerId);
if (c != null)
return (ZipExtraField)c.newInstance();
UnrecognizedExtraField u = new UnrecognizedExtraField();
u.setHeaderId(headerId);
return u;
}
public static ZipExtraField[] parse(byte[] data) throws ZipException {
return parse(data, true, UnparseableExtraField.THROW);
}
public static ZipExtraField[] parse(byte[] data, boolean local) throws ZipException {
return parse(data, local, UnparseableExtraField.THROW);
}
public static ZipExtraField[] parse(byte[] data, boolean local, UnparseableExtraField onUnparseableData) throws ZipException {
List<ZipExtraField> v = new ArrayList<ZipExtraField>();
int start = 0;
while (start <= data.length - 4) {
ZipShort headerId = new ZipShort(data, start);
int length = new ZipShort(data, start + 2).getValue();
if (start + 4 + length > data.length) {
UnparseableExtraFieldData field;
switch (onUnparseableData.getKey()) {
case 0:
throw new ZipException("bad extra field starting at " + start + ". Block length of " + length + " bytes exceeds remaining" + " data of " + (data.length - start - 4) + " bytes.");
case 2:
field = new UnparseableExtraFieldData();
if (local) {
field.parseFromLocalFileData(data, start, data.length - start);
} else {
field.parseFromCentralDirectoryData(data, start, data.length - start);
}
v.add(field);
break;
case 1:
break;
}
throw new ZipException("unknown UnparseableExtraField key: " + onUnparseableData.getKey());
}
try {
ZipExtraField ze = createExtraField(headerId);
if (local) {
ze.parseFromLocalFileData(data, start + 4, length);
} else {
ze.parseFromCentralDirectoryData(data, start + 4, length);
}
v.add(ze);
} catch (InstantiationException ie) {
throw (ZipException)new ZipException(ie.getMessage()).initCause(ie);
} catch (IllegalAccessException iae) {
throw (ZipException)new ZipException(iae.getMessage()).initCause(iae);
}
start += length + 4;
}
ZipExtraField[] result = new ZipExtraField[v.size()];
return v.<ZipExtraField>toArray(result);
}
public static byte[] mergeLocalFileDataData(ZipExtraField[] data) {
boolean lastIsUnparseableHolder = (data.length > 0 && data[data.length - 1] instanceof UnparseableExtraFieldData);
int regularExtraFieldCount = lastIsUnparseableHolder ? (data.length - 1) : data.length;
int sum = 4 * regularExtraFieldCount;
for (ZipExtraField element : data)
sum += element.getLocalFileDataLength().getValue();
byte[] result = new byte[sum];
int start = 0;
for (int i = 0; i < regularExtraFieldCount; i++) {
System.arraycopy(data[i].getHeaderId().getBytes(), 0, result, start, 2);
System.arraycopy(data[i].getLocalFileDataLength().getBytes(), 0, result, start + 2, 2);
start += 4;
byte[] local = data[i].getLocalFileDataData();
if (local != null) {
System.arraycopy(local, 0, result, start, local.length);
start += local.length;
}
}
if (lastIsUnparseableHolder) {
byte[] local = data[data.length - 1].getLocalFileDataData();
if (local != null)
System.arraycopy(local, 0, result, start, local.length);
}
return result;
}
public static byte[] mergeCentralDirectoryData(ZipExtraField[] data) {
boolean lastIsUnparseableHolder = (data.length > 0 && data[data.length - 1] instanceof UnparseableExtraFieldData);
int regularExtraFieldCount = lastIsUnparseableHolder ? (data.length - 1) : data.length;
int sum = 4 * regularExtraFieldCount;
for (ZipExtraField element : data)
sum += element.getCentralDirectoryLength().getValue();
byte[] result = new byte[sum];
int start = 0;
for (int i = 0; i < regularExtraFieldCount; i++) {
System.arraycopy(data[i].getHeaderId().getBytes(), 0, result, start, 2);
System.arraycopy(data[i].getCentralDirectoryLength().getBytes(), 0, result, start + 2, 2);
start += 4;
byte[] local = data[i].getCentralDirectoryData();
if (local != null) {
System.arraycopy(local, 0, result, start, local.length);
start += local.length;
}
}
if (lastIsUnparseableHolder) {
byte[] local = data[data.length - 1].getCentralDirectoryData();
if (local != null)
System.arraycopy(local, 0, result, start, local.length);
}
return result;
}
public static final class UnparseableExtraField {
public static final int THROW_KEY = 0;
public static final int SKIP_KEY = 1;
public static final int READ_KEY = 2;
public static final UnparseableExtraField THROW = new UnparseableExtraField(0);
public static final UnparseableExtraField SKIP = new UnparseableExtraField(1);
public static final UnparseableExtraField READ = new UnparseableExtraField(2);
private final int key;
private UnparseableExtraField(int k) {
this.key = k;
}
public int getKey() {
return this.key;
}
}
}

View file

@ -0,0 +1,32 @@
package org.apache.commons.compress.archivers.zip;
import java.io.IOException;
import java.nio.ByteBuffer;
class FallbackZipEncoding implements ZipEncoding {
private final String charsetName;
public FallbackZipEncoding() {
this.charsetName = null;
}
public FallbackZipEncoding(String charsetName) {
this.charsetName = charsetName;
}
public boolean canEncode(String name) {
return true;
}
public ByteBuffer encode(String name) throws IOException {
if (this.charsetName == null)
return ByteBuffer.wrap(name.getBytes());
return ByteBuffer.wrap(name.getBytes(this.charsetName));
}
public String decode(byte[] data) throws IOException {
if (this.charsetName == null)
return new String(data);
return new String(data, this.charsetName);
}
}

View file

@ -0,0 +1,96 @@
package org.apache.commons.compress.archivers.zip;
public final class GeneralPurposeBit {
private static final int ENCRYPTION_FLAG = 1;
private static final int SLIDING_DICTIONARY_SIZE_FLAG = 2;
private static final int NUMBER_OF_SHANNON_FANO_TREES_FLAG = 4;
private static final int DATA_DESCRIPTOR_FLAG = 8;
private static final int STRONG_ENCRYPTION_FLAG = 64;
public static final int UFT8_NAMES_FLAG = 2048;
private boolean languageEncodingFlag = false;
private boolean dataDescriptorFlag = false;
private boolean encryptionFlag = false;
private boolean strongEncryptionFlag = false;
private int slidingDictionarySize;
private int numberOfShannonFanoTrees;
public boolean usesUTF8ForNames() {
return this.languageEncodingFlag;
}
public void useUTF8ForNames(boolean b) {
this.languageEncodingFlag = b;
}
public boolean usesDataDescriptor() {
return this.dataDescriptorFlag;
}
public void useDataDescriptor(boolean b) {
this.dataDescriptorFlag = b;
}
public boolean usesEncryption() {
return this.encryptionFlag;
}
public void useEncryption(boolean b) {
this.encryptionFlag = b;
}
public boolean usesStrongEncryption() {
return (this.encryptionFlag && this.strongEncryptionFlag);
}
public void useStrongEncryption(boolean b) {
this.strongEncryptionFlag = b;
if (b)
useEncryption(true);
}
int getSlidingDictionarySize() {
return this.slidingDictionarySize;
}
int getNumberOfShannonFanoTrees() {
return this.numberOfShannonFanoTrees;
}
public byte[] encode() {
return ZipShort.getBytes((this.dataDescriptorFlag ? 8 : 0) | (this.languageEncodingFlag ? 2048 : 0) | (this.encryptionFlag ? 1 : 0) | (this.strongEncryptionFlag ? 64 : 0));
}
public static GeneralPurposeBit parse(byte[] data, int offset) {
int generalPurposeFlag = ZipShort.getValue(data, offset);
GeneralPurposeBit b = new GeneralPurposeBit();
b.useDataDescriptor(((generalPurposeFlag & 0x8) != 0));
b.useUTF8ForNames(((generalPurposeFlag & 0x800) != 0));
b.useStrongEncryption(((generalPurposeFlag & 0x40) != 0));
b.useEncryption(((generalPurposeFlag & 0x1) != 0));
b.slidingDictionarySize = ((generalPurposeFlag & 0x2) != 0) ? 8192 : 4096;
b.numberOfShannonFanoTrees = ((generalPurposeFlag & 0x4) != 0) ? 3 : 2;
return b;
}
public int hashCode() {
return 3 * (7 * (13 * (17 * (this.encryptionFlag ? 1 : 0) + (this.strongEncryptionFlag ? 1 : 0)) + (this.languageEncodingFlag ? 1 : 0)) + (this.dataDescriptorFlag ? 1 : 0));
}
public boolean equals(Object o) {
if (!(o instanceof GeneralPurposeBit))
return false;
GeneralPurposeBit g = (GeneralPurposeBit)o;
return (g.encryptionFlag == this.encryptionFlag && g.strongEncryptionFlag == this.strongEncryptionFlag && g.languageEncodingFlag == this.languageEncodingFlag && g.dataDescriptorFlag == this.dataDescriptorFlag);
}
}

View file

@ -0,0 +1,46 @@
package org.apache.commons.compress.archivers.zip;
import java.util.zip.ZipException;
public final class JarMarker implements ZipExtraField {
private static final ZipShort ID = new ZipShort(51966);
private static final ZipShort NULL = new ZipShort(0);
private static final byte[] NO_BYTES = new byte[0];
private static final JarMarker DEFAULT = new JarMarker();
public static JarMarker getInstance() {
return DEFAULT;
}
public ZipShort getHeaderId() {
return ID;
}
public ZipShort getLocalFileDataLength() {
return NULL;
}
public ZipShort getCentralDirectoryLength() {
return NULL;
}
public byte[] getLocalFileDataData() {
return NO_BYTES;
}
public byte[] getCentralDirectoryData() {
return NO_BYTES;
}
public void parseFromLocalFileData(byte[] data, int offset, int length) throws ZipException {
if (length != 0)
throw new ZipException("JarMarker doesn't expect any data");
}
public void parseFromCentralDirectoryData(byte[] buffer, int offset, int length) throws ZipException {
parseFromLocalFileData(buffer, offset, length);
}
}

View file

@ -0,0 +1,57 @@
package org.apache.commons.compress.archivers.zip;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;
import java.nio.charset.CoderResult;
import java.nio.charset.CodingErrorAction;
class NioZipEncoding implements ZipEncoding {
private final Charset charset;
public NioZipEncoding(Charset charset) {
this.charset = charset;
}
public boolean canEncode(String name) {
CharsetEncoder enc = this.charset.newEncoder();
enc.onMalformedInput(CodingErrorAction.REPORT);
enc.onUnmappableCharacter(CodingErrorAction.REPORT);
return enc.canEncode(name);
}
public ByteBuffer encode(String name) {
CharsetEncoder enc = this.charset.newEncoder();
enc.onMalformedInput(CodingErrorAction.REPORT);
enc.onUnmappableCharacter(CodingErrorAction.REPORT);
CharBuffer cb = CharBuffer.wrap(name);
ByteBuffer out = ByteBuffer.allocate(name.length() + (name.length() + 1) / 2);
while (cb.remaining() > 0) {
CoderResult res = enc.encode(cb, out, true);
if (res.isUnmappable() || res.isMalformed()) {
if (res.length() * 6 > out.remaining())
out = ZipEncodingHelper.growBuffer(out, out.position() + res.length() * 6);
for (int i = 0; i < res.length(); i++)
ZipEncodingHelper.appendSurrogate(out, cb.get());
continue;
}
if (res.isOverflow()) {
out = ZipEncodingHelper.growBuffer(out, 0);
continue;
}
if (res.isUnderflow()) {
enc.flush(out);
break;
}
}
out.limit(out.position());
out.rewind();
return out;
}
public String decode(byte[] data) throws IOException {
return this.charset.newDecoder().onMalformedInput(CodingErrorAction.REPORT).onUnmappableCharacter(CodingErrorAction.REPORT).decode(ByteBuffer.wrap(data)).toString();
}
}

View file

@ -0,0 +1,133 @@
package org.apache.commons.compress.archivers.zip;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
class Simple8BitZipEncoding implements ZipEncoding {
private final char[] highChars;
private final List<Simple8BitChar> reverseMapping;
private static final class Simple8BitChar implements Comparable<Simple8BitChar> {
public final char unicode;
public final byte code;
Simple8BitChar(byte code, char unicode) {
this.code = code;
this.unicode = unicode;
}
public int compareTo(Simple8BitChar a) {
return this.unicode - a.unicode;
}
public String toString() {
return "0x" + Integer.toHexString(Character.MAX_VALUE & this.unicode) + "->0x" + Integer.toHexString(0xFF & this.code);
}
public boolean equals(Object o) {
if (o instanceof Simple8BitChar) {
Simple8BitChar other = (Simple8BitChar)o;
return (this.unicode == other.unicode && this.code == other.code);
}
return false;
}
public int hashCode() {
return this.unicode;
}
}
public Simple8BitZipEncoding(char[] highChars) {
this.highChars = (char[])highChars.clone();
List<Simple8BitChar> temp = new ArrayList<Simple8BitChar>(this.highChars.length);
byte code = Byte.MAX_VALUE;
for (char highChar : this.highChars) {
code = (byte)(code + 1);
temp.add(new Simple8BitChar(code, highChar));
}
Collections.<Simple8BitChar>sort(temp);
this.reverseMapping = Collections.<Simple8BitChar>unmodifiableList(temp);
}
public char decodeByte(byte b) {
if (b >= 0)
return (char)b;
return this.highChars[128 + b];
}
public boolean canEncodeChar(char c) {
if (c >= '\000' && c < '\u0080')
return true;
Simple8BitChar r = encodeHighChar(c);
return (r != null);
}
public boolean pushEncodedChar(ByteBuffer bb, char c) {
if (c >= '\000' && c < '\u0080') {
bb.put((byte)c);
return true;
}
Simple8BitChar r = encodeHighChar(c);
if (r == null)
return false;
bb.put(r.code);
return true;
}
private Simple8BitChar encodeHighChar(char c) {
int i0 = 0;
int i1 = this.reverseMapping.size();
while (i1 > i0) {
int i = i0 + (i1 - i0) / 2;
Simple8BitChar m = this.reverseMapping.get(i);
if (m.unicode == c)
return m;
if (m.unicode < c) {
i0 = i + 1;
continue;
}
i1 = i;
}
if (i0 >= this.reverseMapping.size())
return null;
Simple8BitChar r = this.reverseMapping.get(i0);
if (r.unicode != c)
return null;
return r;
}
public boolean canEncode(String name) {
for (int i = 0; i < name.length(); i++) {
char c = name.charAt(i);
if (!canEncodeChar(c))
return false;
}
return true;
}
public ByteBuffer encode(String name) {
ByteBuffer out = ByteBuffer.allocate(name.length() + 6 + (name.length() + 1) / 2);
for (int i = 0; i < name.length(); i++) {
char c = name.charAt(i);
if (out.remaining() < 6)
out = ZipEncodingHelper.growBuffer(out, out.position() + 6);
if (!pushEncodedChar(out, c))
ZipEncodingHelper.appendSurrogate(out, c);
}
out.limit(out.position());
out.rewind();
return out;
}
public String decode(byte[] data) throws IOException {
char[] ret = new char[data.length];
for (int i = 0; i < data.length; i++)
ret[i] = decodeByte(data[i]);
return new String(ret);
}
}

View file

@ -0,0 +1,19 @@
package org.apache.commons.compress.archivers.zip;
public class UnicodeCommentExtraField extends AbstractUnicodeExtraField {
public static final ZipShort UCOM_ID = new ZipShort(25461);
public UnicodeCommentExtraField() {}
public UnicodeCommentExtraField(String text, byte[] bytes, int off, int len) {
super(text, bytes, off, len);
}
public UnicodeCommentExtraField(String comment, byte[] bytes) {
super(comment, bytes);
}
public ZipShort getHeaderId() {
return UCOM_ID;
}
}

View file

@ -0,0 +1,19 @@
package org.apache.commons.compress.archivers.zip;
public class UnicodePathExtraField extends AbstractUnicodeExtraField {
public static final ZipShort UPATH_ID = new ZipShort(28789);
public UnicodePathExtraField() {}
public UnicodePathExtraField(String text, byte[] bytes, int off, int len) {
super(text, bytes, off, len);
}
public UnicodePathExtraField(String name, byte[] bytes) {
super(name, bytes);
}
public ZipShort getHeaderId() {
return UPATH_ID;
}
}

View file

@ -0,0 +1,17 @@
package org.apache.commons.compress.archivers.zip;
public interface UnixStat {
public static final int PERM_MASK = 4095;
public static final int LINK_FLAG = 40960;
public static final int FILE_FLAG = 32768;
public static final int DIR_FLAG = 16384;
public static final int DEFAULT_LINK_PERM = 511;
public static final int DEFAULT_DIR_PERM = 493;
public static final int DEFAULT_FILE_PERM = 420;
}

View file

@ -0,0 +1,41 @@
package org.apache.commons.compress.archivers.zip;
public final class UnparseableExtraFieldData implements ZipExtraField {
private static final ZipShort HEADER_ID = new ZipShort(44225);
private byte[] localFileData;
private byte[] centralDirectoryData;
public ZipShort getHeaderId() {
return HEADER_ID;
}
public ZipShort getLocalFileDataLength() {
return new ZipShort((this.localFileData == null) ? 0 : this.localFileData.length);
}
public ZipShort getCentralDirectoryLength() {
return (this.centralDirectoryData == null) ? getLocalFileDataLength() : new ZipShort(this.centralDirectoryData.length);
}
public byte[] getLocalFileDataData() {
return ZipUtil.copy(this.localFileData);
}
public byte[] getCentralDirectoryData() {
return (this.centralDirectoryData == null) ? getLocalFileDataData() : ZipUtil.copy(this.centralDirectoryData);
}
public void parseFromLocalFileData(byte[] buffer, int offset, int length) {
this.localFileData = new byte[length];
System.arraycopy(buffer, offset, this.localFileData, 0, length);
}
public void parseFromCentralDirectoryData(byte[] buffer, int offset, int length) {
this.centralDirectoryData = new byte[length];
System.arraycopy(buffer, offset, this.centralDirectoryData, 0, length);
if (this.localFileData == null)
parseFromLocalFileData(buffer, offset, length);
}
}

View file

@ -0,0 +1,59 @@
package org.apache.commons.compress.archivers.zip;
public class UnrecognizedExtraField implements ZipExtraField {
private ZipShort headerId;
private byte[] localData;
private byte[] centralData;
public void setHeaderId(ZipShort headerId) {
this.headerId = headerId;
}
public ZipShort getHeaderId() {
return this.headerId;
}
public void setLocalFileDataData(byte[] data) {
this.localData = ZipUtil.copy(data);
}
public ZipShort getLocalFileDataLength() {
return new ZipShort((this.localData != null) ? this.localData.length : 0);
}
public byte[] getLocalFileDataData() {
return ZipUtil.copy(this.localData);
}
public void setCentralDirectoryData(byte[] data) {
this.centralData = ZipUtil.copy(data);
}
public ZipShort getCentralDirectoryLength() {
if (this.centralData != null)
return new ZipShort(this.centralData.length);
return getLocalFileDataLength();
}
public byte[] getCentralDirectoryData() {
if (this.centralData != null)
return ZipUtil.copy(this.centralData);
return getLocalFileDataData();
}
public void parseFromLocalFileData(byte[] data, int offset, int length) {
byte[] tmp = new byte[length];
System.arraycopy(data, offset, tmp, 0, length);
setLocalFileDataData(tmp);
}
public void parseFromCentralDirectoryData(byte[] data, int offset, int length) {
byte[] tmp = new byte[length];
System.arraycopy(data, offset, tmp, 0, length);
setCentralDirectoryData(tmp);
if (this.localData == null)
setLocalFileDataData(tmp);
}
}

View file

@ -0,0 +1,77 @@
package org.apache.commons.compress.archivers.zip;
import java.io.IOException;
import java.io.InputStream;
import org.apache.commons.compress.compressors.z._internal_.InternalLZWInputStream;
class UnshrinkingInputStream extends InternalLZWInputStream {
private static final int MAX_CODE_SIZE = 13;
private static final int MAX_TABLE_SIZE = 8192;
private final boolean[] isUsed;
public UnshrinkingInputStream(InputStream inputStream) throws IOException {
super(inputStream);
setClearCode(this.codeSize);
initializeTables(13);
this.isUsed = new boolean[this.prefixes.length];
for (int i = 0; i < 256; i++)
this.isUsed[i] = true;
this.tableSize = this.clearCode + 1;
}
protected int addEntry(int previousCode, byte character) throws IOException {
while (this.tableSize < 8192 && this.isUsed[this.tableSize])
this.tableSize++;
int idx = addEntry(previousCode, character, 8192);
if (idx >= 0)
this.isUsed[idx] = true;
return idx;
}
private void partialClear() {
boolean[] isParent = new boolean[8192];
for (int j = 0; j < this.isUsed.length; j++) {
if (this.isUsed[j] && this.prefixes[j] != -1)
isParent[this.prefixes[j]] = true;
}
for (int i = this.clearCode + 1; i < isParent.length; i++) {
if (!isParent[i]) {
this.isUsed[i] = false;
this.prefixes[i] = -1;
}
}
}
protected int decompressNextSymbol() throws IOException {
int code = readNextCode();
if (code < 0)
return -1;
if (code == this.clearCode) {
int subCode = readNextCode();
if (subCode < 0)
throw new IOException("Unexpected EOF;");
if (subCode == 1) {
if (this.codeSize < 13) {
this.codeSize++;
} else {
throw new IOException("Attempt to increase code size beyond maximum");
}
} else if (subCode == 2) {
partialClear();
this.tableSize = this.clearCode + 1;
} else {
throw new IOException("Invalid clear code subcode " + subCode);
}
return 0;
}
boolean addedUnfinishedEntry = false;
int effectiveCode = code;
if (!this.isUsed[code]) {
effectiveCode = addRepeatOfPreviousCode();
addedUnfinishedEntry = true;
}
return expandCodeToOutputStack(effectiveCode, addedUnfinishedEntry);
}
}

View file

@ -0,0 +1,57 @@
package org.apache.commons.compress.archivers.zip;
import java.util.zip.ZipException;
public class UnsupportedZipFeatureException extends ZipException {
private final Feature reason;
private final ZipArchiveEntry entry;
private static final long serialVersionUID = 20130101L;
public UnsupportedZipFeatureException(Feature reason, ZipArchiveEntry entry) {
super("unsupported feature " + reason + " used in entry " + entry.getName());
this.reason = reason;
this.entry = entry;
}
public UnsupportedZipFeatureException(ZipMethod method, ZipArchiveEntry entry) {
super("unsupported feature method '" + method.name() + "' used in entry " + entry.getName());
this.reason = Feature.METHOD;
this.entry = entry;
}
public UnsupportedZipFeatureException(Feature reason) {
super("unsupported feature " + reason + " used in archive.");
this.reason = reason;
this.entry = null;
}
public Feature getFeature() {
return this.reason;
}
public ZipArchiveEntry getEntry() {
return this.entry;
}
public static class Feature {
public static final Feature ENCRYPTION = new Feature("encryption");
public static final Feature METHOD = new Feature("compression method");
public static final Feature DATA_DESCRIPTOR = new Feature("data descriptor");
public static final Feature SPLITTING = new Feature("splitting");
private final String name;
private Feature(String name) {
this.name = name;
}
public String toString() {
return this.name;
}
}
}

View file

@ -0,0 +1,231 @@
package org.apache.commons.compress.archivers.zip;
import java.io.Serializable;
import java.util.Date;
import java.util.zip.ZipException;
public class X5455_ExtendedTimestamp implements ZipExtraField, Cloneable, Serializable {
private static final ZipShort HEADER_ID = new ZipShort(21589);
private static final long serialVersionUID = 1L;
public static final byte MODIFY_TIME_BIT = 1;
public static final byte ACCESS_TIME_BIT = 2;
public static final byte CREATE_TIME_BIT = 4;
private byte flags;
private boolean bit0_modifyTimePresent;
private boolean bit1_accessTimePresent;
private boolean bit2_createTimePresent;
private ZipLong modifyTime;
private ZipLong accessTime;
private ZipLong createTime;
public ZipShort getHeaderId() {
return HEADER_ID;
}
public ZipShort getLocalFileDataLength() {
return new ZipShort(1 + (this.bit0_modifyTimePresent ? 4 : 0) + ((this.bit1_accessTimePresent && this.accessTime != null) ? 4 : 0) + ((this.bit2_createTimePresent && this.createTime != null) ? 4 : 0));
}
public ZipShort getCentralDirectoryLength() {
return new ZipShort(1 + (this.bit0_modifyTimePresent ? 4 : 0));
}
public byte[] getLocalFileDataData() {
byte[] data = new byte[getLocalFileDataLength().getValue()];
int pos = 0;
data[pos++] = 0;
if (this.bit0_modifyTimePresent) {
data[0] = (byte)(data[0] | 0x1);
System.arraycopy(this.modifyTime.getBytes(), 0, data, pos, 4);
pos += 4;
}
if (this.bit1_accessTimePresent && this.accessTime != null) {
data[0] = (byte)(data[0] | 0x2);
System.arraycopy(this.accessTime.getBytes(), 0, data, pos, 4);
pos += 4;
}
if (this.bit2_createTimePresent && this.createTime != null) {
data[0] = (byte)(data[0] | 0x4);
System.arraycopy(this.createTime.getBytes(), 0, data, pos, 4);
pos += 4;
}
return data;
}
public byte[] getCentralDirectoryData() {
byte[] centralData = new byte[getCentralDirectoryLength().getValue()];
byte[] localData = getLocalFileDataData();
System.arraycopy(localData, 0, centralData, 0, centralData.length);
return centralData;
}
public void parseFromLocalFileData(byte[] data, int offset, int length) throws ZipException {
reset();
int len = offset + length;
setFlags(data[offset++]);
if (this.bit0_modifyTimePresent) {
this.modifyTime = new ZipLong(data, offset);
offset += 4;
}
if (this.bit1_accessTimePresent && offset + 4 <= len) {
this.accessTime = new ZipLong(data, offset);
offset += 4;
}
if (this.bit2_createTimePresent && offset + 4 <= len) {
this.createTime = new ZipLong(data, offset);
offset += 4;
}
}
public void parseFromCentralDirectoryData(byte[] buffer, int offset, int length) throws ZipException {
reset();
parseFromLocalFileData(buffer, offset, length);
}
private void reset() {
setFlags((byte)0);
this.modifyTime = null;
this.accessTime = null;
this.createTime = null;
}
public void setFlags(byte flags) {
this.flags = flags;
this.bit0_modifyTimePresent = ((flags & 0x1) == 1);
this.bit1_accessTimePresent = ((flags & 0x2) == 2);
this.bit2_createTimePresent = ((flags & 0x4) == 4);
}
public byte getFlags() {
return this.flags;
}
public boolean isBit0_modifyTimePresent() {
return this.bit0_modifyTimePresent;
}
public boolean isBit1_accessTimePresent() {
return this.bit1_accessTimePresent;
}
public boolean isBit2_createTimePresent() {
return this.bit2_createTimePresent;
}
public ZipLong getModifyTime() {
return this.modifyTime;
}
public ZipLong getAccessTime() {
return this.accessTime;
}
public ZipLong getCreateTime() {
return this.createTime;
}
public Date getModifyJavaTime() {
return (this.modifyTime != null) ? new Date(this.modifyTime.getValue() * 1000L) : null;
}
public Date getAccessJavaTime() {
return (this.accessTime != null) ? new Date(this.accessTime.getValue() * 1000L) : null;
}
public Date getCreateJavaTime() {
return (this.createTime != null) ? new Date(this.createTime.getValue() * 1000L) : null;
}
public void setModifyTime(ZipLong l) {
this.bit0_modifyTimePresent = (l != null);
this.flags = (byte)((l != null) ? (this.flags | 0x1) : (this.flags & 0xFFFFFFFE));
this.modifyTime = l;
}
public void setAccessTime(ZipLong l) {
this.bit1_accessTimePresent = (l != null);
this.flags = (byte)((l != null) ? (this.flags | 0x2) : (this.flags & 0xFFFFFFFD));
this.accessTime = l;
}
public void setCreateTime(ZipLong l) {
this.bit2_createTimePresent = (l != null);
this.flags = (byte)((l != null) ? (this.flags | 0x4) : (this.flags & 0xFFFFFFFB));
this.createTime = l;
}
public void setModifyJavaTime(Date d) {
setModifyTime(dateToZipLong(d));
}
public void setAccessJavaTime(Date d) {
setAccessTime(dateToZipLong(d));
}
public void setCreateJavaTime(Date d) {
setCreateTime(dateToZipLong(d));
}
private static ZipLong dateToZipLong(Date d) {
if (d == null)
return null;
long TWO_TO_32 = 4294967296L;
long l = d.getTime() / 1000L;
if (l >= 4294967296L)
throw new IllegalArgumentException("Cannot set an X5455 timestamp larger than 2^32: " + l);
return new ZipLong(l);
}
public String toString() {
StringBuilder buf = new StringBuilder();
buf.append("0x5455 Zip Extra Field: Flags=");
buf.append(Integer.toBinaryString(ZipUtil.unsignedIntToSignedByte(this.flags))).append(" ");
if (this.bit0_modifyTimePresent && this.modifyTime != null) {
Date m = getModifyJavaTime();
buf.append(" Modify:[").append(m).append("] ");
}
if (this.bit1_accessTimePresent && this.accessTime != null) {
Date a = getAccessJavaTime();
buf.append(" Access:[").append(a).append("] ");
}
if (this.bit2_createTimePresent && this.createTime != null) {
Date c = getCreateJavaTime();
buf.append(" Create:[").append(c).append("] ");
}
return buf.toString();
}
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
public boolean equals(Object o) {
if (o instanceof X5455_ExtendedTimestamp) {
X5455_ExtendedTimestamp xf = (X5455_ExtendedTimestamp)o;
return ((this.flags & 0x7) == (xf.flags & 0x7) && (this.modifyTime == xf.modifyTime || (this.modifyTime != null && this.modifyTime.equals(xf.modifyTime))) && (this.accessTime == xf.accessTime || (this.accessTime != null && this.accessTime.equals(xf.accessTime))) && (this.createTime == xf.createTime || (this.createTime != null && this.createTime.equals(xf.createTime))));
}
return false;
}
public int hashCode() {
int hc = -123 * (this.flags & 0x7);
if (this.modifyTime != null)
hc ^= this.modifyTime.hashCode();
if (this.accessTime != null)
hc ^= Integer.rotateLeft(this.accessTime.hashCode(), 11);
if (this.createTime != null)
hc ^= Integer.rotateLeft(this.createTime.hashCode(), 22);
return hc;
}
}

View file

@ -0,0 +1,140 @@
package org.apache.commons.compress.archivers.zip;
import java.io.Serializable;
import java.math.BigInteger;
import java.util.zip.ZipException;
public class X7875_NewUnix implements ZipExtraField, Cloneable, Serializable {
private static final ZipShort HEADER_ID = new ZipShort(30837);
private static final BigInteger ONE_THOUSAND = BigInteger.valueOf(1000L);
private static final long serialVersionUID = 1L;
private int version = 1;
private BigInteger uid;
private BigInteger gid;
public X7875_NewUnix() {
reset();
}
public ZipShort getHeaderId() {
return HEADER_ID;
}
public long getUID() {
return ZipUtil.bigToLong(this.uid);
}
public long getGID() {
return ZipUtil.bigToLong(this.gid);
}
public void setUID(long l) {
this.uid = ZipUtil.longToBig(l);
}
public void setGID(long l) {
this.gid = ZipUtil.longToBig(l);
}
public ZipShort getLocalFileDataLength() {
int uidSize = (trimLeadingZeroesForceMinLength(this.uid.toByteArray())).length;
int gidSize = (trimLeadingZeroesForceMinLength(this.gid.toByteArray())).length;
return new ZipShort(3 + uidSize + gidSize);
}
public ZipShort getCentralDirectoryLength() {
return getLocalFileDataLength();
}
public byte[] getLocalFileDataData() {
byte[] uidBytes = this.uid.toByteArray();
byte[] gidBytes = this.gid.toByteArray();
uidBytes = trimLeadingZeroesForceMinLength(uidBytes);
gidBytes = trimLeadingZeroesForceMinLength(gidBytes);
byte[] data = new byte[3 + uidBytes.length + gidBytes.length];
ZipUtil.reverse(uidBytes);
ZipUtil.reverse(gidBytes);
int pos = 0;
data[pos++] = ZipUtil.unsignedIntToSignedByte(this.version);
data[pos++] = ZipUtil.unsignedIntToSignedByte(uidBytes.length);
System.arraycopy(uidBytes, 0, data, pos, uidBytes.length);
pos += uidBytes.length;
data[pos++] = ZipUtil.unsignedIntToSignedByte(gidBytes.length);
System.arraycopy(gidBytes, 0, data, pos, gidBytes.length);
return data;
}
public byte[] getCentralDirectoryData() {
return getLocalFileDataData();
}
public void parseFromLocalFileData(byte[] data, int offset, int length) throws ZipException {
reset();
this.version = ZipUtil.signedByteToUnsignedInt(data[offset++]);
int uidSize = ZipUtil.signedByteToUnsignedInt(data[offset++]);
byte[] uidBytes = new byte[uidSize];
System.arraycopy(data, offset, uidBytes, 0, uidSize);
offset += uidSize;
this.uid = new BigInteger(1, ZipUtil.reverse(uidBytes));
int gidSize = ZipUtil.signedByteToUnsignedInt(data[offset++]);
byte[] gidBytes = new byte[gidSize];
System.arraycopy(data, offset, gidBytes, 0, gidSize);
this.gid = new BigInteger(1, ZipUtil.reverse(gidBytes));
}
public void parseFromCentralDirectoryData(byte[] buffer, int offset, int length) throws ZipException {
reset();
parseFromLocalFileData(buffer, offset, length);
}
private void reset() {
this.uid = ONE_THOUSAND;
this.gid = ONE_THOUSAND;
}
public String toString() {
return "0x7875 Zip Extra Field: UID=" + this.uid + " GID=" + this.gid;
}
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
public boolean equals(Object o) {
if (o instanceof X7875_NewUnix) {
X7875_NewUnix xf = (X7875_NewUnix)o;
return (this.version == xf.version && this.uid.equals(xf.uid) && this.gid.equals(xf.gid));
}
return false;
}
public int hashCode() {
int hc = -1234567 * this.version;
hc ^= Integer.rotateLeft(this.uid.hashCode(), 16);
hc ^= this.gid.hashCode();
return hc;
}
static byte[] trimLeadingZeroesForceMinLength(byte[] array) {
if (array == null)
return array;
int pos = 0;
for (byte b : array) {
if (b == 0) {
pos++;
} else {
break;
}
}
int MIN_LENGTH = 1;
byte[] trimmedArray = new byte[Math.max(1, array.length - pos)];
int startPos = trimmedArray.length - (array.length - pos);
System.arraycopy(array, pos, trimmedArray, startPos, trimmedArray.length - startPos);
return trimmedArray;
}
}

View file

@ -0,0 +1,179 @@
package org.apache.commons.compress.archivers.zip;
import java.util.zip.ZipException;
public class Zip64ExtendedInformationExtraField implements ZipExtraField {
static final ZipShort HEADER_ID = new ZipShort(1);
private static final String LFH_MUST_HAVE_BOTH_SIZES_MSG = "Zip64 extended information must contain both size values in the local file header.";
private static final byte[] EMPTY = new byte[0];
private ZipEightByteInteger size;
private ZipEightByteInteger compressedSize;
private ZipEightByteInteger relativeHeaderOffset;
private ZipLong diskStart;
private byte[] rawCentralDirectoryData;
public Zip64ExtendedInformationExtraField() {}
public Zip64ExtendedInformationExtraField(ZipEightByteInteger size, ZipEightByteInteger compressedSize) {
this(size, compressedSize, null, null);
}
public Zip64ExtendedInformationExtraField(ZipEightByteInteger size, ZipEightByteInteger compressedSize, ZipEightByteInteger relativeHeaderOffset, ZipLong diskStart) {
this.size = size;
this.compressedSize = compressedSize;
this.relativeHeaderOffset = relativeHeaderOffset;
this.diskStart = diskStart;
}
public ZipShort getHeaderId() {
return HEADER_ID;
}
public ZipShort getLocalFileDataLength() {
return new ZipShort((this.size != null) ? 16 : 0);
}
public ZipShort getCentralDirectoryLength() {
return new ZipShort(((this.size != null) ? 8 : 0) + ((this.compressedSize != null) ? 8 : 0) + ((this.relativeHeaderOffset != null) ? 8 : 0) + ((this.diskStart != null) ? 4 : 0));
}
public byte[] getLocalFileDataData() {
if (this.size != null || this.compressedSize != null) {
if (this.size == null || this.compressedSize == null)
throw new IllegalArgumentException("Zip64 extended information must contain both size values in the local file header.");
byte[] data = new byte[16];
addSizes(data);
return data;
}
return EMPTY;
}
public byte[] getCentralDirectoryData() {
byte[] data = new byte[getCentralDirectoryLength().getValue()];
int off = addSizes(data);
if (this.relativeHeaderOffset != null) {
System.arraycopy(this.relativeHeaderOffset.getBytes(), 0, data, off, 8);
off += 8;
}
if (this.diskStart != null) {
System.arraycopy(this.diskStart.getBytes(), 0, data, off, 4);
off += 4;
}
return data;
}
public void parseFromLocalFileData(byte[] buffer, int offset, int length) throws ZipException {
if (length == 0)
return;
if (length < 16)
throw new ZipException("Zip64 extended information must contain both size values in the local file header.");
this.size = new ZipEightByteInteger(buffer, offset);
offset += 8;
this.compressedSize = new ZipEightByteInteger(buffer, offset);
offset += 8;
int remaining = length - 16;
if (remaining >= 8) {
this.relativeHeaderOffset = new ZipEightByteInteger(buffer, offset);
offset += 8;
remaining -= 8;
}
if (remaining >= 4) {
this.diskStart = new ZipLong(buffer, offset);
offset += 4;
remaining -= 4;
}
}
public void parseFromCentralDirectoryData(byte[] buffer, int offset, int length) throws ZipException {
this.rawCentralDirectoryData = new byte[length];
System.arraycopy(buffer, offset, this.rawCentralDirectoryData, 0, length);
if (length >= 28) {
parseFromLocalFileData(buffer, offset, length);
} else if (length == 24) {
this.size = new ZipEightByteInteger(buffer, offset);
offset += 8;
this.compressedSize = new ZipEightByteInteger(buffer, offset);
offset += 8;
this.relativeHeaderOffset = new ZipEightByteInteger(buffer, offset);
} else if (length % 8 == 4) {
this.diskStart = new ZipLong(buffer, offset + length - 4);
}
}
public void reparseCentralDirectoryData(boolean hasUncompressedSize, boolean hasCompressedSize, boolean hasRelativeHeaderOffset, boolean hasDiskStart) throws ZipException {
if (this.rawCentralDirectoryData != null) {
int expectedLength = (hasUncompressedSize ? 8 : 0) + (hasCompressedSize ? 8 : 0) + (hasRelativeHeaderOffset ? 8 : 0) + (hasDiskStart ? 4 : 0);
if (this.rawCentralDirectoryData.length < expectedLength)
throw new ZipException("central directory zip64 extended information extra field's length doesn't match central directory data. Expected length " + expectedLength + " but is " + this.rawCentralDirectoryData.length);
int offset = 0;
if (hasUncompressedSize) {
this.size = new ZipEightByteInteger(this.rawCentralDirectoryData, offset);
offset += 8;
}
if (hasCompressedSize) {
this.compressedSize = new ZipEightByteInteger(this.rawCentralDirectoryData, offset);
offset += 8;
}
if (hasRelativeHeaderOffset) {
this.relativeHeaderOffset = new ZipEightByteInteger(this.rawCentralDirectoryData, offset);
offset += 8;
}
if (hasDiskStart) {
this.diskStart = new ZipLong(this.rawCentralDirectoryData, offset);
offset += 4;
}
}
}
public ZipEightByteInteger getSize() {
return this.size;
}
public void setSize(ZipEightByteInteger size) {
this.size = size;
}
public ZipEightByteInteger getCompressedSize() {
return this.compressedSize;
}
public void setCompressedSize(ZipEightByteInteger compressedSize) {
this.compressedSize = compressedSize;
}
public ZipEightByteInteger getRelativeHeaderOffset() {
return this.relativeHeaderOffset;
}
public void setRelativeHeaderOffset(ZipEightByteInteger rho) {
this.relativeHeaderOffset = rho;
}
public ZipLong getDiskStartNumber() {
return this.diskStart;
}
public void setDiskStartNumber(ZipLong ds) {
this.diskStart = ds;
}
private int addSizes(byte[] data) {
int off = 0;
if (this.size != null) {
System.arraycopy(this.size.getBytes(), 0, data, 0, 8);
off += 8;
}
if (this.compressedSize != null) {
System.arraycopy(this.compressedSize.getBytes(), 0, data, off, 8);
off += 8;
}
return off;
}
}

View file

@ -0,0 +1,5 @@
package org.apache.commons.compress.archivers.zip;
public enum Zip64Mode {
Always, Never, AsNeeded;
}

View file

@ -0,0 +1,19 @@
package org.apache.commons.compress.archivers.zip;
import java.util.zip.ZipException;
public class Zip64RequiredException extends ZipException {
private static final long serialVersionUID = 20110809L;
static final String ARCHIVE_TOO_BIG_MESSAGE = "archive's size exceeds the limit of 4GByte.";
static final String TOO_MANY_ENTRIES_MESSAGE = "archive contains more than 65535 entries.";
static String getEntryTooBigMessage(ZipArchiveEntry ze) {
return ze.getName() + "'s size exceeds the limit of 4GByte.";
}
public Zip64RequiredException(String reason) {
super(reason);
}
}

View file

@ -0,0 +1,344 @@
package org.apache.commons.compress.archivers.zip;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import org.apache.commons.compress.archivers.ArchiveEntry;
public class ZipArchiveEntry extends ZipEntry implements ArchiveEntry {
public static final int PLATFORM_UNIX = 3;
public static final int PLATFORM_FAT = 0;
private static final int SHORT_MASK = 65535;
private static final int SHORT_SHIFT = 16;
private static final byte[] EMPTY = new byte[0];
private int method = -1;
private long size = -1L;
private int internalAttributes = 0;
private int platform = 0;
private long externalAttributes = 0L;
private LinkedHashMap<ZipShort, ZipExtraField> extraFields = null;
private UnparseableExtraFieldData unparseableExtra = null;
private String name = null;
private byte[] rawName = null;
private GeneralPurposeBit gpb = new GeneralPurposeBit();
public ZipArchiveEntry(String name) {
super(name);
setName(name);
}
public ZipArchiveEntry(ZipEntry entry) throws ZipException {
super(entry);
setName(entry.getName());
byte[] extra = entry.getExtra();
if (extra != null) {
setExtraFields(ExtraFieldUtils.parse(extra, true, ExtraFieldUtils.UnparseableExtraField.READ));
} else {
setExtra();
}
setMethod(entry.getMethod());
this.size = entry.getSize();
}
public ZipArchiveEntry(ZipArchiveEntry entry) throws ZipException {
this((ZipEntry)entry);
setInternalAttributes(entry.getInternalAttributes());
setExternalAttributes(entry.getExternalAttributes());
setExtraFields(entry.getExtraFields(true));
}
protected ZipArchiveEntry() {
this("");
}
public ZipArchiveEntry(File inputFile, String entryName) {
this((inputFile.isDirectory() && !entryName.endsWith("/")) ? (entryName + "/") : entryName);
if (inputFile.isFile())
setSize(inputFile.length());
setTime(inputFile.lastModified());
}
public Object clone() {
ZipArchiveEntry e = (ZipArchiveEntry)super.clone();
e.setInternalAttributes(getInternalAttributes());
e.setExternalAttributes(getExternalAttributes());
e.setExtraFields(getExtraFields(true));
return e;
}
public int getMethod() {
return this.method;
}
public void setMethod(int method) {
if (method < 0)
throw new IllegalArgumentException("ZIP compression method can not be negative: " + method);
this.method = method;
}
public int getInternalAttributes() {
return this.internalAttributes;
}
public void setInternalAttributes(int value) {
this.internalAttributes = value;
}
public long getExternalAttributes() {
return this.externalAttributes;
}
public void setExternalAttributes(long value) {
this.externalAttributes = value;
}
public void setUnixMode(int mode) {
setExternalAttributes((long)(mode << 16 | (((mode & 0x80) == 0) ? 1 : 0) | (isDirectory() ? 16 : 0)));
this.platform = 3;
}
public int getUnixMode() {
return (this.platform != 3) ? 0 : (int)(getExternalAttributes() >> 16L & 0xFFFFL);
}
public boolean isUnixSymlink() {
return ((getUnixMode() & 0xA000) == 40960);
}
public int getPlatform() {
return this.platform;
}
protected void setPlatform(int platform) {
this.platform = platform;
}
public void setExtraFields(ZipExtraField[] fields) {
this.extraFields = new LinkedHashMap<ZipShort, ZipExtraField>();
for (ZipExtraField field : fields) {
if (field instanceof UnparseableExtraFieldData) {
this.unparseableExtra = (UnparseableExtraFieldData)field;
} else {
this.extraFields.put(field.getHeaderId(), field);
}
}
setExtra();
}
public ZipExtraField[] getExtraFields() {
return getExtraFields(false);
}
public ZipExtraField[] getExtraFields(boolean includeUnparseable) {
if (this.extraFields == null)
return (!includeUnparseable || this.unparseableExtra == null) ? new ZipExtraField[0] : new ZipExtraField[] { this.unparseableExtra };
List<ZipExtraField> result = new ArrayList<ZipExtraField>(this.extraFields.values());
if (includeUnparseable && this.unparseableExtra != null)
result.add(this.unparseableExtra);
return result.<ZipExtraField>toArray(new ZipExtraField[0]);
}
public void addExtraField(ZipExtraField ze) {
if (ze instanceof UnparseableExtraFieldData) {
this.unparseableExtra = (UnparseableExtraFieldData)ze;
} else {
if (this.extraFields == null)
this.extraFields = new LinkedHashMap<ZipShort, ZipExtraField>();
this.extraFields.put(ze.getHeaderId(), ze);
}
setExtra();
}
public void addAsFirstExtraField(ZipExtraField ze) {
if (ze instanceof UnparseableExtraFieldData) {
this.unparseableExtra = (UnparseableExtraFieldData)ze;
} else {
LinkedHashMap<ZipShort, ZipExtraField> copy = this.extraFields;
this.extraFields = new LinkedHashMap<ZipShort, ZipExtraField>();
this.extraFields.put(ze.getHeaderId(), ze);
if (copy != null) {
copy.remove(ze.getHeaderId());
this.extraFields.putAll(copy);
}
}
setExtra();
}
public void removeExtraField(ZipShort type) {
if (this.extraFields == null)
throw new NoSuchElementException();
if (this.extraFields.remove(type) == null)
throw new NoSuchElementException();
setExtra();
}
public void removeUnparseableExtraFieldData() {
if (this.unparseableExtra == null)
throw new NoSuchElementException();
this.unparseableExtra = null;
setExtra();
}
public ZipExtraField getExtraField(ZipShort type) {
if (this.extraFields != null)
return this.extraFields.get(type);
return null;
}
public UnparseableExtraFieldData getUnparseableExtraFieldData() {
return this.unparseableExtra;
}
public void setExtra(byte[] extra) throws RuntimeException {
try {
ZipExtraField[] local = ExtraFieldUtils.parse(extra, true, ExtraFieldUtils.UnparseableExtraField.READ);
mergeExtraFields(local, true);
} catch (ZipException e) {
throw new RuntimeException("Error parsing extra fields for entry: " + getName() + " - " + e.getMessage(), e);
}
}
protected void setExtra() {
super.setExtra(ExtraFieldUtils.mergeLocalFileDataData(getExtraFields(true)));
}
public void setCentralDirectoryExtra(byte[] b) {
try {
ZipExtraField[] central = ExtraFieldUtils.parse(b, false, ExtraFieldUtils.UnparseableExtraField.READ);
mergeExtraFields(central, false);
} catch (ZipException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
public byte[] getLocalFileDataExtra() {
byte[] extra = getExtra();
return (extra != null) ? extra : EMPTY;
}
public byte[] getCentralDirectoryExtra() {
return ExtraFieldUtils.mergeCentralDirectoryData(getExtraFields(true));
}
public String getName() {
return (this.name == null) ? super.getName() : this.name;
}
public boolean isDirectory() {
return getName().endsWith("/");
}
protected void setName(String name) {
if (name != null && getPlatform() == 0 && name.indexOf("/") == -1)
name = name.replace('\\', '/');
this.name = name;
}
public long getSize() {
return this.size;
}
public void setSize(long size) {
if (size < 0L)
throw new IllegalArgumentException("invalid entry size");
this.size = size;
}
protected void setName(String name, byte[] rawName) {
setName(name);
this.rawName = rawName;
}
public byte[] getRawName() {
if (this.rawName != null) {
byte[] b = new byte[this.rawName.length];
System.arraycopy(this.rawName, 0, b, 0, this.rawName.length);
return b;
}
return null;
}
public int hashCode() {
return getName().hashCode();
}
public GeneralPurposeBit getGeneralPurposeBit() {
return this.gpb;
}
public void setGeneralPurposeBit(GeneralPurposeBit b) {
this.gpb = b;
}
private void mergeExtraFields(ZipExtraField[] f, boolean local) throws ZipException {
if (this.extraFields == null) {
setExtraFields(f);
} else {
for (ZipExtraField element : f) {
ZipExtraField existing;
if (element instanceof UnparseableExtraFieldData) {
existing = this.unparseableExtra;
} else {
existing = getExtraField(element.getHeaderId());
}
if (existing == null) {
addExtraField(element);
} else if (local) {
byte[] b = element.getLocalFileDataData();
existing.parseFromLocalFileData(b, 0, b.length);
} else {
byte[] b = element.getCentralDirectoryData();
existing.parseFromCentralDirectoryData(b, 0, b.length);
}
}
setExtra();
}
}
public Date getLastModifiedDate() {
return new Date(getTime());
}
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null || getClass() != obj.getClass())
return false;
ZipArchiveEntry other = (ZipArchiveEntry)obj;
String myName = getName();
String otherName = other.getName();
if (myName == null) {
if (otherName != null)
return false;
} else if (!myName.equals(otherName)) {
return false;
}
String myComment = getComment();
String otherComment = other.getComment();
if (myComment == null)
myComment = "";
if (otherComment == null)
otherComment = "";
return (getTime() == other.getTime() && myComment.equals(otherComment) && getInternalAttributes() == other.getInternalAttributes() && getPlatform() == other.getPlatform() && getExternalAttributes() == other.getExternalAttributes() && getMethod() == other.getMethod() && getSize() == other.getSize() && getCrc() == other.getCrc() && getCompressedSize() == other.getCompressedSize() && Arrays.equals(getCentralDirectoryExtra(), other.getCentralDirectoryExtra()) && Arrays.equals(getLocalFileDataExtra(), other.getLocalFileDataExtra()) && this.gpb.equals(other.gpb));
}
}

View file

@ -0,0 +1,595 @@
package org.apache.commons.compress.archivers.zip;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.io.PushbackInputStream;
import java.nio.ByteBuffer;
import java.util.zip.CRC32;
import java.util.zip.DataFormatException;
import java.util.zip.Inflater;
import java.util.zip.ZipException;
import org.apache.commons.compress.archivers.ArchiveEntry;
import org.apache.commons.compress.archivers.ArchiveInputStream;
import org.apache.commons.compress.utils.IOUtils;
public class ZipArchiveInputStream extends ArchiveInputStream {
private final ZipEncoding zipEncoding;
private final boolean useUnicodeExtraFields;
private final InputStream in;
private final Inflater inf = new Inflater(true);
private final ByteBuffer buf = ByteBuffer.allocate(512);
private CurrentEntry current = null;
private boolean closed = false;
private boolean hitCentralDirectory = false;
private ByteArrayInputStream lastStoredEntry = null;
private boolean allowStoredEntriesWithDataDescriptor = false;
private static final int LFH_LEN = 30;
private static final int CFH_LEN = 46;
private static final long TWO_EXP_32 = 4294967296L;
private final byte[] LFH_BUF = new byte[30];
private final byte[] SKIP_BUF = new byte[1024];
private final byte[] SHORT_BUF = new byte[2];
private final byte[] WORD_BUF = new byte[4];
private final byte[] TWO_DWORD_BUF = new byte[16];
private int entriesRead = 0;
public ZipArchiveInputStream(InputStream inputStream) {
this(inputStream, "UTF8");
}
public ZipArchiveInputStream(InputStream inputStream, String encoding) {
this(inputStream, encoding, true);
}
public ZipArchiveInputStream(InputStream inputStream, String encoding, boolean useUnicodeExtraFields) {
this(inputStream, encoding, useUnicodeExtraFields, false);
}
public ZipArchiveInputStream(InputStream inputStream, String encoding, boolean useUnicodeExtraFields, boolean allowStoredEntriesWithDataDescriptor) {
this.zipEncoding = ZipEncodingHelper.getZipEncoding(encoding);
this.useUnicodeExtraFields = useUnicodeExtraFields;
this.in = new PushbackInputStream(inputStream, this.buf.capacity());
this.allowStoredEntriesWithDataDescriptor = allowStoredEntriesWithDataDescriptor;
this.buf.limit(0);
}
public ZipArchiveEntry getNextZipEntry() throws IOException {
boolean firstEntry = true;
if (this.closed || this.hitCentralDirectory)
return null;
if (this.current != null) {
closeEntry();
firstEntry = false;
}
try {
if (firstEntry) {
readFirstLocalFileHeader(this.LFH_BUF);
} else {
readFully(this.LFH_BUF);
}
} catch (EOFException e) {
return null;
}
ZipLong sig = new ZipLong(this.LFH_BUF);
if (sig.equals(ZipLong.CFH_SIG) || sig.equals(ZipLong.AED_SIG)) {
this.hitCentralDirectory = true;
skipRemainderOfArchive();
}
if (!sig.equals(ZipLong.LFH_SIG))
return null;
int off = 4;
this.current = new CurrentEntry();
int versionMadeBy = ZipShort.getValue(this.LFH_BUF, off);
off += 2;
this.current.entry.setPlatform(versionMadeBy >> 8 & 0xF);
GeneralPurposeBit gpFlag = GeneralPurposeBit.parse(this.LFH_BUF, off);
boolean hasUTF8Flag = gpFlag.usesUTF8ForNames();
ZipEncoding entryEncoding = hasUTF8Flag ? ZipEncodingHelper.UTF8_ZIP_ENCODING : this.zipEncoding;
this.current.hasDataDescriptor = gpFlag.usesDataDescriptor();
this.current.entry.setGeneralPurposeBit(gpFlag);
off += 2;
this.current.entry.setMethod(ZipShort.getValue(this.LFH_BUF, off));
off += 2;
long time = ZipUtil.dosToJavaTime(ZipLong.getValue(this.LFH_BUF, off));
this.current.entry.setTime(time);
off += 4;
ZipLong size = null, cSize = null;
if (!this.current.hasDataDescriptor) {
this.current.entry.setCrc(ZipLong.getValue(this.LFH_BUF, off));
off += 4;
cSize = new ZipLong(this.LFH_BUF, off);
off += 4;
size = new ZipLong(this.LFH_BUF, off);
off += 4;
} else {
off += 12;
}
int fileNameLen = ZipShort.getValue(this.LFH_BUF, off);
off += 2;
int extraLen = ZipShort.getValue(this.LFH_BUF, off);
off += 2;
byte[] fileName = new byte[fileNameLen];
readFully(fileName);
this.current.entry.setName(entryEncoding.decode(fileName), fileName);
byte[] extraData = new byte[extraLen];
readFully(extraData);
this.current.entry.setExtra(extraData);
if (!hasUTF8Flag && this.useUnicodeExtraFields)
ZipUtil.setNameAndCommentFromExtraFields(this.current.entry, fileName, null);
processZip64Extra(size, cSize);
if (this.current.entry.getCompressedSize() != -1L)
if (this.current.entry.getMethod() == ZipMethod.UNSHRINKING.getCode()) {
this.current.in = new UnshrinkingInputStream(new BoundedInputStream(this.in, this.current.entry.getCompressedSize()));
} else if (this.current.entry.getMethod() == ZipMethod.IMPLODING.getCode()) {
this.current.in = new ExplodingInputStream(this.current.entry.getGeneralPurposeBit().getSlidingDictionarySize(), this.current.entry.getGeneralPurposeBit().getNumberOfShannonFanoTrees(), new BoundedInputStream(this.in, this.current.entry.getCompressedSize()));
}
this.entriesRead++;
return this.current.entry;
}
private void readFirstLocalFileHeader(byte[] lfh) throws IOException {
readFully(lfh);
ZipLong sig = new ZipLong(lfh);
if (sig.equals(ZipLong.DD_SIG))
throw new UnsupportedZipFeatureException(UnsupportedZipFeatureException.Feature.SPLITTING);
if (sig.equals(ZipLong.SINGLE_SEGMENT_SPLIT_MARKER)) {
byte[] missedLfhBytes = new byte[4];
readFully(missedLfhBytes);
System.arraycopy(lfh, 4, lfh, 0, 26);
System.arraycopy(missedLfhBytes, 0, lfh, 26, 4);
}
}
private void processZip64Extra(ZipLong size, ZipLong cSize) {
Zip64ExtendedInformationExtraField z64 = (Zip64ExtendedInformationExtraField)this.current.entry.getExtraField(Zip64ExtendedInformationExtraField.HEADER_ID);
this.current.usesZip64 = (z64 != null);
if (!this.current.hasDataDescriptor)
if (z64 != null && (cSize.equals(ZipLong.ZIP64_MAGIC) || size.equals(ZipLong.ZIP64_MAGIC))) {
this.current.entry.setCompressedSize(z64.getCompressedSize().getLongValue());
this.current.entry.setSize(z64.getSize().getLongValue());
} else {
this.current.entry.setCompressedSize(cSize.getValue());
this.current.entry.setSize(size.getValue());
}
}
public ArchiveEntry getNextEntry() throws IOException {
return getNextZipEntry();
}
public boolean canReadEntryData(ArchiveEntry ae) {
if (ae instanceof ZipArchiveEntry) {
ZipArchiveEntry ze = (ZipArchiveEntry)ae;
return (ZipUtil.canHandleEntryData(ze) && supportsDataDescriptorFor(ze));
}
return false;
}
public int read(byte[] buffer, int offset, int length) throws IOException {
int read;
if (this.closed)
throw new IOException("The stream is closed");
if (this.current == null)
return -1;
if (offset > buffer.length || length < 0 || offset < 0 || buffer.length - offset < length)
throw new ArrayIndexOutOfBoundsException();
ZipUtil.checkRequestedFeatures(this.current.entry);
if (!supportsDataDescriptorFor(this.current.entry))
throw new UnsupportedZipFeatureException(UnsupportedZipFeatureException.Feature.DATA_DESCRIPTOR, this.current.entry);
if (this.current.entry.getMethod() == 0) {
read = readStored(buffer, offset, length);
} else if (this.current.entry.getMethod() == 8) {
read = readDeflated(buffer, offset, length);
} else if (this.current.entry.getMethod() == ZipMethod.UNSHRINKING.getCode() || this.current.entry.getMethod() == ZipMethod.IMPLODING.getCode()) {
read = this.current.in.read(buffer, offset, length);
} else {
throw new UnsupportedZipFeatureException(ZipMethod.getMethodByCode(this.current.entry.getMethod()), this.current.entry);
}
if (read >= 0)
this.current.crc.update(buffer, offset, read);
return read;
}
private int readStored(byte[] buffer, int offset, int length) throws IOException {
if (this.current.hasDataDescriptor) {
if (this.lastStoredEntry == null)
readStoredEntry();
return this.lastStoredEntry.read(buffer, offset, length);
}
long csize = this.current.entry.getSize();
if (this.current.bytesRead >= csize)
return -1;
if (this.buf.position() >= this.buf.limit()) {
this.buf.position(0);
int l = this.in.read(this.buf.array());
if (l == -1)
return -1;
this.buf.limit(l);
count(l);
this.current.bytesReadFromStream += (long)l;
}
int toRead = Math.min(this.buf.remaining(), length);
if (csize - this.current.bytesRead < (long)toRead)
toRead = (int)(csize - this.current.bytesRead);
this.buf.get(buffer, offset, toRead);
this.current.bytesRead += (long)toRead;
return toRead;
}
private int readDeflated(byte[] buffer, int offset, int length) throws IOException {
int read = readFromInflater(buffer, offset, length);
if (read <= 0) {
if (this.inf.finished())
return -1;
if (this.inf.needsDictionary())
throw new ZipException("This archive needs a preset dictionary which is not supported by Commons Compress.");
if (read == -1)
throw new IOException("Truncated ZIP file");
}
return read;
}
private int readFromInflater(byte[] buffer, int offset, int length) throws IOException {
int read = 0;
do {
if (this.inf.needsInput()) {
int l = fill();
if (l > 0) {
this.current.bytesReadFromStream += (long)this.buf.limit();
} else {
if (l == -1)
return -1;
break;
}
}
try {
read = this.inf.inflate(buffer, offset, length);
} catch (DataFormatException e) {
throw (IOException)new ZipException(e.getMessage()).initCause(e);
}
} while (read == 0 && this.inf.needsInput());
return read;
}
public void close() throws IOException {
if (!this.closed) {
this.closed = true;
this.in.close();
this.inf.end();
}
}
public long skip(long value) throws IOException {
if (value >= 0L) {
long skipped = 0L;
while (skipped < value) {
long rem = value - skipped;
int x = read(this.SKIP_BUF, 0, (int)(((long)this.SKIP_BUF.length > rem) ? rem : (long)this.SKIP_BUF.length));
if (x == -1)
return skipped;
skipped += (long)x;
}
return skipped;
}
throw new IllegalArgumentException();
}
public static boolean matches(byte[] signature, int length) {
if (length < ZipArchiveOutputStream.LFH_SIG.length)
return false;
return (checksig(signature, ZipArchiveOutputStream.LFH_SIG) || checksig(signature, ZipArchiveOutputStream.EOCD_SIG) || checksig(signature, ZipArchiveOutputStream.DD_SIG) || checksig(signature, ZipLong.SINGLE_SEGMENT_SPLIT_MARKER.getBytes()));
}
private static boolean checksig(byte[] signature, byte[] expected) {
for (int i = 0; i < expected.length; i++) {
if (signature[i] != expected[i])
return false;
}
return true;
}
private void closeEntry() throws IOException {
if (this.closed)
throw new IOException("The stream is closed");
if (this.current == null)
return;
if (this.current.bytesReadFromStream <= this.current.entry.getCompressedSize() && !this.current.hasDataDescriptor) {
drainCurrentEntryData();
} else {
skip(Long.MAX_VALUE);
long inB = (this.current.entry.getMethod() == 8) ? getBytesInflated() : this.current.bytesRead;
int diff = (int)(this.current.bytesReadFromStream - inB);
if (diff > 0)
pushback(this.buf.array(), this.buf.limit() - diff, diff);
}
if (this.lastStoredEntry == null && this.current.hasDataDescriptor)
readDataDescriptor();
this.inf.reset();
this.buf.clear().flip();
this.current = null;
this.lastStoredEntry = null;
}
private void drainCurrentEntryData() throws IOException {
long remaining = this.current.entry.getCompressedSize() - this.current.bytesReadFromStream;
while (remaining > 0L) {
long n = (long)this.in.read(this.buf.array(), 0, (int)Math.min((long)this.buf.capacity(), remaining));
if (n < 0L)
throw new EOFException("Truncated ZIP entry: " + this.current.entry.getName());
count(n);
remaining -= n;
}
}
private long getBytesInflated() {
long inB = this.inf.getBytesRead();
if (this.current.bytesReadFromStream >= 4294967296L)
while (inB + 4294967296L <= this.current.bytesReadFromStream)
inB += 4294967296L;
return inB;
}
private int fill() throws IOException {
if (this.closed)
throw new IOException("The stream is closed");
int length = this.in.read(this.buf.array());
if (length > 0) {
this.buf.limit(length);
count(this.buf.limit());
this.inf.setInput(this.buf.array(), 0, this.buf.limit());
}
return length;
}
private void readFully(byte[] b) throws IOException {
int count = IOUtils.readFully(this.in, b);
count(count);
if (count < b.length)
throw new EOFException();
}
private void readDataDescriptor() throws IOException {
readFully(this.WORD_BUF);
ZipLong val = new ZipLong(this.WORD_BUF);
if (ZipLong.DD_SIG.equals(val)) {
readFully(this.WORD_BUF);
val = new ZipLong(this.WORD_BUF);
}
this.current.entry.setCrc(val.getValue());
readFully(this.TWO_DWORD_BUF);
ZipLong potentialSig = new ZipLong(this.TWO_DWORD_BUF, 8);
if (potentialSig.equals(ZipLong.CFH_SIG) || potentialSig.equals(ZipLong.LFH_SIG)) {
pushback(this.TWO_DWORD_BUF, 8, 8);
this.current.entry.setCompressedSize(ZipLong.getValue(this.TWO_DWORD_BUF));
this.current.entry.setSize(ZipLong.getValue(this.TWO_DWORD_BUF, 4));
} else {
this.current.entry.setCompressedSize(ZipEightByteInteger.getLongValue(this.TWO_DWORD_BUF));
this.current.entry.setSize(ZipEightByteInteger.getLongValue(this.TWO_DWORD_BUF, 8));
}
}
private boolean supportsDataDescriptorFor(ZipArchiveEntry entry) {
return (!entry.getGeneralPurposeBit().usesDataDescriptor() || (this.allowStoredEntriesWithDataDescriptor && entry.getMethod() == 0) || entry.getMethod() == 8);
}
private void readStoredEntry() throws IOException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
int off = 0;
boolean done = false;
int ddLen = this.current.usesZip64 ? 20 : 12;
while (!done) {
int r = this.in.read(this.buf.array(), off, 512 - off);
if (r <= 0)
throw new IOException("Truncated ZIP file");
if (r + off < 4) {
off += r;
continue;
}
done = bufferContainsSignature(bos, off, r, ddLen);
if (!done)
off = cacheBytesRead(bos, off, r, ddLen);
}
byte[] b = bos.toByteArray();
this.lastStoredEntry = new ByteArrayInputStream(b);
}
private static final byte[] LFH = ZipLong.LFH_SIG.getBytes();
private static final byte[] CFH = ZipLong.CFH_SIG.getBytes();
private static final byte[] DD = ZipLong.DD_SIG.getBytes();
private boolean bufferContainsSignature(ByteArrayOutputStream bos, int offset, int lastRead, int expectedDDLen) throws IOException {
boolean done = false;
int readTooMuch = 0;
for (int i = 0; !done && i < lastRead - 4; i++) {
if (this.buf.array()[i] == LFH[0] && this.buf.array()[i + 1] == LFH[1]) {
if ((this.buf.array()[i + 2] == LFH[2] && this.buf.array()[i + 3] == LFH[3]) || (this.buf.array()[i] == CFH[2] && this.buf.array()[i + 3] == CFH[3])) {
readTooMuch = offset + lastRead - i - expectedDDLen;
done = true;
} else if (this.buf.array()[i + 2] == DD[2] && this.buf.array()[i + 3] == DD[3]) {
readTooMuch = offset + lastRead - i;
done = true;
}
if (done) {
pushback(this.buf.array(), offset + lastRead - readTooMuch, readTooMuch);
bos.write(this.buf.array(), 0, i);
readDataDescriptor();
}
}
}
return done;
}
private int cacheBytesRead(ByteArrayOutputStream bos, int offset, int lastRead, int expecteDDLen) {
int cacheable = offset + lastRead - expecteDDLen - 3;
if (cacheable > 0) {
bos.write(this.buf.array(), 0, cacheable);
System.arraycopy(this.buf.array(), cacheable, this.buf.array(), 0, expecteDDLen + 3);
offset = expecteDDLen + 3;
} else {
offset += lastRead;
}
return offset;
}
private void pushback(byte[] buf, int offset, int length) throws IOException {
((PushbackInputStream)this.in).unread(buf, offset, length);
pushedBackBytes((long)length);
}
private void skipRemainderOfArchive() throws IOException {
realSkip((long)(this.entriesRead * 46 - 30));
findEocdRecord();
realSkip(16L);
readFully(this.SHORT_BUF);
realSkip((long)ZipShort.getValue(this.SHORT_BUF));
}
private void findEocdRecord() throws IOException {
int currentByte = -1;
boolean skipReadCall = false;
while (skipReadCall || (currentByte = readOneByte()) > -1) {
skipReadCall = false;
if (!isFirstByteOfEocdSig(currentByte))
continue;
currentByte = readOneByte();
if (currentByte != ZipArchiveOutputStream.EOCD_SIG[1]) {
if (currentByte == -1)
break;
skipReadCall = isFirstByteOfEocdSig(currentByte);
continue;
}
currentByte = readOneByte();
if (currentByte != ZipArchiveOutputStream.EOCD_SIG[2]) {
if (currentByte == -1)
break;
skipReadCall = isFirstByteOfEocdSig(currentByte);
continue;
}
currentByte = readOneByte();
if (currentByte == -1 || currentByte == ZipArchiveOutputStream.EOCD_SIG[3])
break;
skipReadCall = isFirstByteOfEocdSig(currentByte);
}
}
private void realSkip(long value) throws IOException {
if (value >= 0L) {
long skipped = 0L;
while (skipped < value) {
long rem = value - skipped;
int x = this.in.read(this.SKIP_BUF, 0, (int)(((long)this.SKIP_BUF.length > rem) ? rem : (long)this.SKIP_BUF.length));
if (x == -1)
return;
count(x);
skipped += (long)x;
}
return;
}
throw new IllegalArgumentException();
}
private int readOneByte() throws IOException {
int b = this.in.read();
if (b != -1)
count(1);
return b;
}
private boolean isFirstByteOfEocdSig(int b) {
return (b == ZipArchiveOutputStream.EOCD_SIG[0]);
}
private static final class CurrentEntry {
private CurrentEntry() {}
private final ZipArchiveEntry entry = new ZipArchiveEntry();
private boolean hasDataDescriptor;
private boolean usesZip64;
private long bytesRead;
private long bytesReadFromStream;
private final CRC32 crc = new CRC32();
private InputStream in;
}
private class BoundedInputStream extends InputStream {
private final InputStream in;
private final long max;
private long pos = 0L;
public BoundedInputStream(InputStream in, long size) {
this.max = size;
this.in = in;
}
public int read() throws IOException {
if (this.max >= 0L && this.pos >= this.max)
return -1;
int result = this.in.read();
this.pos++;
ZipArchiveInputStream.this.count(1);
ZipArchiveInputStream.this.current.bytesReadFromStream++;
return result;
}
public int read(byte[] b) throws IOException {
return read(b, 0, b.length);
}
public int read(byte[] b, int off, int len) throws IOException {
if (this.max >= 0L && this.pos >= this.max)
return -1;
long maxRead = (this.max >= 0L) ? Math.min((long)len, this.max - this.pos) : (long)len;
int bytesRead = this.in.read(b, off, (int)maxRead);
if (bytesRead == -1)
return -1;
this.pos += (long)bytesRead;
ZipArchiveInputStream.this.count(bytesRead);
ZipArchiveInputStream.this.current.bytesReadFromStream += (long)bytesRead;
return bytesRead;
}
public long skip(long n) throws IOException {
long toSkip = (this.max >= 0L) ? Math.min(n, this.max - this.pos) : n;
long skippedBytes = this.in.skip(toSkip);
this.pos += skippedBytes;
return skippedBytes;
}
public int available() throws IOException {
if (this.max >= 0L && this.pos >= this.max)
return 0;
return this.in.available();
}
}
}

View file

@ -0,0 +1,673 @@
package org.apache.commons.compress.archivers.zip;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.zip.CRC32;
import java.util.zip.Deflater;
import java.util.zip.ZipException;
import org.apache.commons.compress.archivers.ArchiveEntry;
import org.apache.commons.compress.archivers.ArchiveOutputStream;
import org.apache.commons.compress.utils.IOUtils;
public class ZipArchiveOutputStream extends ArchiveOutputStream {
static final int BUFFER_SIZE = 512;
protected boolean finished = false;
private static final int DEFLATER_BLOCK_SIZE = 8192;
public static final int DEFLATED = 8;
public static final int DEFAULT_COMPRESSION = -1;
public static final int STORED = 0;
static final String DEFAULT_ENCODING = "UTF8";
@Deprecated
public static final int EFS_FLAG = 2048;
private static final byte[] EMPTY = new byte[0];
private CurrentEntry entry;
private String comment = "";
private int level = -1;
private boolean hasCompressionLevelChanged = false;
private int method = 8;
private final List<ZipArchiveEntry> entries = new LinkedList<ZipArchiveEntry>();
private final CRC32 crc = new CRC32();
private long written = 0L;
private long cdOffset = 0L;
private long cdLength = 0L;
private static final byte[] ZERO = new byte[] { 0, 0 };
private static final byte[] LZERO = new byte[] { 0, 0, 0, 0 };
private final Map<ZipArchiveEntry, Long> offsets = new HashMap<ZipArchiveEntry, Long>();
private String encoding = "UTF8";
private ZipEncoding zipEncoding = ZipEncodingHelper.getZipEncoding("UTF8");
protected final Deflater def = new Deflater(this.level, true);
private final byte[] buf = new byte[512];
private final RandomAccessFile raf;
private final OutputStream out;
private boolean useUTF8Flag = true;
private boolean fallbackToUTF8 = false;
private UnicodeExtraFieldPolicy createUnicodeExtraFields = UnicodeExtraFieldPolicy.NEVER;
private boolean hasUsedZip64 = false;
private Zip64Mode zip64Mode = Zip64Mode.AsNeeded;
public ZipArchiveOutputStream(OutputStream out) {
this.out = out;
this.raf = null;
}
public ZipArchiveOutputStream(File file) throws IOException {
OutputStream o = null;
RandomAccessFile _raf = null;
try {
_raf = new RandomAccessFile(file, "rw");
_raf.setLength(0L);
} catch (IOException e) {
IOUtils.closeQuietly(_raf);
_raf = null;
o = new FileOutputStream(file);
}
this.out = o;
this.raf = _raf;
}
public boolean isSeekable() {
return (this.raf != null);
}
public void setEncoding(String encoding) {
this.encoding = encoding;
this.zipEncoding = ZipEncodingHelper.getZipEncoding(encoding);
if (this.useUTF8Flag && !ZipEncodingHelper.isUTF8(encoding))
this.useUTF8Flag = false;
}
public String getEncoding() {
return this.encoding;
}
public void setUseLanguageEncodingFlag(boolean b) {
this.useUTF8Flag = (b && ZipEncodingHelper.isUTF8(this.encoding));
}
public void setCreateUnicodeExtraFields(UnicodeExtraFieldPolicy b) {
this.createUnicodeExtraFields = b;
}
public void setFallbackToUTF8(boolean b) {
this.fallbackToUTF8 = b;
}
public void setUseZip64(Zip64Mode mode) {
this.zip64Mode = mode;
}
public void finish() throws IOException {
if (this.finished)
throw new IOException("This archive has already been finished");
if (this.entry != null)
throw new IOException("This archive contains unclosed entries.");
this.cdOffset = this.written;
for (ZipArchiveEntry ze : this.entries)
writeCentralFileHeader(ze);
this.cdLength = this.written - this.cdOffset;
writeZip64CentralDirectory();
writeCentralDirectoryEnd();
this.offsets.clear();
this.entries.clear();
this.def.end();
this.finished = true;
}
public void closeArchiveEntry() throws IOException {
if (this.finished)
throw new IOException("Stream has already been finished");
if (this.entry == null)
throw new IOException("No current entry to close");
if (!this.entry.hasWritten)
write(EMPTY, 0, 0);
flushDeflater();
Zip64Mode effectiveMode = getEffectiveZip64Mode(this.entry.entry);
long bytesWritten = this.written - this.entry.dataStart;
long realCrc = this.crc.getValue();
this.crc.reset();
boolean actuallyNeedsZip64 = handleSizesAndCrc(bytesWritten, realCrc, effectiveMode);
if (this.raf != null)
rewriteSizesAndCrc(actuallyNeedsZip64);
writeDataDescriptor(this.entry.entry);
this.entry = null;
}
private void flushDeflater() throws IOException {
if (this.entry.entry.getMethod() == 8) {
this.def.finish();
while (!this.def.finished())
deflate();
}
}
private boolean handleSizesAndCrc(long bytesWritten, long crc, Zip64Mode effectiveMode) throws ZipException {
if (this.entry.entry.getMethod() == 8) {
this.entry.entry.setSize(this.entry.bytesRead);
this.entry.entry.setCompressedSize(bytesWritten);
this.entry.entry.setCrc(crc);
this.def.reset();
} else if (this.raf == null) {
if (this.entry.entry.getCrc() != crc)
throw new ZipException("bad CRC checksum for entry " + this.entry.entry.getName() + ": " + Long.toHexString(this.entry.entry.getCrc()) + " instead of " + Long.toHexString(crc));
if (this.entry.entry.getSize() != bytesWritten)
throw new ZipException("bad size for entry " + this.entry.entry.getName() + ": " + this.entry.entry.getSize() + " instead of " + bytesWritten);
} else {
this.entry.entry.setSize(bytesWritten);
this.entry.entry.setCompressedSize(bytesWritten);
this.entry.entry.setCrc(crc);
}
boolean actuallyNeedsZip64 = (effectiveMode == Zip64Mode.Always || this.entry.entry.getSize() >= 4294967295L || this.entry.entry.getCompressedSize() >= 4294967295L);
if (actuallyNeedsZip64 && effectiveMode == Zip64Mode.Never)
throw new Zip64RequiredException(Zip64RequiredException.getEntryTooBigMessage(this.entry.entry));
return actuallyNeedsZip64;
}
private void rewriteSizesAndCrc(boolean actuallyNeedsZip64) throws IOException {
long save = this.raf.getFilePointer();
this.raf.seek(this.entry.localDataStart);
writeOut(ZipLong.getBytes(this.entry.entry.getCrc()));
if (!hasZip64Extra(this.entry.entry) || !actuallyNeedsZip64) {
writeOut(ZipLong.getBytes(this.entry.entry.getCompressedSize()));
writeOut(ZipLong.getBytes(this.entry.entry.getSize()));
} else {
writeOut(ZipLong.ZIP64_MAGIC.getBytes());
writeOut(ZipLong.ZIP64_MAGIC.getBytes());
}
if (hasZip64Extra(this.entry.entry)) {
this.raf.seek(this.entry.localDataStart + 12L + 4L + (long)getName(this.entry.entry).limit() + 4L);
writeOut(ZipEightByteInteger.getBytes(this.entry.entry.getSize()));
writeOut(ZipEightByteInteger.getBytes(this.entry.entry.getCompressedSize()));
if (!actuallyNeedsZip64) {
this.raf.seek(this.entry.localDataStart - 10L);
writeOut(ZipShort.getBytes(10));
this.entry.entry.removeExtraField(Zip64ExtendedInformationExtraField.HEADER_ID);
this.entry.entry.setExtra();
if (this.entry.causedUseOfZip64)
this.hasUsedZip64 = false;
}
}
this.raf.seek(save);
}
public void putArchiveEntry(ArchiveEntry archiveEntry) throws IOException {
if (this.finished)
throw new IOException("Stream has already been finished");
if (this.entry != null)
closeArchiveEntry();
this.entry = new CurrentEntry((ZipArchiveEntry)archiveEntry);
this.entries.add(this.entry.entry);
setDefaults(this.entry.entry);
Zip64Mode effectiveMode = getEffectiveZip64Mode(this.entry.entry);
validateSizeInformation(effectiveMode);
if (shouldAddZip64Extra(this.entry.entry, effectiveMode)) {
Zip64ExtendedInformationExtraField z64 = getZip64Extra(this.entry.entry);
ZipEightByteInteger size = ZipEightByteInteger.ZERO;
if (this.entry.entry.getMethod() == 0 && this.entry.entry.getSize() != -1L)
size = new ZipEightByteInteger(this.entry.entry.getSize());
z64.setSize(size);
z64.setCompressedSize(size);
this.entry.entry.setExtra();
}
if (this.entry.entry.getMethod() == 8 && this.hasCompressionLevelChanged) {
this.def.setLevel(this.level);
this.hasCompressionLevelChanged = false;
}
writeLocalFileHeader(this.entry.entry);
}
private void setDefaults(ZipArchiveEntry entry) {
if (entry.getMethod() == -1)
entry.setMethod(this.method);
if (entry.getTime() == -1L)
entry.setTime(System.currentTimeMillis());
}
private void validateSizeInformation(Zip64Mode effectiveMode) throws ZipException {
if (this.entry.entry.getMethod() == 0 && this.raf == null) {
if (this.entry.entry.getSize() == -1L)
throw new ZipException("uncompressed size is required for STORED method when not writing to a file");
if (this.entry.entry.getCrc() == -1L)
throw new ZipException("crc checksum is required for STORED method when not writing to a file");
this.entry.entry.setCompressedSize(this.entry.entry.getSize());
}
if ((this.entry.entry.getSize() >= 4294967295L || this.entry.entry.getCompressedSize() >= 4294967295L) && effectiveMode == Zip64Mode.Never)
throw new Zip64RequiredException(Zip64RequiredException.getEntryTooBigMessage(this.entry.entry));
}
private boolean shouldAddZip64Extra(ZipArchiveEntry entry, Zip64Mode mode) {
return (mode == Zip64Mode.Always || entry.getSize() >= 4294967295L || entry.getCompressedSize() >= 4294967295L || (entry.getSize() == -1L && this.raf != null && mode != Zip64Mode.Never));
}
public void setComment(String comment) {
this.comment = comment;
}
public void setLevel(int level) {
if (level < -1 || level > 9)
throw new IllegalArgumentException("Invalid compression level: " + level);
this.hasCompressionLevelChanged = (this.level != level);
this.level = level;
}
public void setMethod(int method) {
this.method = method;
}
public boolean canWriteEntryData(ArchiveEntry ae) {
if (ae instanceof ZipArchiveEntry) {
ZipArchiveEntry zae = (ZipArchiveEntry)ae;
return (zae.getMethod() != ZipMethod.IMPLODING.getCode() && zae.getMethod() != ZipMethod.UNSHRINKING.getCode() && ZipUtil.canHandleEntryData(zae));
}
return false;
}
public void write(byte[] b, int offset, int length) throws IOException {
if (this.entry == null)
throw new IllegalStateException("No current entry");
ZipUtil.checkRequestedFeatures(this.entry.entry);
this.entry.hasWritten = true;
if (this.entry.entry.getMethod() == 8) {
writeDeflated(b, offset, length);
} else {
writeOut(b, offset, length);
this.written += (long)length;
}
this.crc.update(b, offset, length);
count(length);
}
private void writeDeflated(byte[] b, int offset, int length) throws IOException {
if (length > 0 && !this.def.finished()) {
this.entry.bytesRead += (long)length;
if (length <= 8192) {
this.def.setInput(b, offset, length);
deflateUntilInputIsNeeded();
} else {
int fullblocks = length / 8192;
for (int i = 0; i < fullblocks; i++) {
this.def.setInput(b, offset + i * 8192, 8192);
deflateUntilInputIsNeeded();
}
int done = fullblocks * 8192;
if (done < length) {
this.def.setInput(b, offset + done, length - done);
deflateUntilInputIsNeeded();
}
}
}
}
public void close() throws IOException {
if (!this.finished)
finish();
destroy();
}
public void flush() throws IOException {
if (this.out != null)
this.out.flush();
}
static final byte[] LFH_SIG = ZipLong.LFH_SIG.getBytes();
static final byte[] DD_SIG = ZipLong.DD_SIG.getBytes();
static final byte[] CFH_SIG = ZipLong.CFH_SIG.getBytes();
static final byte[] EOCD_SIG = ZipLong.getBytes(101010256L);
static final byte[] ZIP64_EOCD_SIG = ZipLong.getBytes(101075792L);
static final byte[] ZIP64_EOCD_LOC_SIG = ZipLong.getBytes(117853008L);
protected final void deflate() throws IOException {
int len = this.def.deflate(this.buf, 0, this.buf.length);
if (len > 0) {
writeOut(this.buf, 0, len);
this.written += (long)len;
}
}
protected void writeLocalFileHeader(ZipArchiveEntry ze) throws IOException {
boolean encodable = this.zipEncoding.canEncode(ze.getName());
ByteBuffer name = getName(ze);
if (this.createUnicodeExtraFields != UnicodeExtraFieldPolicy.NEVER)
addUnicodeExtraFields(ze, encodable, name);
this.offsets.put(ze, Long.valueOf(this.written));
writeOut(LFH_SIG);
this.written += 4L;
int zipMethod = ze.getMethod();
writeVersionNeededToExtractAndGeneralPurposeBits(zipMethod, (!encodable && this.fallbackToUTF8), hasZip64Extra(ze));
this.written += 4L;
writeOut(ZipShort.getBytes(zipMethod));
this.written += 2L;
writeOut(ZipUtil.toDosTime(ze.getTime()));
this.written += 4L;
this.entry.localDataStart = this.written;
if (zipMethod == 8 || this.raf != null) {
writeOut(LZERO);
if (hasZip64Extra(this.entry.entry)) {
writeOut(ZipLong.ZIP64_MAGIC.getBytes());
writeOut(ZipLong.ZIP64_MAGIC.getBytes());
} else {
writeOut(LZERO);
writeOut(LZERO);
}
} else {
writeOut(ZipLong.getBytes(ze.getCrc()));
byte[] size = ZipLong.ZIP64_MAGIC.getBytes();
if (!hasZip64Extra(ze))
size = ZipLong.getBytes(ze.getSize());
writeOut(size);
writeOut(size);
}
this.written += 12L;
writeOut(ZipShort.getBytes(name.limit()));
this.written += 2L;
byte[] extra = ze.getLocalFileDataExtra();
writeOut(ZipShort.getBytes(extra.length));
this.written += 2L;
writeOut(name.array(), name.arrayOffset(), name.limit() - name.position());
this.written += (long)name.limit();
writeOut(extra);
this.written += (long)extra.length;
this.entry.dataStart = this.written;
}
private void addUnicodeExtraFields(ZipArchiveEntry ze, boolean encodable, ByteBuffer name) throws IOException {
if (this.createUnicodeExtraFields == UnicodeExtraFieldPolicy.ALWAYS || !encodable)
ze.addExtraField(new UnicodePathExtraField(ze.getName(), name.array(), name.arrayOffset(), name.limit() - name.position()));
String comm = ze.getComment();
if (comm != null && !"".equals(comm)) {
boolean commentEncodable = this.zipEncoding.canEncode(comm);
if (this.createUnicodeExtraFields == UnicodeExtraFieldPolicy.ALWAYS || !commentEncodable) {
ByteBuffer commentB = getEntryEncoding(ze).encode(comm);
ze.addExtraField(new UnicodeCommentExtraField(comm, commentB.array(), commentB.arrayOffset(), commentB.limit() - commentB.position()));
}
}
}
protected void writeDataDescriptor(ZipArchiveEntry ze) throws IOException {
if (ze.getMethod() != 8 || this.raf != null)
return;
writeOut(DD_SIG);
writeOut(ZipLong.getBytes(ze.getCrc()));
int sizeFieldSize = 4;
if (!hasZip64Extra(ze)) {
writeOut(ZipLong.getBytes(ze.getCompressedSize()));
writeOut(ZipLong.getBytes(ze.getSize()));
} else {
sizeFieldSize = 8;
writeOut(ZipEightByteInteger.getBytes(ze.getCompressedSize()));
writeOut(ZipEightByteInteger.getBytes(ze.getSize()));
}
this.written += (long)(8 + 2 * sizeFieldSize);
}
protected void writeCentralFileHeader(ZipArchiveEntry ze) throws IOException {
writeOut(CFH_SIG);
this.written += 4L;
long lfhOffset = this.offsets.get(ze);
boolean needsZip64Extra = (hasZip64Extra(ze) || ze.getCompressedSize() >= 4294967295L || ze.getSize() >= 4294967295L || lfhOffset >= 4294967295L);
if (needsZip64Extra && this.zip64Mode == Zip64Mode.Never)
throw new Zip64RequiredException("archive's size exceeds the limit of 4GByte.");
handleZip64Extra(ze, lfhOffset, needsZip64Extra);
writeOut(ZipShort.getBytes(ze.getPlatform() << 8 | (!this.hasUsedZip64 ? 20 : 45)));
this.written += 2L;
int zipMethod = ze.getMethod();
boolean encodable = this.zipEncoding.canEncode(ze.getName());
writeVersionNeededToExtractAndGeneralPurposeBits(zipMethod, (!encodable && this.fallbackToUTF8), needsZip64Extra);
this.written += 4L;
writeOut(ZipShort.getBytes(zipMethod));
this.written += 2L;
writeOut(ZipUtil.toDosTime(ze.getTime()));
this.written += 4L;
writeOut(ZipLong.getBytes(ze.getCrc()));
if (ze.getCompressedSize() >= 4294967295L || ze.getSize() >= 4294967295L) {
writeOut(ZipLong.ZIP64_MAGIC.getBytes());
writeOut(ZipLong.ZIP64_MAGIC.getBytes());
} else {
writeOut(ZipLong.getBytes(ze.getCompressedSize()));
writeOut(ZipLong.getBytes(ze.getSize()));
}
this.written += 12L;
ByteBuffer name = getName(ze);
writeOut(ZipShort.getBytes(name.limit()));
this.written += 2L;
byte[] extra = ze.getCentralDirectoryExtra();
writeOut(ZipShort.getBytes(extra.length));
this.written += 2L;
String comm = ze.getComment();
if (comm == null)
comm = "";
ByteBuffer commentB = getEntryEncoding(ze).encode(comm);
writeOut(ZipShort.getBytes(commentB.limit()));
this.written += 2L;
writeOut(ZERO);
this.written += 2L;
writeOut(ZipShort.getBytes(ze.getInternalAttributes()));
this.written += 2L;
writeOut(ZipLong.getBytes(ze.getExternalAttributes()));
this.written += 4L;
writeOut(ZipLong.getBytes(Math.min(lfhOffset, 4294967295L)));
this.written += 4L;
writeOut(name.array(), name.arrayOffset(), name.limit() - name.position());
this.written += (long)name.limit();
writeOut(extra);
this.written += (long)extra.length;
writeOut(commentB.array(), commentB.arrayOffset(), commentB.limit() - commentB.position());
this.written += (long)commentB.limit();
}
private void handleZip64Extra(ZipArchiveEntry ze, long lfhOffset, boolean needsZip64Extra) {
if (needsZip64Extra) {
Zip64ExtendedInformationExtraField z64 = getZip64Extra(ze);
if (ze.getCompressedSize() >= 4294967295L || ze.getSize() >= 4294967295L) {
z64.setCompressedSize(new ZipEightByteInteger(ze.getCompressedSize()));
z64.setSize(new ZipEightByteInteger(ze.getSize()));
} else {
z64.setCompressedSize(null);
z64.setSize(null);
}
if (lfhOffset >= 4294967295L)
z64.setRelativeHeaderOffset(new ZipEightByteInteger(lfhOffset));
ze.setExtra();
}
}
protected void writeCentralDirectoryEnd() throws IOException {
writeOut(EOCD_SIG);
writeOut(ZERO);
writeOut(ZERO);
int numberOfEntries = this.entries.size();
if (numberOfEntries > 65535 && this.zip64Mode == Zip64Mode.Never)
throw new Zip64RequiredException("archive contains more than 65535 entries.");
if (this.cdOffset > 4294967295L && this.zip64Mode == Zip64Mode.Never)
throw new Zip64RequiredException("archive's size exceeds the limit of 4GByte.");
byte[] num = ZipShort.getBytes(Math.min(numberOfEntries, 65535));
writeOut(num);
writeOut(num);
writeOut(ZipLong.getBytes(Math.min(this.cdLength, 4294967295L)));
writeOut(ZipLong.getBytes(Math.min(this.cdOffset, 4294967295L)));
ByteBuffer data = this.zipEncoding.encode(this.comment);
writeOut(ZipShort.getBytes(data.limit()));
writeOut(data.array(), data.arrayOffset(), data.limit() - data.position());
}
private static final byte[] ONE = ZipLong.getBytes(1L);
protected void writeZip64CentralDirectory() throws IOException {
if (this.zip64Mode == Zip64Mode.Never)
return;
if (!this.hasUsedZip64 && (this.cdOffset >= 4294967295L || this.cdLength >= 4294967295L || this.entries.size() >= 65535))
this.hasUsedZip64 = true;
if (!this.hasUsedZip64)
return;
long offset = this.written;
writeOut(ZIP64_EOCD_SIG);
writeOut(ZipEightByteInteger.getBytes(44L));
writeOut(ZipShort.getBytes(45));
writeOut(ZipShort.getBytes(45));
writeOut(LZERO);
writeOut(LZERO);
byte[] num = ZipEightByteInteger.getBytes((long)this.entries.size());
writeOut(num);
writeOut(num);
writeOut(ZipEightByteInteger.getBytes(this.cdLength));
writeOut(ZipEightByteInteger.getBytes(this.cdOffset));
writeOut(ZIP64_EOCD_LOC_SIG);
writeOut(LZERO);
writeOut(ZipEightByteInteger.getBytes(offset));
writeOut(ONE);
}
protected final void writeOut(byte[] data) throws IOException {
writeOut(data, 0, data.length);
}
protected final void writeOut(byte[] data, int offset, int length) throws IOException {
if (this.raf != null) {
this.raf.write(data, offset, length);
} else {
this.out.write(data, offset, length);
}
}
private void deflateUntilInputIsNeeded() throws IOException {
while (!this.def.needsInput())
deflate();
}
private void writeVersionNeededToExtractAndGeneralPurposeBits(int zipMethod, boolean utfFallback, boolean zip64) throws IOException {
int versionNeededToExtract = 10;
GeneralPurposeBit b = new GeneralPurposeBit();
b.useUTF8ForNames((this.useUTF8Flag || utfFallback));
if (zipMethod == 8 && this.raf == null) {
versionNeededToExtract = 20;
b.useDataDescriptor(true);
}
if (zip64)
versionNeededToExtract = 45;
writeOut(ZipShort.getBytes(versionNeededToExtract));
writeOut(b.encode());
}
public ArchiveEntry createArchiveEntry(File inputFile, String entryName) throws IOException {
if (this.finished)
throw new IOException("Stream has already been finished");
return new ZipArchiveEntry(inputFile, entryName);
}
private Zip64ExtendedInformationExtraField getZip64Extra(ZipArchiveEntry ze) {
if (this.entry != null)
this.entry.causedUseOfZip64 = !this.hasUsedZip64;
this.hasUsedZip64 = true;
Zip64ExtendedInformationExtraField z64 = (Zip64ExtendedInformationExtraField)ze.getExtraField(Zip64ExtendedInformationExtraField.HEADER_ID);
if (z64 == null)
z64 = new Zip64ExtendedInformationExtraField();
ze.addAsFirstExtraField(z64);
return z64;
}
private boolean hasZip64Extra(ZipArchiveEntry ze) {
return (ze.getExtraField(Zip64ExtendedInformationExtraField.HEADER_ID) != null);
}
private Zip64Mode getEffectiveZip64Mode(ZipArchiveEntry ze) {
if (this.zip64Mode != Zip64Mode.AsNeeded || this.raf != null || ze.getMethod() != 8 || ze.getSize() != -1L)
return this.zip64Mode;
return Zip64Mode.Never;
}
private ZipEncoding getEntryEncoding(ZipArchiveEntry ze) {
boolean encodable = this.zipEncoding.canEncode(ze.getName());
return (!encodable && this.fallbackToUTF8) ? ZipEncodingHelper.UTF8_ZIP_ENCODING : this.zipEncoding;
}
private ByteBuffer getName(ZipArchiveEntry ze) throws IOException {
return getEntryEncoding(ze).encode(ze.getName());
}
void destroy() throws IOException {
if (this.raf != null)
this.raf.close();
if (this.out != null)
this.out.close();
}
public static final class UnicodeExtraFieldPolicy {
public static final UnicodeExtraFieldPolicy ALWAYS = new UnicodeExtraFieldPolicy("always");
public static final UnicodeExtraFieldPolicy NEVER = new UnicodeExtraFieldPolicy("never");
public static final UnicodeExtraFieldPolicy NOT_ENCODEABLE = new UnicodeExtraFieldPolicy("not encodeable");
private final String name;
private UnicodeExtraFieldPolicy(String n) {
this.name = n;
}
public String toString() {
return this.name;
}
}
private static final class CurrentEntry {
private final ZipArchiveEntry entry;
private CurrentEntry(ZipArchiveEntry entry) {
this.entry = entry;
}
private long localDataStart = 0L;
private long dataStart = 0L;
private long bytesRead = 0L;
private boolean causedUseOfZip64 = false;
private boolean hasWritten;
}
}

View file

@ -0,0 +1,21 @@
package org.apache.commons.compress.archivers.zip;
final class ZipConstants {
static final int BYTE_MASK = 255;
static final int SHORT = 2;
static final int WORD = 4;
static final int DWORD = 8;
static final int INITIAL_VERSION = 10;
static final int DATA_DESCRIPTOR_MIN_VERSION = 20;
static final int ZIP64_MIN_VERSION = 45;
static final int ZIP64_MAGIC_SHORT = 65535;
static final long ZIP64_MAGIC = 4294967295L;
}

View file

@ -0,0 +1,145 @@
package org.apache.commons.compress.archivers.zip;
import java.io.Serializable;
import java.math.BigInteger;
public final class ZipEightByteInteger implements Serializable {
private static final long serialVersionUID = 1L;
private static final int BYTE_1 = 1;
private static final int BYTE_1_MASK = 65280;
private static final int BYTE_1_SHIFT = 8;
private static final int BYTE_2 = 2;
private static final int BYTE_2_MASK = 16711680;
private static final int BYTE_2_SHIFT = 16;
private static final int BYTE_3 = 3;
private static final long BYTE_3_MASK = 4278190080L;
private static final int BYTE_3_SHIFT = 24;
private static final int BYTE_4 = 4;
private static final long BYTE_4_MASK = 1095216660480L;
private static final int BYTE_4_SHIFT = 32;
private static final int BYTE_5 = 5;
private static final long BYTE_5_MASK = 280375465082880L;
private static final int BYTE_5_SHIFT = 40;
private static final int BYTE_6 = 6;
private static final long BYTE_6_MASK = 71776119061217280L;
private static final int BYTE_6_SHIFT = 48;
private static final int BYTE_7 = 7;
private static final long BYTE_7_MASK = 9151314442816847872L;
private static final int BYTE_7_SHIFT = 56;
private static final int LEFTMOST_BIT_SHIFT = 63;
private static final byte LEFTMOST_BIT = -128;
private final BigInteger value;
public static final ZipEightByteInteger ZERO = new ZipEightByteInteger(0L);
public ZipEightByteInteger(long value) {
this(BigInteger.valueOf(value));
}
public ZipEightByteInteger(BigInteger value) {
this.value = value;
}
public ZipEightByteInteger(byte[] bytes) {
this(bytes, 0);
}
public ZipEightByteInteger(byte[] bytes, int offset) {
this.value = getValue(bytes, offset);
}
public byte[] getBytes() {
return getBytes(this.value);
}
public long getLongValue() {
return this.value.longValue();
}
public BigInteger getValue() {
return this.value;
}
public static byte[] getBytes(long value) {
return getBytes(BigInteger.valueOf(value));
}
public static byte[] getBytes(BigInteger value) {
byte[] result = new byte[8];
long val = value.longValue();
result[0] = (byte)(int)(val & 0xFFL);
result[1] = (byte)(int)((val & 0xFF00L) >> 8L);
result[2] = (byte)(int)((val & 0xFF0000L) >> 16L);
result[3] = (byte)(int)((val & 0xFF000000L) >> 24L);
result[4] = (byte)(int)((val & 0xFF00000000L) >> 32L);
result[5] = (byte)(int)((val & 0xFF0000000000L) >> 40L);
result[6] = (byte)(int)((val & 0xFF000000000000L) >> 48L);
result[7] = (byte)(int)((val & 0x7F00000000000000L) >> 56L);
if (value.testBit(63))
result[7] = (byte)(result[7] | Byte.MIN_VALUE);
return result;
}
public static long getLongValue(byte[] bytes, int offset) {
return getValue(bytes, offset).longValue();
}
public static BigInteger getValue(byte[] bytes, int offset) {
long value = (long)bytes[offset + 7] << 56L & 0x7F00000000000000L;
value += (long)bytes[offset + 6] << 48L & 0xFF000000000000L;
value += (long)bytes[offset + 5] << 40L & 0xFF0000000000L;
value += (long)bytes[offset + 4] << 32L & 0xFF00000000L;
value += (long)bytes[offset + 3] << 24L & 0xFF000000L;
value += (long)bytes[offset + 2] << 16L & 0xFF0000L;
value += (long)bytes[offset + 1] << 8L & 0xFF00L;
value += (long)bytes[offset] & 0xFFL;
BigInteger val = BigInteger.valueOf(value);
return ((bytes[offset + 7] & Byte.MIN_VALUE) == -128) ? val.setBit(63) : val;
}
public static long getLongValue(byte[] bytes) {
return getLongValue(bytes, 0);
}
public static BigInteger getValue(byte[] bytes) {
return getValue(bytes, 0);
}
public boolean equals(Object o) {
if (o == null || !(o instanceof ZipEightByteInteger))
return false;
return this.value.equals(((ZipEightByteInteger)o).getValue());
}
public int hashCode() {
return this.value.hashCode();
}
public String toString() {
return "ZipEightByteInteger value: " + this.value;
}
}

View file

@ -0,0 +1,12 @@
package org.apache.commons.compress.archivers.zip;
import java.io.IOException;
import java.nio.ByteBuffer;
public interface ZipEncoding {
boolean canEncode(String paramString);
ByteBuffer encode(String paramString) throws IOException;
String decode(byte[] paramArrayOfbyte) throws IOException;
}

View file

@ -0,0 +1,128 @@
package org.apache.commons.compress.archivers.zip;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.nio.charset.UnsupportedCharsetException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.compress.utils.Charsets;
public abstract class ZipEncodingHelper {
private static final Map<String, SimpleEncodingHolder> simpleEncodings;
private static class SimpleEncodingHolder {
private final char[] highChars;
private Simple8BitZipEncoding encoding;
SimpleEncodingHolder(char[] highChars) {
this.highChars = highChars;
}
public synchronized Simple8BitZipEncoding getEncoding() {
if (this.encoding == null)
this.encoding = new Simple8BitZipEncoding(this.highChars);
return this.encoding;
}
}
static {
Map<String, SimpleEncodingHolder> se = new HashMap<String, SimpleEncodingHolder>();
char[] cp437_high_chars = {
'Ç', 'ü', 'é', 'â', 'ä', 'à', 'å', 'ç', 'ê', 'ë',
'è', 'ï', 'î', 'ì', 'Ä', 'Å', 'É', 'æ', 'Æ', 'ô',
'ö', 'ò', 'û', 'ù', 'ÿ', 'Ö', 'Ü', '¢', '£', '¥',
'₧', 'ƒ', 'á', 'í', 'ó', 'ú', 'ñ', 'Ñ', 'ª', 'º',
'¿', '⌐', '¬', '½', '¼', '¡', '«', '»', '░', '▒',
'▓', '│', '┤', '╡', '╢', '╖', '╕', '╣', '║', '╗',
'╝', '╜', '╛', '┐', '└', '┴', '┬', '├', '─', '┼',
'╞', '╟', '╚', '╔', '╩', '╦', '╠', '═', '╬', '╧',
'╨', '╤', '╥', '╙', '╘', '╒', '╓', '╫', '╪', '┘',
'┌', '█', '▄', '▌', '▐', '▀', 'α', 'ß', 'Γ', 'π',
'Σ', 'σ', 'µ', 'τ', 'Φ', 'Θ', 'Ω', 'δ', '∞', 'φ',
'ε', '∩', '≡', '±', '≥', '≤', '⌠', '⌡', '÷', '≈',
'°', '∙', '·', '√', 'ⁿ', '²', '■', '\u00A0' };
SimpleEncodingHolder cp437 = new SimpleEncodingHolder(cp437_high_chars);
se.put("CP437", cp437);
se.put("Cp437", cp437);
se.put("cp437", cp437);
se.put("IBM437", cp437);
se.put("ibm437", cp437);
char[] cp850_high_chars = {
'Ç', 'ü', 'é', 'â', 'ä', 'à', 'å', 'ç', 'ê', 'ë',
'è', 'ï', 'î', 'ì', 'Ä', 'Å', 'É', 'æ', 'Æ', 'ô',
'ö', 'ò', 'û', 'ù', 'ÿ', 'Ö', 'Ü', 'ø', '£', 'Ø',
'×', 'ƒ', 'á', 'í', 'ó', 'ú', 'ñ', 'Ñ', 'ª', 'º',
'¿', '®', '¬', '½', '¼', '¡', '«', '»', '░', '▒',
'▓', '│', '┤', 'Á', 'Â', 'À', '©', '╣', '║', '╗',
'╝', '¢', '¥', '┐', '└', '┴', '┬', '├', '─', '┼',
'ã', 'Ã', '╚', '╔', '╩', '╦', '╠', '═', '╬', '¤',
'ð', 'Ð', 'Ê', 'Ë', 'È', 'ı', 'Í', 'Î', 'Ï', '┘',
'┌', '█', '▄', '¦', 'Ì', '▀', 'Ó', 'ß', 'Ô', 'Ò',
'õ', 'Õ', 'µ', 'þ', 'Þ', 'Ú', 'Û', 'Ù', 'ý', 'Ý',
'¯', '´', '\u00AD', '±', '‗', '¾', '¶', '§', '÷', '¸',
'°', '¨', '·', '¹', '³', '²', '■', '\u00A0' };
SimpleEncodingHolder cp850 = new SimpleEncodingHolder(cp850_high_chars);
se.put("CP850", cp850);
se.put("Cp850", cp850);
se.put("cp850", cp850);
se.put("IBM850", cp850);
se.put("ibm850", cp850);
simpleEncodings = Collections.<String, SimpleEncodingHolder>unmodifiableMap(se);
}
static ByteBuffer growBuffer(ByteBuffer b, int newCapacity) {
b.limit(b.position());
b.rewind();
int c2 = b.capacity() * 2;
ByteBuffer on = ByteBuffer.allocate((c2 < newCapacity) ? newCapacity : c2);
on.put(b);
return on;
}
private static final byte[] HEX_DIGITS = new byte[] {
48, 49, 50, 51, 52, 53, 54, 55, 56, 57,
65, 66, 67, 68, 69, 70 };
static final String UTF8 = "UTF8";
static void appendSurrogate(ByteBuffer bb, char c) {
bb.put((byte)37);
bb.put((byte)85);
bb.put(HEX_DIGITS[c >> 12 & 0xF]);
bb.put(HEX_DIGITS[c >> 8 & 0xF]);
bb.put(HEX_DIGITS[c >> 4 & 0xF]);
bb.put(HEX_DIGITS[c & 0xF]);
}
static final ZipEncoding UTF8_ZIP_ENCODING = new FallbackZipEncoding("UTF8");
public static ZipEncoding getZipEncoding(String name) {
if (isUTF8(name))
return UTF8_ZIP_ENCODING;
if (name == null)
return new FallbackZipEncoding();
SimpleEncodingHolder h = simpleEncodings.get(name);
if (h != null)
return h.getEncoding();
try {
Charset cs = Charset.forName(name);
return new NioZipEncoding(cs);
} catch (UnsupportedCharsetException e) {
return new FallbackZipEncoding(name);
}
}
static boolean isUTF8(String charsetName) {
if (charsetName == null)
charsetName = System.getProperty("file.encoding");
if (Charsets.UTF_8.name().equalsIgnoreCase(charsetName))
return true;
for (String alias : Charsets.UTF_8.aliases()) {
if (alias.equalsIgnoreCase(charsetName))
return true;
}
return false;
}
}

View file

@ -0,0 +1,19 @@
package org.apache.commons.compress.archivers.zip;
import java.util.zip.ZipException;
public interface ZipExtraField {
ZipShort getHeaderId();
ZipShort getLocalFileDataLength();
ZipShort getCentralDirectoryLength();
byte[] getLocalFileDataData();
byte[] getCentralDirectoryData();
void parseFromLocalFileData(byte[] paramArrayOfbyte, int paramInt1, int paramInt2) throws ZipException;
void parseFromCentralDirectoryData(byte[] paramArrayOfbyte, int paramInt1, int paramInt2) throws ZipException;
}

View file

@ -0,0 +1,540 @@
package org.apache.commons.compress.archivers.zip;
import java.io.BufferedInputStream;
import java.io.Closeable;
import java.io.EOFException;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.zip.Inflater;
import java.util.zip.InflaterInputStream;
import java.util.zip.ZipException;
import org.apache.commons.compress.utils.IOUtils;
public class ZipFile implements Closeable {
private static final int HASH_SIZE = 509;
static final int NIBLET_MASK = 15;
static final int BYTE_SHIFT = 8;
private static final int POS_0 = 0;
private static final int POS_1 = 1;
private static final int POS_2 = 2;
private static final int POS_3 = 3;
private final List<ZipArchiveEntry> entries = new LinkedList<ZipArchiveEntry>();
private final Map<String, LinkedList<ZipArchiveEntry>> nameMap = new HashMap<String, LinkedList<ZipArchiveEntry>>(509);
private final String encoding;
private final ZipEncoding zipEncoding;
private final String archiveName;
private final RandomAccessFile archive;
private final boolean useUnicodeExtraFields;
private boolean closed;
private static final class OffsetEntry {
private OffsetEntry() {}
private long headerOffset = -1L;
private long dataOffset = -1L;
}
private final byte[] DWORD_BUF = new byte[8];
private final byte[] WORD_BUF = new byte[4];
private final byte[] CFH_BUF = new byte[42];
private final byte[] SHORT_BUF = new byte[2];
private static final int CFH_LEN = 42;
public ZipFile(File f) throws IOException {
this(f, "UTF8");
}
public ZipFile(String name) throws IOException {
this(new File(name), "UTF8");
}
public ZipFile(String name, String encoding) throws IOException {
this(new File(name), encoding, true);
}
public ZipFile(File f, String encoding) throws IOException {
this(f, encoding, true);
}
public ZipFile(File f, String encoding, boolean useUnicodeExtraFields) throws IOException {
this.archiveName = f.getAbsolutePath();
this.encoding = encoding;
this.zipEncoding = ZipEncodingHelper.getZipEncoding(encoding);
this.useUnicodeExtraFields = useUnicodeExtraFields;
this.archive = new RandomAccessFile(f, "r");
boolean success = false;
try {
Map<ZipArchiveEntry, NameAndComment> entriesWithoutUTF8Flag = populateFromCentralDirectory();
resolveLocalFileHeaderData(entriesWithoutUTF8Flag);
success = true;
} finally {
if (!success) {
this.closed = true;
IOUtils.closeQuietly(this.archive);
}
}
}
public String getEncoding() {
return this.encoding;
}
public void close() throws IOException {
this.closed = true;
this.archive.close();
}
public static void closeQuietly(ZipFile zipfile) {
IOUtils.closeQuietly(zipfile);
}
public Enumeration<ZipArchiveEntry> getEntries() {
return Collections.<ZipArchiveEntry>enumeration(this.entries);
}
public Enumeration<ZipArchiveEntry> getEntriesInPhysicalOrder() {
ZipArchiveEntry[] allEntries = this.entries.<ZipArchiveEntry>toArray(new ZipArchiveEntry[0]);
Arrays.<ZipArchiveEntry>sort(allEntries, this.OFFSET_COMPARATOR);
return Collections.<ZipArchiveEntry>enumeration(Arrays.<ZipArchiveEntry>asList(allEntries));
}
public ZipArchiveEntry getEntry(String name) {
LinkedList<ZipArchiveEntry> entriesOfThatName = this.nameMap.get(name);
return (entriesOfThatName != null) ? entriesOfThatName.getFirst() : null;
}
public Iterable<ZipArchiveEntry> getEntries(String name) {
List<ZipArchiveEntry> entriesOfThatName = this.nameMap.get(name);
return (entriesOfThatName != null) ? entriesOfThatName : Collections.<ZipArchiveEntry>emptyList();
}
public Iterable<ZipArchiveEntry> getEntriesInPhysicalOrder(String name) {
ZipArchiveEntry[] entriesOfThatName = new ZipArchiveEntry[0];
if (this.nameMap.containsKey(name)) {
entriesOfThatName = (ZipArchiveEntry[])this.nameMap.get(name).toArray(entriesOfThatName);
Arrays.<ZipArchiveEntry>sort(entriesOfThatName, this.OFFSET_COMPARATOR);
}
return Arrays.<ZipArchiveEntry>asList(entriesOfThatName);
}
public boolean canReadEntryData(ZipArchiveEntry ze) {
return ZipUtil.canHandleEntryData(ze);
}
public InputStream getInputStream(ZipArchiveEntry ze) throws IOException, ZipException {
final Inflater inflater;
if (!(ze instanceof Entry))
return null;
OffsetEntry offsetEntry = ((Entry)ze).getOffsetEntry();
ZipUtil.checkRequestedFeatures(ze);
long start = offsetEntry.dataOffset;
BoundedInputStream bis = new BoundedInputStream(start, ze.getCompressedSize());
switch (ZipMethod.getMethodByCode(ze.getMethod())) {
case STORED:
return bis;
case UNSHRINKING:
return new UnshrinkingInputStream(bis);
case IMPLODING:
return new ExplodingInputStream(ze.getGeneralPurposeBit().getSlidingDictionarySize(), ze.getGeneralPurposeBit().getNumberOfShannonFanoTrees(), new BufferedInputStream(bis));
case DEFLATED:
bis.addDummy();
inflater = new Inflater(true);
return new InflaterInputStream(bis, inflater) {
public void close() throws IOException {
super.close();
inflater.end();
}
};
}
throw new ZipException("Found unsupported compression method " + ze.getMethod());
}
public String getUnixSymlink(ZipArchiveEntry entry) throws IOException {
if (entry != null && entry.isUnixSymlink()) {
InputStream in = null;
try {
in = getInputStream(entry);
byte[] symlinkBytes = IOUtils.toByteArray(in);
return this.zipEncoding.decode(symlinkBytes);
} finally {
if (in != null)
in.close();
}
}
return null;
}
protected void finalize() throws Throwable {
try {
if (!this.closed) {
System.err.println("Cleaning up unclosed ZipFile for archive " + this.archiveName);
close();
}
} finally {
super.finalize();
}
}
private static final long CFH_SIG = ZipLong.getValue(ZipArchiveOutputStream.CFH_SIG);
static final int MIN_EOCD_SIZE = 22;
private static final int MAX_EOCD_SIZE = 65557;
private static final int CFD_LOCATOR_OFFSET = 16;
private static final int ZIP64_EOCDL_LENGTH = 20;
private static final int ZIP64_EOCDL_LOCATOR_OFFSET = 8;
private static final int ZIP64_EOCD_CFD_LOCATOR_OFFSET = 48;
private static final long LFH_OFFSET_FOR_FILENAME_LENGTH = 26L;
private Map<ZipArchiveEntry, NameAndComment> populateFromCentralDirectory() throws IOException {
HashMap<ZipArchiveEntry, NameAndComment> noUTF8Flag = new HashMap<ZipArchiveEntry, NameAndComment>();
positionAtCentralDirectory();
this.archive.readFully(this.WORD_BUF);
long sig = ZipLong.getValue(this.WORD_BUF);
if (sig != CFH_SIG && startsWithLocalFileHeader())
throw new IOException("central directory is empty, can't expand corrupt archive.");
while (sig == CFH_SIG) {
readCentralDirectoryEntry(noUTF8Flag);
this.archive.readFully(this.WORD_BUF);
sig = ZipLong.getValue(this.WORD_BUF);
}
return noUTF8Flag;
}
private void readCentralDirectoryEntry(Map<ZipArchiveEntry, NameAndComment> noUTF8Flag) throws IOException {
this.archive.readFully(this.CFH_BUF);
int off = 0;
OffsetEntry offset = new OffsetEntry();
Entry ze = new Entry(offset);
int versionMadeBy = ZipShort.getValue(this.CFH_BUF, off);
off += 2;
ze.setPlatform(versionMadeBy >> 8 & 0xF);
off += 2;
GeneralPurposeBit gpFlag = GeneralPurposeBit.parse(this.CFH_BUF, off);
boolean hasUTF8Flag = gpFlag.usesUTF8ForNames();
ZipEncoding entryEncoding = hasUTF8Flag ? ZipEncodingHelper.UTF8_ZIP_ENCODING : this.zipEncoding;
ze.setGeneralPurposeBit(gpFlag);
off += 2;
ze.setMethod(ZipShort.getValue(this.CFH_BUF, off));
off += 2;
long time = ZipUtil.dosToJavaTime(ZipLong.getValue(this.CFH_BUF, off));
ze.setTime(time);
off += 4;
ze.setCrc(ZipLong.getValue(this.CFH_BUF, off));
off += 4;
ze.setCompressedSize(ZipLong.getValue(this.CFH_BUF, off));
off += 4;
ze.setSize(ZipLong.getValue(this.CFH_BUF, off));
off += 4;
int fileNameLen = ZipShort.getValue(this.CFH_BUF, off);
off += 2;
int extraLen = ZipShort.getValue(this.CFH_BUF, off);
off += 2;
int commentLen = ZipShort.getValue(this.CFH_BUF, off);
off += 2;
int diskStart = ZipShort.getValue(this.CFH_BUF, off);
off += 2;
ze.setInternalAttributes(ZipShort.getValue(this.CFH_BUF, off));
off += 2;
ze.setExternalAttributes(ZipLong.getValue(this.CFH_BUF, off));
off += 4;
byte[] fileName = new byte[fileNameLen];
this.archive.readFully(fileName);
ze.setName(entryEncoding.decode(fileName), fileName);
offset.headerOffset = ZipLong.getValue(this.CFH_BUF, off);
this.entries.add(ze);
byte[] cdExtraData = new byte[extraLen];
this.archive.readFully(cdExtraData);
ze.setCentralDirectoryExtra(cdExtraData);
setSizesAndOffsetFromZip64Extra(ze, offset, diskStart);
byte[] comment = new byte[commentLen];
this.archive.readFully(comment);
ze.setComment(entryEncoding.decode(comment));
if (!hasUTF8Flag && this.useUnicodeExtraFields)
noUTF8Flag.put(ze, new NameAndComment(fileName, comment));
}
private void setSizesAndOffsetFromZip64Extra(ZipArchiveEntry ze, OffsetEntry offset, int diskStart) throws IOException {
Zip64ExtendedInformationExtraField z64 = (Zip64ExtendedInformationExtraField)ze.getExtraField(Zip64ExtendedInformationExtraField.HEADER_ID);
if (z64 != null) {
boolean hasUncompressedSize = (ze.getSize() == 4294967295L);
boolean hasCompressedSize = (ze.getCompressedSize() == 4294967295L);
boolean hasRelativeHeaderOffset = (offset.headerOffset == 4294967295L);
z64.reparseCentralDirectoryData(hasUncompressedSize, hasCompressedSize, hasRelativeHeaderOffset, (diskStart == 65535));
if (hasUncompressedSize) {
ze.setSize(z64.getSize().getLongValue());
} else if (hasCompressedSize) {
z64.setSize(new ZipEightByteInteger(ze.getSize()));
}
if (hasCompressedSize) {
ze.setCompressedSize(z64.getCompressedSize().getLongValue());
} else if (hasUncompressedSize) {
z64.setCompressedSize(new ZipEightByteInteger(ze.getCompressedSize()));
}
if (hasRelativeHeaderOffset)
offset.headerOffset = z64.getRelativeHeaderOffset().getLongValue();
}
}
private void positionAtCentralDirectory() throws IOException {
positionAtEndOfCentralDirectoryRecord();
boolean found = false;
boolean searchedForZip64EOCD = (this.archive.getFilePointer() > 20L);
if (searchedForZip64EOCD) {
this.archive.seek(this.archive.getFilePointer() - 20L);
this.archive.readFully(this.WORD_BUF);
found = Arrays.equals(ZipArchiveOutputStream.ZIP64_EOCD_LOC_SIG, this.WORD_BUF);
}
if (!found) {
if (searchedForZip64EOCD)
skipBytes(16);
positionAtCentralDirectory32();
} else {
positionAtCentralDirectory64();
}
}
private void positionAtCentralDirectory64() throws IOException {
skipBytes(4);
this.archive.readFully(this.DWORD_BUF);
this.archive.seek(ZipEightByteInteger.getLongValue(this.DWORD_BUF));
this.archive.readFully(this.WORD_BUF);
if (!Arrays.equals(this.WORD_BUF, ZipArchiveOutputStream.ZIP64_EOCD_SIG))
throw new ZipException("archive's ZIP64 end of central directory locator is corrupt.");
skipBytes(44);
this.archive.readFully(this.DWORD_BUF);
this.archive.seek(ZipEightByteInteger.getLongValue(this.DWORD_BUF));
}
private void positionAtCentralDirectory32() throws IOException {
skipBytes(16);
this.archive.readFully(this.WORD_BUF);
this.archive.seek(ZipLong.getValue(this.WORD_BUF));
}
private void positionAtEndOfCentralDirectoryRecord() throws IOException {
boolean found = tryToLocateSignature(22L, 65557L, ZipArchiveOutputStream.EOCD_SIG);
if (!found)
throw new ZipException("archive is not a ZIP archive");
}
private boolean tryToLocateSignature(long minDistanceFromEnd, long maxDistanceFromEnd, byte[] sig) throws IOException {
boolean found = false;
long off = this.archive.length() - minDistanceFromEnd;
long stopSearching = Math.max(0L, this.archive.length() - maxDistanceFromEnd);
if (off >= 0L)
for (; off >= stopSearching; off--) {
this.archive.seek(off);
int curr = this.archive.read();
if (curr == -1)
break;
if (curr == sig[0]) {
curr = this.archive.read();
if (curr == sig[1]) {
curr = this.archive.read();
if (curr == sig[2]) {
curr = this.archive.read();
if (curr == sig[3]) {
found = true;
break;
}
}
}
}
}
if (found)
this.archive.seek(off);
return found;
}
private void skipBytes(int count) throws IOException {
int totalSkipped = 0;
while (totalSkipped < count) {
int skippedNow = this.archive.skipBytes(count - totalSkipped);
if (skippedNow <= 0)
throw new EOFException();
totalSkipped += skippedNow;
}
}
private void resolveLocalFileHeaderData(Map<ZipArchiveEntry, NameAndComment> entriesWithoutUTF8Flag) throws IOException {
for (ZipArchiveEntry zipArchiveEntry : this.entries) {
Entry ze = (Entry)zipArchiveEntry;
OffsetEntry offsetEntry = ze.getOffsetEntry();
long offset = offsetEntry.headerOffset;
this.archive.seek(offset + 26L);
this.archive.readFully(this.SHORT_BUF);
int fileNameLen = ZipShort.getValue(this.SHORT_BUF);
this.archive.readFully(this.SHORT_BUF);
int extraFieldLen = ZipShort.getValue(this.SHORT_BUF);
int lenToSkip = fileNameLen;
while (lenToSkip > 0) {
int skipped = this.archive.skipBytes(lenToSkip);
if (skipped <= 0)
throw new IOException("failed to skip file name in local file header");
lenToSkip -= skipped;
}
byte[] localExtraData = new byte[extraFieldLen];
this.archive.readFully(localExtraData);
ze.setExtra(localExtraData);
offsetEntry.dataOffset = offset + 26L + 2L + 2L + (long)fileNameLen + (long)extraFieldLen;
if (entriesWithoutUTF8Flag.containsKey(ze)) {
NameAndComment nc = entriesWithoutUTF8Flag.get(ze);
ZipUtil.setNameAndCommentFromExtraFields(ze, nc.name, nc.comment);
}
String name = ze.getName();
LinkedList<ZipArchiveEntry> entriesOfThatName = this.nameMap.get(name);
if (entriesOfThatName == null) {
entriesOfThatName = new LinkedList<ZipArchiveEntry>();
this.nameMap.put(name, entriesOfThatName);
}
entriesOfThatName.addLast(ze);
}
}
private boolean startsWithLocalFileHeader() throws IOException {
this.archive.seek(0L);
this.archive.readFully(this.WORD_BUF);
return Arrays.equals(this.WORD_BUF, ZipArchiveOutputStream.LFH_SIG);
}
private class BoundedInputStream extends InputStream {
private long remaining;
private long loc;
private boolean addDummyByte = false;
BoundedInputStream(long start, long remaining) {
this.remaining = remaining;
this.loc = start;
}
public int read() throws IOException {
if (this.remaining-- <= 0L) {
if (this.addDummyByte) {
this.addDummyByte = false;
return 0;
}
return -1;
}
synchronized (ZipFile.this.archive) {
ZipFile.this.archive.seek(this.loc++);
return ZipFile.this.archive.read();
}
}
public int read(byte[] b, int off, int len) throws IOException {
if (this.remaining <= 0L) {
if (this.addDummyByte) {
this.addDummyByte = false;
b[off] = 0;
return 1;
}
return -1;
}
if (len <= 0)
return 0;
if ((long)len > this.remaining)
len = (int)this.remaining;
int ret = -1;
synchronized (ZipFile.this.archive) {
ZipFile.this.archive.seek(this.loc);
ret = ZipFile.this.archive.read(b, off, len);
}
if (ret > 0) {
this.loc += (long)ret;
this.remaining -= (long)ret;
}
return ret;
}
void addDummy() {
this.addDummyByte = true;
}
}
private static final class NameAndComment {
private final byte[] name;
private final byte[] comment;
private NameAndComment(byte[] name, byte[] comment) {
this.name = name;
this.comment = comment;
}
}
private final Comparator<ZipArchiveEntry> OFFSET_COMPARATOR = new Comparator<ZipArchiveEntry>() {
public int compare(ZipArchiveEntry e1, ZipArchiveEntry e2) {
if (e1 == e2)
return 0;
ZipFile.Entry ent1 = (e1 instanceof ZipFile.Entry) ? (ZipFile.Entry)e1 : null;
ZipFile.Entry ent2 = (e2 instanceof ZipFile.Entry) ? (ZipFile.Entry)e2 : null;
if (ent1 == null)
return 1;
if (ent2 == null)
return -1;
long val = (ent1.getOffsetEntry()).headerOffset - (ent2.getOffsetEntry()).headerOffset;
return (val == 0L) ? 0 : ((val < 0L) ? -1 : 1);
}
};
private static class Entry extends ZipArchiveEntry {
private final ZipFile.OffsetEntry offsetEntry;
Entry(ZipFile.OffsetEntry offset) {
this.offsetEntry = offset;
}
ZipFile.OffsetEntry getOffsetEntry() {
return this.offsetEntry;
}
public int hashCode() {
return 3 * super.hashCode() + (int)(this.offsetEntry.headerOffset % Integer.MAX_VALUE);
}
public boolean equals(Object other) {
if (super.equals(other)) {
Entry otherEntry = (Entry)other;
return (this.offsetEntry.headerOffset == otherEntry.offsetEntry.headerOffset && this.offsetEntry.dataOffset == otherEntry.offsetEntry.dataOffset);
}
return false;
}
}
}

View file

@ -0,0 +1,102 @@
package org.apache.commons.compress.archivers.zip;
import java.io.Serializable;
public final class ZipLong implements Cloneable, Serializable {
private static final long serialVersionUID = 1L;
private static final int BYTE_1 = 1;
private static final int BYTE_1_MASK = 65280;
private static final int BYTE_1_SHIFT = 8;
private static final int BYTE_2 = 2;
private static final int BYTE_2_MASK = 16711680;
private static final int BYTE_2_SHIFT = 16;
private static final int BYTE_3 = 3;
private static final long BYTE_3_MASK = 4278190080L;
private static final int BYTE_3_SHIFT = 24;
private final long value;
public static final ZipLong CFH_SIG = new ZipLong(33639248L);
public static final ZipLong LFH_SIG = new ZipLong(67324752L);
public static final ZipLong DD_SIG = new ZipLong(134695760L);
static final ZipLong ZIP64_MAGIC = new ZipLong(4294967295L);
public static final ZipLong SINGLE_SEGMENT_SPLIT_MARKER = new ZipLong(808471376L);
public static final ZipLong AED_SIG = new ZipLong(134630224L);
public ZipLong(long value) {
this.value = value;
}
public ZipLong(byte[] bytes) {
this(bytes, 0);
}
public ZipLong(byte[] bytes, int offset) {
this.value = getValue(bytes, offset);
}
public byte[] getBytes() {
return getBytes(this.value);
}
public long getValue() {
return this.value;
}
public static byte[] getBytes(long value) {
byte[] result = new byte[4];
result[0] = (byte)(int)(value & 0xFFL);
result[1] = (byte)(int)((value & 0xFF00L) >> 8L);
result[2] = (byte)(int)((value & 0xFF0000L) >> 16L);
result[3] = (byte)(int)((value & 0xFF000000L) >> 24L);
return result;
}
public static long getValue(byte[] bytes, int offset) {
long value = (long)(bytes[offset + 3] << 24) & 0xFF000000L;
value += (long)(bytes[offset + 2] << 16 & 0xFF0000);
value += (long)(bytes[offset + 1] << 8 & 0xFF00);
value += (long)(bytes[offset] & 0xFF);
return value;
}
public static long getValue(byte[] bytes) {
return getValue(bytes, 0);
}
public boolean equals(Object o) {
if (o == null || !(o instanceof ZipLong))
return false;
return (this.value == ((ZipLong)o).getValue());
}
public int hashCode() {
return (int)this.value;
}
public Object clone() {
try {
return super.clone();
} catch (CloneNotSupportedException cnfe) {
throw new RuntimeException(cnfe);
}
}
public String toString() {
return "ZipLong value: " + this.value;
}
}

View file

@ -0,0 +1,49 @@
package org.apache.commons.compress.archivers.zip;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
public enum ZipMethod {
STORED(0),
UNSHRINKING(1),
EXPANDING_LEVEL_1(2),
EXPANDING_LEVEL_2(3),
EXPANDING_LEVEL_3(4),
EXPANDING_LEVEL_4(5),
IMPLODING(6),
TOKENIZATION(7),
DEFLATED(8),
ENHANCED_DEFLATED(9),
PKWARE_IMPLODING(10),
BZIP2(12),
LZMA(14),
JPEG(96),
WAVPACK(97),
PPMD(98),
AES_ENCRYPTED(99),
UNKNOWN(-1);
private final int code;
private static final Map<Integer, ZipMethod> codeToEnum;
static {
Map<Integer, ZipMethod> cte = new HashMap<Integer, ZipMethod>();
for (ZipMethod method : values())
cte.put(Integer.valueOf(method.getCode()), method);
codeToEnum = Collections.<Integer, ZipMethod>unmodifiableMap(cte);
}
ZipMethod(int code) {
this.code = code;
}
public int getCode() {
return this.code;
}
public static ZipMethod getMethodByCode(int code) {
return codeToEnum.get(Integer.valueOf(code));
}
}

View file

@ -0,0 +1,75 @@
package org.apache.commons.compress.archivers.zip;
import java.io.Serializable;
public final class ZipShort implements Cloneable, Serializable {
private static final long serialVersionUID = 1L;
private static final int BYTE_1_MASK = 65280;
private static final int BYTE_1_SHIFT = 8;
private final int value;
public ZipShort(int value) {
this.value = value;
}
public ZipShort(byte[] bytes) {
this(bytes, 0);
}
public ZipShort(byte[] bytes, int offset) {
this.value = getValue(bytes, offset);
}
public byte[] getBytes() {
byte[] result = new byte[2];
result[0] = (byte)(this.value & 0xFF);
result[1] = (byte)((this.value & 0xFF00) >> 8);
return result;
}
public int getValue() {
return this.value;
}
public static byte[] getBytes(int value) {
byte[] result = new byte[2];
result[0] = (byte)(value & 0xFF);
result[1] = (byte)((value & 0xFF00) >> 8);
return result;
}
public static int getValue(byte[] bytes, int offset) {
int value = bytes[offset + 1] << 8 & 0xFF00;
value += bytes[offset] & 0xFF;
return value;
}
public static int getValue(byte[] bytes) {
return getValue(bytes, 0);
}
public boolean equals(Object o) {
if (o == null || !(o instanceof ZipShort))
return false;
return (this.value == ((ZipShort)o).getValue());
}
public int hashCode() {
return this.value;
}
public Object clone() {
try {
return super.clone();
} catch (CloneNotSupportedException cnfe) {
throw new RuntimeException(cnfe);
}
}
public String toString() {
return "ZipShort value: " + this.value;
}
}

Some files were not shown because too many files have changed in this diff Show more