Skip to content

Commit

Permalink
PR 351: Add support for Filename field in GZip
Browse files Browse the repository at this point in the history
  • Loading branch information
piksel committed Apr 27, 2021
1 parent 8ab21b0 commit d9fb8a4
Show file tree
Hide file tree
Showing 5 changed files with 171 additions and 105 deletions.
73 changes: 46 additions & 27 deletions src/ICSharpCode.SharpZipLib/GZip/GZipConstants.cs
@@ -1,3 +1,6 @@
using System;
using System.Text;

namespace ICSharpCode.SharpZipLib.GZip
{
/// <summary>
Expand All @@ -7,53 +10,69 @@ namespace ICSharpCode.SharpZipLib.GZip
sealed public class GZipConstants
{
/// <summary>
/// Magic number found at start of GZIP header
/// First GZip identification byte
/// </summary>
public const int GZIP_MAGIC = 0x1F8B;
public const byte ID1 = 0x1F;

/* The flag byte is divided into individual bits as follows:
/// <summary>
/// Second GZip identification byte
/// </summary>
public const byte ID2 = 0x8B;

bit 0 FTEXT
bit 1 FHCRC
bit 2 FEXTRA
bit 3 FNAME
bit 4 FCOMMENT
bit 5 reserved
bit 6 reserved
bit 7 reserved
*/
/// <summary>
/// Deflate compression method
/// </summary>
public const byte CompressionMethodDeflate = 0x8;

/// <summary>
/// Flag bit mask for text
/// Get the GZip specified encoding (CP-1252 if supported, otherwise ASCII)
/// </summary>
public const int FTEXT = 0x1;
public static Encoding Encoding
{
get
{
try
{
return Encoding.GetEncoding(1252);
}
catch
{
return Encoding.ASCII;
}
}
}

}

/// <summary>
/// GZip header flags
/// </summary>
[Flags]
public enum GZipFlags: byte
{
/// <summary>
/// Flag bitmask for Crc
/// Text flag hinting that the file is in ASCII
/// </summary>
public const int FHCRC = 0x2;
FTEXT = 0x1 << 0,

/// <summary>
/// Flag bit mask for extra
/// CRC flag indicating that a CRC16 preceeds the data
/// </summary>
public const int FEXTRA = 0x4;
FHCRC = 0x1 << 1,

/// <summary>
/// flag bitmask for name
/// Extra flag indicating that extra fields are present
/// </summary>
public const int FNAME = 0x8;
FEXTRA = 0x1 << 2,

/// <summary>
/// flag bit mask indicating comment is present
/// Filename flag indicating that the original filename is present
/// </summary>
public const int FCOMMENT = 0x10;
FNAME = 0x1 << 3,

/// <summary>
/// Initialise default instance.
/// Flag bit mask indicating that a comment is present
/// </summary>
/// <remarks>Constructor is private to prevent instances being created.</remarks>
private GZipConstants()
{
}
FCOMMENT = 0x1 << 4,
}
}
117 changes: 44 additions & 73 deletions src/ICSharpCode.SharpZipLib/GZip/GzipInputStream.cs
Expand Up @@ -3,6 +3,7 @@
using ICSharpCode.SharpZipLib.Zip.Compression.Streams;
using System;
using System.IO;
using System.Text;

namespace ICSharpCode.SharpZipLib.GZip
{
Expand Down Expand Up @@ -54,6 +55,8 @@ public class GZipInputStream : InflaterInputStream
/// </summary>
private bool completedLastBlock;

private string fileName;

#endregion Instance Fields

#region Constructors
Expand Down Expand Up @@ -149,6 +152,15 @@ public override int Read(byte[] buffer, int offset, int count)
}
}

/// <summary>
/// Retrieves the filename header field for the block last read
/// </summary>
/// <returns></returns>
public string GetFilename()
{
return fileName;
}

#endregion Stream overrides

#region Support routines
Expand All @@ -170,149 +182,108 @@ private bool ReadHeader()
}
}

// 1. Check the two magic bytes
var headCRC = new Crc32();
int magic = inputBuffer.ReadLeByte();

if (magic < 0)
{
throw new EndOfStreamException("EOS reading GZIP header");
}
// 1. Check the two magic bytes

var magic = inputBuffer.ReadLeByte();
headCRC.Update(magic);
if (magic != (GZipConstants.GZIP_MAGIC >> 8))
if (magic != GZipConstants.ID1)
{
throw new GZipException("Error GZIP header, first magic byte doesn't match");
}

