ZipIOCentralDirectoryFileHeader.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ Net / Net / 3.5.50727.3053 / DEVDIV / depot / DevDiv / releases / Orcas / SP / wpf / src / Base / MS / Internal / IO / Zip / ZipIOCentralDirectoryFileHeader.cs / 1 / ZipIOCentralDirectoryFileHeader.cs

                            //------------------------------------------------------------------------------ 
//-------------   *** WARNING ***
//-------------    This file is part of a legally monitored development project.
//-------------    Do not check in changes to this project.  Do not raid bugs on this
//-------------    code in the main PS database.  Do not contact the owner of this 
//-------------    code directly.  Contact the legal team at ‘ZSLegal’ for assistance.
//-------------   *** WARNING *** 
//----------------------------------------------------------------------------- 

//----------------------------------------------------------------------------- 
//
// 
//    Copyright (C) Microsoft Corporation.  All rights reserved.
//  
//
// Description: 
//  This is an internal class that enables interactions with Zip archives 
//  for OPC scenarios
// 
// History:
//  11/19/2004: IgorBel: Initial creation.
//
//----------------------------------------------------------------------------- 

using System; 
using System.IO; 
using System.Diagnostics;
using System.Text; 
using System.Collections;
using System.Runtime.Serialization;
using System.Windows;
 
namespace MS.Internal.IO.Zip
{ 
    internal class ZipIOCentralDirectoryFileHeader 
    {
        internal static ZipIOCentralDirectoryFileHeader CreateNew(Encoding encoding, ZipIOLocalFileBlock fileBlock) 
        {
            ZipIOCentralDirectoryFileHeader header = new ZipIOCentralDirectoryFileHeader(encoding);

            // initialize fields that are not duplicated in the local file block(header) 
            header._fileCommentLength =0;
            header._fileComment = null; 
            header._diskNumberStart = 0; 
            header._internalFileAttributes = 0;
            header._externalFileAttributes = 0; 
            header._versionMadeBy = (ushort)ZipIOVersionNeededToExtract.Zip64FileFormat;
            header._extraField = ZipIOExtraField.CreateNew(false /* no padding */);

            // update the rest of the fields based on the local file header 
            header.UpdateFromLocalFileBlock(fileBlock);
 
            return header; 
        }
 
        internal static ZipIOCentralDirectoryFileHeader ParseRecord(BinaryReader reader, Encoding encoding)
        {
            ZipIOCentralDirectoryFileHeader header = new ZipIOCentralDirectoryFileHeader(encoding);
 
            header._signature =  reader.ReadUInt32();
            header._versionMadeBy = reader.ReadUInt16(); 
            header._versionNeededToExtract = reader.ReadUInt16(); 
            header._generalPurposeBitFlag = reader.ReadUInt16();
            header._compressionMethod = reader.ReadUInt16(); 
            header._lastModFileDateTime = reader.ReadUInt32();
            header._crc32 = reader.ReadUInt32();
            header._compressedSize = reader.ReadUInt32();
            header._uncompressedSize = reader.ReadUInt32(); 
            header._fileNameLength = reader.ReadUInt16();
            header._extraFieldLength = reader.ReadUInt16(); 
            header._fileCommentLength = reader.ReadUInt16(); 
            header._diskNumberStart = reader.ReadUInt16();
            header._internalFileAttributes = reader.ReadUInt16(); 
            header._externalFileAttributes = reader.ReadUInt32();
            header._relativeOffsetOfLocalHeader = reader.ReadUInt32();

            header._fileName = reader.ReadBytes(header._fileNameLength); 

            // check for the ZIP 64 version and escaped values 
            ZipIOZip64ExtraFieldUsage zip64extraFieldUsage = ZipIOZip64ExtraFieldUsage.None; 
            if (header._versionNeededToExtract >= (ushort)ZipIOVersionNeededToExtract.Zip64FileFormat)
            { 
                if (header._compressedSize == UInt32.MaxValue)
                {
                    zip64extraFieldUsage |= ZipIOZip64ExtraFieldUsage.CompressedSize;
                } 
                if (header._uncompressedSize == UInt32.MaxValue)
                { 
                    zip64extraFieldUsage |= ZipIOZip64ExtraFieldUsage.UncompressedSize; 
                }
                if (header._relativeOffsetOfLocalHeader == UInt32.MaxValue) 
                {
                    zip64extraFieldUsage |= ZipIOZip64ExtraFieldUsage.OffsetOfLocalHeader;
                }
                if (header._diskNumberStart == UInt16.MaxValue) 
                {
                    zip64extraFieldUsage |= ZipIOZip64ExtraFieldUsage.DiskNumber; 
                } 
            }
 
            // if the ZIP 64 record is missing the zip64extraFieldUsage value will be ignored
            header._extraField = ZipIOExtraField.ParseRecord(reader,
                                             zip64extraFieldUsage,
                                            header._extraFieldLength); 

            header._fileComment = reader.ReadBytes(header._fileCommentLength); 
 
            //populate frequently used field with user friendly data representations
            header._stringFileName = ZipIOBlockManager.ValidateNormalizeFileName(encoding.GetString(header._fileName)); 

            header.Validate();

            return header; 
        }
 
