package zsig import ( "bufio" "encoding/binary" "errors" "hash/crc32" "io" "time" ) var ErrHeader = errors.New("invalid header") const ( gzipID1 = 0x1f gzipID2 = 0x8b gzipDeflate = 8 flagText = 1 << 0 flagHdrCrc = 1 << 1 flagExtra = 1 << 2 flagName = 1 << 3 flagComment = 1 << 4 ) var fake = []byte{gzipID1, gzipID2, gzipDeflate, flagComment, 0, 0, 0, 0, 0, 3} type Header struct { Comment string Extra []byte ModTime time.Time Name string OS byte } type Reader struct { Header r *bufio.Reader digest uint32 // CRC32 IEEE } func noEOF(err error) error { if err == io.EOF { return io.ErrUnexpectedEOF } return err } var le = binary.LittleEndian func NewReader(r io.Reader) (*Reader, error) { z := &Reader{r: bufio.NewReader(r)} hdr, err := z.readHeader() if err != nil { return nil, err } z.Header = hdr return z, nil } func (z *Reader) readHeader() (hdr Header, err error) { var buf [10]byte if _, err := io.ReadFull(z.r, buf[:10]); err != nil { return hdr, err } if buf[0] != gzipID1 || buf[1] != gzipID2 || buf[2] != gzipDeflate { return hdr, ErrHeader } flg := buf[3] if t := le.Uint32(buf[4:8]); t > 0 { hdr.ModTime = time.Unix(int64(t), 0) } hdr.OS = buf[9] z.digest = crc32.ChecksumIEEE(buf[:10]) if flg&flagExtra != 0 { if _, err = io.ReadFull(z.r, buf[:2]); err != nil { return hdr, noEOF(err) } z.digest = crc32.Update(z.digest, crc32.IEEETable, buf[:2]) data := make([]byte, le.Uint16(buf[:2])) if _, err := io.ReadFull(z.r, data); err != nil { return hdr, noEOF(err) } z.digest = crc32.Update(z.digest, crc32.IEEETable, data) hdr.Extra = data } if flg&flagName != 0 { s, err := z.readString() if err != nil { return hdr, err } hdr.Name = s } if flg&flagComment != 0 { s, err := z.readString() if err != nil { return hdr, err } hdr.Comment = s } if flg&flagHdrCrc != 0 { if _, err := io.ReadFull(z.r, buf[:2]); err != nil { return hdr, noEOF(err) } digest := le.Uint16(buf[:2]) if digest != uint16(z.digest) { return hdr, ErrHeader } } return hdr, nil } func (z *Reader) readString() (string, error) { s, err := z.r.ReadString(0) if err != nil { return "", err } if l := len(s); l > 0 && s[l-1] == 0 { s = s[:l-1] // strip last zero } return s, nil } func (z *Reader) Read(p []byte) (n int, err error) { return z.r.Read(p) }