Author: j16sdiz
Date: 2009-03-03 05:00:59 +0000 (Tue, 03 Mar 2009)
New Revision: 25876
Modified:
trunk/freenet/src/freenet/clients/http/filter/PNGFilter.java
Log:
PNGFilter should throw DataFormatException
It was throwing ArrayIndexOutOfBoundException and NegativeIndexException for
invalid images.
Modified: trunk/freenet/src/freenet/clients/http/filter/PNGFilter.java
===================================================================
--- trunk/freenet/src/freenet/clients/http/filter/PNGFilter.java
2009-03-03 03:12:22 UTC (rev 25875)
+++ trunk/freenet/src/freenet/clients/http/filter/PNGFilter.java
2009-03-03 05:00:59 UTC (rev 25876)
@@ -8,6 +8,7 @@
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
+import java.io.EOFException;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
@@ -32,7 +33,7 @@
/**
* Content filter for PNG's. Only allows valid chunks (valid CRC, known chunk
type).
- *
+ *
* It can strip the timestamp and "text(.)*" chunks if asked to
*
* FIXME: validate chunk contents where possible.
@@ -42,31 +43,22 @@
private final boolean deleteText;
private final boolean deleteTimestamp;
private final boolean checkCRCs;
- static final byte[] pngHeader =
- {(byte) 137, (byte) 80, (byte) 78, (byte) 71, (byte) 13, (byte)
10, (byte) 26, (byte) 10};
+ static final byte[] pngHeader = { (byte) 137, (byte) 80, (byte) 78,
(byte) 71, (byte) 13, (byte) 10, (byte) 26,
+ (byte) 10 };
static final String[] HARMLESS_CHUNK_TYPES = {
- // http://www.w3.org/TR/PNG/
- "tRNS",
- "cHRM",
- "gAMA",
- "iCCP", // FIXME Embedded ICC profile: could this conceivably
cause a web lookup?
- "sBIT", // FIXME rather obscure ??
- "sRGB",
- "bKGD",
- "hIST",
- "pHYs",
- "sPLT",
- // APNG chunks (Firefox 3 will support APNG)
- // http://wiki.mozilla.org/APNG_Specification
- "acTL",
- "fcTL",
- "fdAT"
- // MNG isn't supported by Firefox and IE because of lack of
market demand. Big surprise
- // given nobody supports it! It is supported by Konqueror
though. Complex standard,
- // not worth it for the time being.
-
- // This might be a useful source of info too (e.g. on private
chunks):
- //
http://fresh.t-systems-sfr.com/unix/privat/pngcheck-2.3.0.tar.gz:a/pngcheck-2.3.0/pngcheck.c
+ // http://www.w3.org/TR/PNG/
+ "tRNS", "cHRM", "gAMA", "iCCP", // FIXME Embedded ICC profile:
could this conceivably cause a web lookup?
+ "sBIT", // FIXME rather obscure ??
+ "sRGB", "bKGD", "hIST", "pHYs", "sPLT",
+ // APNG chunks (Firefox 3 will support APNG)
+ // http://wiki.mozilla.org/APNG_Specification
+ "acTL", "fcTL", "fdAT"
+ // MNG isn't supported by Firefox and IE because of lack of market
demand. Big surprise
+ // given nobody supports it! It is supported by Konqueror though.
Complex standard,
+ // not worth it for the time being.
+
+ // This might be a useful source of info too (e.g. on private chunks):
+ //
http://fresh.t-systems-sfr.com/unix/privat/pngcheck-2.3.0.tar.gz:a/pngcheck-2.3.0/pngcheck.c
};
PNGFilter(boolean deleteText, boolean deleteTimestamp, boolean
checkCRCs) {
@@ -78,9 +70,9 @@
public Bucket readFilter(Bucket data, BucketFactory bf, String charset,
HashMap<String, String> otherParams,
FilterCallback cb) throws DataFilterException, IOException {
Bucket output = readFilter(data, bf, charset, otherParams, cb,
deleteText, deleteTimestamp, checkCRCs, null);
- if(output != null)
+ if (output != null)
return output;
- if(Logger.shouldLog(Logger.MINOR, this))
+ if (Logger.shouldLog(Logger.MINOR, this))
Logger.minor(this, "Need to modify PNG...");
Bucket filtered = bf.makeBucket(data.size());
OutputStream os = new
BufferedOutputStream(filtered.getOutputStream());
@@ -110,21 +102,21 @@
// Check the header
byte[] headerCheck = new byte[pngHeader.length];
dis.readFully(headerCheck);
- if(!Arrays.equals(headerCheck, pngHeader)) {
+ if (!Arrays.equals(headerCheck, pngHeader)) {
// Throw an exception
String message = l10n("invalidHeader");
String title = l10n("invalidHeaderTitle");
- throw new DataFilterException(title, title,
- "<p>" + message + "</p>", new
HTMLNode("p").addChild("#", message));
+ throw new DataFilterException(title, title,
"<p>" + message + "</p>", new HTMLNode("p").addChild("#",
+ message));
}
ByteArrayOutputStream baos = null;
DataOutputStream dos = null;
- if(output != null) {
+ if (output != null) {
baos = new ByteArrayOutputStream();
dos = new DataOutputStream(baos);
output.write(pngHeader);
- if(logMINOR)
+ if (logMINOR)
Logger.minor(this, "Writing the PNG
header to the output bucket");
}
@@ -135,149 +127,166 @@
boolean hasSeenIEND = false;
boolean hasSeenIDAT = false;
String lastChunkType = "";
-
- while(!finished) {
+
+ while (!finished) {
boolean skip = false;
- if(baos != null)
+ if (baos != null)
baos.reset();
String chunkTypeString = null;
// Length of the chunk
byte[] lengthBytes = new byte[4];
dis.readFully(lengthBytes);
-
- int length = ((lengthBytes[0] & 0xff) << 24) +
((lengthBytes[1] & 0xff) << 16) + ((lengthBytes[2] & 0xff) << 8) +
(lengthBytes[3] & 0xff);
- if(logMINOR)
+
+ int length = ((lengthBytes[0] & 0xff) << 24) +
((lengthBytes[1] & 0xff) << 16)
+ + ((lengthBytes[2] & 0xff) << 8) +
(lengthBytes[3] & 0xff);
+ if (logMINOR)
Logger.minor(this, "length " + length);
- if(dos != null)
+ if (dos != null)
dos.write(lengthBytes);
// Type of the chunk : Should match [a-zA-Z]{4}
dis.readFully(lengthBytes);
StringBuilder sb = new StringBuilder();
byte[] chunkTypeBytes = new byte[4];
- for(int i = 0; i < 4; i++) {
+ for (int i = 0; i < 4; i++) {
char val = (char) lengthBytes[i];
- if((val >= 65 && val <= 99) || (val >=
97 && val <= 122)) {
+ if ((val >= 65 && val <= 99) || (val >=
97 && val <= 122)) {
chunkTypeBytes[i] =
lengthBytes[i];
sb.append(val);
- } else {
+ } else {
String chunkName =
HexUtil.bytesToHex(lengthBytes, 0, 4);
- throwError("Unknown Chunk" ,
"The name of the chunk is invalid! (" + chunkName+")");
+ throwError("Unknown Chunk",
"The name of the chunk is invalid! (" + chunkName + ")");
}
}
chunkTypeString = sb.toString();
- if(logMINOR)
+ if (logMINOR)
Logger.minor(this, "name " +
chunkTypeString);
// Content of the chunk
byte[] chunkData = new byte[length];
dis.readFully(chunkData, 0, length);
- if(logMINOR)
- if(logDEBUG)
+ if (logMINOR)
+ if (logDEBUG)
Logger.minor(this, "data " +
(chunkData.length == 0 ? "null" : HexUtil.bytesToHex(chunkData)));
else
Logger.minor(this, "data " +
chunkData.length);
- if(dos != null)
+ if (dos != null)
dos.write(chunkTypeBytes);
- if(dos != null)
+ if (dos != null)
dos.write(chunkData);
// CRC of the chunk
byte[] crcLengthBytes = new byte[4];
dis.readFully(crcLengthBytes);
- if(dos != null)
+ if (dos != null)
dos.write(crcLengthBytes);
- if(checkCRCs) {
- long readCRC = (((crcLengthBytes[0] &
0xff) << 24) + ((crcLengthBytes[1] & 0xff) << 16) + ((crcLengthBytes[2] & 0xff)
<< 8) + (crcLengthBytes[3] & 0xff)) & 0x00000000ffffffffL;
+ if (checkCRCs) {
+ long readCRC = (((crcLengthBytes[0] &
0xff) << 24) + ((crcLengthBytes[1] & 0xff) << 16)
+ + ((crcLengthBytes[2] & 0xff)
<< 8) + (crcLengthBytes[3] & 0xff)) & 0x00000000ffffffffL;
CRC32 crc = new CRC32();
crc.update(chunkTypeBytes);
crc.update(chunkData);
long computedCRC = crc.getValue();
-
- if(readCRC != computedCRC) {
+
+ if (readCRC != computedCRC) {
skip = true;
- if(logMINOR)
- Logger.minor(this, "CRC
of the chunk " + chunkTypeString + " doesn't match (" +
Long.toHexString(readCRC) + " but should be " + Long.toHexString(computedCRC) +
")!");
+ if (logMINOR)
+ Logger.minor(this, "CRC
of the chunk " + chunkTypeString + " doesn't match ("
+ +
Long.toHexString(readCRC) + " but should be " + Long.toHexString(computedCRC)
+ + ")!");
}
}
boolean validChunkType = false;
-
- if(!skip && "IHDR".equals(chunkTypeString)) {
- if(hasSeenIHDR)
+
+ if (!skip && "IHDR".equals(chunkTypeString)) {
+ if (hasSeenIHDR)
throwError("Duplicate IHDR",
"Two IHDR chunks detected!!");
hasSeenIHDR = true;
validChunkType = true;
}
- if(!hasSeenIHDR)
+ if (!hasSeenIHDR)
throwError("No IHDR chunk!", "No IHDR
chunk!");
- if(!skip && "IEND".equals(chunkTypeString)) {
- if(hasSeenIEND) // XXX impossible code
path: it should have throwed as "IEND not last chunk"
+ if (!skip && "IEND".equals(chunkTypeString)) {
+ if (hasSeenIEND) // XXX impossible code
path: it should have throwed as "IEND not last chunk"
throwError("Two IEND chunks
detected!!", "Two IEND chunks detected!!");
hasSeenIEND = true;
validChunkType = true;
}
-
- if(!skip &&
"PLTE".equalsIgnoreCase(chunkTypeString)) {
- if(hasSeenIDAT)
+
+ if (!skip &&
"PLTE".equalsIgnoreCase(chunkTypeString)) {
+ if (hasSeenIDAT)
throwError("PLTE must be before
IDAT", "PLTE must be before IDAT");
validChunkType = true;
}
-
- if(!skip &&
"IDAT".equalsIgnoreCase(chunkTypeString)) {
- if(hasSeenIDAT &&
!"IDAT".equalsIgnoreCase(lastChunkType))
- throwError("Multiple IDAT
chunks must be consecutive!", "Multiple IDAT chunks must be consecutive!");
+
+ if (!skip &&
"IDAT".equalsIgnoreCase(chunkTypeString)) {
+ if (hasSeenIDAT &&
!"IDAT".equalsIgnoreCase(lastChunkType))
+ throwError("Multiple IDAT
chunks must be consecutive!",
+ "Multiple IDAT chunks
must be consecutive!");
hasSeenIDAT = true;
validChunkType = true;
}
-
- if(!validChunkType) {
- for(int
i=0;i<HARMLESS_CHUNK_TYPES.length;i++) {
-
if(HARMLESS_CHUNK_TYPES[i].equals(chunkTypeString))
+
+ if (!validChunkType) {
+ for (int i = 0; i <
HARMLESS_CHUNK_TYPES.length; i++) {
+ if
(HARMLESS_CHUNK_TYPES[i].equals(chunkTypeString))
validChunkType = true;
}
}
-
- if(dis.available() < 1) {
- if(!(hasSeenIEND && hasSeenIHDR))
+
+ if (dis.available() < 1) {
+ if (!(hasSeenIEND && hasSeenIHDR))
throwError("Missing IEND or
IHDR!", "Missing IEND or IHDR!");
finished = true;
}
- if("text".equalsIgnoreCase(chunkTypeString) ||
"itxt".equalsIgnoreCase(chunkTypeString)
- ||
"ztxt".equalsIgnoreCase(chunkTypeString)) {
- if(deleteText) skip = true;
- else validChunkType = true;
- } else if(deleteTimestamp &&
"time".equalsIgnoreCase(chunkTypeString)) {
- if(deleteTimestamp) skip = true;
- else validChunkType = true;
+ if ("text".equalsIgnoreCase(chunkTypeString) ||
"itxt".equalsIgnoreCase(chunkTypeString)
+ ||
"ztxt".equalsIgnoreCase(chunkTypeString)) {
+ if (deleteText)
+ skip = true;
+ else
+ validChunkType = true;
+ } else if (deleteTimestamp &&
"time".equalsIgnoreCase(chunkTypeString)) {
+ if (deleteTimestamp)
+ skip = true;
+ else
+ validChunkType = true;
}
-
- if(!validChunkType) {
- if(logMINOR)
- Logger.minor(this, "Skipping
unknown chunk type "+chunkTypeString);
- if(output == null)
+
+ if (!validChunkType) {
+ if (logMINOR)
+ Logger.minor(this, "Skipping
unknown chunk type " + chunkTypeString);
+ if (output == null)
return null;
skip = true;
}
-
- if(skip && output == null)
+
+ if (skip && output == null)
return null;
- else if(!skip && output != null) {
- if(logMINOR)
- Logger.minor(this, "Writing " +
chunkTypeString + " (" + baos.size() + ") to the output bucket");
+ else if (!skip && output != null) {
+ if (logMINOR)
+ Logger
+ .minor(this, "Writing "
+ chunkTypeString + " (" + baos.size()
+ + ") to the
output bucket");
baos.writeTo(output);
baos.flush();
}
lastChunkType = chunkTypeString;
}
- if(hasSeenIEND && dis.available() > 0)
+ if (hasSeenIEND && dis.available() > 0)
throwError("IEND not last chunk", "IEND not
last chunk");
-
+
dis.close();
+ } catch (ArrayIndexOutOfBoundsException e) {
+ throwError("ArrayIndexOutOfBoundsException while
filtering", "ArrayIndexOutOfBoundsException while filtering");
+ } catch (NegativeArraySizeException e) {
+ throwError("NegativeArraySizeException while
filtering", "NegativeArraySizeException while filtering");
+ } catch (EOFException e) {
+ throwError("EOF Exception while filtering", "EOF
Exception while filtering");
} finally {
Closer.close(dis);
Closer.close(bis);
@@ -304,13 +313,14 @@
final Bucket out = new FileBucket(fout, false, true, false,
false, false);
try {
Logger.setupStdoutLogging(Logger.MINOR, "");
- ContentFilter.FilterOutput output =
ContentFilter.filter(data, new ArrayBucketFactory(), "image/png", new
URI("http://127.0.0.1:8888/"), null);
+ ContentFilter.FilterOutput output =
ContentFilter.filter(data, new ArrayBucketFactory(), "image/png",
+ new URI("http://127.0.0.1:8888/"), null);
BucketTools.copy(output.data, out);
- } catch(IOException e) {
+ } catch (IOException e) {
System.out.println("Bucket error?: " + e.getMessage());
- } catch(URISyntaxException e) {
+ } catch (URISyntaxException e) {
System.out.println("Internal error: " + e.getMessage());
- } catch(InvalidThresholdException e) {
+ } catch (InvalidThresholdException e) {
} finally {
data.free();
}
@@ -319,14 +329,14 @@
private void throwError(String shortReason, String reason) throws
DataFilterException {
// Throw an exception
String message = "Invalid PNG";
- if(reason != null)
+ if (reason != null)
message += ' ' + reason;
- if(shortReason != null)
+ if (shortReason != null)
message += " - " + shortReason;
- DataFilterException e = new DataFilterException(shortReason,
shortReason,
- "<p>"+message+"</p>", new
HTMLNode("p").addChild("#", message));
- if(Logger.shouldLog(Logger.NORMAL, this))
- Logger.normal(this, "Throwing "+e, e);
+ DataFilterException e = new DataFilterException(shortReason,
shortReason, "<p>" + message + "</p>",
+ new HTMLNode("p").addChild("#", message));
+ if (Logger.shouldLog(Logger.NORMAL, this))
+ Logger.normal(this, "Throwing " + e, e);
throw e;
}
}
_______________________________________________
cvs mailing list
[email protected]
http://emu.freenetproject.org/cgi-bin/mailman/listinfo/cvs