1. /*
  2. * @(#)JarFile.java 1.31 01/11/29
  3. *
  4. * Copyright 2002 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.io.*;
  9. import java.util.*;
  10. import java.util.zip.*;
  11. import java.security.cert.Certificate;
  12. import sun.security.util.ManifestEntryVerifier;
  13. /**
  14. * The <code>JarFile</code> class is used to read the contents of a JAR file
  15. * from any file that can be opened with <code>java.io.RandomAccessFile</code>.
  16. * It extends the class <code>java.util.zip.ZipFile</code> with support
  17. * for reading an optional <code>Manifest</code> entry. The
  18. * <code>Manifest</code> can be used to specify meta-information about the
  19. * JAR file and its entries.
  20. *
  21. * @author David Connelly
  22. * @version 1.31, 11/29/01
  23. * @see Manifest
  24. * @see java.util.zip.ZipFile
  25. * @see java.util.jar.JarEntry
  26. * @since JDK1.2
  27. */
  28. public
  29. class JarFile extends ZipFile {
  30. private Manifest man;
  31. private JarEntry manEntry;
  32. private boolean manLoaded;
  33. private JarVerifier jv;
  34. private boolean jvInitialized;
  35. private boolean verify;
  36. public static final String MANIFEST_NAME = "META-INF/MANIFEST.MF";
  37. /**
  38. * Creates a new <code>JarFile</code> to read from the specified
  39. * file <code>name</code>. The <code>JarFile</code> will be verified if
  40. * it is signed.
  41. * @param name the name of the JAR file to be opened for reading
  42. * @exception FileNotFoundException if the file could not be found
  43. * @exception IOException if an I/O error has occurred
  44. */
  45. public JarFile(String name) throws IOException {
  46. this(name, true);
  47. }
  48. /**
  49. * Creates a new <code>JarFile</code> to read from the specified
  50. * file <code>name</code>.
  51. * @param name the name of the JAR file to be opened for reading
  52. * @param verify whether or not to verify the JarFile if
  53. * it is signed.
  54. * @exception FileNotFoundException if the file could not be found
  55. * @exception IOException if an I/O error has occurred
  56. */
  57. public JarFile(String name, boolean verify) throws IOException {
  58. super(name);
  59. this.verify = verify;
  60. }
  61. /**
  62. * Creates a new <code>JarFile</code> to read from the specified
  63. * <code>File</code> object. The <code>JarFile</code> will be verified if
  64. * it is signed.
  65. * @param file the JAR file to be opened for reading
  66. * @exception FileNotFoundException if the file could not be found
  67. * @exception IOException if an I/O error has occurred
  68. */
  69. public JarFile(File file) throws IOException {
  70. this(file.getPath(), true);
  71. }
  72. /**
  73. * Creates a new <code>JarFile</code> to read from the specified
  74. * <code>File</code> object.
  75. * @param file the JAR file to be opened for reading
  76. * @param verify whether or not to verify the JarFile if
  77. * it is signed.
  78. * @exception FileNotFoundException if the file could not be found
  79. * @exception IOException if an I/O error has occurred
  80. */
  81. public JarFile(File file, boolean verify) throws IOException {
  82. this(file.getPath(), verify);
  83. }
  84. /**
  85. * Returns the JAR file manifest, or <code>null</code> if none.
  86. */
  87. public Manifest getManifest() throws IOException {
  88. if (!manLoaded) {
  89. // First look up manifest entry using standard name
  90. manEntry = getJarEntry(MANIFEST_NAME);
  91. if (manEntry == null) {
  92. // If not found, then iterate through all the "META-INF/"
  93. // entries to find a match.
  94. String[] names = getMetaInfEntryNames();
  95. if (names != null) {
  96. for (int i = 0; i < names.length; i++) {
  97. if (MANIFEST_NAME.equals(names[i].toUpperCase())) {
  98. manEntry = getJarEntry(names[i]);
  99. break;
  100. }
  101. }
  102. }
  103. }
  104. // If found then load the manifest
  105. if (manEntry != null) {
  106. if (verify) {
  107. byte[] b = getBytes(manEntry);
  108. man = new Manifest(new ByteArrayInputStream(b));
  109. jv = new JarVerifier(man, b);
  110. } else {
  111. man = new Manifest(super.getInputStream(manEntry));
  112. }
  113. }
  114. manLoaded = true;
  115. }
  116. return man;
  117. }
  118. private native String[] getMetaInfEntryNames();
  119. /**
  120. * Returns the <code>JarEntry</code> for the given entry name or
  121. * <code>null</code> if not found.
  122. *
  123. * @param name the JAR file entry name
  124. * @see java.util.jar.JarEntry
  125. */
  126. public JarEntry getJarEntry(String name) {
  127. return (JarEntry)getEntry(name);
  128. }
  129. public ZipEntry getEntry(String name) {
  130. ZipEntry ze = super.getEntry(name);
  131. if (ze != null) {
  132. return new JarFileEntry(ze);
  133. }
  134. return null;
  135. }
  136. public Enumeration entries() {
  137. final Enumeration enum = super.entries();
  138. return new Enumeration() {
  139. public boolean hasMoreElements() {
  140. return enum.hasMoreElements();
  141. }
  142. public Object nextElement() {
  143. ZipEntry ze = (ZipEntry)enum.nextElement();
  144. return new JarFileEntry(ze);
  145. }
  146. };
  147. }
  148. private class JarFileEntry extends JarEntry {
  149. JarFileEntry(ZipEntry ze) {
  150. super(ze);
  151. }
  152. public Attributes getAttributes() throws IOException {
  153. Manifest man = JarFile.this.getManifest();
  154. if (man != null) {
  155. return man.getAttributes(getName());
  156. } else {
  157. return null;
  158. }
  159. }
  160. public java.security.cert.Certificate[] getCertificates() {
  161. if (certs == null && jv != null) {
  162. Certificate[] cs = jv.getCerts(getName());
  163. if (cs != null) {
  164. certs = (Certificate[])cs.clone();
  165. }
  166. }
  167. return certs;
  168. }
  169. }
  170. /*
  171. * Initializes the verifier object by reading all the manifest
  172. * entries and passing them to the verifier.
  173. */
  174. private void initializeVerifier() {
  175. ManifestEntryVerifier mev = null;
  176. // Verify "META-INF/" entries...
  177. try {
  178. String[] names = getMetaInfEntryNames();
  179. if (names != null) {
  180. for (int i = 0; i < names.length; i++) {
  181. JarEntry e = getJarEntry(names[i]);
  182. if (!e.isDirectory()) {
  183. if (mev == null) {
  184. mev = new ManifestEntryVerifier(man);
  185. }
  186. byte[] b = getBytes(e);
  187. if (b != null && b.length > 0) {
  188. jv.beginEntry(e, mev);
  189. jv.update(b.length, b, 0, b.length, mev);
  190. jv.update(-1, null, 0, 0, mev);
  191. }
  192. }
  193. }
  194. }
  195. } catch (IOException ex) {
  196. // if we had an error parsing any blocks, just
  197. // treat the jar file as being unsigned
  198. jv = null;
  199. }
  200. // if after initializing the verifier we have nothing
  201. // signed, we null it out.
  202. if (jv != null) {
  203. jv.doneWithMeta();
  204. if (JarVerifier.debug != null) {
  205. JarVerifier.debug.println("done with meta!");
  206. }
  207. if (jv.nothingToVerify()) {
  208. if (JarVerifier.debug != null) {
  209. JarVerifier.debug.println("nothing to verify!");
  210. }
  211. jv = null;
  212. }
  213. }
  214. }
  215. /*
  216. * Reads all the bytes for a given entry. Used to process the
  217. * the META-INF files.
  218. */
  219. private byte[] getBytes(ZipEntry ze) throws IOException {
  220. byte[] b = new byte[(int)ze.getSize()];
  221. DataInputStream is = new DataInputStream(super.getInputStream(ze));
  222. is.readFully(b, 0, b.length);
  223. is.close();
  224. return b;
  225. }
  226. /**
  227. * Returns an input stream for reading the contents of the specified
  228. * ZIP file entry.
  229. * @param ze the zip file entry
  230. * @exception ZipException if a ZIP format error has occurred
  231. * @exception IOException if an I/O error has occurred
  232. */
  233. public synchronized InputStream getInputStream(ZipEntry ze)
  234. throws IOException
  235. {
  236. if (!manLoaded) {
  237. getManifest();
  238. }
  239. if (jv == null) {
  240. return super.getInputStream(ze);
  241. }
  242. if (!jvInitialized) {
  243. initializeVerifier();
  244. jvInitialized = true;
  245. // could be set to null after a call to
  246. // initializeVerifier if we have nothing to
  247. // verify
  248. if (jv == null)
  249. return super.getInputStream(ze);
  250. }
  251. // wrap a verifier stream around the real stream
  252. return new JarVerifier.VerifierStream(man, (JarEntry)ze,
  253. super.getInputStream(ze), jv);
  254. }
  255. }