Casey Marshall wrote:
I do think it is a better approach than what we have now, though, so I
think it should go in.
I will try to take a look at the mainfest class next week. Maybe I can
spare a couple of hours and do it right. So please do not yet commit it.

Unfortunately I don't find the time to do that. So I suggest to commit it as it is. I attached the patch again...

OK. We can check this in now, though, regardless (beauty of CVS, etc.).
I think I have a fix for the "enumeration" issue (JarEntry aren't
reusable) too.

Here the suggested changelog entry:

2006-11-08  Marco Trudel <[EMAIL PROTECTED]>

* classpath/java/util/jar/JarFile.java
(readSignatures): Parse the manifest once and reuse that data. Also adds support for line breaks.
   (verifyHashes): Use the parsed manifest entry.
   (readManifestEntry): Removed.


Marco
Index: classpath/java/util/jar/JarFile.java
===================================================================
--- classpath/java/util/jar/JarFile.java        (revision 117867)
+++ classpath/java/util/jar/JarFile.java        (working copy)
@@ -68,6 +68,8 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipException;
 import java.util.zip.ZipFile;
@@ -599,6 +601,30 @@
         validCerts.clear();
       }
 
+    // Read the manifest into a HashMap (String fileName, String entry)
+    // The fileName might be split into multiple lines in the manifest.
+    // Such additional lines will start with a space.
+    InputStream in = super.getInputStream(super.getEntry(MANIFEST_NAME));
+    ByteArrayOutputStream baStream = new ByteArrayOutputStream();
+    byte[] ba = new byte[1024];
+    while(true)
+      {
+        int len = in.read(ba);
+        if(len < 0) break;
+        baStream.write(ba, 0, len);
+      }
+    in.close();
+
+    HashMap hmManifestEntries = new HashMap();
+    Pattern p = Pattern.compile("Name: (.+?\r?\n(?: .+?\r?\n)*)" +
+      ".+?-Digest: .+?\r?\n\r?\n");
+    Matcher m = p.matcher(baStream.toString());
+    while(m.find())
+      {
+        String fileName = m.group(1).replaceAll("\r?\n ?", "");
+        hmManifestEntries.put(fileName, m.group());
+      }
+
     // Phase 3: verify the signature file signatures against the manifest,
     // mapping the entry name to the target certificates.
     this.entryCerts = new HashMap();
@@ -614,7 +640,7 @@
             Map.Entry e2 = (Map.Entry) it2.next();
             String entryname = String.valueOf(e2.getKey());
             Attributes attr = (Attributes) e2.getValue();
-            if (verifyHashes(entryname, attr))
+            if (verifyHashes(entryname, attr, hmManifestEntries))
               {
                 if (DEBUG)
                   debug("entry " + entryname + " has certificates " + 
certificates);
@@ -727,34 +753,23 @@
    * @param entry The entry name.
    * @param attr The attributes from the signature file to verify.
    */
-  private boolean verifyHashes(String entry, Attributes attr)
+  private boolean verifyHashes(String entry, Attributes attr,
+    HashMap hmManifestEntries)
   {
     int verified = 0;
 
-    // The bytes for ENTRY's manifest entry, which are signed in the
-    // signature file.
-    byte[] entryBytes = null;
-    try
+    String stringEntry = (String)hmManifestEntries.get(entry);
+    if(stringEntry == null)
       {
-       ZipEntry e = super.getEntry(entry);
-       if (e == null)
-         {
-           if (DEBUG)
-             debug("verifyHashes: no entry '" + entry + "'");
-           return false;
-         }
-        entryBytes = readManifestEntry(e);
-      }
-    catch (IOException ioe)
-      {
-        if (DEBUG)
-          {
-            debug(ioe);
-            ioe.printStackTrace();
-          }
+        if (DEBUG) debug("could not find " + entry + " in manifest");
         return false;
       }
 
+    // The bytes for ENTRY's manifest entry, which are signed in the
+    // signature file.
+    byte[] entryBytes = stringEntry.getBytes();
+
+
     for (Iterator it = attr.entrySet().iterator(); it.hasNext(); )
       {
         Map.Entry e = (Map.Entry) it.next();
@@ -801,100 +816,6 @@
   }
 
   /**
-   * Read the raw bytes that comprise a manifest entry. We can't use the
-   * Manifest object itself, because that loses information (such as line
-   * endings, and order of entries).
-   */
-  private byte[] readManifestEntry(ZipEntry entry) throws IOException
-  {
-    InputStream in = super.getInputStream(super.getEntry(MANIFEST_NAME));
-    ByteArrayOutputStream out = new ByteArrayOutputStream();
-    byte[] target = ("Name: " + entry.getName()).getBytes();
-    int t = 0, c, prev = -1, state = 0, l = -1;
-
-    while ((c = in.read()) != -1)
-      {
-//         if (DEBUG)
-//           debug("read "
-//                 + (c == '\n' ? "\\n" : (c == '\r' ? "\\r" : 
String.valueOf((char) c)))
-//                 + " state=" + state + " prev="
-//                 + (prev == '\n' ? "\\n" : (prev == '\r' ? "\\r" : 
String.valueOf((char) prev)))
-//                 + " t=" + t + (t < target.length ? (" target[t]=" + (char) 
target[t]) : "")
-//                 + " l=" + l);
-        switch (state)
-          {
-
-          // Step 1: read until we find the "target" bytes: the start
-          // of the entry we need to read.
-          case 0:
-            if (((byte) c) != target[t])
-              t = 0;
-            else
-              {
-                t++;
-                if (t == target.length)
-                  {
-                    out.write(target);
-                    state = 1;
-                  }
-              }
-            break;
-
-          // Step 2: assert that there is a newline character after
-          // the "target" bytes.
-          case 1:
-            if (c != '\n' && c != '\r')
-              {
-                out.reset();
-                t = 0;
-                state = 0;
-              }
-            else
-              {
-                out.write(c);
-                state = 2;
-              }
-            break;
-
-          // Step 3: read this whole entry, until we reach an empty
-          // line.
-          case 2:
-            if (c == '\n')
-              {
-                out.write(c);
-                // NL always terminates a line.
-                if (l == 0 || (l == 1 && prev == '\r'))
-                  return out.toByteArray();
-                l = 0;
-              }
-            else
-              {
-                // Here we see a blank line terminated by a CR,
-                // followed by the next entry. Technically, `c' should
-                // always be 'N' at this point.
-                if (l == 1 && prev == '\r')
-                  return out.toByteArray();
-                out.write(c);
-                l++;
-              }
-            prev = c;
-            break;
-
-          default:
-            throw new RuntimeException("this statement should be unreachable");
-          }
-      }
-
-    // The last entry, with a single CR terminating the line.
-    if (state == 2 && prev == '\r' && l == 0)
-      return out.toByteArray();
-
-    // We should not reach this point, we didn't find the entry (or, possibly,
-    // it is the last entry and is malformed).
-    throw new IOException("could not find " + entry + " in manifest");
-  }
-
-  /**
    * A utility class that verifies jar entries as they are read.
    */
   private static class EntryInputStream extends FilterInputStream

Reply via email to