/* meta4a -- Metalink 4.0 checker Copyright (C) 2021-2023 Sergey Matveev 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 . */ // Metalink 4.0 checker package main import ( "bufio" "crypto/sha256" "crypto/sha512" "encoding/hex" "encoding/xml" "flag" "fmt" "hash" "io" "io/fs" "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 := os.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 = os.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) } }