diff options
-rw-r--r-- | NativeCode.mk | 4 | ||||
-rw-r--r-- | luni/src/main/java/java/util/jar/JarFile.java | 3 | ||||
-rw-r--r-- | luni/src/main/java/java/util/jar/JarInputStream.java | 3 | ||||
-rw-r--r-- | luni/src/main/java/java/util/jar/JarVerifier.java | 5 | ||||
-rw-r--r-- | luni/src/main/java/java/util/jar/StrictJarFile.java | 197 | ||||
-rw-r--r-- | luni/src/main/java/java/util/zip/ZipEntry.java | 33 | ||||
-rw-r--r-- | luni/src/main/java/java/util/zip/ZipFile.java | 16 | ||||
-rw-r--r-- | luni/src/main/native/Register.cpp | 1 | ||||
-rw-r--r-- | luni/src/main/native/java_util_jar_StrictJarFile.cpp | 173 | ||||
-rw-r--r-- | luni/src/main/native/sub.mk | 4 | ||||
-rw-r--r-- | luni/src/test/java/libcore/java/util/jar/StrictJarFileTest.java | 180 |
11 files changed, 604 insertions, 15 deletions
diff --git a/NativeCode.mk b/NativeCode.mk index f8312ae..ec9aca5 100644 --- a/NativeCode.mk +++ b/NativeCode.mk @@ -83,7 +83,7 @@ LOCAL_CPPFLAGS += $(core_cppflags) LOCAL_SRC_FILES += $(core_src_files) LOCAL_C_INCLUDES += $(core_c_includes) LOCAL_SHARED_LIBRARIES += $(core_shared_libraries) libcrypto libexpat libicuuc libicui18n libnativehelper libz -LOCAL_STATIC_LIBRARIES += $(core_static_libraries) +LOCAL_STATIC_LIBRARIES += $(core_static_libraries) libziparchive LOCAL_MODULE_TAGS := optional LOCAL_MODULE := libjavacore LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/NativeCode.mk @@ -123,7 +123,7 @@ ifeq ($(WITH_HOST_DALVIK),true) LOCAL_MODULE := libjavacore LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/NativeCode.mk LOCAL_SHARED_LIBRARIES += $(core_shared_libraries) libexpat-host libicuuc-host libicui18n-host libcrypto-host libz-host - LOCAL_STATIC_LIBRARIES += $(core_static_libraries) + LOCAL_STATIC_LIBRARIES += $(core_static_libraries) libziparchive-host include $(BUILD_HOST_SHARED_LIBRARY) ifeq ($(LIBCORE_SKIP_TESTS),) diff --git a/luni/src/main/java/java/util/jar/JarFile.java b/luni/src/main/java/java/util/jar/JarFile.java index b219637..6b147f6 100644 --- a/luni/src/main/java/java/util/jar/JarFile.java +++ b/luni/src/main/java/java/util/jar/JarFile.java @@ -215,8 +215,7 @@ public class JarFile extends ZipFile { // We create the manifest straight away, so that we can create // the jar verifier as well. manifest = new Manifest(metaEntries.get(MANIFEST_NAME), true); - verifier = new JarVerifier(getName(), manifest, metaEntries, - manifest.getMainAttributesEnd()); + verifier = new JarVerifier(getName(), manifest, metaEntries); } else { verifier = null; manifestBytes = metaEntries.get(MANIFEST_NAME); diff --git a/luni/src/main/java/java/util/jar/JarInputStream.java b/luni/src/main/java/java/util/jar/JarInputStream.java index 31bd05f..585c135 100644 --- a/luni/src/main/java/java/util/jar/JarInputStream.java +++ b/luni/src/main/java/java/util/jar/JarInputStream.java @@ -90,8 +90,7 @@ public class JarInputStream extends ZipInputStream { if (verify) { HashMap<String, byte[]> metaEntries = new HashMap<String, byte[]>(); metaEntries.put(JarFile.MANIFEST_NAME, manifestBytes); - verifier = new JarVerifier("JarInputStream", manifest, - metaEntries, manifest.getMainAttributesEnd()); + verifier = new JarVerifier("JarInputStream", manifest, metaEntries); } } diff --git a/luni/src/main/java/java/util/jar/JarVerifier.java b/luni/src/main/java/java/util/jar/JarVerifier.java index eb88990..c545a02 100644 --- a/luni/src/main/java/java/util/jar/JarVerifier.java +++ b/luni/src/main/java/java/util/jar/JarVerifier.java @@ -152,12 +152,11 @@ class JarVerifier { * @param name * the name of the JAR file being verified. */ - JarVerifier(String name, Manifest manifest, HashMap<String, byte[]> metaEntries, - int mainAttributesEnd) { + JarVerifier(String name, Manifest manifest, HashMap<String, byte[]> metaEntries) { jarName = name; this.manifest = manifest; this.metaEntries = metaEntries; - this.mainAttributesEnd = mainAttributesEnd; + this.mainAttributesEnd = manifest.getMainAttributesEnd(); } /** diff --git a/luni/src/main/java/java/util/jar/StrictJarFile.java b/luni/src/main/java/java/util/jar/StrictJarFile.java new file mode 100644 index 0000000..fa175d8 --- /dev/null +++ b/luni/src/main/java/java/util/jar/StrictJarFile.java @@ -0,0 +1,197 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +package java.util.jar; + +import dalvik.system.CloseGuard; +import java.io.IOException; +import java.io.InputStream; +import java.io.RandomAccessFile; +import java.security.cert.Certificate; +import java.util.HashMap; +import java.util.Iterator; +import java.util.zip.Inflater; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; +import libcore.io.IoUtils; +import libcore.io.Streams; + +/** + * A subset of the JarFile API implemented as a thin wrapper over + * system/core/libziparchive. + * + * @hide for internal use only. Not API compatible (or as forgiving) as + * {@link java.util.jar.JarFile} + */ +public final class StrictJarFile { + + private final long nativeHandle; + + // NOTE: It's possible to share a file descriptor with the native + // code, at the cost of some additional complexity. + private final RandomAccessFile raf; + + private final Manifest manifest; + private final JarVerifier verifier; + + private final boolean isSigned; + + private final CloseGuard guard = CloseGuard.get(); + private boolean closed; + + public StrictJarFile(String fileName) throws IOException { + this.nativeHandle = nativeOpenJarFile(fileName); + this.raf = new RandomAccessFile(fileName, "r"); + + try { + // Read the MANIFEST and signature files up front and try to + // parse them. We never want to accept a JAR File with broken signatures + // or manifests, so it's best to throw as early as possible. + HashMap<String, byte[]> metaEntries = getMetaEntries(); + this.manifest = new Manifest(metaEntries.get(JarFile.MANIFEST_NAME), true); + this.verifier = new JarVerifier(fileName, manifest, metaEntries); + + isSigned = verifier.readCertificates() && verifier.isSignedJar(); + } catch (IOException ioe) { + nativeClose(this.nativeHandle); + throw ioe; + } + + guard.open("close"); + } + + public Manifest getManifest() { + return manifest; + } + + public Iterator<ZipEntry> iterator() throws IOException { + return new EntryIterator(nativeHandle, ""); + } + + public ZipEntry findEntry(String name) { + return nativeFindEntry(nativeHandle, name); + } + + /** + * Return all certificates for a given {@link ZipEntry} belonging to this jar. + * This method MUST be called only after fully exhausting the InputStream belonging + * to this entry. + * + * Returns {@code null} if this jar file isn't signed or if this method is + * called before the stream is processed. + */ + public Certificate[] getCertificates(ZipEntry ze) { + if (isSigned) { + return verifier.getCertificates(ze.getName()); + } + + return null; + } + + public InputStream getInputStream(ZipEntry ze) { + final InputStream is = getZipInputStream(ze); + + if (isSigned) { + JarVerifier.VerifierEntry entry = verifier.initEntry(ze.getName()); + if (entry == null) { + return is; + } + + return new JarFile.JarFileInputStream(is, ze.getSize(), entry); + } + + return is; + } + + public void close() throws IOException { + if (!closed) { + guard.close(); + + nativeClose(nativeHandle); + IoUtils.closeQuietly(raf); + closed = true; + } + } + + private InputStream getZipInputStream(ZipEntry ze) { + if (ze.getMethod() == ZipEntry.STORED) { + return new ZipFile.RAFStream(raf, ze.getDataOffset(), + ze.getDataOffset() + ze.getSize()); + } else { + final ZipFile.RAFStream wrapped = new ZipFile.RAFStream( + raf, ze.getDataOffset(), ze.getDataOffset() + ze.getCompressedSize()); + + int bufSize = Math.max(1024, (int) Math.min(ze.getSize(), 65535L)); + return new ZipFile.ZipInflaterInputStream(wrapped, new Inflater(true), bufSize, ze); + } + } + + static final class EntryIterator implements Iterator<ZipEntry> { + private final long iterationHandle; + private ZipEntry nextEntry; + + EntryIterator(long nativeHandle, String prefix) throws IOException { + iterationHandle = nativeStartIteration(nativeHandle, prefix); + } + + public ZipEntry next() { + if (nextEntry != null) { + final ZipEntry ze = nextEntry; + nextEntry = null; + return ze; + } + + return nativeNextEntry(iterationHandle); + } + + public boolean hasNext() { + if (nextEntry != null) { + return true; + } + + final ZipEntry ze = nativeNextEntry(iterationHandle); + if (ze == null) { + return false; + } + + nextEntry = ze; + return true; + } + + public void remove() { + throw new UnsupportedOperationException(); + } + } + + private HashMap<String, byte[]> getMetaEntries() throws IOException { + HashMap<String, byte[]> metaEntries = new HashMap<String, byte[]>(); + + Iterator<ZipEntry> entryIterator = new EntryIterator(nativeHandle, "META-INF/"); + while (entryIterator.hasNext()) { + final ZipEntry entry = entryIterator.next(); + metaEntries.put(entry.getName(), Streams.readFully(getInputStream(entry))); + } + + return metaEntries; + } + + private static native long nativeOpenJarFile(String fileName) throws IOException; + private static native long nativeStartIteration(long nativeHandle, String prefix); + private static native ZipEntry nativeNextEntry(long iterationHandle); + private static native ZipEntry nativeFindEntry(long nativeHandle, String entryName); + private static native void nativeClose(long nativeHandle); +} diff --git a/luni/src/main/java/java/util/zip/ZipEntry.java b/luni/src/main/java/java/util/zip/ZipEntry.java index f64c717..69f027a 100644 --- a/luni/src/main/java/java/util/zip/ZipEntry.java +++ b/luni/src/main/java/java/util/zip/ZipEntry.java @@ -25,9 +25,9 @@ import java.util.Arrays; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; -import libcore.io.Streams; import libcore.io.BufferIterator; import libcore.io.HeapBufferIterator; +import libcore.io.Streams; /** * An entry within a zip file. @@ -54,6 +54,8 @@ public class ZipEntry implements ZipConstants, Cloneable { int nameLength = -1; long localHeaderRelOffset = -1; + long dataOffset = -1; + /** * Zip entry state: Deflated. */ @@ -64,6 +66,23 @@ public class ZipEntry implements ZipConstants, Cloneable { */ public static final int STORED = 0; + ZipEntry(String name, String comment, long crc, long compressedSize, + long size, int compressionMethod, int time, int modDate, byte[] extra, + int nameLength, long localHeaderRelOffset, long dataOffset) { + this.name = name; + this.comment = comment; + this.crc = crc; + this.compressedSize = compressedSize; + this.size = size; + this.compressionMethod = compressionMethod; + this.time = time; + this.modDate = modDate; + this.extra = extra; + this.nameLength = nameLength; + this.localHeaderRelOffset = localHeaderRelOffset; + this.dataOffset = dataOffset; + } + /** * Constructs a new {@code ZipEntry} with the specified name. The name is actually a path, * and may contain {@code /} characters. @@ -287,6 +306,17 @@ public class ZipEntry implements ZipConstants, Cloneable { } } + + /** @hide */ + public void setDataOffset(long value) { + dataOffset = value; + } + + /** @hide */ + public long getDataOffset() { + return dataOffset; + } + /** * Returns the string representation of this {@code ZipEntry}. * @@ -316,6 +346,7 @@ public class ZipEntry implements ZipConstants, Cloneable { extra = ze.extra; nameLength = ze.nameLength; localHeaderRelOffset = ze.localHeaderRelOffset; + dataOffset = ze.dataOffset; } /** diff --git a/luni/src/main/java/java/util/zip/ZipFile.java b/luni/src/main/java/java/util/zip/ZipFile.java index c25bbc1..40036cf 100644 --- a/luni/src/main/java/java/util/zip/ZipFile.java +++ b/luni/src/main/java/java/util/zip/ZipFile.java @@ -434,16 +434,23 @@ public class ZipFile implements Closeable, ZipConstants { * collisions.) * * <p>We could support mark/reset, but we don't currently need them. + * + * @hide */ - static class RAFStream extends InputStream { + public static class RAFStream extends InputStream { private final RandomAccessFile sharedRaf; private long endOffset; private long offset; - public RAFStream(RandomAccessFile raf, long initialOffset) throws IOException { + + public RAFStream(RandomAccessFile raf, long initialOffset, long endOffset) { sharedRaf = raf; offset = initialOffset; - endOffset = raf.length(); + this.endOffset = endOffset; + } + + public RAFStream(RandomAccessFile raf, long initialOffset) throws IOException { + this(raf, initialOffset, raf.length()); } @Override public int available() throws IOException { @@ -491,7 +498,8 @@ public class ZipFile implements Closeable, ZipConstants { } } - static class ZipInflaterInputStream extends InflaterInputStream { + /** @hide */ + public static class ZipInflaterInputStream extends InflaterInputStream { private final ZipEntry entry; private long bytesRead = 0; diff --git a/luni/src/main/native/Register.cpp b/luni/src/main/native/Register.cpp index 15f6953..68c59c3 100644 --- a/luni/src/main/native/Register.cpp +++ b/luni/src/main/native/Register.cpp @@ -49,6 +49,7 @@ jint JNI_OnLoad(JavaVM* vm, void*) { REGISTER(register_java_nio_ByteOrder); REGISTER(register_java_nio_charset_Charsets); REGISTER(register_java_text_Bidi); + REGISTER(register_java_util_jar_StrictJarFile); REGISTER(register_java_util_regex_Matcher); REGISTER(register_java_util_regex_Pattern); REGISTER(register_java_util_zip_Adler32); diff --git a/luni/src/main/native/java_util_jar_StrictJarFile.cpp b/luni/src/main/native/java_util_jar_StrictJarFile.cpp new file mode 100644 index 0000000..7611749 --- /dev/null +++ b/luni/src/main/native/java_util_jar_StrictJarFile.cpp @@ -0,0 +1,173 @@ +/* + * 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. + */ + +#define LOG_TAG "StrictJarFile" + +#include <string> + +#include "JNIHelp.h" +#include "JniConstants.h" +#include "ScopedLocalRef.h" +#include "ScopedUtfChars.h" +#include "UniquePtr.h" +#include "jni.h" +#include "ziparchive/zip_archive.h" +#include "cutils/log.h" + +static void throwIoException(JNIEnv* env, const int32_t errorCode) { + jniThrowException(env, "java/io/IOException", ErrorCodeString(errorCode)); +} + +static jobject newZipEntry(JNIEnv* env, const ZipEntry& entry, jstring entryName, + const uint16_t nameLength) { + ScopedLocalRef<jclass> zipEntryClass(env, env->FindClass("java/util/zip/ZipEntry")); + const jmethodID zipEntryCtor = env->GetMethodID(zipEntryClass.get(), "<init>", + "(Ljava/lang/String;Ljava/lang/String;JJJIII[BIJJ)V"); + + return env->NewObject(zipEntryClass.get(), + zipEntryCtor, + entryName, + NULL, // comment + static_cast<jlong>(entry.crc32), + static_cast<jlong>(entry.compressed_length), + static_cast<jlong>(entry.uncompressed_length), + static_cast<jint>(entry.method), + static_cast<jint>(0), // time + static_cast<jint>(0), // modData + NULL, // byte[] extra + static_cast<jint>(nameLength), + static_cast<jlong>(-1), // local header offset + static_cast<jlong>(entry.offset)); +} + +static jlong StrictJarFile_nativeOpenJarFile(JNIEnv* env, jobject, jstring fileName) { + ScopedUtfChars fileChars(env, fileName); + if (fileChars.c_str() == NULL) { + return static_cast<jlong>(-1); + } + + ZipArchiveHandle handle; + int32_t error = OpenArchive(fileChars.c_str(), &handle); + if (error) { + throwIoException(env, error); + return static_cast<jlong>(-1); + } + + return reinterpret_cast<jlong>(handle); +} + +class IterationHandle { + public: + IterationHandle(const char* prefix) : + cookie_(NULL), prefix_(strdup(prefix)) { + } + + void** CookieAddress() { + return &cookie_; + } + + const char* Prefix() const { + return prefix_; + } + + ~IterationHandle() { + free(prefix_); + } + + private: + void* cookie_; + char* prefix_; +}; + + +static jlong StrictJarFile_nativeStartIteration(JNIEnv* env, jobject, jlong nativeHandle, + jstring prefix) { + ScopedUtfChars prefixChars(env, prefix); + if (prefixChars.c_str() == NULL) { + return static_cast<jlong>(-1); + } + + IterationHandle* handle = new IterationHandle(prefixChars.c_str()); + int32_t error = 0; + if (prefixChars.size() == 0) { + error = StartIteration(reinterpret_cast<ZipArchiveHandle>(nativeHandle), + handle->CookieAddress(), NULL); + } else { + error = StartIteration(reinterpret_cast<ZipArchiveHandle>(nativeHandle), + handle->CookieAddress(), handle->Prefix()); + } + + if (error) { + throwIoException(env, error); + return static_cast<jlong>(-1); + } + + return reinterpret_cast<jlong>(handle); +} + +static jobject StrictJarFile_nativeNextEntry(JNIEnv* env, jobject, jlong iterationHandle) { + ZipEntry data; + ZipEntryName entryName; + + IterationHandle* handle = reinterpret_cast<IterationHandle*>(iterationHandle); + const int32_t error = Next(*handle->CookieAddress(), &data, &entryName); + if (error) { + delete handle; + return NULL; + } + + UniquePtr<char[]> entryNameCString(new char[entryName.name_length + 1]); + memcpy(entryNameCString.get(), entryName.name, entryName.name_length); + entryNameCString[entryName.name_length] = '\0'; + ScopedLocalRef<jstring> entryNameString(env, env->NewStringUTF(entryNameCString.get())); + + return newZipEntry(env, data, entryNameString.get(), entryName.name_length); +} + +static jobject StrictJarFile_nativeFindEntry(JNIEnv* env, jobject, jlong nativeHandle, + jstring entryName) { + ScopedUtfChars entryNameChars(env, entryName); + if (entryNameChars.c_str() == NULL) { + return NULL; + } + + ZipEntry data; + const int32_t error = FindEntry(reinterpret_cast<ZipArchiveHandle>(nativeHandle), + entryNameChars.c_str(), &data); + if (error) { + return NULL; + } + + return newZipEntry(env, data, entryName, entryNameChars.size()); +} + +static void StrictJarFile_nativeClose(JNIEnv*, jobject, jlong nativeHandle) { + CloseArchive(reinterpret_cast<ZipArchiveHandle>(nativeHandle)); +} + +static JNINativeMethod gMethods[] = { + NATIVE_METHOD(StrictJarFile, nativeOpenJarFile, "(Ljava/lang/String;)J"), + NATIVE_METHOD(StrictJarFile, nativeStartIteration, "(JLjava/lang/String;)J"), + NATIVE_METHOD(StrictJarFile, nativeNextEntry, "(J)Ljava/util/zip/ZipEntry;"), + NATIVE_METHOD(StrictJarFile, nativeFindEntry, "(JLjava/lang/String;)Ljava/util/zip/ZipEntry;"), + NATIVE_METHOD(StrictJarFile, nativeClose, "(J)V"), +}; + +void register_java_util_jar_StrictJarFile(JNIEnv* env) { + jniRegisterNativeMethods(env, "java/util/jar/StrictJarFile", gMethods, NELEM(gMethods)); + +} diff --git a/luni/src/main/native/sub.mk b/luni/src/main/native/sub.mk index 9e1a05b..4e10cc7 100644 --- a/luni/src/main/native/sub.mk +++ b/luni/src/main/native/sub.mk @@ -28,6 +28,7 @@ LOCAL_SRC_FILES := \ java_nio_ByteOrder.cpp \ java_nio_charset_Charsets.cpp \ java_text_Bidi.cpp \ + java_util_jar_StrictJarFile.cpp \ java_util_regex_Matcher.cpp \ java_util_regex_Pattern.cpp \ java_util_zip_Adler32.cpp \ @@ -60,7 +61,8 @@ LOCAL_C_INCLUDES += \ external/icu4c/common \ external/icu4c/i18n \ external/openssl/include \ - external/zlib + external/zlib \ + system/core/include LOCAL_STATIC_LIBRARIES += \ libfdlibm diff --git a/luni/src/test/java/libcore/java/util/jar/StrictJarFileTest.java b/luni/src/test/java/libcore/java/util/jar/StrictJarFileTest.java new file mode 100644 index 0000000..0b194f5 --- /dev/null +++ b/luni/src/test/java/libcore/java/util/jar/StrictJarFileTest.java @@ -0,0 +1,180 @@ +/* + * Copyright (C) 2013 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package libcore.java.util.jar; + +import junit.framework.TestCase; +import tests.support.resource.Support_Resources; +import java.io.File; +import java.io.IOException; +import java.nio.charset.Charset; +import java.util.HashMap; +import java.util.Iterator; +import java.util.jar.StrictJarFile; +import java.util.zip.ZipEntry; +import libcore.io.Streams; + +public class StrictJarFileTest extends TestCase { + + // A well formed jar file with 6 entries. + private static final String JAR_1 = "hyts_patch.jar"; + + private File resources; + + @Override + protected void setUp() { + resources = Support_Resources.createTempFolder(); + } + + public void testConstructor() throws Exception { + try { + StrictJarFile jarFile = new StrictJarFile("Wrong.file"); + fail("Should throw IOException"); + } catch (IOException e) { + // expected + } + + Support_Resources.copyFile(resources, null, JAR_1); + String fileName = (new File(resources, JAR_1)).getCanonicalPath(); + StrictJarFile jarFile = new StrictJarFile(fileName); + jarFile.close(); + } + + public void testIteration() throws Exception { + Support_Resources.copyFile(resources, null, JAR_1); + StrictJarFile jarFile = new StrictJarFile(new File(resources, JAR_1).getAbsolutePath()); + + Iterator<ZipEntry> it = jarFile.iterator(); + HashMap<String, ZipEntry> entries = new HashMap<String, ZipEntry>(); + while (it.hasNext()) { + final ZipEntry ze = it.next(); + entries.put(ze.getName(), ze); + } + + assertEquals(6, entries.size()); + assertTrue(entries.containsKey("META-INF/")); + + assertTrue(entries.containsKey("META-INF/MANIFEST.MF")); + ZipEntry ze = entries.get("META-INF/MANIFEST.MF"); + assertEquals(62, ze.getSize()); + assertEquals(ZipEntry.DEFLATED, ze.getMethod()); + assertEquals(61, ze.getCompressedSize()); + + assertTrue(entries.containsKey("Blah.txt")); + ze = entries.get("Blah.txt"); + assertEquals(4, ze.getSize()); + assertEquals(ZipEntry.DEFLATED, ze.getMethod()); + assertEquals(6, ze.getCompressedSize()); + assertEquals("Blah", new String(Streams.readFully(jarFile.getInputStream(ze)), + Charset.forName("UTF-8"))); + + assertTrue(entries.containsKey("foo/")); + assertTrue(entries.containsKey("foo/bar/")); + assertTrue(entries.containsKey("foo/bar/A.class")); + ze = entries.get("foo/bar/A.class"); + assertEquals(311, ze.getSize()); + assertEquals(ZipEntry.DEFLATED, ze.getMethod()); + assertEquals(225, ze.getCompressedSize()); + } + + public void testFindEntry() throws Exception { + Support_Resources.copyFile(resources, null, JAR_1); + StrictJarFile jarFile = new StrictJarFile(new File(resources, JAR_1).getAbsolutePath()); + + assertNull(jarFile.findEntry("foobar")); + assertNull(jarFile.findEntry("blah.txt")); + assertNotNull(jarFile.findEntry("Blah.txt")); + final ZipEntry ze = jarFile.findEntry("Blah.txt"); + assertEquals(4, ze.getSize()); + assertEquals(ZipEntry.DEFLATED, ze.getMethod()); + assertEquals(6, ze.getCompressedSize()); + assertEquals("Blah", new String(Streams.readFully(jarFile.getInputStream(ze)), + Charset.forName("UTF-8"))); + } + + public void testGetManifest() throws Exception { + Support_Resources.copyFile(resources, null, JAR_1); + StrictJarFile jarFile = new StrictJarFile(new File(resources, JAR_1).getAbsolutePath()); + + assertNotNull(jarFile.getManifest()); + assertEquals("1.4.2 (IBM Corporation)", jarFile.getManifest().getMainAttributes().getValue("Created-By")); + } + + public void testJarSigning_wellFormed() throws IOException { + Support_Resources.copyFile(resources, null, "Integrate.jar"); + StrictJarFile jarFile = new StrictJarFile(new File(resources, "Integrate.jar").getAbsolutePath()); + Iterator<ZipEntry> entries = jarFile.iterator(); + while (entries.hasNext()) { + ZipEntry zipEntry = entries.next(); + jarFile.getInputStream(zipEntry).skip(Long.MAX_VALUE); + if ("Test.class".equals(zipEntry.getName())) { + assertNotNull(jarFile.getCertificates(zipEntry)); + } + } + } + + public void testJarSigning_fudgedEntry() throws IOException { + Support_Resources.copyFile(resources, null, "Integrate.jar"); + StrictJarFile jarFile = new StrictJarFile( + new File(resources, "Integrate.jar").getAbsolutePath()); + + ZipEntry ze = jarFile.findEntry("Test.class"); + jarFile.getInputStream(ze).skip(Long.MAX_VALUE); + + // Fudge the size so that certificates do not match. + ze.setSize(ze.getSize() - 1); + try { + jarFile.getInputStream(ze).skip(Long.MAX_VALUE); + fail(); + } catch (SecurityException expected) { + } + } + + public void testJarSigning_modifiedClass() throws IOException { + Support_Resources.copyFile(resources, null, "Modified_Class.jar"); + StrictJarFile jarFile = new StrictJarFile( + new File(resources, "Modified_Class.jar").getAbsolutePath()); + + ZipEntry ze = jarFile.findEntry("Test.class"); + try { + jarFile.getInputStream(ze).skip(Long.MAX_VALUE); + fail(); + } catch (SecurityException expected) { + } + } + + public void testJarSigning_brokenMainAttributes() throws Exception { + assertThrowsOnInit("Modified_Manifest_MainAttributes.jar"); + } + + public void testJarSigning_brokenEntryAttributes() throws Exception { + assertThrowsOnInit("Modified_Manifest_EntryAttributes.jar"); + } + + public void testJarSigning_brokenSignatureFile() throws Exception { + assertThrowsOnInit("Modified_SF_EntryAttributes.jar"); + } + + private void assertThrowsOnInit(String name) throws Exception { + Support_Resources.copyFile(resources, null, name); + try { + StrictJarFile jarFile = new StrictJarFile( + new File(resources, name).getAbsolutePath()); + fail(); + } catch (SecurityException expected) { + } + } +} |