File: Signing\Archive\ExtraField.cs
Web Access
Project: src\src\nuget-client\src\NuGet.Core\NuGet.Packaging\NuGet.Packaging.csproj (NuGet.Packaging)
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;

// ZIP specification: http://www.pkware.com/documents/casestudies/APPNOTE.TXT

namespace NuGet.Packaging.Signing
{
    internal class ExtraField
    {
        internal ushort HeaderId { get; private set; }
        internal ushort DataSize { get; private set; }
        internal byte[] Data { get; private set; }

        protected ExtraField(ushort headerId, ushort dataSize, byte[] data)
        {
            HeaderId = headerId;
            DataSize = dataSize;
            Data = data;
        }

        internal static bool TryRead(CentralDirectoryHeader header, [NotNullWhen(returnValue: true)] out IReadOnlyList<ExtraField>? extraFields)
        {
            extraFields = null;

            if (header.ExtraFieldLength == 0)
            {
                return false;
            }

            var readUncompressedFileSize = header.UncompressedSize == ZipConstants.Mask32Bit;
            var readCompressedFileSize = header.CompressedSize == ZipConstants.Mask32Bit;
            var readRelativeOffsetOfLocalHeader = header.RelativeOffsetOfLocalHeader == ZipConstants.Mask32Bit;
            var readDiskNumberStart = header.DiskNumberStart == ZipConstants.Mask16Bit;

            return TryRead(
                header.ExtraField,
                readUncompressedFileSize,
                readCompressedFileSize,
                readRelativeOffsetOfLocalHeader,
                readDiskNumberStart,
                out extraFields);
        }

        internal static bool TryRead(LocalFileHeader header, [NotNullWhen(returnValue: true)] out IReadOnlyList<ExtraField>? extraFields)
        {
            extraFields = null;

            if (header.ExtraFieldLength == 0)
            {
                return false;
            }

            var readUncompressedFileSize = header.UncompressedSize == ZipConstants.Mask32Bit;
            var readCompressedFileSize = header.CompressedSize == ZipConstants.Mask32Bit;

            return TryRead(
                header.ExtraField,
                readUncompressedFileSize,
                readCompressedFileSize,
                readRelativeOffsetOfLocalHeader: false,
                readDiskNumberStart: false,
                extraFields: out extraFields);
        }

        private static bool TryRead(
            byte[] extraFieldBytes,
            bool readUncompressedFileSize,
            bool readCompressedFileSize,
            bool readRelativeOffsetOfLocalHeader,
            bool readDiskNumberStart, out IReadOnlyList<ExtraField> extraFields)
        {
            var fields = new List<ExtraField>();

            extraFields = fields;

            using (var stream = new MemoryStream(extraFieldBytes))
            using (var reader = new BinaryReader(stream))
            {
                while (stream.Position < stream.Length - 1)
                {
                    var headerId = reader.ReadUInt16();
                    var dataSize = reader.ReadUInt16();
                    var data = reader.ReadBytes(dataSize);
                    ExtraField extraField;

                    if (headerId == 0x0001)
                    {
                        extraField = Zip64ExtendedInformationExtraField.Read(
                            headerId,
                            dataSize,
                            data,
                            readUncompressedFileSize,
                            readCompressedFileSize,
                            readRelativeOffsetOfLocalHeader,
                            readDiskNumberStart);
                    }
                    else
                    {
                        extraField = new ExtraField(headerId, dataSize, data);
                    }

                    fields.Add(extraField);
                }
            }

            return true;
        }
    }
}