示例#1
0
        /// <inheritdoc />
        public override void Write(byte[] buffer, int offset, int count)
        {
            // Implementation caveat:
            // The .NET GZipStream/DeflateStream implementation, as of .NET 4.7.1, writes the entire header at once.
            // This method depends on that functionality.

            if (Position == 0 && count >= 10)
            {
                var modifiedBuffer = new List <byte>();
                var i = offset;
                modifiedBuffer.Add(buffer[i++]); // should be 31/0x1f (GZIP constant)
                modifiedBuffer.Add(buffer[i++]); // should be 139/0x8b (GZIP constant)
                modifiedBuffer.Add(buffer[i++]); // should be 8 (deflate)
                var flags         = (GzipFlags)buffer[i++];
                var writeFileName = false;
                if (!flags.HasFlag(GzipFlags.FNAME) && !string.IsNullOrWhiteSpace(InternalFilename))
                {
                    writeFileName = true;
                    flags         = flags | GzipFlags.FNAME;
                }

                var writeComment = false;
                if (!flags.HasFlag(GzipFlags.FCOMMENT) && !string.IsNullOrWhiteSpace(InternalComment))
                {
                    writeComment = true;
                    flags        = flags | GzipFlags.FCOMMENT;
                }

                var addingHeaderCrc = true;
                if (AddOrCheckHeaderCrc)
                {
                    if (flags.HasFlag(GzipFlags.FHCRC))
                    {
                        addingHeaderCrc = false;
                    }

                    flags = flags | GzipFlags.FHCRC;
                }
                modifiedBuffer.Add((byte)flags);

                var unixTimeStart = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
                if (InternalLastModified.ToUniversalTime() > unixTimeStart)
                {
                    i += 4;
                    var utc           = InternalLastModified.ToUniversalTime();
                    var timestampUnix = (long)utc.Subtract(unixTimeStart).TotalSeconds;

                    modifiedBuffer.Add((byte)((timestampUnix & 0x000000FF)));
                    modifiedBuffer.Add((byte)((timestampUnix & 0x0000FF00) >> 8));
                    modifiedBuffer.Add((byte)((timestampUnix & 0x00FF0000) >> 16));
                    modifiedBuffer.Add((byte)((timestampUnix & 0xFF000000) >> 24));
                }
                else
                {
                    modifiedBuffer.Add(buffer[i++]);
                    modifiedBuffer.Add(buffer[i++]);
                    modifiedBuffer.Add(buffer[i++]);
                    modifiedBuffer.Add(buffer[i++]);
                }

                modifiedBuffer.Add(buffer[i++]); // Extra Fields byte
                modifiedBuffer.Add(buffer[i++]); // OS byte

                if (flags.HasFlag(GzipFlags.FEXTRA))
                {
                    int length = buffer[i];
                    modifiedBuffer.Add(buffer[i++]);
                    length += buffer[i] << 8;
                    modifiedBuffer.Add(buffer[i++]);

                    for (var j = 0; j < length && i < offset + count; j++)
                    {
                        modifiedBuffer.Add(buffer[i++]);
                    }
                }

                var iso8859Encoding = Encoding.GetEncoding("ISO-8859-1");
                if (writeFileName)
                {
                    var bytes = iso8859Encoding.GetBytes(InternalFilename);
                    modifiedBuffer.AddRange(bytes);
                    modifiedBuffer.Add(0); // add zero-byte terminator
                }

                if (writeComment)
                {
                    var bytes = iso8859Encoding.GetBytes(InternalComment);
                    modifiedBuffer.AddRange(bytes);
                    modifiedBuffer.Add(0); // add zero-byte terminator
                }

                if (flags.HasFlag(GzipFlags.FHCRC))
                {
                    if (!addingHeaderCrc)
                    {
                        i += 2;
                    }

                    // Least two significant bytes of the CRC32 for all bytes of the gzip header, up to (but not including) the CRC16
                    var crc = Crc32.Crc(modifiedBuffer.ToArray());
                    modifiedBuffer.Add((byte)(crc & 0x000000FF));
                    modifiedBuffer.Add((byte)((crc & 0x0000FF00) >> 8));
                    HeaderCrc = (ushort)crc;
                }

                for (var j = i; j < offset + count; j++)
                {
                    modifiedBuffer.Add(buffer[j]);
                }

                BaseStream.Write(modifiedBuffer.ToArray(), 0, modifiedBuffer.Count);
            }
            else
            {
                BaseStream.Write(buffer, offset, count);
            }
        }