        internal void Save(BinaryWriter writer) 
        {
            writer.Write(_signatureConstant); 
            writer.Write(_versionMadeBy);
            writer.Write(_versionNeededToExtract);
            writer.Write(_generalPurposeBitFlag);
            writer.Write(_compressionMethod); 
            writer.Write(_lastModFileDateTime);
            writer.Write(_crc32); 
            writer.Write(_compressedSize); 
            writer.Write(_uncompressedSize);
            writer.Write(_fileNameLength); 
            writer.Write(_extraField.Size);
            writer.Write(_fileCommentLength);
            writer.Write(_diskNumberStart);
            writer.Write(_internalFileAttributes); 
            writer.Write(_externalFileAttributes);
            writer.Write(_relativeOffsetOfLocalHeader); 
 

            Debug.Assert(_fileNameLength > 0); // we validate this for both parsing and API entry points 
            writer.Write(_fileName, 0, _fileNameLength);

            _extraField.Save(writer);
 
            if (_fileCommentLength > 0)
            { 
                writer.Write(_fileComment , 0, _fileCommentLength); 
            }
        } 

        internal bool UpdateIfNeeded(ZipIOLocalFileBlock fileBlock)
        {
            if (CheckIfUpdateNeeded(fileBlock)) 
            {
                UpdateFromLocalFileBlock(fileBlock); 
                return true; 
            }
            else 
            {
                return false;
            }
        } 

        internal string FileName 
        { 
            get
            { 
                return _stringFileName;
            }
            // set method if needed will have to update both the _stringFileName and
            // _fileName 
        }
 
        internal UInt16 VersionNeededToExtract 
        {
            get 
            {
                return _versionNeededToExtract;
            }
        } 

        internal UInt16 GeneralPurposeBitFlag 
        { 
            get
            { 
                return _generalPurposeBitFlag;
            }
        }
 
        internal CompressionMethodEnum CompressionMethod
        { 
            get 
            {
                // cast is safe because the value is validated in Validate() 
                return (CompressionMethodEnum)_compressionMethod;
            }
        }
 
        internal long Size
        { 
            get 
            {
                return checked(_fixedMinimalRecordSize + _fileNameLength + _extraField.Size + _fileCommentLength); 
            }
        }

        internal long OffsetOfLocalHeader 
        {
            get 
            { 
                if ((_extraField.Zip64ExtraFieldUsage & ZipIOZip64ExtraFieldUsage.OffsetOfLocalHeader) != 0)
                { 
                    // zip 64 extra field is there
                    return _extraField.OffsetOfLocalHeader;
                }
                else 
                {
                    // 32 bit case 
                    return _relativeOffsetOfLocalHeader; 
                }
            } 
        }

        internal long CompressedSize
        { 
            get
            { 
                if ((_extraField.Zip64ExtraFieldUsage & ZipIOZip64ExtraFieldUsage.CompressedSize) != 0) 
                {
                    // zip 64 extra field is there 
                    return _extraField.CompressedSize;
                }
                else
                { 
                    // 32 bit case
                    return _compressedSize; 
                } 
            }
        } 

        internal long UncompressedSize
        {
            get 
            {
                if ((_extraField.Zip64ExtraFieldUsage & ZipIOZip64ExtraFieldUsage.UncompressedSize) != 0) 
                { 
                    // zip 64 extra field is there
                    return _extraField.UncompressedSize; 
                }
                else
                {
                    // 32 bit case 
                    return _uncompressedSize;
                } 
            } 
        }
 
        internal UInt32 Crc32
        {
            get
            { 
                return _crc32;
            } 
        } 

        internal UInt32 DiskNumberStart 
        {
            get
            {
                if ((_extraField.Zip64ExtraFieldUsage & ZipIOZip64ExtraFieldUsage.DiskNumber) != 0) 
                {
                    // zip 64 extra field is there (32 bit value returned) 
                    return _extraField.DiskNumberOfFileStart; 
                }
                else 
                {
                    // 16 bit case
                    return _diskNumberStart;;
                } 
            }
        } 
 
        internal bool FolderFlag
        { 
            get
            {
                // The upper byte of version made by indicates the compatibility of the file attribute information.
                // If the external file attributes are compatible with MS-DOS then this value 
                // will be zero.
 
                // lower byte of the external file attribute is the the MS-DOS directory attribute byte 
                //
                //                          0x20 5   file has been changed since last backup 
                //                          0x10 4   entry represents a subdirectory XXXXXXXXX
                //                          0x08 3   entry represents a volume label
                //                          0x04 2   system file
                //                          0x02 1   hidden file 
                //                          0x01 0   read-only
 
                return  ((_versionMadeBy & 0xFF00) == _constantUpperVersionMadeByMsDos) 
                        &&
                            ((_externalFileAttributes & 0x10) != 0); 
            }
        }

