packages feed

zip-archive 0.4.3 → 0.4.3.1

raw patch · 3 files changed

+85/−42 lines, 3 filesdep ~base

Dependency ranges changed: base

Files

changelog view
@@ -1,4 +1,19 @@-zip-archive 0.4.3+zip-archive 0.4.3.1++  * Use streaming decompress to identify extent of compressed data (#66).+    This fixes a problem that arises for local files with bit 3+    of the general purpose bit flag set. In this case, we don't+    get information up front about the size of the compressed+    data.  So how do we know where the compressed data ends?+    Previously, we tried to determine this by looking for the+    signature of the data descriptor. But the data descriptor doesn't+    always HAVE a signature, and it is also possible for signatures to+    occur accidentally in the compressed data itself (#65).+    Instead, we now use the streaming decompression interface from+    zlib's Internal module to identify where the compressed data+    ends. Fixes both #65 and #25.++    zip-archive 0.4.3    * Improve code for retrieving compressed data of unknown length (#63).     Do not assume we'll have the signature 0x08074b50 that is
src/Codec/Archive/Zip.hs view
@@ -101,8 +101,6 @@ import Data.Maybe (fromJust) #endif -import GHC.Int (Int64)- -- from bytestring import qualified Data.ByteString as S import qualified Data.ByteString.Lazy as B@@ -114,8 +112,11 @@  -- from zlib import qualified Codec.Compression.Zlib.Raw as Zlib+import qualified Codec.Compression.Zlib.Internal as ZlibInt import System.IO.Error (isAlreadyExistsError) +-- import Debug.Trace+ manySig :: Word32 -> Get a -> Get [a] manySig sig p = do     sig' <- lookAhead getWord32le@@ -768,7 +769,11 @@   getWord32le >>= ensure (== 0x04034b50)   skip 2  -- version   bitflag <- getWord16le-  skip 2  -- compressionMethod+  rawCompressionMethod <- getWord16le+  compressionMethod <- case rawCompressionMethod of+                        0 -> return NoCompression+                        8 -> return Deflate+                        _ -> fail $ "Unknown compression method " ++ show rawCompressionMethod   skip 2  -- last mod file time   skip 2  -- last mod file date   skip 4  -- crc32@@ -780,12 +785,13 @@   extraFieldLength <- getWord16le   skip (fromIntegral fileNameLength)  -- filename   skip (fromIntegral extraFieldLength) -- extra field-  compressedData <- if bitflag .&. 0O10 == 0+  compressedData <-+    if bitflag .&. 0O10 == 0       then getLazyByteString (fromIntegral compressedSize)       else -- If bit 3 of general purpose bit flag is set,            -- then we need to read until we get to the            -- data descriptor record.-           do raw <- getCompressedData+           do raw <- getCompressedData compressionMethod               sig <- lookAhead getWord32le               when (sig == 0x08074b50) $ skip 4               skip 4 -- crc32@@ -793,43 +799,12 @@               skip 4 -- uncompressed size               if fromIntegral cs == B.length raw                  then return raw-                 else fail "Content size mismatch in data descriptor record"+                 else fail $ printf+                       ("Content size mismatch in data descriptor record: "+                         <> "expected %d, got %d bytes")+                       cs (B.length raw)   return (fromIntegral offset, compressedData) --- Move forward over data (not consuming it) until:--- - start of the next local file header--- - start of archive decryption header--- Then back up 12 bytes (the data description record)--- and possibly 4 more bytes--- (conventional but not required sig 0x08074b50 for data description record).-getCompressedData :: Get B.ByteString-getCompressedData = do-  numbytes <- lookAhead $ findEnd 0-  getLazyByteString numbytes- where-   chunkSize :: Int64-   chunkSize = 16384-   findEnd :: Int64 -> Get Int64-   findEnd n = do-     sig <- lookAhead getWord32le-     case sig of-       0x08074b50 -> skip 4 >> return n-       0x04034b50 -> -- sig for local file header-        return (n - 12) -- rewind past data description-       0x02014b50 -> -- sig for file header-        return (n - 12) -- rewind past data description-       0x06054b50 -> -- sig for end of central directory header-        return (n - 12) -- rewind past data description-       x | x .&. 0xFF == 0x50 -> skip 1 >> findEnd (n + 1)-       _ -> do bs <- lookAhead $ getLazyByteString chunkSize-                             <|> getRemainingLazyByteString-               let bsLen = B.length bs-               let mbIdx = B.elemIndex 0x50 bs-               case mbIdx of-                 Nothing -> skip (fromIntegral bsLen) >> findEnd (n + bsLen)-                 Just 0  -> skip 1 >> findEnd (n + 1)-                 Just idx -> skip (fromIntegral idx) >> findEnd (n + idx)- putLocalFile :: Entry -> Put putLocalFile f = do   putWord32le 0x04034b50@@ -992,3 +967,56 @@  fromString :: String -> B.ByteString fromString = TL.encodeUtf8 . TL.pack++data DecompressResult =+    DecompressSuccess B.ByteString -- bytes remaining+      -- (we just discard decompressed chunks, because we only+      -- want to know where the compressed data ends)+  | DecompressFailure ZlibInt.DecompressError++getCompressedData :: CompressionMethod -> Get B.ByteString+getCompressedData NoCompression = do+  -- we assume there will be a signature on the data descriptor,+  -- otherwise we have no way of identifying where the data ends!+  -- The signature 0x08074b50 is commonly used but not required by spec.+  let findSigPos = do+        w1 <- getWord8+        if w1 == 0x50+           then do+             w2 <- getWord8+             if w2 == 0x4b+                then do+                  w3 <- getWord8+                  if w3 == 0x07+                     then do+                       w4 <- getWord8+                       if w4 == 0x08+                          then (\x -> x - 4) <$> bytesRead+                          else findSigPos+                     else findSigPos+                else findSigPos+           else findSigPos+  pos <- bytesRead+  sigpos <- lookAhead findSigPos <|>+              fail "getCompressedData can't find data descriptor signature"+  let compressedBytes = sigpos - pos+  getLazyByteString compressedBytes+getCompressedData Deflate = do+  remainingBytes <- lookAhead getRemainingLazyByteString+  let result = ZlibInt.foldDecompressStreamWithInput+                (\_bs res -> res)+                DecompressSuccess+                DecompressFailure+                (ZlibInt.decompressST ZlibInt.rawFormat+                 ZlibInt.defaultDecompressParams{+                     ZlibInt.decompressAllMembers = False })+                remainingBytes+  case result of+    DecompressFailure err -> fail (show err)+    DecompressSuccess afterCompressedBytes ->+      -- Consume the compressed bytes; we don't do anything with+      -- the decompressed chunks. We are just decompressing as a+      -- way of finding where the compressed data ends.+      getLazyByteString+        (fromIntegral (B.length remainingBytes - B.length afterCompressedBytes))+
zip-archive.cabal view
@@ -1,5 +1,5 @@ Name:                zip-archive-Version:             0.4.3+Version:             0.4.3.1 Cabal-Version:       2.0 Build-type:          Simple Synopsis:            Library for creating and modifying zip archives.