]> Sergey Matveev's repositories - meta4ra.git/blobdiff - cmd/meta4-create/main.go
OpenSSH signatures inclusion support
[meta4ra.git] / cmd / meta4-create / main.go
index 2289e8a26f4216e47f79441e7efeea8669887cf1..bd539b9edb998c1aae365e4fab127f3e34dbb76b 100644 (file)
@@ -1,6 +1,6 @@
 /*
-meta4a -- Metalink 4.0 utility
-Copyright (C) 2021 Sergey Matveev <stargrave@stargrave.org>
+meta4a -- Metalink 4.0 creator
+Copyright (C) 2021-2023 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
@@ -15,121 +15,93 @@ 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 (
-       "crypto/sha256"
-       "crypto/sha512"
-       "encoding/hex"
+       "bufio"
        "encoding/xml"
        "flag"
        "io"
-       "io/ioutil"
        "log"
        "os"
-       "path/filepath"
+       "path"
+       "strings"
        "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")
+       fn := flag.String("fn", "", "Filename")
+       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")
+       sigPGP := flag.String("sig-pgp", "", "Path to OpenPGP .asc signature file")
+       sigSSH := flag.String("sig-ssh", "", "Path to OpenSSH .sig signature file")
+       hashes := flag.String("hashes", strings.Join(meta4ra.HashesDefault, ","), "hash-name:command-s")
        torrent := flag.String("torrent", "", "Torrent URL")
        log.SetFlags(log.Lshortfile)
        flag.Parse()
-       urls := make([]URL, 0, len(flag.Args()))
-       for _, u := range flag.Args() {
-               urls = append(urls, URL{URL: u})
+       if *fn == "" {
+               log.Fatalln("empty -fn")
        }
-       fd, err := os.Open(*file)
-       if err != nil {
-               log.Fatalln(err)
-       }
-       fi, err := fd.Stat()
-       if err != nil {
-               log.Fatalln(err)
+       urls := make([]meta4ra.URL, 0, len(flag.Args()))
+       for _, u := range flag.Args() {
+               urls = append(urls, meta4ra.URL{URL: u})
        }
-       sha256Hasher := sha256.New()
-       sha512Hasher := sha512.New()
-       _, err = io.Copy(io.MultiWriter(sha256Hasher, sha512Hasher), fd)
+       h := meta4ra.NewHasher(*hashes)
+       h.Start()
+       br := bufio.NewReaderSize(os.Stdin, 1<<20)
+       buf := make([]byte, 1<<20)
+       size, err := io.CopyBuffer(h, br, buf)
        if err != nil {
                log.Fatalln(err)
        }
-       f := File{
-               Name:        filepath.Base(*file),
+       f := meta4ra.File{
+               Name:        path.Base(*fn),
                Description: *desc,
-               Size:        uint64(fi.Size()),
+               Size:        uint64(size),
                URLs:        urls,
-               Hashes: []Hash{
-                       {Type: "sha-256", Hash: hex.EncodeToString(sha256Hasher.Sum(nil))},
-                       {Type: "sha-512", Hash: hex.EncodeToString(sha512Hasher.Sum(nil))},
-               },
+               Hashes:      h.Sums(),
        }
-       if *sig != "" {
-               sigData, err := ioutil.ReadFile(*sig)
+       if *sigPGP != "" {
+               sigData, err := os.ReadFile(*sigPGP)
                if err != nil {
                        log.Fatalln(err)
                }
-               f.Signature = &Signature{
-                       MediaType: GPGSigMediaType,
+               f.Signature = append(f.Signature, meta4ra.Signature{
+                       MediaType: meta4ra.SigMediaTypePGP,
                        Signature: "\n" + string(sigData),
+               })
+       }
+       if *sigSSH != "" {
+               sigData, err := os.ReadFile(*sigSSH)
+               if err != nil {
+                       log.Fatalln(err)
                }
+               f.Signature = append(f.Signature, meta4ra.Signature{
+                       MediaType: meta4ra.SigMediaTypeSSH,
+                       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 {