        internal bool VolumeLabelFlag 
        {
            get 
            { 
                // The upper byte of version made by indicates the compatibility of the file attribute information.
                // If the external file attributes are compatible with MS-DOS then this value 
                // will be zero.

                // lower byte of the external file attribute is the the MS-DOS directory attribute byte
                // 
                //                          0x20 5   file has been changed since last backup
                //                          0x10 4   entry represents a subdirectory 
                //                          0x08 3   entry represents a volume label  XXXXXXXXX 
                //                          0x04 2   system file
                //                          0x02 1   hidden file 
                //                          0x01 0   read-only

                return  ((_versionMadeBy & 0xFF00) == _constantUpperVersionMadeByMsDos)
                        && 
                            ((_externalFileAttributes & 0x08) != 0);
            } 
        } 

        // this function is called by the Central Dir in order to notify us that 
        // the appropriate file item was shifted (as detected by the shift in the Raw Data Block)
        // holding given file item.
        // for us it means that although all the size characteristics are preserved (local file header
        // wasn't even parsed if it still in the Raw). But the offset could have changed which 
        // might result in Zip64 struicture.
        internal void MoveReference(long shiftSize) 
        { 

            UpdateZip64Structures(CompressedSize, 
                                                            UncompressedSize,
                                                            checked(OffsetOfLocalHeader +shiftSize));
        }
 
        // this function is sets the sizes into the either 64 or 32 bit structures based on values of the fields
        // It used in 2 places by the MoveReference and by the UpdateFromLocalFileBlock 
        private void UpdateZip64Structures 
                (long compressedSize, long uncompressedSize, long offset)
        { 
            Debug.Assert((compressedSize >= 0) && (uncompressedSize>=0) && (offset >=0));

            // according to the appnote central directory extra field might be a mix of any values based on escaping
            // we will fully (without disk number) use it every time we are building a ZIP 64 arhichive 
            // we also trying to stay on the safe side and treeat the boundary case of 32 escape values
            // as a zip 64 scenrio 
            if ((compressedSize >= UInt32.MaxValue) || 
                    (uncompressedSize >= UInt32.MaxValue) ||
                    (offset >= UInt32.MaxValue)) 
            {
                // Zip 64 case
                _extraField.CompressedSize = compressedSize;
                _extraField.UncompressedSize = uncompressedSize; 
                _extraField.OffsetOfLocalHeader = offset;
 
                //set proper escape values 
               _compressedSize = UInt32.MaxValue;
               _uncompressedSize = UInt32.MaxValue; 
               _relativeOffsetOfLocalHeader = UInt32.MaxValue;

                // update version needed to extract to 4.5
                _versionNeededToExtract = (UInt16)ZipIOVersionNeededToExtract.Zip64FileFormat; 
            }
            else 
            { 
                // 32 bit case
               _compressedSize = checked((UInt32)compressedSize); 
               _uncompressedSize = checked((UInt32)uncompressedSize);
               _relativeOffsetOfLocalHeader = checked((UInt32)offset);

                // reset the extra ZIP 64 field to empty 
               _extraField.Zip64ExtraFieldUsage = ZipIOZip64ExtraFieldUsage.None;
 
                // version needed to extract needs to be recalculated from scratch based on compression 
                _versionNeededToExtract = (UInt16)ZipIOBlockManager.CalcVersionNeededToExtractFromCompression
                                                                                            ((CompressionMethodEnum)_compressionMethod); 
            }
        }

        private void  UpdateFromLocalFileBlock(ZipIOLocalFileBlock fileBlock) 
        {
            Debug.Assert(DiskNumberStart == 0); 
 
            _signature = _signatureConstant;
            _generalPurposeBitFlag = fileBlock.GeneralPurposeBitFlag; 
            _compressionMethod = (UInt16)fileBlock.CompressionMethod;
            _lastModFileDateTime = fileBlock.LastModFileDateTime;
            _crc32 = fileBlock.Crc32;
 
            // file name is easy to copy
            _fileNameLength = (UInt16)fileBlock.FileName.Length; // this is safe cast as file name is always validate for size 
            _fileName = _encoding.GetBytes(fileBlock.FileName); 
            _stringFileName = fileBlock.FileName;
 
            // this will properly update the 32 or zip 64 fields
            UpdateZip64Structures(fileBlock.CompressedSize,
                                    fileBlock.UncompressedSize,
                                    fileBlock.Offset); 

            // Previous instruction may determine that we don't really need 4.5, but we 
            // want to ensure that the version is identical with what is stored in the local file header. 
            Debug.Assert(_versionNeededToExtract <= fileBlock.VersionNeededToExtract, "Should never be making this smaller");
 
            _versionNeededToExtract = fileBlock.VersionNeededToExtract;

            // These fields are intentionally ignored, as they are not present in the local header
                        //_fileCommentLength; 
                        //_fileComment;
                        //_diskNumberStart; 
                        //_internalFileAttributes; 
                        //_externalFileAttributes;
        } 

