aboutsummaryrefslogtreecommitdiff
path: root/zsig/zsig.go
blob: a254e05f95a129b89a0a14e7ddae69b92dae54a4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
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 Header struct {
	Comment string
	Extra   []byte
	ModTime time.Time
	Name    string
	OS      byte
	CRC     uint16
}

type Reader struct {
	Header
	*bufio.Reader
	crc 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.crc = crc32.NewIEEE()
	z.crc.Write(buf[:10]) // init CRC
	if flg&flagExtra != 0 {
		if _, err := io.ReadFull(z, buf[:2]); err != nil {
			return noEOF(err)
		}
		z.crc.Write(buf[:2]) // update CRC
		data := make([]byte, le.Uint16(buf[:2]))
		if _, err := io.ReadFull(z, data); err != nil {
			return noEOF(err)
		}
		z.crc.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 = uint16(z.crc.Sum32())
		if z.CRC != le.Uint16(buf[:2]) {
			return ErrHeader
		}
	}
	return nil
}

func (z *Reader) readString() (string, error) {
	s, err := z.ReadString(0)
	if err != nil {
		return "", err
	}
	io.WriteString(z.crc, s) // update CRC
	if l := len(s); l > 0 && s[l-1] == 0 {
		s = s[:l-1] // strip last zero
	}
	return s, nil
}