1. /*
  2. * @(#)JarInputStream.java 1.29 03/01/23
  3. *
  4. * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package java.util.jar;
  8. import java.util.zip.*;
  9. import java.io.*;
  10. import sun.security.util.ManifestEntryVerifier;
  11. /**
  12. * The <code>JarInputStream</code> class is used to read the contents of
  13. * a JAR file from any input stream. It extends the class
  14. * <code>java.util.zip.ZipInputStream</code> with support for reading
  15. * an optional <code>Manifest</code> entry. The <code>Manifest</code>
  16. * can be used to store meta-information about the JAR file and its entries.
  17. *
  18. * @author David Connelly
  19. * @version 1.29, 01/23/03
  20. * @see Manifest
  21. * @see java.util.zip.ZipInputStream
  22. * @since 1.2
  23. */
  24. public
  25. class JarInputStream extends ZipInputStream {
  26. private Manifest man;
  27. private JarEntry first;
  28. private JarVerifier jv;
  29. private ManifestEntryVerifier mev;
  30. /**
  31. * Creates a new <code>JarInputStream</code> and reads the optional
  32. * manifest. If a manifest is present, also attempts to verify
  33. * the signatures if the JarInputStream is signed.
  34. * @param in the actual input stream
  35. * @exception IOException if an I/O error has occurred
  36. */
  37. public JarInputStream(InputStream in) throws IOException {
  38. this(in, true);
  39. }
  40. /**
  41. * Creates a new <code>JarInputStream</code> and reads the optional
  42. * manifest. If a manifest is present and verify is true, also attempts
  43. * to verify the signatures if the JarInputStream is signed.
  44. *
  45. * @param in the actual input stream
  46. * @param verify whether or not to verify the JarInputStream if
  47. * it is signed.
  48. * @exception IOException if an I/O error has occurred
  49. */
  50. public JarInputStream(InputStream in, boolean verify) throws IOException {
  51. super(in);
  52. JarEntry e = (JarEntry)super.getNextEntry();
  53. if (e != null && e.getName().equalsIgnoreCase("META-INF/"))
  54. e = (JarEntry)super.getNextEntry();
  55. if (e != null && JarFile.MANIFEST_NAME.equalsIgnoreCase(e.getName())) {
  56. man = new Manifest();
  57. byte bytes[] = getBytes(new BufferedInputStream(this));
  58. man.read(new ByteArrayInputStream(bytes));
  59. //man.read(new BufferedInputStream(this));
  60. closeEntry();
  61. if (verify) {
  62. jv = new JarVerifier(man, bytes);
  63. mev = new ManifestEntryVerifier(man);
  64. }
  65. first = getNextJarEntry();
  66. } else {
  67. first = e;
  68. }
  69. }
  70. private byte[] getBytes(InputStream is)
  71. throws IOException
  72. {
  73. byte[] buffer = new byte[8192];
  74. ByteArrayOutputStream baos = new ByteArrayOutputStream(2048);
  75. int n;
  76. baos.reset();
  77. while ((n = is.read(buffer, 0, buffer.length)) != -1) {
  78. baos.write(buffer, 0, n);
  79. }
  80. return baos.toByteArray();
  81. }
  82. /**
  83. * Returns the <code>Manifest</code> for this JAR file, or
  84. * <code>null</code> if none.
  85. *
  86. * @return the <code>Manifest</code> for this JAR file, or
  87. * <code>null</code> if none.
  88. */
  89. public Manifest getManifest() {
  90. return man;
  91. }
  92. /**
  93. * Reads the next ZIP file entry and positions stream at the beginning
  94. * of the entry data.
  95. * @exception ZipException if a ZIP file error has occurred
  96. * @exception IOException if an I/O error has occurred
  97. */
  98. public ZipEntry getNextEntry() throws IOException {
  99. JarEntry e;
  100. if (first == null) {
  101. e = (JarEntry)super.getNextEntry();
  102. } else {
  103. e = first;
  104. first = null;
  105. }
  106. if (jv != null && e != null) {
  107. // At this point, we might have parsed all the meta-inf
  108. // entries and have nothing to verify. If we have
  109. // nothing to verify, get rid of the JarVerifier object.
  110. if (jv.nothingToVerify() == true) {
  111. jv = null;
  112. mev = null;
  113. } else {
  114. jv.beginEntry(e, mev);
  115. }
  116. }
  117. return e;
  118. }
  119. /**
  120. * Reads the next JAR file entry and positions the stream at the
  121. * beginning of the entry data.
  122. *
  123. * @return the next JAR file entry
  124. * @exception ZipException if a ZIP file error has occurred
  125. * @exception IOException if an I/O error has occurred
  126. */
  127. public JarEntry getNextJarEntry() throws IOException {
  128. return (JarEntry)getNextEntry();
  129. }
  130. /**
  131. * Reads from the current JAR file entry into an array of bytes.
  132. * Blocks until some input is available.
  133. * @param b the buffer into which the data is read
  134. * @param off the start offset of the data
  135. * @param len the maximum number of bytes to read
  136. * @return the actual number of bytes read, or -1 if the end of the
  137. * entry is reached
  138. * @exception ZipException if a ZIP file error has occurred
  139. * @exception IOException if an I/O error has occurred
  140. */
  141. public int read(byte[] b, int off, int len) throws IOException {
  142. int n;
  143. if (first == null) {
  144. n = super.read(b, off, len);
  145. } else {
  146. n = -1;
  147. }
  148. if (jv != null) {
  149. jv.update(n, b, off, len, mev);
  150. }
  151. return n;
  152. }
  153. /**
  154. * Creates a new <code>JarEntry</code> (<code>ZipEntry</code>) for the
  155. * specified JAR file entry name.
  156. *
  157. * @param name the name of the JAR/ZIP file entry
  158. * @return the <code>JarEntry</code> object just created
  159. */
  160. protected ZipEntry createZipEntry(String name) {
  161. JarEntry e = new JarEntry(name);
  162. if (man != null) {
  163. e.attr = man.getAttributes(name);
  164. }
  165. return e;
  166. }
  167. }