content: /*
   * 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.
   */
  
  package android.framework.util.jar;
  
  import java.io.ByteArrayOutputStream;
  import java.io.IOException;
  import java.io.InputStream;
  import java.io.OutputStream;
  import java.util.Locale;
  import java.util.zip.ZipEntry;
  import java.util.zip.ZipInputStream;
  
  /**
   * The input stream from which the JAR file to be read may be fetched. It is
   * used like the {@code ZipInputStream}.
   *
   * @see ZipInputStream
   */
  public class JarInputStream extends ZipInputStream {
  
      private Manifest manifest;
  
      private boolean eos = false;
  
      private JarEntry mEntry;
  
      private JarEntry jarEntry;
  
      private boolean isMeta;
  
      private JarVerifier verifier;
  
      private OutputStream verStream;
  
      /**
       * Constructs a new {@code JarInputStream} from an input stream.
       *
       * @param stream
       *            the input stream containing the JAR file.
       * @param verify
       *            if the file should be verified with a {@code JarVerifier}.
       * @throws IOException
       *             If an error occurs reading entries from the input stream.
       * @see ZipInputStream#ZipInputStream(InputStream)
       */
      public JarInputStream(InputStream stream, boolean verify) throws IOException {
          super(stream);
          if (verify) {
              verifier = new JarVerifier("JarInputStream");
          }
          if ((mEntry = getNextJarEntry()) == null) {
              return;
          }
          if (mEntry.getName().equalsIgnoreCase(JarFile.META_DIR)) {
              mEntry = null; // modifies behavior of getNextJarEntry()
              closeEntry();
              mEntry = getNextJarEntry();
          }
          if (mEntry.getName().equalsIgnoreCase(JarFile.MANIFEST_NAME)) {
              mEntry = null;
              manifest = new Manifest(this, verify);
              closeEntry();
              if (verify) {
                  verifier.setManifest(manifest);
                  if (manifest != null) {
                      verifier.mainAttributesEnd = manifest.getMainAttributesEnd();
                  }
              }
  
          } else {
              Attributes temp = new Attributes(3);
              temp.map.put("hidden", null);
              mEntry.setAttributes(temp);
              /*
               * if not from the first entry, we will not get enough
               * information,so no verify will be taken out.
               */
              verifier = null;
          }
      }
  
      /**
       * Constructs a new {@code JarInputStream} from an input stream.
       *
       * @param stream
       *            the input stream containing the JAR file.
       * @throws IOException
       *             If an error occurs reading entries from the input stream.
       * @see ZipInputStream#ZipInputStream(InputStream)
       */
      public JarInputStream(InputStream stream) throws IOException {
          this(stream, true);
      }
  
      /**
       * Returns the {@code Manifest} object associated with this {@code
       * JarInputStream} or {@code null} if no manifest entry exists.
       *
       * @return the MANIFEST specifying the contents of the JAR file.
       */
      public Manifest getManifest() {
          return manifest;
      }
  
      /**
       * Returns the next {@code JarEntry} contained in this stream or {@code
       * null} if no more entries are present.
       *
       * @return the next JAR entry.
       * @throws IOException
       *             if an error occurs while reading the entry.
       */
      public JarEntry getNextJarEntry() throws IOException {
          return (JarEntry) getNextEntry();
      }
  
      /**
       * Reads up to {@code length} of decompressed data and stores it in
       * {@code buffer} starting at {@code offset}.
       *
       * @param buffer
       *            Buffer to store into
       * @param offset
       *            offset in buffer to store at
       * @param length
       *            number of bytes to store
       * @return Number of uncompressed bytes read
       * @throws IOException
       *             if an IOException occurs.
       */
      @Override
      public int read(byte[] buffer, int offset, int length) throws IOException {
          if (mEntry != null) {
              return -1;
          }
          int r = super.read(buffer, offset, length);
          if (verStream != null && !eos) {
              if (r == -1) {
                  eos = true;
                  if (verifier != null) {
                      if (isMeta) {
                          verifier.addMetaEntry(jarEntry.getName(),
                                  ((ByteArrayOutputStream) verStream)
                                          .toByteArray());
                          try {
                              verifier.readCertificates();
                          } catch (SecurityException e) {
                              verifier = null;
                              throw e;
                          }
                      } else {
                          ((JarVerifier.VerifierEntry) verStream).verify();
                      }
                  }
              } else {
                  verStream.write(buffer, offset, r);
              }
          }
          return r;
      }
  
      /**
       * Returns the next {@code ZipEntry} contained in this stream or {@code
       * null} if no more entries are present.
       *
       * @return the next extracted ZIP entry.
       * @throws IOException
       *             if an error occurs while reading the entry.
       */
      @Override
      public ZipEntry getNextEntry() throws IOException {
          if (mEntry != null) {
              jarEntry = mEntry;
              mEntry = null;
              jarEntry.setAttributes(null);
          } else {
              jarEntry = (JarEntry) super.getNextEntry();
              if (jarEntry == null) {
                  return null;
              }
              if (verifier != null) {
                  isMeta = jarEntry.getName().toUpperCase(Locale.US).startsWith(JarFile.META_DIR);
                  if (isMeta) {
                      verStream = new ByteArrayOutputStream();
                  } else {
                      verStream = verifier.initEntry(jarEntry.getName());
                  }
              }
          }
          eos = false;
          return jarEntry;
      }
  
      @Override
      protected ZipEntry createZipEntry(String name) {
          JarEntry entry = new JarEntry(name);
          if (manifest != null) {
              entry.setAttributes(manifest.getAttributes(name));
          }
          return entry;
      }
  }
  