Signed-off-by: Matheus Sampaio Queiroga <srherobrine20@gmail.com>
285 lines
7.7 KiB
Go
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
|
|
}
|