]> Sergey Matveev's repositories - meta4ra.git/commitdiff
meta4-check
authorSergey Matveev <stargrave@stargrave.org>
Thu, 30 Sep 2021 11:40:15 +0000 (14:40 +0300)
committerSergey Matveev <stargrave@stargrave.org>
Thu, 30 Sep 2021 11:40:15 +0000 (14:40 +0300)
cmd/meta4-check/main.go [new file with mode: 0644]
cmd/meta4-create/main.go
common.go [new file with mode: 0644]

diff --git a/cmd/meta4-check/main.go b/cmd/meta4-check/main.go
new file mode 100644 (file)
index 0000000..3615c0e
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+meta4a -- Metalink 4.0 checker
+Copyright (C) 2021 Sergey Matveev <stargrave@stargrave.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, version 3 of the License.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+// Metalink 4.0 checker
+package main
+
+import (
+       "bufio"
+       "crypto/sha256"
+       "crypto/sha512"
+       "encoding/hex"
+       "encoding/xml"
+       "flag"
+       "fmt"
+       "hash"
+       "io"
+       "io/fs"
+       "io/ioutil"
+       "log"
+       "os"
+
+       "go.stargrave.org/meta4ra"
+)
+
+func main() {
+       extractSig := flag.Bool("extract-sig", false, "Extract signature files")
+       log.SetFlags(log.Lshortfile)
+       flag.Parse()
+       sha256Hasher := sha256.New()
+       sha512Hasher := sha512.New()
+       bad := false
+       for _, metaPath := range flag.Args() {
+               data, err := ioutil.ReadFile(metaPath)
+               if err != nil {
+                       log.Fatalln(err)
+               }
+               var meta meta4ra.Metalink
+               err = xml.Unmarshal(data, &meta)
+               if err != nil {
+                       log.Fatalln(err)
+               }
+               for _, f := range meta.Files {
+                       if f.Signature != nil && *extractSig {
+                               if err = ioutil.WriteFile(
+                                       f.Name+".asc",
+                                       []byte(f.Signature.Signature),
+                                       fs.FileMode(0666),
+                               ); err != nil {
+                                       fmt.Println("Error:", f.Name, "can not save signature:", err)
+                                       bad = true
+                               }
+                       }
+                       var sha256Digest string
+                       var sha512Digest string
+                       fd, err := os.Open(f.Name)
+                       if err != nil {
+                               continue
+                       }
+                       for _, h := range f.Hashes {
+                               switch h.Type {
+                               case meta4ra.HashSHA256:
+                                       sha256Digest = h.Hash
+                               case meta4ra.HashSHA512:
+                                       sha512Digest = h.Hash
+                               }
+                       }
+                       var digestTheir string
+                       var digestName string
+                       var hasher hash.Hash
+                       if sha512Digest != "" {
+                               digestName = meta4ra.HashSHA512
+                               digestTheir = sha512Digest
+                               hasher = sha512Hasher
+                       } else if sha256Digest != "" {
+                               digestName = meta4ra.HashSHA256
+                               digestTheir = sha256Digest
+                               hasher = sha256Hasher
+                       } else {
+                               fd.Close()
+                               fmt.Println("Error:", f.Name, "no satisfiable hash algorithm found")
+                               bad = true
+                               continue
+                       }
+                       hasher.Reset()
+                       _, err = io.Copy(hasher, bufio.NewReader(fd))
+                       fd.Close()
+                       if err != nil {
+                               fmt.Println("Error:", f.Name, err)
+                               bad = true
+                               continue
+                       }
+                       digestOur := hex.EncodeToString(hasher.Sum(nil))
+                       if digestOur == digestTheir {
+                               fmt.Println(f.Name, digestName, "good")
+                       } else {
+                               fmt.Println(
+                                       "Hash does not match:", f.Name, digestName,
+                                       "our:", digestOur,
+                                       "their:", digestTheir,
+                               )
+                               bad = true
+                               continue
+                       }
+               }
+       }
+       if bad {
+               os.Exit(1)
+       }
+}
index 2289e8a26f4216e47f79441e7efeea8669887cf1..f11fa70754b1f7be6c0ee19ee92909fc4519318b 100644 (file)
@@ -1,5 +1,5 @@
 /*
-meta4a -- Metalink 4.0 utility
+meta4a -- Metalink 4.0 creator
 Copyright (C) 2021 Sergey Matveev <stargrave@stargrave.org>
 
 This program is free software: you can redistribute it and/or modify
@@ -15,10 +15,11 @@ You should have received a copy of the GNU General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
-// Metalink 4.0 utility
+// Metalink 4.0 creator
 package main
 
 import (
+       "bufio"
        "crypto/sha256"
        "crypto/sha512"
        "encoding/hex"
@@ -30,64 +31,21 @@ import (
        "os"
        "path/filepath"
        "time"
-)
 
-const (
-       Generator       = "meta4ra/0.1.0"
-       GPGSigMediaType = "application/pgp-signature"
+       "go.stargrave.org/meta4ra"
 )
 
-type Metalink struct {
-       XMLName   xml.Name  `xml:"urn:ietf:params:xml:ns:metalink metalink"`
-       Files     []File    `xml:"file"`
-       Generator string    `xml:"generator,,omitempty"`
-       Published time.Time `xml:"published,,omitempty"`
-}
-
-type File struct {
-       XMLName     xml.Name   `xml:"file"`
-       Name        string     `xml:"name,attr"`
-       Description string     `xml:"description,,omitempty"`
-       Hashes      []Hash     `xml:"hash,,omitempty"`
-       MetaURLs    []MetaURL  `xml:"metaurl,,omitempty"`
-       Signature   *Signature `xml:"signature"`
-       Size        uint64     `xml:"size,,omitempty"`
-       URLs        []URL      `xml:"url,,omitempty"`
-}
-
-type URL struct {
-       XMLName xml.Name `xml:"url"`
-       URL     string   `xml:",chardata"`
-}
-
-type Signature struct {
-       XMLName   xml.Name `xml:"signature"`
-       MediaType string   `xml:"mediatype,attr"`
-       Signature string   `xml:",cdata"`
-}
-
-type Hash struct {
-       XMLName xml.Name `xml:"hash"`
-       Type    string   `xml:"type,attr"`
-       Hash    string   `xml:",chardata"`
-}
-
-type MetaURL struct {
-       XMLName   xml.Name `xml:"metaurl"`
-       MediaType string   `xml:"mediatype,attr"`
-       URL       string   `xml:",chardata"`
-}
-
 func main() {
        file := flag.String("file", "", "Path to file")
+       mtime := flag.String("mtime", "", "Take that file's mtime as a Published date")
        desc := flag.String("desc", "", "Description")
        sig := flag.String("sig", "", "Path to signature file")
        torrent := flag.String("torrent", "", "Torrent URL")
        log.SetFlags(log.Lshortfile)
        flag.Parse()
-       urls := make([]URL, 0, len(flag.Args()))
+       urls := make([]meta4ra.URL, 0, len(flag.Args()))
        for _, u := range flag.Args() {
-               urls = append(urls, URL{URL: u})
+               urls = append(urls, meta4ra.URL{URL: u})
        }
        fd, err := os.Open(*file)
        if err != nil {
@@ -99,18 +57,21 @@ func main() {
        }
        sha256Hasher := sha256.New()
        sha512Hasher := sha512.New()
-       _, err = io.Copy(io.MultiWriter(sha256Hasher, sha512Hasher), fd)
+       _, err = io.Copy(
+               io.MultiWriter(sha256Hasher, sha512Hasher),
+               bufio.NewReader(fd),
+       )
        if err != nil {
                log.Fatalln(err)
        }
-       f := File{
+       f := meta4ra.File{
                Name:        filepath.Base(*file),
                Description: *desc,
                Size:        uint64(fi.Size()),
                URLs:        urls,
-               Hashes: []Hash{
-                       {Type: "sha-256", Hash: hex.EncodeToString(sha256Hasher.Sum(nil))},
-                       {Type: "sha-512", Hash: hex.EncodeToString(sha512Hasher.Sum(nil))},
+               Hashes: []meta4ra.Hash{
+                       {Type: meta4ra.HashSHA256, Hash: hex.EncodeToString(sha256Hasher.Sum(nil))},
+                       {Type: meta4ra.HashSHA512, Hash: hex.EncodeToString(sha512Hasher.Sum(nil))},
                },
        }
        if *sig != "" {
@@ -118,18 +79,29 @@ func main() {
                if err != nil {
                        log.Fatalln(err)
                }
-               f.Signature = &Signature{
-                       MediaType: GPGSigMediaType,
+               f.Signature = &meta4ra.Signature{
+                       MediaType: meta4ra.GPGSigMediaType,
                        Signature: "\n" + string(sigData),
                }
        }
        if *torrent != "" {
-               f.MetaURLs = []MetaURL{{MediaType: "torrent", URL: *torrent}}
+               f.MetaURLs = []meta4ra.MetaURL{{MediaType: "torrent", URL: *torrent}}
+       }
+       var published time.Time
+       if *mtime == "" {
+               published = time.Now()
+       } else {
+               fi, err := os.Stat(*mtime)
+               if err != nil {
+                       log.Fatalln(err)
+               }
+               published = fi.ModTime()
        }
-       m := Metalink{
-               Files:     []File{f},
-               Generator: Generator,
-               Published: time.Now().UTC().Truncate(time.Second),
+       published = published.UTC().Truncate(time.Second)
+       m := meta4ra.Metalink{
+               Files:     []meta4ra.File{f},
+               Generator: meta4ra.Generator,
+               Published: published,
        }
        out, err := xml.MarshalIndent(&m, "", "  ")
        if err != nil {
diff --git a/common.go b/common.go
new file mode 100644 (file)
index 0000000..da28885
--- /dev/null
+++ b/common.go
@@ -0,0 +1,72 @@
+/*
+meta4a -- Metalink 4.0 creator
+Copyright (C) 2021 Sergey Matveev <stargrave@stargrave.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, version 3 of the License.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+// Metalink 4.0 utilities
+package meta4ra
+
+import (
+       "encoding/xml"
+       "time"
+)
+
+const (
+       Generator       = "meta4ra/0.1.0"
+       GPGSigMediaType = "application/pgp-signature"
+       HashSHA256      = "sha-256"
+       HashSHA512      = "sha-512"
+)
+
+type Metalink struct {
+       XMLName   xml.Name  `xml:"urn:ietf:params:xml:ns:metalink metalink"`
+       Files     []File    `xml:"file"`
+       Generator string    `xml:"generator,,omitempty"`
+       Published time.Time `xml:"published,,omitempty"`
+}
+
+type File struct {
+       XMLName     xml.Name   `xml:"file"`
+       Name        string     `xml:"name,attr"`
+       Description string     `xml:"description,,omitempty"`
+       Hashes      []Hash     `xml:"hash,,omitempty"`
+       MetaURLs    []MetaURL  `xml:"metaurl,,omitempty"`
+       Signature   *Signature `xml:"signature"`
+       Size        uint64     `xml:"size,,omitempty"`
+       URLs        []URL      `xml:"url,,omitempty"`
+}
+
+type URL struct {
+       XMLName xml.Name `xml:"url"`
+       URL     string   `xml:",chardata"`
+}
+
+type Signature struct {
+       XMLName   xml.Name `xml:"signature"`
+       MediaType string   `xml:"mediatype,attr"`
+       Signature string   `xml:",cdata"`
+}
+
+type Hash struct {
+       XMLName xml.Name `xml:"hash"`
+       Type    string   `xml:"type,attr"`
+       Hash    string   `xml:",chardata"`
+}
+
+type MetaURL struct {
+       XMLName   xml.Name `xml:"metaurl"`
+       MediaType string   `xml:"mediatype,attr"`
+       URL       string   `xml:",chardata"`
+}