2003/09/29 01:41:51
authoremile <emile@xwt.org>
Fri, 30 Jan 2004 07:38:42 +0000 (07:38 +0000)
committeremile <emile@xwt.org>
Fri, 30 Jan 2004 07:38:42 +0000 (07:38 +0000)
darcs-hash:20040130073842-22ae6-befbd03d020687eb2de18373cfea0654ad66ea71.gz

src/org/xwt/Res.java
src/org/xwt/util/CAB.java

index 862b12d..01856f2 100644 (file)
@@ -173,7 +173,7 @@ public abstract class Res extends JS {
             return ((i & 0xff) << 24) | ((i & 0xff00) << 8) | ((i & 0xff0000) >>> 8) | (i >>> 24);
         }
         public InputStream getInputStream(String path) throws IOException {
-            InputStream is = parent.getInputStream();
+            /* InputStream is = parent.getInputStream();
             byte[] scan = new byte[4];
             int ofs = 0;
             for(int i=0; i<2; i++)  {
@@ -189,6 +189,14 @@ public abstract class Res extends JS {
             }
             Log.log(this, "found MSCF header at offset " + ofs);
             return org.xwt.util.CAB.getFileInputStream(is, path, true);
+            */
+            try {
+               return org.xwt.util.CAB.getFileInputStream(is, 2, path);
+            } catch (EOFException eof) {
+               throw new JS.Exn("MSCF header tag not found in file");
+            } catch (EOFException eof) {
+               throw new JS.Exn("IOException while reading file");
+            }
         }
     }
 
