aboutsummaryrefslogtreecommitdiff
path: root/internal/slackware_com/mirror.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/slackware_com/mirror.go')
-rw-r--r--internal/slackware_com/mirror.go202
1 files changed, 202 insertions, 0 deletions
diff --git a/internal/slackware_com/mirror.go b/internal/slackware_com/mirror.go
new file mode 100644
index 0000000..f3bb4a8
--- /dev/null
+++ b/internal/slackware_com/mirror.go
@@ -0,0 +1,202 @@
+package slackware_com
+
+import (
+ "bytes"
+ "context"
+ "fmt"
+ "hash"
+ "io"
+ "net"
+ "net/http"
+ "net/url"
+ "path"
+ "strings"
+)
+
+type Mirror struct {
+ client *http.Client
+ url *url.URL
+
+ ChangeLog *ChangeLog
+ Checksums *Checksums
+}
+
+func OpenMirror(mirrorURL *url.URL) (*Mirror, error) {
+ var zeroDialer net.Dialer
+ transport := http.DefaultTransport.(*http.Transport).Clone()
+ transport.DialContext = func(ctx context.Context, network, addr string) (net.Conn, error) {
+ return zeroDialer.DialContext(ctx, "tcp4", addr)
+ }
+ http.DefaultClient.Transport = transport
+
+ mirror := Mirror{
+ client: http.DefaultClient,
+ url: mirrorURL,
+ }
+
+ err := mirror.Sync()
+ if err != nil {
+ return nil, err
+ }
+
+ return &mirror, nil
+}
+
+func signaturePath(filePath string) string {
+ return fmt.Sprintf("%s.asc", filePath)
+}
+
+func isSignature(filePath string) bool {
+ return strings.HasSuffix(filePath, ".asc")
+}
+
+func (m *Mirror) hasSignature(filePath string) bool {
+ if isSignature(filePath) {
+ return false
+ }
+ if path.Base(filePath) == ChecksumsMD5 {
+ return true
+ }
+ return m.Checksums.HasFilePath(signaturePath(filePath))
+}
+
+func (m *Mirror) DownloadAndVerify(wr io.Writer, filePath string) error {
+ fileUrl := m.url.JoinPath(filePath).String()
+ resp, err := m.client.Get(fileUrl)
+ if err != nil {
+ return err
+ }
+
+ if resp.StatusCode != http.StatusOK {
+ return fmt.Errorf("bad HTTP response: %s", http.StatusText(resp.StatusCode))
+ }
+
+ var r io.Reader = resp.Body
+ defer resp.Body.Close()
+
+ var hash *hash.Hash
+ if filePath != ChecksumsMD5 && filePath != signaturePath(ChecksumsMD5) {
+ h := m.Checksums.Hash()
+ r = io.TeeReader(r, h)
+
+ hash = &h
+ }
+
+ var signatureVerifier *GnuPGVerifier
+ if m.hasSignature(filePath) {
+ var signature bytes.Buffer
+ err := m.DownloadAndVerify(&signature, signaturePath(filePath))
+ if err != nil {
+ return err
+ }
+
+ signatureVerifier = RunSignatureVerifier(signature.String())
+ r = io.TeeReader(r, signatureVerifier)
+ }
+
+ _, err = io.Copy(wr, r)
+ if err != nil {
+ return err
+ }
+
+ if hash != nil {
+ ok, err := m.Checksums.Verify(filePath, (*hash).Sum(nil))
+ if err != nil {
+ return err
+ }
+ if !ok {
+ return fmt.Errorf("MD5 verification failed")
+ }
+ }
+
+ if signatureVerifier != nil {
+ ok, _ := signatureVerifier.IsValid()
+ if !ok {
+ return fmt.Errorf("signature verification failed")
+ }
+ }
+
+ return nil
+}
+
+func (m *Mirror) downloadChangeLog() error {
+ var buf bytes.Buffer
+ err := m.DownloadAndVerify(&buf, ChangeLogTxt)
+ if err != nil {
+ return fmt.Errorf("downloading %s: %w", ChangeLogTxt, err)
+ }
+
+ var changeLog ChangeLog
+ err = changeLog.UnmarshalText(buf.Bytes())
+ if err != nil {
+ return fmt.Errorf("%s: %w", ChangeLogTxt, err)
+ }
+ m.ChangeLog = &changeLog
+
+ return nil
+}
+
+func (m *Mirror) downloadChecksums() error {
+ var buf bytes.Buffer
+ err := m.DownloadAndVerify(&buf, ChecksumsMD5)
+ if err != nil {
+ return fmt.Errorf("downloading %s: %w", ChecksumsMD5, err)
+ }
+
+ var checksums Checksums
+ err = checksums.UnmarshalText(buf.Bytes())
+ if err != nil {
+ return fmt.Errorf("%s: %w", ChecksumsMD5, err)
+ }
+ m.Checksums = &checksums
+
+ return nil
+}
+
+// FindPackage returns the file path for the most appropriate package.
+func (m *Mirror) FindPackage(pattern PackageNamePattern) (string, bool) {
+ found := ""
+ priority := 0
+
+ pathPriority := 0
+ for _, file := range m.Checksums.Files() {
+ if !IsPackage(file) {
+ continue
+ }
+ file = path.Clean(file)
+
+ if strings.HasPrefix(file, "patches/packages") {
+ pathPriority = 5
+ } else if strings.HasPrefix(file, "slackware") {
+ pathPriority = 4
+ } else if strings.HasPrefix(file, "extra") {
+ pathPriority = 3
+ } else if strings.HasPrefix(file, "testing/packages") {
+ pathPriority = 2
+ } else if strings.HasPrefix(file, "pasture") {
+ pathPriority = 1
+ } else {
+ continue
+ }
+
+ pkg, _ := NewPackageNameFromPath(file)
+ if pattern.IsMatch(pkg) && pathPriority > priority {
+ found = file
+ priority = pathPriority
+ }
+ }
+
+ return found, found != ""
+}
+
+func (m *Mirror) Sync() error {
+ err := m.downloadChecksums()
+ if err != nil {
+ return err
+ }
+ err = m.downloadChangeLog()
+ if err != nil {
+ return err
+ }
+ return nil
+}