        private bool CheckIfUpdateNeeded(ZipIOLocalFileBlock fileBlock)
        {
            // there is a special case for the _generalPurposeBitFlag.Bit #3 
            // it could be set in the local file header indicating streaming
            // creation, while it doesn't need to be set in the Central directory 
            // so having 
            //  (fileBlock.GeneralPurposeBitFlag == 8 && and _generalPurposeBitFlag  == 0)
            // is a valid case when update is not required 

            // let's compare the 3rd bit of the general purpose bit flag
            bool localFileHeaderStreamingFlag = (0 != (fileBlock.GeneralPurposeBitFlag & _streamingBitMask));
            bool centralDirStreamingFlag = (0 != (_generalPurposeBitFlag & _streamingBitMask)); 

            if (!localFileHeaderStreamingFlag  && centralDirStreamingFlag) 
            { 
                // the mismatch if local file header in non streaming but the central directory is in streaming mode
                // all the other combinations do not require an update and valid as is 
                // this includes scenario when local file header is in streaming and central dir is not
                return true;
            }
 
            Debug.Assert(String.CompareOrdinal(_stringFileName, fileBlock.FileName) == 0);
 
            return 
                (_signature != _signatureConstant) ||
                (_versionNeededToExtract != fileBlock.VersionNeededToExtract) || 
                (_generalPurposeBitFlag != fileBlock.GeneralPurposeBitFlag) ||
                (_compressionMethod != (UInt16)fileBlock.CompressionMethod) ||
                (_crc32 != fileBlock.Crc32) ||
                (CompressedSize != fileBlock.CompressedSize) || 
                (UncompressedSize != fileBlock.UncompressedSize) ||
                (OffsetOfLocalHeader != fileBlock.Offset); 
 
                // These fields are intentionally ignored, as they are not present in the local header
                            //_fileCommentLength; 
                            //_fileComment;
                            //_diskNumberStart;
                            //_internalFileAttributes;
                            //_externalFileAttributes; 
        }
 
 
        private ZipIOCentralDirectoryFileHeader(Encoding encoding)
        { 
            _encoding = encoding;
        }

        private void Validate () 
        {
            if (_signature != _signatureConstant) 
            { 
                throw new FileFormatException(SR.Get(SRID.CorruptedData));
            } 

            if (DiskNumberStart != 0)
            {
                throw new NotSupportedException(SR.Get(SRID.NotSupportedMultiDisk)); 
            }
 
            if (_fileNameLength != _fileName.Length) 
            {
                throw new FileFormatException(SR.Get(SRID.CorruptedData)); 
            }

            if (_extraFieldLength != _extraField.Size)
            { 
                throw new FileFormatException(SR.Get(SRID.CorruptedData));
            } 
 
            ZipArchive.VerifyVersionNeededToExtract(_versionNeededToExtract);
 
            // if verson is below 4.5 make sure that ZIP 64 extra filed isn't present
            // if it is it might be a security concern
            if ((_versionNeededToExtract < (UInt16)ZipIOVersionNeededToExtract.Zip64FileFormat) &&
                (_extraField.Zip64ExtraFieldUsage != ZipIOZip64ExtraFieldUsage.None)) 
            {
                throw new FileFormatException(SR.Get(SRID.CorruptedData)); 
            } 

            if (_fileCommentLength != _fileComment.Length) 
            {
                throw new FileFormatException(SR.Get(SRID.CorruptedData));
            }
 
            if ((_compressionMethod != (UInt16)CompressionMethodEnum.Stored) &&
                (_compressionMethod != (UInt16)CompressionMethodEnum.Deflated)) 
            { 
                throw new NotSupportedException(SR.Get(SRID.ZipNotSupportedCompressionMethod));
            } 
        }

        private Encoding _encoding;
        private const int _fixedMinimalRecordSize = 46; 

        private const byte _constantUpperVersionMadeByMsDos = 0x0; 
 
        private const UInt16 _streamingBitMask = 0x08; // bit #3
 
        private const UInt32 _signatureConstant = 0x02014b50;
        private UInt32 _signature = _signatureConstant;

        // we expect all variables to be initialized to 0 
        private UInt16 _versionMadeBy;
        private UInt16 _versionNeededToExtract; 
        private UInt16 _generalPurposeBitFlag; 
        private UInt16 _compressionMethod;
        private UInt32 _lastModFileDateTime; 
        private UInt32 _crc32;
        private UInt32 _compressedSize;
        private UInt32 _uncompressedSize;
        private UInt16 _fileNameLength; 
        private UInt16 _extraFieldLength;
        private UInt16 _fileCommentLength; 
        private UInt16 _diskNumberStart; 
        private UInt16 _internalFileAttributes;
        private UInt32 _externalFileAttributes; 
        private UInt32 _relativeOffsetOfLocalHeader;
        private byte[] _fileName;
        private ZipIOExtraField _extraField;
        private byte[] _fileComment; 