//magic = baseInputStream.ReadByte();
magic = inputBuffer.ReadLeByte();

if (magic < 0)
{
throw new EndOfStreamException("EOS reading GZIP header");
}

if (magic != (GZipConstants.GZIP_MAGIC & 0xFF))
if (magic != GZipConstants.ID2)
{
throw new GZipException("Error GZIP header, second magic byte doesn't match");
}

headCRC.Update(magic);

// 2. Check the compression type (must be 8)
int compressionType = inputBuffer.ReadLeByte();

if (compressionType < 0)
{
throw new EndOfStreamException("EOS reading GZIP header");
}
var compressionType = inputBuffer.ReadLeByte();

if (compressionType != 8)
if (compressionType != GZipConstants.CompressionMethodDeflate)
{
throw new GZipException("Error GZIP header, data not in deflate format");
}
headCRC.Update(compressionType);

// 3. Check the flags
int flags = inputBuffer.ReadLeByte();
if (flags < 0)
{
throw new EndOfStreamException("EOS reading GZIP header");
}
headCRC.Update(flags);

/* This flag byte is divided into individual bits as follows:
var flagsByte = inputBuffer.ReadLeByte();

bit 0 FTEXT
bit 1 FHCRC
bit 2 FEXTRA
bit 3 FNAME
bit 4 FCOMMENT
bit 5 reserved
bit 6 reserved
bit 7 reserved
*/
headCRC.Update(flagsByte);

// 3.1 Check the reserved bits are zero

if ((flags & 0xE0) != 0)
if ((flagsByte & 0xE0) != 0)
{
throw new GZipException("Reserved flag bits in GZIP header != 0");
}

var flags = (GZipFlags)flagsByte;

// 4.-6. Skip the modification time, extra flags, and OS type
for (int i = 0; i < 6; i++)
{
int readByte = inputBuffer.ReadLeByte();
if (readByte < 0)
{
throw new EndOfStreamException("EOS reading GZIP header");
}
headCRC.Update(readByte);
headCRC.Update(inputBuffer.ReadLeByte());
}

// 7. Read extra field
if ((flags & GZipConstants.FEXTRA) != 0)
if (flags.HasFlag(GZipFlags.FEXTRA))
{
// XLEN is total length of extra subfields, we will skip them all
int len1, len2;
len1 = inputBuffer.ReadLeByte();
len2 = inputBuffer.ReadLeByte();
if ((len1 < 0) || (len2 < 0))
{
throw new EndOfStreamException("EOS reading GZIP header");
}
var len1 = inputBuffer.ReadLeByte();
var len2 = inputBuffer.ReadLeByte();

headCRC.Update(len1);
headCRC.Update(len2);

int extraLen = (len2 << 8) | len1; // gzip is LSB first
for (int i = 0; i < extraLen; i++)
{
int readByte = inputBuffer.ReadLeByte();
if (readByte < 0)
{
throw new EndOfStreamException("EOS reading GZIP header");
}
headCRC.Update(readByte);
headCRC.Update(inputBuffer.ReadLeByte());
}
}

// 8. Read file name
if ((flags & GZipConstants.FNAME) != 0)
if (flags.HasFlag(GZipFlags.FNAME))
{
var fname = new byte[1024];
var fnamePos = 0;
int readByte;
while ((readByte = inputBuffer.ReadLeByte()) > 0)
{
if (fnamePos < 1024)
{
fname[fnamePos++] = (byte)readByte;
}
headCRC.Update(readByte);
}

if (readByte < 0)
{
throw new EndOfStreamException("EOS reading GZIP header");
}
headCRC.Update(readByte);

fileName = GZipConstants.Encoding.GetString(fname, 0, fnamePos);
}
else
{
fileName = null;
}

// 9. Read comment
if ((flags & GZipConstants.FCOMMENT) != 0)
if (flags.HasFlag(GZipFlags.FCOMMENT))
{
int readByte;
while ((readByte = inputBuffer.ReadLeByte()) > 0)
{
headCRC.Update(readByte);
}

if (readByte < 0)
{
throw new EndOfStreamException("EOS reading GZIP header");
}

headCRC.Update(readByte);
}

// 10. Read header CRC
if ((flags & GZipConstants.FHCRC) != 0)
if (flags.HasFlag(GZipFlags.FHCRC))
{
int tempByte;
int crcval = inputBuffer.ReadLeByte();
Expand Down

0 comments on commit d9fb8a4

Please sign in to comment.