package zsig import ( "bufio" "encoding/binary" "errors" "hash" "hash/crc32" "io" "time" ) var ErrHeader = errors.New("invalid header") const ( gzipID1 = 0x1f gzipID2 = 0x8b gzipDeflate = 8 osUnix = 3 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, osUnix} type Reader struct { *bufio.Reader Comment string Extra []byte ModTime time.Time Name string OS byte CRC uint16 h hash.Hash32 } 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{Reader: bufio.NewReader(r)} if err := z.readHeader(); err != nil { return nil, err } return z, nil } func (z *Reader) readHeader() error { var buf [10]byte if _, err := io.ReadFull(z, buf[:10]); err != nil { return err } if buf[0] != gzipID1 || buf[1] != gzipID2 || buf[2] != gzipDeflate { return ErrHeader } flg := buf[3] if t := le.Uint32(buf[4:8]); t > 0 { z.ModTime = time.Unix(int64(t), 0) } z.OS = buf[9] z.h = crc32.NewIEEE() z.h.Write(buf[:10]) // init CRC if flg&flagExtra != 0 { if _, err := io.ReadFull(z, buf[:2]); err != nil { return noEOF(err) } z.h.Write(buf[:2]) // update CRC data := make([]byte, le.Uint16(buf[:2])) if _, err := io.ReadFull(z, data); err != nil { return noEOF(err) } z.h.Write(data) // update CRC z.Extra = data } if flg&flagName != 0 { s, err := z.readString() if err != nil { return err } z.Name = s } if flg&flagComment != 0 { s, err := z.readString() if err != nil { return err } z.Comment = s } if flg&flagHdrCrc != 0 { if _, err := io.ReadFull(z, buf[:2]); err != nil { return noEOF(err) } z.CRC = le.Uint16(buf[:2]) crc := uint16(z.h.Sum32()) if z.CRC != crc { return ErrHeader } } return nil } func (z *Reader) readString() (string, error) { s, err := z.ReadString(0) if err != nil { return "", err } io.WriteString(z.h, s) // update CRC if l := len(s); l > 0 && s[l-1] == 0 { s = s[:l-1] // strip last zero } return s, nil }