repo-sync/mirror/control.go
James Coleman 5af1dd8928
Some checks failed
Go package / build (push) Has been cancelled
first commit
2026-07-28 19:22:20 -05:00

100 lines
2.5 KiB
Go

package mirror
import (
"bufio"
"io"
"strings"
)
// stanza is one RFC 822 style field block from an apt control file.
type stanza map[string]string
// Get returns the first matching field, trying each name in order to cope
// with casing differences between repositories.
func (s stanza) Get(names ...string) string {
for _, name := range names {
if v, ok := s[name]; ok {
return v
}
}
return ""
}
// controlReader reads stanzas from an apt control file.
type controlReader struct {
br *bufio.Reader
}
// newControlReader wraps a reader for stanza-by-stanza parsing.
func newControlReader(r io.Reader) *controlReader {
return &controlReader{br: bufio.NewReaderSize(r, 64*1024)}
}
// readStanza parses the next stanza, returning io.EOF when the input is
// exhausted. Continuation lines are joined with newlines so multi-line
// checksum fields keep one entry per line.
func (r *controlReader) readStanza() (stanza, error) {
st := stanza{}
var last string
for {
line, err := r.br.ReadString('\n')
if err != nil && err != io.EOF {
return nil, err
}
done := err == io.EOF && line == ""
line = strings.TrimRight(line, "\r\n")
switch {
case done || strings.TrimSpace(line) == "":
if len(st) > 0 {
return st, nil
}
if done {
return nil, io.EOF
}
case line[0] == ' ' || line[0] == '\t':
if last != "" {
st[last] += "\n" + strings.TrimSpace(line)
}
default:
name, value, ok := strings.Cut(line, ":")
if !ok {
continue
}
last = name
st[name] = strings.TrimSpace(value)
}
}
}
// stripClearsign extracts the signed payload from a clearsigned InRelease
// document, returning the input unchanged when it is not clearsigned.
func stripClearsign(data []byte) []byte {
const begin = "-----BEGIN PGP SIGNED MESSAGE-----"
const sig = "-----BEGIN PGP SIGNATURE-----"
text := strings.ReplaceAll(string(data), "\r\n", "\n")
idx := strings.Index(text, begin)
if idx < 0 {
return data
}
text = text[idx+len(begin):]
// Skip the armor headers up to the first blank line. Without one the
// document is malformed, so treat it as not clearsigned rather than
// returning the armor headers as payload.
i := strings.Index(text, "\n\n")
if i < 0 {
return data
}
text = text[i+2:]
if i := strings.Index(text, sig); i >= 0 {
text = text[:i]
}
// Reverse the dash escaping applied by the signer.
var b strings.Builder
for _, line := range strings.Split(text, "\n") {
b.WriteString(strings.TrimPrefix(line, "- "))
b.WriteString("\n")
}
return []byte(b.String())
}