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