add apt gestion
Signed-off-by: GnomeZworc <nicolas.boufidjeline@g3e.fr>
This commit is contained in:
parent
274ea454dd
commit
c4776d81dd
22 changed files with 1465 additions and 163 deletions
154
internal/clone/apt/release.go
Normal file
154
internal/clone/apt/release.go
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
package apt
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// ReleaseFile represents a parsed Debian Release / InRelease file.
|
||||
type ReleaseFile struct {
|
||||
Origin string
|
||||
Suite string
|
||||
Codename string
|
||||
Components string
|
||||
Architectures string
|
||||
SHA256 []ReleaseEntry
|
||||
}
|
||||
|
||||
// ReleaseEntry is a single line from the SHA256 section of a Release file.
|
||||
type ReleaseEntry struct {
|
||||
Hash string
|
||||
Size int64
|
||||
Path string
|
||||
}
|
||||
|
||||
// ParseRelease strips PGP armor (if present) and parses an InRelease or Release file.
|
||||
func ParseRelease(data []byte) (*ReleaseFile, error) {
|
||||
text := stripPGPArmor(string(data))
|
||||
|
||||
rf := &ReleaseFile{}
|
||||
inSHA256 := false
|
||||
|
||||
for _, rawLine := range strings.Split(text, "\n") {
|
||||
line := strings.TrimRight(rawLine, " \t\r")
|
||||
|
||||
if inSHA256 {
|
||||
if line == "" || (len(line) > 0 && line[0] != ' ' && line[0] != '\t') {
|
||||
// A non-indented non-empty line ends the SHA256 block.
|
||||
if line != "" {
|
||||
inSHA256 = false
|
||||
// Fall through to parse this line as a key-value pair.
|
||||
} else {
|
||||
inSHA256 = false
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
// Indented line: " <hash> <size> <path>"
|
||||
entry, err := parseReleaseEntry(line)
|
||||
if err == nil {
|
||||
rf.SHA256 = append(rf.SHA256, entry)
|
||||
}
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if strings.HasPrefix(line, "SHA256:") {
|
||||
inSHA256 = true
|
||||
continue
|
||||
}
|
||||
|
||||
key, value, ok := strings.Cut(line, ":")
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
key = strings.TrimSpace(key)
|
||||
value = strings.TrimSpace(value)
|
||||
|
||||
switch key {
|
||||
case "Origin":
|
||||
rf.Origin = value
|
||||
case "Suite":
|
||||
rf.Suite = value
|
||||
case "Codename":
|
||||
rf.Codename = value
|
||||
case "Components":
|
||||
rf.Components = value
|
||||
case "Architectures":
|
||||
rf.Architectures = value
|
||||
}
|
||||
}
|
||||
|
||||
return rf, nil
|
||||
}
|
||||
|
||||
// parseReleaseEntry parses a single indented line from the SHA256 section.
|
||||
// Format: " <hash> <size> <path>"
|
||||
func parseReleaseEntry(line string) (ReleaseEntry, error) {
|
||||
fields := strings.Fields(line)
|
||||
if len(fields) != 3 {
|
||||
return ReleaseEntry{}, fmt.Errorf("malformed SHA256 entry: %q", line)
|
||||
}
|
||||
size, err := strconv.ParseInt(fields[1], 10, 64)
|
||||
if err != nil {
|
||||
return ReleaseEntry{}, fmt.Errorf("malformed size in SHA256 entry: %w", err)
|
||||
}
|
||||
return ReleaseEntry{Hash: fields[0], Size: size, Path: fields[2]}, nil
|
||||
}
|
||||
|
||||
// stripPGPArmor removes the PGP signed-message wrapper if present.
|
||||
// It returns only the signed body (between the header and the signature).
|
||||
func stripPGPArmor(text string) string {
|
||||
const beginSigned = "-----BEGIN PGP SIGNED MESSAGE-----"
|
||||
const beginSig = "-----BEGIN PGP SIGNATURE-----"
|
||||
|
||||
if !strings.Contains(text, beginSigned) {
|
||||
return text
|
||||
}
|
||||
|
||||
// Drop the header lines (Hash: etc.) up to the first blank line.
|
||||
after, found := strings.CutPrefix(text, beginSigned)
|
||||
if !found {
|
||||
// beginSigned not at start — find it
|
||||
idx := strings.Index(text, beginSigned)
|
||||
if idx < 0 {
|
||||
return text
|
||||
}
|
||||
after = text[idx+len(beginSigned):]
|
||||
}
|
||||
|
||||
// Skip the armor header lines (e.g. "Hash: SHA512") until the blank line.
|
||||
blankIdx := strings.Index(after, "\n\n")
|
||||
if blankIdx < 0 {
|
||||
return text
|
||||
}
|
||||
body := after[blankIdx+2:]
|
||||
|
||||
// Cut off at the PGP signature block.
|
||||
if sigIdx := strings.Index(body, beginSig); sigIdx >= 0 {
|
||||
body = body[:sigIdx]
|
||||
}
|
||||
|
||||
return strings.TrimRight(body, "\n\r ") + "\n"
|
||||
}
|
||||
|
||||
// GenerateRelease generates a plain unsigned Release file.
|
||||
func GenerateRelease(suite, codename string, components, architectures []string, entries []ReleaseEntry) []byte {
|
||||
var buf bytes.Buffer
|
||||
|
||||
fmt.Fprintf(&buf, "Origin: ClonePack Mirror\n")
|
||||
fmt.Fprintf(&buf, "Suite: %s\n", suite)
|
||||
fmt.Fprintf(&buf, "Codename: %s\n", codename)
|
||||
fmt.Fprintf(&buf, "Components: %s\n", strings.Join(components, " "))
|
||||
fmt.Fprintf(&buf, "Architectures: %s\n", strings.Join(architectures, " "))
|
||||
fmt.Fprintf(&buf, "Date: %s\n", time.Now().UTC().Format("Mon, 02 Jan 2006 15:04:05 UTC"))
|
||||
fmt.Fprintf(&buf, "SHA256:\n")
|
||||
|
||||
for _, e := range entries {
|
||||
fmt.Fprintf(&buf, " %s %d %s\n", e.Hash, e.Size, e.Path)
|
||||
}
|
||||
|
||||
return buf.Bytes()
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue