jdk/src/share/classes/java/util/jar/JarInputStream.java
changeset 2 90ce3da70b43
child 5506 202f599c92aa
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/java/util/jar/JarInputStream.java	Sat Dec 01 00:00:00 2007 +0000
@@ -0,0 +1,220 @@
+/*
+ * Copyright 1997-2006 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.util.jar;
+
+import java.util.zip.*;
+import java.io.*;
+import sun.security.util.ManifestEntryVerifier;
+
+/**
+ * The <code>JarInputStream</code> class is used to read the contents of
+ * a JAR file from any input stream. It extends the class
+ * <code>java.util.zip.ZipInputStream</code> with support for reading
+ * an optional <code>Manifest</code> entry. The <code>Manifest</code>
+ * can be used to store meta-information about the JAR file and its entries.
+ *
+ * @author  David Connelly
+ * @see     Manifest
+ * @see     java.util.zip.ZipInputStream
+ * @since   1.2
+ */
+public
+class JarInputStream extends ZipInputStream {
+    private Manifest man;
+    private JarEntry first;
+    private JarVerifier jv;
+    private ManifestEntryVerifier mev;
+
+
+    /**
+     * Creates a new <code>JarInputStream</code> and reads the optional
+     * manifest. If a manifest is present, also attempts to verify
+     * the signatures if the JarInputStream is signed.
+     * @param in the actual input stream
+     * @exception IOException if an I/O error has occurred
+     */
+    public JarInputStream(InputStream in) throws IOException {
+        this(in, true);
+    }
+
+    /**
+     * Creates a new <code>JarInputStream</code> and reads the optional
+     * manifest. If a manifest is present and verify is true, also attempts
+     * to verify the signatures if the JarInputStream is signed.
+     *
+     * @param in the actual input stream
+     * @param verify whether or not to verify the JarInputStream if
+     * it is signed.
+     * @exception IOException if an I/O error has occurred
+     */
+    public JarInputStream(InputStream in, boolean verify) throws IOException {
+        super(in);
+        JarEntry e = (JarEntry)super.getNextEntry();
+
+        if (e != null && e.getName().equalsIgnoreCase("META-INF/"))
+            e = (JarEntry)super.getNextEntry();
+
+        if (e != null && JarFile.MANIFEST_NAME.equalsIgnoreCase(e.getName())) {
+            man = new Manifest();
+            byte bytes[] = getBytes(new BufferedInputStream(this));
+            man.read(new ByteArrayInputStream(bytes));
+            //man.read(new BufferedInputStream(this));
+            closeEntry();
+            if (verify) {
+                jv = new JarVerifier(bytes);
+                mev = new ManifestEntryVerifier(man);
+            }
+            first = getNextJarEntry();
+        } else {
+            first = e;
+        }
+    }
+
+    private byte[] getBytes(InputStream is)
+        throws IOException
+    {
+        byte[] buffer = new byte[8192];
+        ByteArrayOutputStream baos = new ByteArrayOutputStream(2048);
+
+        int n;
+
+        baos.reset();
+        while ((n = is.read(buffer, 0, buffer.length)) != -1) {
+            baos.write(buffer, 0, n);
+        }
+        return baos.toByteArray();
+    }
+
+    /**
+     * Returns the <code>Manifest</code> for this JAR file, or
+     * <code>null</code> if none.
+     *
+     * @return the <code>Manifest</code> for this JAR file, or
+     *         <code>null</code> if none.
+     */
+    public Manifest getManifest() {
+        return man;
+    }
+
+    /**
+     * Reads the next ZIP file entry and positions the stream at the
+     * beginning of the entry data. If verification has been enabled,
+     * any invalid signature detected while positioning the stream for
+     * the next entry will result in an exception.
+     * @exception ZipException if a ZIP file error has occurred
+     * @exception IOException if an I/O error has occurred
+     * @exception SecurityException if any of the jar file entries
+     *         are incorrectly signed.
+     */
+    public ZipEntry getNextEntry() throws IOException {
+        JarEntry e;
+        if (first == null) {
+            e = (JarEntry)super.getNextEntry();
+        } else {
+            e = first;
+            first = null;
+        }
+        if (jv != null && e != null) {
+            // At this point, we might have parsed all the meta-inf
+            // entries and have nothing to verify. If we have
+            // nothing to verify, get rid of the JarVerifier object.
+            if (jv.nothingToVerify() == true) {
+                jv = null;
+                mev = null;
+            } else {
+                jv.beginEntry(e, mev);
+            }
+        }
+        return e;
+    }
+
+    /**
+     * Reads the next JAR file entry and positions the stream at the
+     * beginning of the entry data. If verification has been enabled,
+     * any invalid signature detected while positioning the stream for
+     * the next entry will result in an exception.
+     * @return the next JAR file entry, or null if there are no more entries
+     * @exception ZipException if a ZIP file error has occurred
+     * @exception IOException if an I/O error has occurred
+     * @exception SecurityException if any of the jar file entries
+     *         are incorrectly signed.
+     */
+    public JarEntry getNextJarEntry() throws IOException {
+        return (JarEntry)getNextEntry();
+    }
+
+    /**
+     * Reads from the current JAR file entry into an array of bytes.
+     * If <code>len</code> is not zero, the method
+     * blocks until some input is available; otherwise, no
+     * bytes are read and <code>0</code> is returned.
+     * If verification has been enabled, any invalid signature
+     * on the current entry will be reported at some point before the
+     * end of the entry is reached.
+     * @param b the buffer into which the data is read
+     * @param off the start offset in the destination array <code>b</code>
+     * @param len the maximum number of bytes to read
+     * @return the actual number of bytes read, or -1 if the end of the
+     *         entry is reached
+     * @exception  NullPointerException If <code>b</code> is <code>null</code>.
+     * @exception  IndexOutOfBoundsException If <code>off</code> is negative,
+     * <code>len</code> is negative, or <code>len</code> is greater than
+     * <code>b.length - off</code>
+     * @exception ZipException if a ZIP file error has occurred
+     * @exception IOException if an I/O error has occurred
+     * @exception SecurityException if any of the jar file entries
+     *         are incorrectly signed.
+     */
+    public int read(byte[] b, int off, int len) throws IOException {
+        int n;
+        if (first == null) {
+            n = super.read(b, off, len);
+        } else {
+            n = -1;
+        }
+        if (jv != null) {
+            jv.update(n, b, off, len, mev);
+        }
+        return n;
+    }
+
+    /**
+     * Creates a new <code>JarEntry</code> (<code>ZipEntry</code>) for the
+     * specified JAR file entry name. The manifest attributes of
+     * the specified JAR file entry name will be copied to the new
+     * <CODE>JarEntry</CODE>.
+     *
+     * @param name the name of the JAR/ZIP file entry
+     * @return the <code>JarEntry</code> object just created
+     */
+    protected ZipEntry createZipEntry(String name) {
+        JarEntry e = new JarEntry(name);
+        if (man != null) {
+            e.attr = man.getAttributes(name);
+        }
+        return e;
+    }
+}