Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for Filename field in GZip #351

Merged
merged 2 commits into from Apr 27, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
75 changes: 47 additions & 28 deletions src/ICSharpCode.SharpZipLib/GZip/GZipConstants.cs
@@ -1,58 +1,77 @@
using System;
using System.Text;

namespace ICSharpCode.SharpZipLib.GZip
{
/// <summary>
/// This class contains constants used for gzip.
/// </summary>
sealed public class GZipConstants
public static 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 @@ -148,6 +151,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 @@ -169,149 +181,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");
}
Comment on lines -290 to -293
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ReadLeByte() never returns anything but a byte, it will itself throw an exception in the case of an EOS

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