        //duplicate dat for fast access 
        private string _stringFileName; 
    }
} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
//------------------------------------------------------------------------------ 
//-------------   *** WARNING ***
//-------------    This file is part of a legally monitored development project.
//-------------    Do not check in changes to this project.  Do not raid bugs on this
//-------------    code in the main PS database.  Do not contact the owner of this 
//-------------    code directly.  Contact the legal team at ‘ZSLegal’ for assistance.
//-------------   *** WARNING *** 
//----------------------------------------------------------------------------- 

//----------------------------------------------------------------------------- 
//
// 
//    Copyright (C) Microsoft Corporation.  All rights reserved.
//  
//
// Description: 
//  This is an internal class that enables interactions with Zip archives 
//  for OPC scenarios
// 
// History:
//  11/19/2004: IgorBel: Initial creation.
//
//----------------------------------------------------------------------------- 

using System; 
using System.IO; 
using System.Diagnostics;
using System.Text; 
using System.Collections;
using System.Runtime.Serialization;
using System.Windows;
 
namespace MS.Internal.IO.Zip
{ 
    internal class ZipIOCentralDirectoryFileHeader 
    {
        internal static ZipIOCentralDirectoryFileHeader CreateNew(Encoding encoding, ZipIOLocalFileBlock fileBlock) 
        {
            ZipIOCentralDirectoryFileHeader header = new ZipIOCentralDirectoryFileHeader(encoding);

            // initialize fields that are not duplicated in the local file block(header) 
            header._fileCommentLength =0;
            header._fileComment = null; 
            header._diskNumberStart = 0; 
            header._internalFileAttributes = 0;
            header._externalFileAttributes = 0; 
            header._versionMadeBy = (ushort)ZipIOVersionNeededToExtract.Zip64FileFormat;
            header._extraField = ZipIOExtraField.CreateNew(false /* no padding */);

            // update the rest of the fields based on the local file header 
            header.UpdateFromLocalFileBlock(fileBlock);
 
            return header; 
        }
 
        internal static ZipIOCentralDirectoryFileHeader ParseRecord(BinaryReader reader, Encoding encoding)
        {
            ZipIOCentralDirectoryFileHeader header = new ZipIOCentralDirectoryFileHeader(encoding);
 
            header._signature =  reader.ReadUInt32();
            header._versionMadeBy = reader.ReadUInt16(); 
            header._versionNeededToExtract = reader.ReadUInt16(); 
            header._generalPurposeBitFlag = reader.ReadUInt16();
            header._compressionMethod = reader.ReadUInt16(); 
            header._lastModFileDateTime = reader.ReadUInt32();
            header._crc32 = reader.ReadUInt32();
            header._compressedSize = reader.ReadUInt32();
            header._uncompressedSize = reader.ReadUInt32(); 
            header._fileNameLength = reader.ReadUInt16();
            header._extraFieldLength = reader.ReadUInt16(); 
            header._fileCommentLength = reader.ReadUInt16(); 
            header._diskNumberStart = reader.ReadUInt16();
            header._internalFileAttributes = reader.ReadUInt16(); 
            header._externalFileAttributes = reader.ReadUInt32();
            header._relativeOffsetOfLocalHeader = reader.ReadUInt32();

            header._fileName = reader.ReadBytes(header._fileNameLength); 

            // check for the ZIP 64 version and escaped values 
            ZipIOZip64ExtraFieldUsage zip64extraFieldUsage = ZipIOZip64ExtraFieldUsage.None; 
            if (header._versionNeededToExtract >= (ushort)ZipIOVersionNeededToExtract.Zip64FileFormat)
            { 
                if (header._compressedSize == UInt32.MaxValue)
                {
                    zip64extraFieldUsage |= ZipIOZip64ExtraFieldUsage.CompressedSize;
                } 
                if (header._uncompressedSize == UInt32.MaxValue)
                { 
                    zip64extraFieldUsage |= ZipIOZip64ExtraFieldUsage.UncompressedSize; 
                }
                if (header._relativeOffsetOfLocalHeader == UInt32.MaxValue) 
                {
                    zip64extraFieldUsage |= ZipIOZip64ExtraFieldUsage.OffsetOfLocalHeader;
                }
                if (header._diskNumberStart == UInt16.MaxValue) 
                {
                    zip64extraFieldUsage |= ZipIOZip64ExtraFieldUsage.DiskNumber; 
                } 
            }
 
            // if the ZIP 64 record is missing the zip64extraFieldUsage value will be ignored
            header._extraField = ZipIOExtraField.ParseRecord(reader,
                                             zip64extraFieldUsage,
                                            header._extraFieldLength); 

            header._fileComment = reader.ReadBytes(header._fileCommentLength); 
 
            //populate frequently used field with user friendly data representations
            header._stringFileName = ZipIOBlockManager.ValidateNormalizeFileName(encoding.GetString(header._fileName)); 

            header.Validate();

            return header; 
        }
 