示例#2
0
        private bool ReadMetadata()
        {
            if (!CanSeek)
            {
                // Can't seek, so don't read metadata, because it will cause the GZipStream to fail.
                HeaderRead = false;
                return(false);
            }

            // ReSharper disable UnusedVariable
            var id1 = BaseStream.ReadByte();               // should be 31/0x1f (GZIP constant)
            var id2 = BaseStream.ReadByte();               // should be 139/0x8b (GZIP constant)
            var compressionMethod = BaseStream.ReadByte(); // should be 8 (deflate)
            // ReSharper restore UnusedVariable

            // ReSharper disable CommentTypo

            // Bit flags:
            //  &0x1 = FTEXT (probably ASCII)
            //  &0x2 = CRC16 for gzip header present
            //  &0x4 = FEXTRA (extra fields)
            //  &0x8 = FNAME (name)
            //  &0x10 = FCOMMENT (comment)
            //  &0x20, &0x40, &0x80 = reserved
            var flags = (GzipFlags)BaseStream.ReadByte();

            // ReSharper restore CommentTypo

            uint timestamp = 0;

            for (var x = 0; x < 4; x++)
            {
                timestamp += (uint)BaseStream.ReadByte() << (8 * x);
            }

            // If timestamp == 0, no timestamp is available; use the compressed file timestamp
            if (timestamp > 0)
            {
                InternalLastModified = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddSeconds(timestamp); // Gzip times are stored in universal time
            }
            else
            {
                InternalLastModified = DateTime.MinValue;
            }

            // ReSharper disable UnusedVariable
            var extraFlags = BaseStream.ReadByte();
            var osId       = BaseStream.ReadByte();

            // ReSharper restore UnusedVariable

            if (flags.HasFlag(GzipFlags.FEXTRA))
            {
                var length = BaseStream.ReadByte();
                length += BaseStream.ReadByte() << 8;
                BaseStream.Seek(length, SeekOrigin.Current);
            }

            // ReSharper disable CommentTypo

            /*
             * FNAME and FCOMMENT both must be written (and read) as ISO-8859-1 (LATIN-1) characters
             * FNAME is filename only (no path), and if written on case-insensitive file system, lower-case only,
             */

            // ReSharper restore CommentTypo

            var iso8859Encoding = Encoding.GetEncoding("ISO-8859-1");

            if (flags.HasFlag(GzipFlags.FNAME))
            {
                var nameBytes = new List <byte>();
                int c;
                while ((c = BaseStream.ReadByte()) > 0)
                {
                    nameBytes.Add((byte)c);
                }

                InternalFilename = iso8859Encoding.GetString(nameBytes.ToArray());
            }

            if (flags.HasFlag(GzipFlags.FCOMMENT))
            {
                var commentBytes = new List <byte>();
                int c;
                while ((c = BaseStream.ReadByte()) > 0)
                {
                    commentBytes.Add((byte)c);
                }

                InternalComment = iso8859Encoding.GetString(commentBytes.ToArray());
            }

            var headerCorrupted = false;

            if (flags.HasFlag(GzipFlags.FHCRC))
            {
                var headerCrcPosition = (int)BaseStream.Position;
                // Applies to all bytes prior to this item
                var headerCrcBytes = new byte[2];
                headerCrcBytes[0] = (byte)BaseStream.ReadByte();
                headerCrcBytes[1] = (byte)BaseStream.ReadByte();
                HeaderCrc         = (ushort)(headerCrcBytes[0] + (headerCrcBytes[1] << 8));

                BaseStream.Seek(0, SeekOrigin.Begin);
                var bytes = new byte[headerCrcPosition];
                BaseStream.Read(bytes, 0, headerCrcPosition);

                var crc   = Crc32.Crc(bytes);
                var crc16 = (ushort)crc;
                headerCorrupted = HeaderCrc != crc16;
            }

            // Then compressed data

            // ReSharper disable once CommentTypo
            // Then 4 CRC32 bytes, then 4 ISIZE bytes

            // Reset stream position before decompressing file
            BaseStream.Seek(0, SeekOrigin.Begin);

            HeaderRead = true;
            return(headerCorrupted);
        }