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