        internal void Save(BinaryWriter writer) 
        {
            writer.Write(_signatureConstant); 
            writer.Write(_versionMadeBy);
            writer.Write(_versionNeededToExtract);
            writer.Write(_generalPurposeBitFlag);
            writer.Write(_compressionMethod); 
            writer.Write(_lastModFileDateTime);
            writer.Write(_crc32); 
            writer.Write(_compressedSize); 
            writer.Write(_uncompressedSize);
            writer.Write(_fileNameLength); 
            writer.Write(_extraField.Size);
            writer.Write(_fileCommentLength);
            writer.Write(_diskNumberStart);
            writer.Write(_internalFileAttributes); 
            writer.Write(_externalFileAttributes);
            writer.Write(_relativeOffsetOfLocalHeader); 
 

            Debug.Assert(_fileNameLength > 0); // we validate this for both parsing and API entry points 
            writer.Write(_fileName, 0, _fileNameLength);

            _extraField.Save(writer);
 
            if (_fileCommentLength > 0)
            { 
                writer.Write(_fileComment , 0, _fileCommentLength); 
            }
        } 

        internal bool UpdateIfNeeded(ZipIOLocalFileBlock fileBlock)
        {
            if (CheckIfUpdateNeeded(fileBlock)) 
            {
                UpdateFromLocalFileBlock(fileBlock); 
                return true; 
            }
            else 
            {
                return false;
            }
        } 

        internal string FileName 
        { 
            get
            { 
                return _stringFileName;
            }
            // set method if needed will have to update both the _stringFileName and
            // _fileName 
        }
 
        internal UInt16 VersionNeededToExtract 
        {
            get 
            {
                return _versionNeededToExtract;
            }
        } 

        internal UInt16 GeneralPurposeBitFlag 
        { 
            get
            { 
                return _generalPurposeBitFlag;
            }
        }
 
        internal CompressionMethodEnum CompressionMethod
        { 
            get 
            {
                // cast is safe because the value is validated in Validate() 
                return (CompressionMethodEnum)_compressionMethod;
            }
        }
 
        internal long Size
        { 
            get 
            {
                return checked(_fixedMinimalRecordSize + _fileNameLength + _extraField.Size + _fileCommentLength); 
            }
        }

        internal long OffsetOfLocalHeader 
        {
            get 
            { 
                if ((_extraField.Zip64ExtraFieldUsage & ZipIOZip64ExtraFieldUsage.OffsetOfLocalHeader) != 0)
                { 
                    // zip 64 extra field is there
                    return _extraField.OffsetOfLocalHeader;
                }
                else 
                {
                    // 32 bit case 
                    return _relativeOffsetOfLocalHeader; 
                }
            } 
        }

        internal long CompressedSize
        { 
            get
            { 
                if ((_extraField.Zip64ExtraFieldUsage & ZipIOZip64ExtraFieldUsage.CompressedSize) != 0) 
                {
                    // zip 64 extra field is there 
                    return _extraField.CompressedSize;
                }
                else
                { 
                    // 32 bit case
                    return _compressedSize; 
                } 
            }
        } 

        internal long UncompressedSize
        {
            get 
            {
                if ((_extraField.Zip64ExtraFieldUsage & ZipIOZip64ExtraFieldUsage.UncompressedSize) != 0) 
                { 
                    // zip 64 extra field is there
                    return _extraField.UncompressedSize; 
                }
                else
                {
                    // 32 bit case 
                    return _uncompressedSize;
                } 
            } 
        }
 
        internal UInt32 Crc32
        {
            get
            { 
                return _crc32;
            } 
        } 

        internal UInt32 DiskNumberStart 
        {
            get
            {
                if ((_extraField.Zip64ExtraFieldUsage & ZipIOZip64ExtraFieldUsage.DiskNumber) != 0) 
                {
                    // zip 64 extra field is there (32 bit value returned) 
                    return _extraField.DiskNumberOfFileStart; 
                }
                else 
                {
                    // 16 bit case
                    return _diskNumberStart;;
                } 
            }
        } 
 
        internal bool FolderFlag
        { 
            get
            {
                // The upper byte of version made by indicates the compatibility of the file attribute information.
                // If the external file attributes are compatible with MS-DOS then this value 
                // will be zero.
 
                // lower byte of the external file attribute is the the MS-DOS directory attribute byte 
                //
                //                          0x20 5   file has been changed since last backup 
                //                          0x10 4   entry represents a subdirectory XXXXXXXXX
                //                          0x08 3   entry represents a volume label
                //                          0x04 2   system file
                //                          0x02 1   hidden file 
                //                          0x01 0   read-only
 
                return  ((_versionMadeBy & 0xFF00) == _constantUpperVersionMadeByMsDos) 
                        &&
                            ((_externalFileAttributes & 0x10) != 0); 
            }
        }

