1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152
|
// Package opts helps to write commands which may take multihash
// options.
package opts
import (
"bytes"
"errors"
"flag"
"fmt"
"io"
"sort"
"strings"
mh "github.com/multiformats/go-multihash"
)
// package errors
var (
ErrMatch = errors.New("multihash checksums did not match")
)
// Options is a struct used to parse cli flags.
type Options struct {
Encoding string
Algorithm string
AlgorithmCode uint64
Length int
fs *flag.FlagSet
}
// FlagValues are the values the various option flags can take.
var FlagValues = struct {
Encodings []string
Algorithms []string
}{
Encodings: []string{"raw", "hex", "base58", "base64"},
Algorithms: func() []string {
names := make([]string, 0, len(mh.Names))
for n := range mh.Names {
// There are too many of these for now.
// We can figure something better out later.
if strings.HasPrefix(n, "blake2") {
switch n {
case "blake2s-256":
case "blake2b-128":
case "blake2b-224":
case "blake2b-256":
case "blake2b-384":
case "blake2b-512":
default:
continue
}
}
names = append(names, n)
}
sort.Strings(names)
return names
}(),
}
// SetupFlags adds multihash related options to given flagset.
func SetupFlags(f *flag.FlagSet) *Options {
// TODO: add arg for adding opt prefix and/or overriding opts
o := new(Options)
algoStr := "one of: " + strings.Join(FlagValues.Algorithms, ", ")
f.StringVar(&o.Algorithm, "algorithm", "sha2-256", algoStr)
f.StringVar(&o.Algorithm, "a", "sha2-256", algoStr+" (shorthand)")
encStr := "one of: " + strings.Join(FlagValues.Encodings, ", ")
f.StringVar(&o.Encoding, "encoding", "base58", encStr)
f.StringVar(&o.Encoding, "e", "base58", encStr+" (shorthand)")
lengthStr := "checksums length in bits (truncate). -1 is default"
f.IntVar(&o.Length, "length", -1, lengthStr)
f.IntVar(&o.Length, "l", -1, lengthStr+" (shorthand)")
return o
}
// Parse parses the values of flags from given argument slice.
// It is equivalent to flags.Parse(args)
func (o *Options) Parse(args []string) error {
if err := o.fs.Parse(args); err != nil {
return err
}
return o.ParseError()
}
// ParseError checks the parsed options for errors.
func (o *Options) ParseError() error {
if !strIn(o.Encoding, FlagValues.Encodings) {
return fmt.Errorf("encoding '%s' not %s", o.Encoding, FlagValues.Encodings)
}
if !strIn(o.Algorithm, FlagValues.Algorithms) {
return fmt.Errorf("algorithm '%s' not %s", o.Algorithm, FlagValues.Algorithms)
}
var found bool
o.AlgorithmCode, found = mh.Names[o.Algorithm]
if !found {
return fmt.Errorf("algorithm '%s' not found (lib error, pls report)", o.Algorithm)
}
if o.Length >= 0 {
if o.Length%8 != 0 {
return fmt.Errorf("length must be multiple of 8")
}
o.Length = o.Length / 8
h, _ := mh.GetHasher(o.AlgorithmCode)
hsize := 0
if h != nil {
hsize = h.Size()
}
if o.Length > hsize {
o.Length = hsize
}
}
return nil
}
// strIn checks wither string a is in set.
func strIn(a string, set []string) bool {
for _, s := range set {
if s == a {
return true
}
}
return false
}
// Check reads all the data in r, calculates its multihash,
// and checks it matches h1
func (o *Options) Check(r io.Reader, h1 mh.Multihash) error {
h2, err := o.Multihash(r)
if err != nil {
return err
}
if !bytes.Equal(h1, h2) {
return fmt.Errorf("computed checksum did not match")
}
return nil
}
// Multihash reads all the data in r and calculates its multihash.
func (o *Options) Multihash(r io.Reader) (mh.Multihash, error) {
return mh.SumStream(r, o.AlgorithmCode, o.Length)
}
|