Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
HowToDoThis committed Aug 11, 2021
1 parent 9d0941a commit 045aba6
Show file tree
Hide file tree
Showing 4 changed files with 132 additions and 116 deletions.
72 changes: 45 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 @@ -6,53 +9,68 @@ namespace ICSharpCode.SharpZipLib.GZip
public sealed 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,
}
}
128 changes: 44 additions & 84 deletions src/ICSharpCode.SharpZipLib/GZip/GzipInputStream.cs
Expand Up @@ -54,6 +54,8 @@ public class GZipInputStream : InflaterInputStream
/// </summary>
private bool completedLastBlock;

private string fileName;

#endregion Instance Fields

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

#endregion Stream overrides

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

#region Support routines

private bool ReadHeader()
Expand All @@ -170,149 +181,98 @@ 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");
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");
}

if (compressionType != 8)
var compressionType = inputBuffer.ReadLeByte();
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:
bit 0 FTEXT
bit 1 FHCRC
bit 2 FEXTRA
bit 3 FNAME
bit 4 FCOMMENT
bit 5 reserved
bit 6 reserved
bit 7 reserved
*/
var flagsByte = inputBuffer.ReadLeByte();
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
46 changes: 42 additions & 4 deletions src/ICSharpCode.SharpZipLib/GZip/GzipOutputStream.cs
Expand Up @@ -53,6 +53,10 @@ private enum OutputState

private OutputState state_ = OutputState.Header;

private string fileName;

private GZipFlags flags = 0;

#endregion Instance Fields

#region Constructors
Expand Down Expand Up @@ -111,6 +115,26 @@ public int GetLevel()
return deflater_.GetLevel();
}

/// <summary>
/// Original filename
/// </summary>
public string FileName
{
get => fileName;
set
{
fileName = CleanFilename(value);
if (string.IsNullOrEmpty(fileName))
{
flags &= ~GZipFlags.FNAME;
}
else
{
flags |= GZipFlags.FNAME;
}
}
}

#endregion Public API

#region Stream overrides
Expand Down Expand Up @@ -218,6 +242,9 @@ public override void Finish()

#region Support Routines

private string CleanFilename(string path)
=> path.Substring(path.LastIndexOf('/') + 1);

private void WriteHeader()
{
if (state_ == OutputState.Header)
Expand All @@ -227,13 +254,14 @@ private void WriteHeader()
var mod_time = (int)((DateTime.Now.Ticks - new DateTime(1970, 1, 1).Ticks) / 10000000L); // Ticks give back 100ns intervals
byte[] gzipHeader = {
// The two magic bytes
GZipConstants.GZIP_MAGIC >> 8, GZipConstants.GZIP_MAGIC & 0xff,
GZipConstants.ID1,
GZipConstants.ID2,

// The compression type
Deflater.DEFLATED,
GZipConstants.CompressionMethodDeflate,

// The flags (not set)
0,
(byte)flags,

// The modification time
(byte) mod_time, (byte) (mod_time >> 8),
Expand All @@ -243,9 +271,19 @@ private void WriteHeader()
0,

// The OS type (unknown)
255
255
};

baseOutputStream_.Write(gzipHeader, 0, gzipHeader.Length);

if (flags.HasFlag(GZipFlags.FNAME))
{
var fname = GZipConstants.Encoding.GetBytes(fileName);
baseOutputStream_.Write(fname, 0, fname.Length);

// End filename string with a \0
baseOutputStream_.Write(new byte[] { 0 }, 0, 1);
}
}
}

Expand Down

0 comments on commit 045aba6

Please sign in to comment.