ICSharpCode.SharpZipLib.Zip.ZipFile.TestLocalHeader C# (CSharp) Method

TestLocalHeader() private method

Test a local header against that provided from the central directory
private TestLocalHeader ( ZipEntry entry, HeaderTest tests ) : long
entry ZipEntry /// The entry to test against ///
tests HeaderTest The type of tests to carry out.
return long
        private long TestLocalHeader(ZipEntry entry, HeaderTest tests) {
            lock (baseStream_) {
                bool testHeader=(tests&HeaderTest.Header)!=0;
                bool testData=(tests&HeaderTest.Extract)!=0;

                baseStream_.Seek(offsetOfFirstEntry+entry.Offset, SeekOrigin.Begin);
                if ((int)ReadLEUint()!=ZipConstants.LocalHeaderSignature) {
                    throw new ZipException(string.Format("Wrong local header signature @{0:X}",
                                                         offsetOfFirstEntry+entry.Offset));
                }

                var extractVersion=(short)(ReadLEUshort()&0x00ff);
                var localFlags=(short)ReadLEUshort();
                var compressionMethod=(short)ReadLEUshort();
                var fileTime=(short)ReadLEUshort();
                var fileDate=(short)ReadLEUshort();
                uint crcValue=ReadLEUint();
                long compressedSize=ReadLEUint();
                long size=ReadLEUint();
                int storedNameLength=ReadLEUshort();
                int extraDataLength=ReadLEUshort();

                var nameData=new byte[storedNameLength];
                StreamUtils.ReadFully(baseStream_, nameData);

                var extraData=new byte[extraDataLength];
                StreamUtils.ReadFully(baseStream_, extraData);

                var localExtraData=new ZipExtraData(extraData);

                // Extra data / zip64 checks
                if (localExtraData.Find(1)) {
                    // 2010-03-04 Forum 10512: removed checks for version >= ZipConstants.VersionZip64
                    // and size or compressedSize = MaxValue, due to rogue creators.

                    size=localExtraData.ReadLong();
                    compressedSize=localExtraData.ReadLong();

                    if ((localFlags&(int)GeneralBitFlags.Descriptor)!=0) {
                        // These may be valid if patched later
                        if ((size!=-1)&&(size!=entry.Size)) {
                            throw new ZipException("Size invalid for descriptor");
                        }

                        if ((compressedSize!=-1)&&(compressedSize!=entry.CompressedSize)) {
                            throw new ZipException("Compressed size invalid for descriptor");
                        }
                    }
                } else {
                    // No zip64 extra data but entry requires it.
                    if ((extractVersion>=ZipConstants.VersionZip64)&&
                        (((uint)size==uint.MaxValue)||((uint)compressedSize==uint.MaxValue))) {
                        throw new ZipException("Required Zip64 extended information missing");
                    }
                }

                if (testData) {
                    if (entry.IsFile) {
                        if (!entry.IsCompressionMethodSupported()) {
                            throw new ZipException("Compression method not supported");
                        }

                        if ((extractVersion>ZipConstants.VersionMadeBy)
                            ||((extractVersion>20)&&(extractVersion<ZipConstants.VersionZip64))) {
                            throw new ZipException(
                                string.Format("Version required to extract this entry not supported ({0})",
                                              extractVersion));
                        }

                        if ((localFlags&
                             (int)
                             (GeneralBitFlags.Patched|GeneralBitFlags.StrongEncryption|
                              GeneralBitFlags.EnhancedCompress|GeneralBitFlags.HeaderMasked))!=0) {
                            throw new ZipException(
                                "The library does not support the zip version required to extract this entry");
                        }
                    }
                }

                if (testHeader) {
                    if ((extractVersion<=63)&& // Ignore later versions as we dont know about them..
                        (extractVersion!=10)&&
                        (extractVersion!=11)&&
                        (extractVersion!=20)&&
                        (extractVersion!=21)&&
                        (extractVersion!=25)&&
                        (extractVersion!=27)&&
                        (extractVersion!=45)&&
                        (extractVersion!=46)&&
                        (extractVersion!=50)&&
                        (extractVersion!=51)&&
                        (extractVersion!=52)&&
                        (extractVersion!=61)&&
                        (extractVersion!=62)&&
                        (extractVersion!=63)
                        ) {
                        throw new ZipException(string.Format("Version required to extract this entry is invalid ({0})",
                                                             extractVersion));
                    }

                    // Local entry flags dont have reserved bit set on.
                    if ((localFlags&
                         (int)
                         (GeneralBitFlags.ReservedPKware4|GeneralBitFlags.ReservedPkware14|
                          GeneralBitFlags.ReservedPkware15))!=0) {
                        throw new ZipException("Reserved bit flags cannot be set.");
                    }

                    // Encryption requires extract version >= 20
                    if (((localFlags&(int)GeneralBitFlags.Encrypted)!=0)&&(extractVersion<20)) {
                        throw new ZipException(
                            string.Format("Version required to extract this entry is too low for encryption ({0})",
                                          extractVersion));
                    }

                    // Strong encryption requires encryption flag to be set and extract version >= 50.
                    if ((localFlags&(int)GeneralBitFlags.StrongEncryption)!=0) {
                        if ((localFlags&(int)GeneralBitFlags.Encrypted)==0) {
                            throw new ZipException("Strong encryption flag set but encryption flag is not set");
                        }

                        if (extractVersion<50) {
                            throw new ZipException(
                                string.Format("Version required to extract this entry is too low for encryption ({0})",
                                              extractVersion));
                        }
                    }

                    // Patched entries require extract version >= 27
                    if (((localFlags&(int)GeneralBitFlags.Patched)!=0)&&(extractVersion<27)) {
                        throw new ZipException(string.Format("Patched data requires higher version than ({0})",
                                                             extractVersion));
                    }

                    // Central header flags match local entry flags.
                    if (localFlags!=entry.Flags) {
                        throw new ZipException("Central header/local header flags mismatch");
                    }

                    // Central header compression method matches local entry
                    if (entry.CompressionMethod!=(CompressionMethod)compressionMethod) {
                        throw new ZipException("Central header/local header compression method mismatch");
                    }

                    if (entry.Version!=extractVersion) {
                        throw new ZipException("Extract version mismatch");
                    }

                    // Strong encryption and extract version match
                    if ((localFlags&(int)GeneralBitFlags.StrongEncryption)!=0) {
                        if (extractVersion<62) {
                            throw new ZipException("Strong encryption flag set but version not high enough");
                        }
                    }

                    if ((localFlags&(int)GeneralBitFlags.HeaderMasked)!=0) {
                        if ((fileTime!=0)||(fileDate!=0)) {
                            throw new ZipException("Header masked set but date/time values non-zero");
                        }
                    }

                    if ((localFlags&(int)GeneralBitFlags.Descriptor)==0) {
                        if (crcValue!=(uint)entry.Crc) {
                            throw new ZipException("Central header/local header crc mismatch");
                        }
                    }

                    // Crc valid for empty entry.
                    // This will also apply to streamed entries where size isnt known and the header cant be patched
                    if ((size==0)&&(compressedSize==0)) {
                        if (crcValue!=0) {
                            throw new ZipException("Invalid CRC for empty entry");
                        }
                    }

                    // TODO: make test more correct...  can't compare lengths as was done originally as this can fail for MBCS strings
                    // Assuming a code page at this point is not valid?  Best is to store the name length in the ZipEntry probably
                    if (entry.Name.Length>storedNameLength) {
                        throw new ZipException("File name length mismatch");
                    }

                    // Name data has already been read convert it and compare.
                    string localName=ZipConstants.ConvertToStringExt(localFlags, nameData);

                    // Central directory and local entry name match
                    if (localName!=entry.Name) {
                        throw new ZipException("Central header and local header file name mismatch");
                    }

                    // Directories have zero actual size but can have compressed size
                    if (entry.IsDirectory) {
                        if (size>0) {
                            throw new ZipException("Directory cannot have size");
                        }

                        // There may be other cases where the compressed size can be greater than this?
                        // If so until details are known we will be strict.
                        if (entry.IsCrypted) {
                            if (compressedSize>ZipConstants.CryptoHeaderSize+2) {
                                throw new ZipException("Directory compressed size invalid");
                            }
                        } else if (compressedSize>2) {
                            // When not compressed the directory size can validly be 2 bytes
                            // if the true size wasnt known when data was originally being written.
                            // NOTE: Versions of the library 0.85.4 and earlier always added 2 bytes
                            throw new ZipException("Directory compressed size invalid");
                        }
                    }

                    if (!ZipNameTransform.IsValidName(localName, true)) {
                        throw new ZipException("Name is invalid");
                    }
                }

                // Tests that apply to both data and header.

                // Size can be verified only if it is known in the local header.
                // it will always be known in the central header.
                if (((localFlags&(int)GeneralBitFlags.Descriptor)==0)||
                    ((size>0)||(compressedSize>0))) {
                    if (size!=entry.Size) {
                        throw new ZipException(
                            string.Format("Size mismatch between central header({0}) and local header({1})",
                                          entry.Size, size));
                    }

                    if (compressedSize!=entry.CompressedSize&&
                        compressedSize!=0xFFFFFFFF&&compressedSize!=-1) {
                        throw new ZipException(
                            string.Format("Compressed size mismatch between central header({0}) and local header({1})",
                                          entry.CompressedSize, compressedSize));
                    }
                }

                int extraLength=storedNameLength+extraDataLength;
                return offsetOfFirstEntry+entry.Offset+ZipConstants.LocalHeaderBaseSize+extraLength;
            }
        }