Skip to content

Commit 045aba6

Browse files
committed
1 parent 9d0941a commit 045aba6

File tree

4 files changed

+132
-116
lines changed

4 files changed

+132
-116
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
using System;
2+
using System.Text;
3+
14
namespace ICSharpCode.SharpZipLib.GZip
25
{
36
/// <summary>
@@ -6,53 +9,68 @@ namespace ICSharpCode.SharpZipLib.GZip
69
public sealed class GZipConstants
710
{
811
/// <summary>
9-
/// Magic number found at start of GZIP header
12+
/// First GZip identification byte
1013
/// </summary>
11-
public const int GZIP_MAGIC = 0x1F8B;
14+
public const byte ID1 = 0x1F;
1215

13-
/* The flag byte is divided into individual bits as follows:
16+
/// <summary>
17+
/// Second GZip identification byte
18+
/// </summary>
19+
public const byte ID2 = 0x8B;
1420

15-
bit 0 FTEXT
16-
bit 1 FHCRC
17-
bit 2 FEXTRA
18-
bit 3 FNAME
19-
bit 4 FCOMMENT
20-
bit 5 reserved
21-
bit 6 reserved
22-
bit 7 reserved
23-
*/
21+
/// <summary>
22+
/// Deflate compression method
23+
/// </summary>
24+
public const byte CompressionMethodDeflate = 0x8;
2425

2526
/// <summary>
26-
/// Flag bit mask for text
27+
/// Get the GZip specified encoding (CP-1252 if supported, otherwise ASCII)
2728
/// </summary>
28-
public const int FTEXT = 0x1;
29+
public static Encoding Encoding
30+
{
31+
get
32+
{
33+
try
34+
{
35+
return Encoding.GetEncoding(1252);
36+
}
37+
catch
38+
{
39+
return Encoding.ASCII;
40+
}
41+
}
42+
}
43+
}
2944

45+
/// <summary>
46+
/// GZip header flags
47+
/// </summary>
48+
[Flags]
49+
public enum GZipFlags : byte
50+
{
3051
/// <summary>
31-
/// Flag bitmask for Crc
52+
/// Text flag hinting that the file is in ASCII
3253
/// </summary>
33-
public const int FHCRC = 0x2;
54+
FTEXT = 0x1 << 0,
3455

3556
/// <summary>
36-
/// Flag bit mask for extra
57+
/// CRC flag indicating that a CRC16 preceeds the data
3758
/// </summary>
38-
public const int FEXTRA = 0x4;
59+
FHCRC = 0x1 << 1,
3960

4061
/// <summary>
41-
/// flag bitmask for name
62+
/// Extra flag indicating that extra fields are present
4263
/// </summary>
43-
public const int FNAME = 0x8;
64+
FEXTRA = 0x1 << 2,
4465

4566
/// <summary>
46-
/// flag bit mask indicating comment is present
67+
/// Filename flag indicating that the original filename is present
4768
/// </summary>
48-
public const int FCOMMENT = 0x10;
69+
FNAME = 0x1 << 3,
4970

5071
/// <summary>
51-
/// Initialise default instance.
72+
/// Flag bit mask indicating that a comment is present
5273
/// </summary>
53-
/// <remarks>Constructor is private to prevent instances being created.</remarks>
54-
private GZipConstants()
55-
{
56-
}
74+
FCOMMENT = 0x1 << 4,
5775
}
5876
}

src/ICSharpCode.SharpZipLib/GZip/GzipInputStream.cs

+44-84
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ public class GZipInputStream : InflaterInputStream
5454
/// </summary>
5555
private bool completedLastBlock;
5656

57+
private string fileName;
58+
5759
#endregion Instance Fields
5860

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

152154
#endregion Stream overrides
153155

156+
/// <summary>
157+
/// Retrieves the filename header field for the block last read
158+
/// </summary>
159+
/// <returns></returns>
160+
public string GetFilename()
161+
{
162+
return fileName;
163+
}
164+
154165
#region Support routines
155166

156167
private bool ReadHeader()
@@ -170,149 +181,98 @@ private bool ReadHeader()
170181
}
171182
}
172183

173-
// 1. Check the two magic bytes
174184
var headCRC = new Crc32();
175-
int magic = inputBuffer.ReadLeByte();
176-
177-
if (magic < 0)
178-
{
179-
throw new EndOfStreamException("EOS reading GZIP header");
180-
}
181185

