Files
Matheus Sampaio Queiroga fb6024ec5d big update and move dpkg module
Signed-off-by: Matheus Sampaio Queiroga <srherobrine20@gmail.com>
2025-03-13 14:40:14 -03:00

285 lines
7.7 KiB
Go

package apt
import (
"bytes"
"errors"
"fmt"
"io"
"net/http"
"net/url"
"path"
"slices"
"strconv"
"strings"
"time"
"sirherobrine23.com.br/sirherobrine23/go-dpkg/deb822"
"sirherobrine23.com.br/sirherobrine23/go-dpkg/dpkg"
"sirherobrine23.com.br/sirherobrine23/go-dpkg/internal/descompress"
"sirherobrine23.com.br/sirherobrine23/go-dpkg/internal/scanner"
opengpg_crypto "github.com/ProtonMail/gopenpgp/v3/crypto"
)
var ErrDownServer error = errors.New("server is shutdown or not avaible")
type Package struct {
*dpkg.Package
poolURL *url.URL // Package pool url
}
// Get file URL
func (pkg *Package) URL() *url.URL { return pkg.poolURL.ResolveReference(&url.URL{}) }
// Return .deb Downloader
func (pkg *Package) Download() (io.ReadCloser, error) {
res, err := http.Get(pkg.poolURL.String())
if err != nil {
return nil, err
} else if res.StatusCode != 200 {
return res.Body, errors.New("invalid request code")
}
return res.Body, nil
}
// Package sum
type Sum struct {
File string
Hash string
Size int64
}
// Return packages avaible in target
func (sum Sum) Packages(suite string, src *AptSource) ([]*Package, error) {
if path.Base(sum.File[:len(sum.File)-len(path.Ext(sum.File))]) != "Packages" {
return nil, fmt.Errorf("this sum require have Package last element")
} else if !slices.Contains(src.Suites, suite) {
return nil, fmt.Errorf("invalid suite")
}
r, metaURI := io.Reader(nil), (*url.URL)(nil)
for _, metaURI = range src.URIs {
metaURI = metaURI.ResolveReference(&url.URL{Path: path.Join(metaURI.Path, "dists", suite, sum.File)})
res, err := http.Get(metaURI.String())
if err != nil {
return nil, err
} else if res.StatusCode != 200 {
return nil, fmt.Errorf("http client return %d", res.StatusCode)
}
defer res.Body.Close()
if r, err = descompress.Descompress(res.Body); err != nil {
return nil, fmt.Errorf("cannot descompress body: %s", err)
}
}
if metaURI == nil {
return nil, fmt.Errorf("return valid AptSiurce")
}
packageSplit := scanner.NewScannerSplit(r, func(data []byte, atEOF bool) (advance int, token []byte, err error) {
if atEOF && len(data) == 0 {
return 0, nil, nil
}
if i := bytes.Index(data, []byte("\n\n")); i > 0 {
// We have a full newline-terminated line.
return i + 2, data[0 : i+2], nil
}
// If we're at EOF, we have a final, non-terminated line. Return it.
if atEOF {
return len(data), data, nil
}
// Request more data.
return 0, nil, nil
})
pkgs := []*Package{}
for packageSplit.Scan() {
pkg, err := dpkg.ParsePackage(packageSplit.Bytes())
if err != nil {
return nil, err
}
pkgs = append(pkgs, &Package{
Package: pkg,
poolURL: metaURI.ResolveReference(&url.URL{Path: path.Join(metaURI.Path, pkg.Extra.Get("Filename").String())}),
})
}
if err := packageSplit.Err(); err != nil {
if len(pkgs) == 0 {
return nil, fmt.Errorf("cannot process Packages: %s", err)
}
return nil, err
}
return pkgs, nil
}
type ReleaseSum map[string][]Sum
func ParseSumLines(lines []string) (ReleaseSum, error) {
sums := ReleaseSum{}
for _, name := range lines {
hash := name[:strings.Index(name, " ")]
name = strings.TrimSpace(name[len(hash):])
sizeStr := name[:strings.Index(name, " ")]
name = strings.TrimSpace(name[len(sizeStr):])
size, err := strconv.ParseInt(sizeStr, 10, 64)
if err != nil {
return nil, err
}
ext := path.Ext(name)
mapText := name[:len(name)-len(ext)]
sums[mapText] = append(sums[mapText], Sum{
Hash: hash,
Size: size,
File: name,
})
}
return sums, nil
}
type AptRelease struct {
Label string
Codename string
Origin string
Suite string
Date time.Time
AcquireByHash bool
Archs []string
Components []string
Description string
Extra deb822.Deb822
MD5 ReleaseSum
SHA1 ReleaseSum
SHA256 ReleaseSum
SHA512 ReleaseSum
}
func (apt AptSource) Release() (map[string]*AptRelease, error) {
suites := map[string]*AptRelease{}
for _, uriMain := range apt.URIs {
for _, suite := range apt.Suites {
fmt.Println(uriMain.ResolveReference(&url.URL{Path: path.Join(uriMain.Path, "dists", suite, "Release")}).String())
isInRelease, isGpg := false, false
res, err := http.Get(uriMain.ResolveReference(&url.URL{Path: path.Join(uriMain.Path, "dists", suite, "Release")}).String())
if err != nil {
return nil, err
} else if res.StatusCode != http.StatusOK {
isInRelease = true
if res, err = http.Get(uriMain.ResolveReference(&url.URL{Path: path.Join(uriMain.Path, "dists", suite, "InRelease")}).String()); err != nil {
return nil, err
} else if res.StatusCode != http.StatusOK {
isGpg = true
if res, err = http.Get(uriMain.ResolveReference(&url.URL{Path: path.Join(uriMain.Path, "dists", suite, "Release.gpg")}).String()); err != nil {
return nil, err
} else if res.StatusCode != http.StatusOK {
continue
}
}
}
defer res.Body.Close()
bodyReader := io.Reader(res.Body)
if !isGpg && isInRelease {
body, err := io.ReadAll(res.Body)
if err != nil {
return nil, err
}
message, err := opengpg_crypto.NewPGPMessageFromArmored(string(body))
if err != nil {
return nil, err
}
bodyReader = message.NewReader()
} else if isGpg && isInRelease {
// key, err := crypto.NewKeyFromArmored(apt.SignedBy)
// if err != nil {
// data, err := os.ReadFile(apt.SignedBy)
// if err != nil {
// return nil, err
// } else if key, err = crypto.NewKeyFromArmored(string(data)); err != nil {
// return nil, err
// }
// }
// body, err := io.ReadAll(res.Body)
// if err != nil {
// return nil, err
// }
// msg := crypto.NewPGPSplitMessage(key.GetFingerprintBytes(), body)
// bodyReader = msg.NewReader()
continue
}
debRelease, err := deb822.NewParse(bodyReader)
if err != nil {
return nil, err
}
release := &AptRelease{
Label: debRelease.Get("Label").String(),
Archs: strings.Fields(debRelease.Get("Architectures").String()),
Description: debRelease.Get("Description").String(),
Components: strings.Fields(debRelease.Get("Components").String()),
Codename: debRelease.Get("Codename").String(),
Origin: debRelease.Get("Origin").String(),
Suite: debRelease.Get("Suite").String(),
AcquireByHash: debRelease.Get("Acquire-By-Hash").ToBool(),
Extra: debRelease,
}
if date, ok := debRelease["Date"]; ok {
if release.Date, err = time.Parse(time.RFC1123, date.String()); err != nil {
return nil, err
}
}
if value, ok := debRelease["MD5Sum"]; ok {
if release.MD5, err = ParseSumLines(value.ToSlice()); err != nil {
return nil, err
}
}
if value, ok := debRelease["SHA1"]; ok {
if release.SHA1, err = ParseSumLines(value.ToSlice()); err != nil {
return nil, err
}
}
if value, ok := debRelease["SHA256"]; ok {
if release.SHA256, err = ParseSumLines(value.ToSlice()); err != nil {
return nil, err
}
}
if value, ok := debRelease["SHA512"]; ok {
if release.SHA512, err = ParseSumLines(value.ToSlice()); err != nil {
return nil, err
}
}
delete(debRelease, "Label")
delete(debRelease, "Architectures")
delete(debRelease, "Description")
delete(debRelease, "Components")
delete(debRelease, "Codename")
delete(debRelease, "Origin")
delete(debRelease, "Suite")
delete(debRelease, "Acquire-By-Hash")
delete(debRelease, "Date")
delete(debRelease, "MD5Sum")
delete(debRelease, "SHA1")
delete(debRelease, "SHA256")
delete(debRelease, "SHA512")
suites[suite] = release
}
}
if len(suites) > 0 {
return suites, nil
}
return nil, ErrDownServer
}