diff --git a/src/ICSharpCode.SharpZipLib/GZip/GZipConstants.cs b/src/ICSharpCode.SharpZipLib/GZip/GZipConstants.cs
index ae700135e..9bab5616d 100644
--- a/src/ICSharpCode.SharpZipLib/GZip/GZipConstants.cs
+++ b/src/ICSharpCode.SharpZipLib/GZip/GZipConstants.cs
@@ -1,3 +1,6 @@
+using System;
+using System.Text;
+
namespace ICSharpCode.SharpZipLib.GZip
{
///
@@ -6,53 +9,68 @@ namespace ICSharpCode.SharpZipLib.GZip
public sealed class GZipConstants
{
///
- /// Magic number found at start of GZIP header
+ /// First GZip identification byte
///
- public const int GZIP_MAGIC = 0x1F8B;
+ public const byte ID1 = 0x1F;
- /* The flag byte is divided into individual bits as follows:
+ ///
+ /// Second GZip identification byte
+ ///
+ 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
- */
+ ///
+ /// Deflate compression method
+ ///
+ public const byte CompressionMethodDeflate = 0x8;
///
- /// Flag bit mask for text
+ /// Get the GZip specified encoding (CP-1252 if supported, otherwise ASCII)
///
- public const int FTEXT = 0x1;
+ public static Encoding Encoding
+ {
+ get
+ {
+ try
+ {
+ return Encoding.GetEncoding(1252);
+ }
+ catch
+ {
+ return Encoding.ASCII;
+ }
+ }
+ }
+ }
+ ///
+ /// GZip header flags
+ ///
+ [Flags]
+ public enum GZipFlags : byte
+ {
///
- /// Flag bitmask for Crc
+ /// Text flag hinting that the file is in ASCII
///
- public const int FHCRC = 0x2;
+ FTEXT = 0x1 << 0,
///
- /// Flag bit mask for extra
+ /// CRC flag indicating that a CRC16 preceeds the data
///
- public const int FEXTRA = 0x4;
+ FHCRC = 0x1 << 1,
///
- /// flag bitmask for name
+ /// Extra flag indicating that extra fields are present
///
- public const int FNAME = 0x8;
+ FEXTRA = 0x1 << 2,
///
- /// flag bit mask indicating comment is present
+ /// Filename flag indicating that the original filename is present
///
- public const int FCOMMENT = 0x10;
+ FNAME = 0x1 << 3,
///
- /// Initialise default instance.
+ /// Flag bit mask indicating that a comment is present
///
- /// Constructor is private to prevent instances being created.
- private GZipConstants()
- {
- }
+ FCOMMENT = 0x1 << 4,
}
}
diff --git a/src/ICSharpCode.SharpZipLib/GZip/GzipInputStream.cs b/src/ICSharpCode.SharpZipLib/GZip/GzipInputStream.cs
index e4a906e6f..aba619ad2 100644
--- a/src/ICSharpCode.SharpZipLib/GZip/GzipInputStream.cs
+++ b/src/ICSharpCode.SharpZipLib/GZip/GzipInputStream.cs
@@ -54,6 +54,8 @@ public class GZipInputStream : InflaterInputStream
///
private bool completedLastBlock;
+ private string fileName;
+
#endregion Instance Fields
#region Constructors
@@ -151,6 +153,15 @@ public override int Read(byte[] buffer, int offset, int count)
#endregion Stream overrides
+ ///
+ /// Retrieves the filename header field for the block last read
+ ///
+ ///
+ public string GetFilename()
+ {
+ return fileName;
+ }
+
#region Support routines
private bool ReadHeader()
@@ -170,132 +181,86 @@ 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)
@@ -303,16 +268,11 @@ bit 7 reserved
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();
diff --git a/src/ICSharpCode.SharpZipLib/GZip/GzipOutputStream.cs b/src/ICSharpCode.SharpZipLib/GZip/GzipOutputStream.cs
index 796a5a401..83b0e4894 100644
--- a/src/ICSharpCode.SharpZipLib/GZip/GzipOutputStream.cs
+++ b/src/ICSharpCode.SharpZipLib/GZip/GzipOutputStream.cs
@@ -53,6 +53,10 @@ private enum OutputState
private OutputState state_ = OutputState.Header;
+ private string fileName;
+
+ private GZipFlags flags = 0;
+
#endregion Instance Fields
#region Constructors
@@ -111,6 +115,26 @@ public int GetLevel()
return deflater_.GetLevel();
}
+ ///
+ /// Original filename
+ ///
+ 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
@@ -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)
@@ -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),
@@ -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);
+ }
}
}
diff --git a/src/ICSharpCode.SharpZipLib/Zip/Compression/Streams/InflaterInputStream.cs b/src/ICSharpCode.SharpZipLib/Zip/Compression/Streams/InflaterInputStream.cs
index b5360e248..73f377233 100644
--- a/src/ICSharpCode.SharpZipLib/Zip/Compression/Streams/InflaterInputStream.cs
+++ b/src/ICSharpCode.SharpZipLib/Zip/Compression/Streams/InflaterInputStream.cs
@@ -226,7 +226,7 @@ public int ReadClearTextBuffer(byte[] outBuffer, int offset, int length)
/// Read a from the input stream.
///
/// Returns the byte read.
- public int ReadLeByte()
+ public byte ReadLeByte()
{
if (available <= 0)
{