        internal bool VolumeLabelFlag 
        {
            get 
            { 
                // The upper byte of version made by indicates the compatibility of the file attribute information.
                // If the external file attributes are compatible with MS-DOS then this value 
                // will be zero.

                // lower byte of the external file attribute is the the MS-DOS directory attribute byte
                // 
                //                          0x20 5   file has been changed since last backup
                //                          0x10 4   entry represents a subdirectory 
                //                          0x08 3   entry represents a volume label  XXXXXXXXX 
                //                          0x04 2   system file
                //                          0x02 1   hidden file 
                //                          0x01 0   read-only

                return  ((_versionMadeBy & 0xFF00) == _constantUpperVersionMadeByMsDos)
                        && 
                            ((_externalFileAttributes & 0x08) != 0);
            } 
        } 

        // this function is called by the Central Dir in order to notify us that 
        // the appropriate file item was shifted (as detected by the shift in the Raw Data Block)
        // holding given file item.
        // for us it means that although all the size characteristics are preserved (local file header
        // wasn't even parsed if it still in the Raw). But the offset could have changed which 
        // might result in Zip64 struicture.
        internal void MoveReference(long shiftSize) 
        { 

            UpdateZip64Structures(CompressedSize, 
                                                            UncompressedSize,
                                                            checked(OffsetOfLocalHeader +shiftSize));
        }
 
        // this function is sets the sizes into the either 64 or 32 bit structures based on values of the fields
        // It used in 2 places by the MoveReference and by the UpdateFromLocalFileBlock 
        private void UpdateZip64Structures 
                (long compressedSize, long uncompressedSize, long offset)
        { 
            Debug.Assert((compressedSize >= 0) && (uncompressedSize>=0) && (offset >=0));

            // according to the appnote central directory extra field might be a mix of any values based on escaping
            // we will fully (without disk number) use it every time we are building a ZIP 64 arhichive 
            // we also trying to stay on the safe side and treeat the boundary case of 32 escape values
            // as a zip 64 scenrio 
            if ((compressedSize >= UInt32.MaxValue) || 
                    (uncompressedSize >= UInt32.MaxValue) ||
                    (offset >= UInt32.MaxValue)) 
            {
                // Zip 64 case
                _extraField.CompressedSize = compressedSize;
                _extraField.UncompressedSize = uncompressedSize; 
                _extraField.OffsetOfLocalHeader = offset;
 
                //set proper escape values 
               _compressedSize = UInt32.MaxValue;
               _uncompressedSize = UInt32.MaxValue; 
               _relativeOffsetOfLocalHeader = UInt32.MaxValue;

                // update version needed to extract to 4.5
                _versionNeededToExtract = (UInt16)ZipIOVersionNeededToExtract.Zip64FileFormat; 
            }
            else 
            { 
                // 32 bit case
               _compressedSize = checked((UInt32)compressedSize); 
               _uncompressedSize = checked((UInt32)uncompressedSize);
               _relativeOffsetOfLocalHeader = checked((UInt32)offset);

                // reset the extra ZIP 64 field to empty 
               _extraField.Zip64ExtraFieldUsage = ZipIOZip64ExtraFieldUsage.None;
 
                // version needed to extract needs to be recalculated from scratch based on compression 
                _versionNeededToExtract = (UInt16)ZipIOBlockManager.CalcVersionNeededToExtractFromCompression
                                                                                            ((CompressionMethodEnum)_compressionMethod); 
            }
        }

        private void  UpdateFromLocalFileBlock(ZipIOLocalFileBlock fileBlock) 
        {
            Debug.Assert(DiskNumberStart == 0); 
 
            _signature = _signatureConstant;
            _generalPurposeBitFlag = fileBlock.GeneralPurposeBitFlag; 
            _compressionMethod = (UInt16)fileBlock.CompressionMethod;
            _lastModFileDateTime = fileBlock.LastModFileDateTime;
            _crc32 = fileBlock.Crc32;
 
            // file name is easy to copy
            _fileNameLength = (UInt16)fileBlock.FileName.Length; // this is safe cast as file name is always validate for size 
            _fileName = _encoding.GetBytes(fileBlock.FileName); 
            _stringFileName = fileBlock.FileName;
 
            // this will properly update the 32 or zip 64 fields
            UpdateZip64Structures(fileBlock.CompressedSize,
                                    fileBlock.UncompressedSize,
                                    fileBlock.Offset); 

            // Previous instruction may determine that we don't really need 4.5, but we 
            // want to ensure that the version is identical with what is stored in the local file header. 
            Debug.Assert(_versionNeededToExtract <= fileBlock.VersionNeededToExtract, "Should never be making this smaller");
 
            _versionNeededToExtract = fileBlock.VersionNeededToExtract;

            // These fields are intentionally ignored, as they are not present in the local header
                        //_fileCommentLength; 
                        //_fileComment;
                        //_diskNumberStart; 
                        //_internalFileAttributes; 
                        //_externalFileAttributes;
        } 