index 529bdae..b76337d 100644 (file)
@@ -10,14 +10,29 @@ import java.math.*;
 public class CAB {
 
     /** reads a CAB file, parses it, and returns an InputStream representing the named file */
-    public static InputStream getFileInputStream(InputStream is, String fileName, boolean mscfAlreadyConsumed) throws IOException {
+    public static InputStream getFileInputStream(InputStream is, String fileName) throws IOException, EOFException {
+      return getFileInputStream(is, 0, fileName);
+    }
+
+    public static InputStream getFileInputStream(InputStream is, int skipHeaders, String fileName) throws IOException, EOFException {
         DataInputStream dis = new DataInputStream(is);
         CFHEADER h = new CFHEADER();
-        h.read(dis, mscfAlreadyConsumed);
+
+        while (skipHeaders > 0) { CFHEADER.seekMSCF(dis); skipHeaders--; }
+
+        try {
+         h.read(dis);
+        } catch (CFHEADER.BogusHeaderException bhe) {
+         throw new EOFException();
+        }
 
         for(int i=0; i<h.folders.length; i++) {
             CFFOLDER f = new CFFOLDER(h);
-            f.read(dis);
+            try {
+               f.read(dis);
+            } catch (CFFOLDER.UnsupportedCompressionTypeException ucte) {
+               throw new IOException(ucte.toString());
+            }
         }
 
         for(int i=0; i<h.files.length; i++) {
@@ -142,13 +157,8 @@ public class CAB {
         }
 
         /** fills in all fields in the header and positions the stream at the first folder */
-        public void read(DataInputStream dis, boolean mscfAlreadyConsumed) throws IOException {
-            if (!mscfAlreadyConsumed) {
-                if (dis.readByte() != 0x4d) throw new CABException("First byte of header signature malformed");
-                if (dis.readByte() != 0x53) throw new CABException("Second byte of header signature malformed");
-                if (dis.readByte() != 0x43) throw new CABException("Third byte of header signature malformed");
-                if (dis.readByte() != 0x46) throw new CABException("Fourth byte of header signature malformed");
-            }
+        public void read(DataInputStream dis) throws IOException, BogusHeaderException {
+            seekMSCF(dis);
             dis.readFully(reserved1);
 
             byte[] headerHashable = new byte[28];
@@ -170,6 +180,10 @@ public class CAB {
             setID = readLittleShort(hhis);
             indexInCabinetSet = readLittleShort(hhis);
 
+            if (offsetOfFirstCFFILEEntry < 0 || fileSize < 0) {
+               throw new BogusHeaderException();
+            }
+
             if (hasReserved) {
                 perCabinetReservedArea = new byte[readLittleShort(dis)];
                 perCFFOLDERReservedSize = dis.readByte();
@@ -177,19 +191,55 @@ public class CAB {
                 if (perCabinetReservedArea.length > 0)
                     dis.readFully(perCabinetReservedArea);
             }
-            if (prevCAB) {
-                previousCabinet = readZeroTerminatedString(dis);
-                previousDisk = readZeroTerminatedString(dis);
-            }
-            if (nextCAB) {
-                nextCabinet = readZeroTerminatedString(dis);
-                nextDisk = readZeroTerminatedString(dis);
+
+            try {
+               if (prevCAB) {
+                   previousCabinet = readZeroTerminatedString(dis);
+                   previousDisk = readZeroTerminatedString(dis);
+               }
+               if (nextCAB) {
+                   nextCabinet = readZeroTerminatedString(dis);
+                   nextDisk = readZeroTerminatedString(dis);
+               }
+            } catch (ArrayIndexOutOfBoundsException e) {
+               throw new BogusHeaderException();
             }
         }
+
+        public static void seekMSCF(DataInputStream dis) throws EOFException, IOException
+        {
+           int state;
+           // skip up to and including the 'MSCF' signature
+           state = 0;
+           while (state != 4) {
+              // M
+              while (state == 0 && dis.readByte() != 0x4D) { }
+              state = 1;
+              // S
+              switch (dis.readByte()) {
+                 case 0x53 : state = 2; break;
+                 case 0x4D : state = 1; continue;
+                 default :   state = 0; continue;
+              }
+              // C
+              if (dis.readByte() == 0x43) { state = 3; }
+              else { state = 0; continue; }
+              // F
+              if (dis.readByte() == 0x46) { state = 4; }
+              else { state = 0; }
+           }
+        }
+
+        public static class BogusHeaderException extends Exception {}
     }
 
     /** Encapsulates a CFFOLDER entry */
     public static class CFFOLDER {
+        public static final int COMPRESSION_NONE      = 0;
+        public static final int COMPRESSION_MSZIP     = 1;
+        public static final int COMPRESSION_QUANTUM   = 2;
+        public static final int COMPRESSION_LZX       = 3;
+
         int      firstBlockOffset = 0;          // offset of first data block within this folder
         int      numBlocks = 0;                 // number of data blocks
         int      compressionType = 0;           // compression type for this folder
@@ -203,19 +253,48 @@ public class CAB {
 
         public String toString() {
             return "[ CAB CFFOLDER, " + numBlocks + " data blocks, compression type " +
-                (compressionType == 1 ? "MSZIP" : "unknown") +
+                compressionName(compressionType) +
                 ", " + reservedArea.length + " bytes of reserved data ]";
         }
 
-        public void read(DataInputStream dis) throws IOException {
+        public void read(DataInputStream dis) throws IOException, UnsupportedCompressionTypeException {
             firstBlockOffset = readLittleInt(dis);
             numBlocks = readLittleShort(dis);
-            compressionType = readLittleShort(dis);
+            compressionType = readLittleShort(dis) & 0x000F;
+            if (compressionType != COMPRESSION_MSZIP) {
+               throw new UnsupportedCompressionTypeException(compressionType);
+            }
             reservedArea = new byte[header.perCFFOLDERReservedSize];
             if (reservedArea.length > 0) dis.readFully(reservedArea);
             indexInCFHEADER = header.readCFFOLDERs++;
             header.folders[indexInCFHEADER] = this;
         }
+
+        public static String compressionName(int type) {
+            switch (type) {
+               case COMPRESSION_NONE:
+                  return "NONE";
+               case COMPRESSION_MSZIP:
+                  return "MSZIP";
+               case COMPRESSION_QUANTUM:
+                  return "QUANTUM";
+               case COMPRESSION_LZX:
+                  return "LZX";
+               default:
+                  return "<Unknown type " + type + ">";
+            }
+        }
+
+        public static class UnsupportedCompressionTypeException extends Exception {
+            private int compressionType;
+
+            UnsupportedCompressionTypeException(int type) {
+               compressionType = type;
+            }
+            public String toString() {
+               return "UnsupportedCompressionTypeException: no support for compression type " + compressionName(compressionType);
+            }
+        }
     }
 
     /** Encapsulates a CFFILE entry */