186+
// 1. Check the two magic bytes
187+
var magic = inputBuffer.ReadLeByte();
182188
headCRC.Update(magic);
183-
if (magic != (GZipConstants.GZIP_MAGIC >> 8))
189+
190+
if (magic != GZipConstants.ID1)
184191
{
185192
throw new GZipException("Error GZIP header, first magic byte doesn't match");
186193
}
187194

188-
//magic = baseInputStream.ReadByte();
189195
magic = inputBuffer.ReadLeByte();
190-
191-
if (magic < 0)
192-
{
193-
throw new EndOfStreamException("EOS reading GZIP header");
194-
}
195-
196-
if (magic != (GZipConstants.GZIP_MAGIC & 0xFF))
196+
if (magic != GZipConstants.ID2)
197197
{
198-
throw new GZipException("Error GZIP header, second magic byte doesn't match");
198+
throw new GZipException("Error GZIP header, second magic byte doesn't match");
199199
}
200-
201200
headCRC.Update(magic);
202201

203202
// 2. Check the compression type (must be 8)
204-
int compressionType = inputBuffer.ReadLeByte();
205-
206-
if (compressionType < 0)
207-
{
208-
throw new EndOfStreamException("EOS reading GZIP header");
209-
}
210-
211-
if (compressionType != 8)
203+
var compressionType = inputBuffer.ReadLeByte();
204+
if (compressionType != GZipConstants.CompressionMethodDeflate)
212205
{
213206
throw new GZipException("Error GZIP header, data not in deflate format");
214207
}
215208
headCRC.Update(compressionType);
216209

217210
// 3. Check the flags
218-
int flags = inputBuffer.ReadLeByte();
219-
if (flags < 0)
220-
{
221-
throw new EndOfStreamException("EOS reading GZIP header");
222-
}
223-
headCRC.Update(flags);
224-
225-
/* This flag byte is divided into individual bits as follows:
226-
227-
bit 0 FTEXT
228-
bit 1 FHCRC
229-
bit 2 FEXTRA
230-
bit 3 FNAME
231-
bit 4 FCOMMENT
232-
bit 5 reserved
233-
bit 6 reserved
234-
bit 7 reserved
235-
*/
211+
var flagsByte = inputBuffer.ReadLeByte();
212+
headCRC.Update(flagsByte);
236213

237214
// 3.1 Check the reserved bits are zero
238-
239-
if ((flags & 0xE0) != 0)
240-
{
215+
if ((flagsByte & 0xE0) != 0)
241216
throw new GZipException("Reserved flag bits in GZIP header != 0");
242-
}
217+
218+
var flags = (GZipFlags)flagsByte;
243219

244220
// 4.-6. Skip the modification time, extra flags, and OS type
245221
for (int i = 0; i < 6; i++)
246-
{
247-
int readByte = inputBuffer.ReadLeByte();
248-
if (readByte < 0)
249-
{
250-
throw new EndOfStreamException("EOS reading GZIP header");
251-
}
252-
headCRC.Update(readByte);
253-
}
222+
headCRC.Update(inputBuffer.ReadLeByte());
254223

255224
// 7. Read extra field
256-
if ((flags & GZipConstants.FEXTRA) != 0)
225+
if (flags.HasFlag(GZipFlags.FEXTRA))
257226
{
258227
// XLEN is total length of extra subfields, we will skip them all
259-
int len1, len2;
260-
len1 = inputBuffer.ReadLeByte();
261-
len2 = inputBuffer.ReadLeByte();
262-
if ((len1 < 0) || (len2 < 0))
263-
{
264-
throw new EndOfStreamException("EOS reading GZIP header");
265-
}
228+
var len1 = inputBuffer.ReadLeByte();
229+
var len2 = inputBuffer.ReadLeByte();
266230
headCRC.Update(len1);
267231
headCRC.Update(len2);
268232

269233
int extraLen = (len2 << 8) | len1; // gzip is LSB first
270234
for (int i = 0; i < extraLen; i++)
271-
{
272-
int readByte = inputBuffer.ReadLeByte();
273-
if (readByte < 0)
274-
{
275-
throw new EndOfStreamException("EOS reading GZIP header");
276-
}
277-
headCRC.Update(readByte);
278-
}
235+
headCRC.Update(inputBuffer.ReadLeByte());
279236
}
280237