        private bool CheckIfUpdateNeeded(ZipIOLocalFileBlock fileBlock)
        {
            // there is a special case for the _generalPurposeBitFlag.Bit #3 
            // it could be set in the local file header indicating streaming
            // creation, while it doesn't need to be set in the Central directory 
            // so having 
            //  (fileBlock.GeneralPurposeBitFlag == 8 && and _generalPurposeBitFlag  == 0)
            // is a valid case when update is not required 

            // let's compare the 3rd bit of the general purpose bit flag
            bool localFileHeaderStreamingFlag = (0 != (fileBlock.GeneralPurposeBitFlag & _streamingBitMask));
            bool centralDirStreamingFlag = (0 != (_generalPurposeBitFlag & _streamingBitMask)); 

            if (!localFileHeaderStreamingFlag  && centralDirStreamingFlag) 
            { 
                // the mismatch if local file header in non streaming but the central directory is in streaming mode
                // all the other combinations do not require an update and valid as is 
                // this includes scenario when local file header is in streaming and central dir is not
                return true;
            }
 
            Debug.Assert(String.CompareOrdinal(_stringFileName, fileBlock.FileName) == 0);
 
            return 
                (_signature != _signatureConstant) ||
                (_versionNeededToExtract != fileBlock.VersionNeededToExtract) || 
                (_generalPurposeBitFlag != fileBlock.GeneralPurposeBitFlag) ||
                (_compressionMethod != (UInt16)fileBlock.CompressionMethod) ||
                (_crc32 != fileBlock.Crc32) ||
                (CompressedSize != fileBlock.CompressedSize) || 
                (UncompressedSize != fileBlock.UncompressedSize) ||
                (OffsetOfLocalHeader != fileBlock.Offset); 
 
                // These fields are intentionally ignored, as they are not present in the local header
                            //_fileCommentLength; 
                            //_fileComment;
                            //_diskNumberStart;
                            //_internalFileAttributes;
                            //_externalFileAttributes; 
        }
 
 
        private ZipIOCentralDirectoryFileHeader(Encoding encoding)
        { 
            _encoding = encoding;
        }

        private void Validate () 
        {
            if (_signature != _signatureConstant) 
            { 
                throw new FileFormatException(SR.Get(SRID.CorruptedData));
            } 

            if (DiskNumberStart != 0)
            {
                throw new NotSupportedException(SR.Get(SRID.NotSupportedMultiDisk)); 
            }
 
            if (_fileNameLength != _fileName.Length) 
            {
                throw new FileFormatException(SR.Get(SRID.CorruptedData)); 
            }

            if (_extraFieldLength != _extraField.Size)
            { 
                throw new FileFormatException(SR.Get(SRID.CorruptedData));
            } 
 
            ZipArchive.VerifyVersionNeededToExtract(_versionNeededToExtract);
 
            // if verson is below 4.5 make sure that ZIP 64 extra filed isn't present
            // if it is it might be a security concern
            if ((_versionNeededToExtract < (UInt16)ZipIOVersionNeededToExtract.Zip64FileFormat) &&
                (_extraField.Zip64ExtraFieldUsage != ZipIOZip64ExtraFieldUsage.None)) 
            {
                throw new FileFormatException(SR.Get(SRID.CorruptedData)); 
            } 

            if (_fileCommentLength != _fileComment.Length) 
            {
                throw new FileFormatException(SR.Get(SRID.CorruptedData));
            }
 
            if ((_compressionMethod != (UInt16)CompressionMethodEnum.Stored) &&
                (_compressionMethod != (UInt16)CompressionMethodEnum.Deflated)) 
            { 
                throw new NotSupportedException(SR.Get(SRID.ZipNotSupportedCompressionMethod));
            } 
        }

        private Encoding _encoding;
        private const int _fixedMinimalRecordSize = 46; 

        private const byte _constantUpperVersionMadeByMsDos = 0x0; 
 
        private const UInt16 _streamingBitMask = 0x08; // bit #3
 
        private const UInt32 _signatureConstant = 0x02014b50;
        private UInt32 _signature = _signatureConstant;

        // we expect all variables to be initialized to 0 
        private UInt16 _versionMadeBy;
        private UInt16 _versionNeededToExtract; 
        private UInt16 _generalPurposeBitFlag; 
        private UInt16 _compressionMethod;
        private UInt32 _lastModFileDateTime; 
        private UInt32 _crc32;
        private UInt32 _compressedSize;
        private UInt32 _uncompressedSize;
        private UInt16 _fileNameLength; 
        private UInt16 _extraFieldLength;
        private UInt16 _fileCommentLength; 
        private UInt16 _diskNumberStart; 
        private UInt16 _internalFileAttributes;
        private UInt32 _externalFileAttributes; 
        private UInt32 _relativeOffsetOfLocalHeader;
        private byte[] _fileName;
        private ZipIOExtraField _extraField;
        private byte[] _fileComment; 

        //duplicate dat for fast access 
        private string _stringFileName; 
    }
} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.

                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK