private long TestLocalHeader()

in src/ICSharpCode.SharpZipLib/Zip/ZipFile.cs [1099:1374]


		private long TestLocalHeader(ZipEntry entry, HeaderTest tests)
		{
			lock (baseStream_)
			{
				bool testHeader = (tests & HeaderTest.Header) != 0;
				bool testData = (tests & HeaderTest.Extract) != 0;

				var entryAbsOffset = offsetOfFirstEntry + entry.Offset;
				
				baseStream_.Seek(entryAbsOffset, SeekOrigin.Begin);
				var signature = (int)ReadLEUint();

				if (signature != ZipConstants.LocalHeaderSignature)
				{
					throw new ZipException(string.Format("Wrong local header signature at 0x{0:x}, expected 0x{1:x8}, actual 0x{2:x8}",
						entryAbsOffset, ZipConstants.LocalHeaderSignature, signature));
				}

				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();

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

				byte[] 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));
					}

					var localEncoding = _stringCodec.ZipInputEncoding(localFlags);

					// 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.CompressionMethodForHeader != (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 = localEncoding.GetString(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 > entry.EncryptionOverheadSize + 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 wasn't 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) && entry.Size > 0))
				{
					if ((size != 0)
						&& (size != entry.Size))
					{
						throw new ZipException(
							string.Format("Size mismatch between central header({0}) and local header({1})",
								entry.Size, size));
					}

					if ((compressedSize != 0)
						&& (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;
			}
		}