File: Base\MS\Internal\IO\Zip\ZipIOExtraFieldElement.cs
Project: wpf\src\WindowsBase.csproj (WindowsBase)
//-----------------------------------------------------------------------------
//-------------   *** 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 file="ZipIOExtraFieldElement.cs" company="Microsoft">
//    Copyright (C) Microsoft Corporation.  All rights reserved.
// </copyright>
//
// Description:
//  This is an internal class that is used to implement parsing and editing of a generic extra field structure.   
//  It doesn't contain informaton about any specific (like Zip64) extra fields that are supported by 
//  this implementation. In order to isolate this from IO and streams, it deals with the data in the Byte[] form 
//
// History:
//  11/19/2004: IgorBel: Initial creation.
//  03/23/2006: Microsoft: Added support for Padding Extra Field
//
//-----------------------------------------------------------------------------
 
using System;
using System.IO;
using System.Diagnostics;
using System.Collections;
using System.Globalization;
using System.Runtime.Serialization;
using System.Windows;
 
using MS.Internal.IO.Packaging;
using MS.Internal.WindowsBase;
 
namespace MS.Internal.IO.Zip
{
 
    /// <summary>
    ///  This is an internal class that is used to implement parsing and editing of generic extra field structures.   
    ///  It doesn't contain informaton about any specific (like Zip64) extra fields that are supported by 
    ///  this implementation. In order to isolate this from IO and streams, it deals with the data in the Byte[] form 
    /// </summary>
    internal class ZipIOExtraFieldElement
    {
        internal static ZipIOExtraFieldElement Parse(BinaryReader reader, ZipIOZip64ExtraFieldUsage zip64extraFieldUsage)
        {
            // we can safely parse the Id and Size here
            //  since the minimum size is checked from the caller (ZipIoExtraField)
            UInt16 id = reader.ReadUInt16();
            UInt16 size = reader.ReadUInt16();
 
            ZipIOExtraFieldElement newElement;
 
            if (id == ZipIOExtraFieldZip64Element.ConstantFieldId)  // Found Zip64 Extra Field element
            {
                newElement = new ZipIOExtraFieldZip64Element();
                ((ZipIOExtraFieldZip64Element) newElement).Zip64ExtraFieldUsage = zip64extraFieldUsage;
            }
            else if (id == ZipIOExtraFieldPaddingElement.ConstantFieldId)   // Found potential Padding Extra Field element
            {
                // Even if the id matches Padding Extra Element id, we cannot be sure if it is Microsoft defined one
                // until its signature is verified
 
                // This extra field matches Padding Extra Element id, but it is not big enough to be ours
                if (size < ZipIOExtraFieldPaddingElement.MinimumFieldDataSize)
                {
                    // Treat it as an unknown Extra Field element
                    newElement = new ZipIOExtraFieldElement(id);
                }
                else
                {
                    // Sniff bytes to check it it matches our Padding extra field signature
                    byte[] sniffedBytes = reader.ReadBytes(ZipIOExtraFieldPaddingElement.SignatureSize);
                    if (ZipIOExtraFieldPaddingElement.MatchesPaddingSignature(sniffedBytes))
                    {
                        // Signature matches the Padding Extra Field signature
                        newElement = new ZipIOExtraFieldPaddingElement();
                    }
                    else
                    {
                        // Signature doesn't match; treat it a an unknown Extra Field element
                        newElement = new ZipIOExtraFieldElement(id, sniffedBytes);
                    }
                }
            }
            else
            {
                newElement = new ZipIOExtraFieldElement(id);
            }
 
            // Parse the data field of the Extra Field element
            newElement.ParseDataField(reader, size);
 
            return newElement;
        }
 
        internal virtual void ParseDataField(BinaryReader reader, UInt16 size)
        {
            if (_data == null)
            {
                _data = reader.ReadBytes(size);
 
                //vaiadte that we didn't reach the end of stream too early 
                if (_data.Length != size)
                {
                    throw new FileFormatException(SR.Get(SRID.CorruptedData));
                }
            }
            else    // There were some data we sniffed already
            {
                Byte[] tempBuffer =  _data;
                _data = new Byte[size];
 
                Array.Copy(tempBuffer, _data, _size);   // _size contains the size of data in _data
 
                checked
                {
                    Debug.Assert(size >= _size);
 
                    if ((PackagingUtilities.ReliableRead(reader, _data, _size, size - _size) + _size) != size)
                        throw new FileFormatException(SR.Get(SRID.CorruptedData));
                }
            }
            _size = size;
        }
 
        internal virtual void Save(BinaryWriter writer)
        {
            Debug.Assert(_size == _data.Length);
 
            writer.Write(_id);
            writer.Write(_size);
            writer.Write(_data);
        }
     
        // This property calculates the value of the size record whch holds the size without the Id and without the size itself.
        // we are alkways guranteed that   Size == SizeField + 2 * sizeof(UInt16))
        internal virtual UInt16 SizeField
        {
            get
            {
                return _size;
            }
        }            
 
        // this fiels needs to be used very carefully as it retuns a writeble array 
        // element of which could be potentially modifyed by the caller 
        internal virtual byte[] DataField
        {
            get
            {
                return _data;
            }
        }            
 
        // This property calculates size of the field on disk (how many bytes need to be allocated on the disk)
        internal virtual UInt16 Size
        {
            get
            {
                return checked((UInt16) (_size +  _minimumSize)); // the real field size has 2 UInt16 fields for Id and size 
            }
        }
 
        // Minimum size of any type of Extra Field Element
        //  UInt16 id
        //  UInt16 size
        internal static UInt16 MinimumSize
        {
            get
            {
                return _minimumSize;
            }
        }
 
        //------------------------------------------------------
        //
        //  Private Constructor 
        //
        //------------------------------------------------------
        internal ZipIOExtraFieldElement(UInt16 id)
        {
            _id = id;
        }
 
        // data: sniffied data field
        private ZipIOExtraFieldElement(UInt16 id, byte[] data)
        {
            Debug.Assert(data != null);
 
            _id = id;
            _data = data;
            _size = checked((UInt16) data.Length);
        }
 
        //------------------------------------------------------
        //
        //  Private fields 
        //
        //------------------------------------------------------
        private UInt16 _id;
        private UInt16 _size;
        private byte[] _data;
 
        // UInt16 id:   2
        // UInt16 size: 2
        private static readonly UInt16 _minimumSize = (UInt16) (2 * sizeof(UInt16));
    }
}