281238
// 8. Read file name
282-
if ((flags & GZipConstants.FNAME) != 0)
239+
if (flags.HasFlag(GZipFlags.FNAME))
283240
{
241+
var fname = new byte[1024];
242+
var fnamePos = 0;
284243
int readByte;
285244
while ((readByte = inputBuffer.ReadLeByte()) > 0)
286245
{
246+
if (fnamePos < 1024)
247+
{
248+
fname[fnamePos++] = (byte)readByte;
249+
}
287250
headCRC.Update(readByte);
288251
}
289252

290-
if (readByte < 0)
291-
{
292-
throw new EndOfStreamException("EOS reading GZIP header");
293-
}
294253
headCRC.Update(readByte);
254+
255+
fileName = GZipConstants.Encoding.GetString(fname, 0, fnamePos);
256+
}
257+
else
258+
{
259+
fileName = null;
295260
}
296261

297262
// 9. Read comment
298-
if ((flags & GZipConstants.FCOMMENT) != 0)
263+
if (flags.HasFlag(GZipFlags.FCOMMENT))
299264
{
300265
int readByte;
301266
while ((readByte = inputBuffer.ReadLeByte()) > 0)
302267
{
303268
headCRC.Update(readByte);
304269
}
305270

306-
if (readByte < 0)
307-
{
308-
throw new EndOfStreamException("EOS reading GZIP header");
309-
}
310-
311271
headCRC.Update(readByte);
312272
}
313273

314274
// 10. Read header CRC
315-
if ((flags & GZipConstants.FHCRC) != 0)
275+
if (flags.HasFlag(GZipFlags.FHCRC))
316276
{
317277
int tempByte;
318278
int crcval = inputBuffer.ReadLeByte();

src/ICSharpCode.SharpZipLib/GZip/GzipOutputStream.cs

+42-4
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@ private enum OutputState
5353

5454
private OutputState state_ = OutputState.Header;
5555

56+
private string fileName;
57+
58+
private GZipFlags flags = 0;
59+
5660
#endregion Instance Fields
5761

5862
#region Constructors
@@ -111,6 +115,26 @@ public int GetLevel()
111115
return deflater_.GetLevel();
112116
}
113117

118+
/// <summary>
119+
/// Original filename
120+
/// </summary>
121+
public string FileName
122+
{
123+
get => fileName;
124+
set
125+
{
126+
fileName = CleanFilename(value);
127+
if (string.IsNullOrEmpty(fileName))
128+
{
129+
flags &= ~GZipFlags.FNAME;
130+
}
131+
else
132+
{
133+
flags |= GZipFlags.FNAME;
134+
}
135+
}
136+
}
137+
114138
#endregion Public API
115139

116140
#region Stream overrides
@@ -218,6 +242,9 @@ public override void Finish()
218242

219243
#region Support Routines
220244

245+
private string CleanFilename(string path)
246+
=> path.Substring(path.LastIndexOf('/') + 1);
247+
221248
private void WriteHeader()
222249
{
223250
if (state_ == OutputState.Header)
@@ -227,13 +254,14 @@ private void WriteHeader()
227254
var mod_time = (int)((DateTime.Now.Ticks - new DateTime(1970, 1, 1).Ticks) / 10000000L); // Ticks give back 100ns intervals
228255
byte[] gzipHeader = {
229256
// The two magic bytes
230-
GZipConstants.GZIP_MAGIC >> 8, GZipConstants.GZIP_MAGIC & 0xff,
257+
GZipConstants.ID1,
258+
GZipConstants.ID2,
231259

232260
// The compression type
233-
Deflater.DEFLATED,
261+
GZipConstants.CompressionMethodDeflate,
234262

235263
// The flags (not set)
236-
0,
264+
(byte)flags,
237265

238266
// The modification time
239267
(byte) mod_time, (byte) (mod_time >> 8),
@@ -243,9 +271,19 @@ private void WriteHeader()
243271
0,
244272

245273
// The OS type (unknown)
246-
255
274+
255
247275
};
276+
248277
baseOutputStream_.Write(gzipHeader, 0, gzipHeader.Length);
278+
279+
if (flags.HasFlag(GZipFlags.FNAME))
280+
{
281+
var fname = GZipConstants.Encoding.GetBytes(fileName);
282+
baseOutputStream_.Write(fname, 0, fname.Length);
283+
284+
// End filename string with a \0
285+
baseOutputStream_.Write(new byte[] { 0 }, 0, 1);
286+
}
249287
}
250288
}
251289

0 commit comments

Comments
 (0)