Skip to content

Commit f0b7a96

Browse files
authored
Fix #1843 (java.util.zip.ZipException in HttpZipLocator) (#1842)
* HttpZipLocator:fix invalid code lengths set & invalid distance too far back ZipExceptions. * Get file name length and extra field length from local file header to calculate file data offset. * Make fields `byteBuf`, `charBuf`, `utf8Decoder` non-static because they are not thread-safe. * Surround streams with try-with-resources block.
1 parent 9bcfc46 commit f0b7a96

File tree

1 file changed

+36
-28
lines changed

1 file changed

+36
-28
lines changed

jme3-core/src/plugins/java/com/jme3/asset/plugins/HttpZipLocator.java

Lines changed: 36 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@
4242
import java.nio.ByteBuffer;
4343
import java.nio.CharBuffer;
4444
import java.nio.charset.CharacterCodingException;
45-
import java.nio.charset.Charset;
45+
import java.nio.charset.StandardCharsets;
4646
import java.nio.charset.CharsetDecoder;
4747
import java.nio.charset.CoderResult;
4848
import java.util.HashMap;
@@ -80,14 +80,9 @@ public class HttpZipLocator implements AssetLocator {
8080
private int tableLength;
8181
private HashMap<String, ZipEntry2> entries;
8282

83-
private static final ByteBuffer byteBuf = ByteBuffer.allocate(250);
84-
private static final CharBuffer charBuf = CharBuffer.allocate(250);
85-
private static final CharsetDecoder utf8Decoder;
86-
87-
static {
88-
Charset utf8 = Charset.forName("UTF-8");
89-
utf8Decoder = utf8.newDecoder();
90-
}
83+
private final ByteBuffer byteBuf = ByteBuffer.allocate(250);
84+
private final CharBuffer charBuf = CharBuffer.allocate(250);
85+
private final CharsetDecoder utf8Decoder = StandardCharsets.UTF_8.newDecoder();
9186

9287
private static class ZipEntry2 {
9388
String name;
@@ -96,6 +91,10 @@ private static class ZipEntry2 {
9691
int compSize;
9792
long crc;
9893
boolean deflate;
94+
// These fields will be fetched later from local file header,
95+
// once asset requested.
96+
Integer nameLength;
97+
Integer extraLength;
9998

10099
@Override
101100
public String toString(){
@@ -125,7 +124,7 @@ private static long getu32(byte[] b, int off) throws IOException{
125124
(((long)(b[off]&0xff)) << 24);
126125
}
127126

128-
private static String getUTF8String(byte[] b, int off, int len) throws CharacterCodingException {
127+
private String getUTF8String(byte[] b, int off, int len) throws CharacterCodingException {
129128
StringBuilder sb = new StringBuilder();
130129

131130
int read = 0;
@@ -143,7 +142,7 @@ private static String getUTF8String(byte[] b, int off, int len) throws Character
143142
byteBuf.flip();
144143

145144
// decode data in byteBuf
146-
CoderResult result = utf8Decoder.decode(byteBuf, charBuf, endOfInput);
145+
CoderResult result = utf8Decoder.decode(byteBuf, charBuf, endOfInput);
147146

148147
// If the result is not an underflow, it's an error
149148
// that cannot be handled.
@@ -234,10 +233,6 @@ private int readTableEntry(byte[] table, int offset) throws IOException{
234233
entry.compSize = get32(table, offset + ZipEntry.CENSIZ);
235234
entry.offset = get32(table, offset + ZipEntry.CENOFF);
236235

237-
// We want the offset in the file data:
238-
// move the offset forward to skip the LOC header.
239-
entry.offset += ZipEntry.LOCHDR + nameLen + extraLen;
240-
241236
entries.put(entry.name, entry);
242237

243238
return newOffset;
@@ -256,16 +251,15 @@ private void fillByteArray(byte[] array, InputStream source) throws IOException{
256251
}
257252

258253
private void readCentralDirectory() throws IOException{
259-
InputStream in = readData(tableOffset, tableLength);
260254
byte[] header = new byte[tableLength];
261-
262-
// Fix for "PK12 bug in town.zip": sometimes
263-
// not entire byte array will be read with InputStream.read()
264-
// (especially for big headers)
265-
fillByteArray(header, in);
255+
try (InputStream in = readData(tableOffset, tableLength)) {
256+
// Fix for "PK12 bug in town.zip": sometimes
257+
// not entire byte array will be read with InputStream.read()
258+
// (especially for big headers)
259+
fillByteArray(header, in);
266260

267261
// in.read(header);
268-
in.close();
262+
}
269263

270264
entries = new HashMap<String, ZipEntry2>(numEntries);
271265
int offset = 0;
@@ -292,10 +286,10 @@ private void readEndHeader() throws IOException{
292286
// In that case, we have to search for it.
293287
// Increase search space to 200 bytes
294288

295-
InputStream in = readData(Integer.MAX_VALUE, 200);
296289
byte[] header = new byte[200];
297-
fillByteArray(header, in);
298-
in.close();
290+
try (InputStream in = readData(Integer.MAX_VALUE, 200)) {
291+
fillByteArray(header, in);
292+
}
299293

300294
int offset = -1;
301295
for (int i = 200 - 22; i >= 0; i--){
@@ -323,9 +317,23 @@ public void load(URL url) throws IOException {
323317
readCentralDirectory();
324318
}
325319

326-
private InputStream openStream(ZipEntry2 entry) throws IOException{
327-
InputStream in = readData(entry.offset, entry.compSize);
328-
if (entry.deflate){
320+
private InputStream openStream(ZipEntry2 entry) throws IOException {
321+
if (entry.nameLength == null && entry.extraLength == null) {
322+
// Need to fetch local file header to obtain file name length
323+
// and extra field length.
324+
try (InputStream in = readData(entry.offset, ZipEntry.LOCHDR)) {
325+
byte[] localHeader = new byte[ZipEntry.LOCHDR];
326+
in.read(localHeader);
327+
entry.nameLength = get16(localHeader, ZipEntry.LOCNAM);
328+
entry.extraLength = get16(localHeader, ZipEntry.LOCEXT);
329+
}
330+
}
331+
332+
// We want the offset in the file data:
333+
// move the offset forward to skip the LOC header.
334+
int fileDataOffset = entry.offset + ZipEntry.LOCHDR + entry.nameLength + entry.extraLength;
335+
InputStream in = readData(fileDataOffset, entry.compSize);
336+
if (entry.deflate) {
329337
return new InflaterInputStream(in, new Inflater(true));
330338
}
331339
return in;

0 commit comments

Comments
 (0)