package feed import ( "bufio" "encoding/hex" "fmt" "io" "strconv" "strings" "time" ) // NVD metadata. type Meta struct { LastModifiedDate time.Time // last modified time Size uint64 // uncompressed size, in bytes ZipSize uint64 // zip file size, in bytes GzSize uint64 // gz file size, in bytes Sha256 [32]byte // sha256 hash of uncompressed data } func parseMetaSize(name, val string) (uint64, error) { // parse value, check for error v, err := strconv.ParseUint(val, 10, 64) if err == nil { // return size return v, nil } else { // return error return 0, fmt.Errorf("invalid %s: \"%s\"", name, val) } } // Unmarshal new Metadata from reader. func NewMeta(r io.Reader) (*Meta, error) { // declare result var m Meta // create scanner scanner := bufio.NewScanner(r) // read lines for scanner.Scan() { // split into key/value pair, check for error pair := strings.SplitN(scanner.Text(), ":", 2) if len(pair) != 2 { return nil, fmt.Errorf("bad meta line: \"%s\"", scanner.Text()) } switch pair[0] { case "lastModifiedDate": // parse time, check for error if err := m.LastModifiedDate.UnmarshalText([]byte(pair[1])); err != nil { return nil, err } case "size": if v, err := parseMetaSize("size", pair[1]); err == nil { m.Size = v } else { return nil, err } case "zipSize": if v, err := parseMetaSize("zipSize", pair[1]); err == nil { m.ZipSize = v } else { return nil, err } case "gzSize": if v, err := parseMetaSize("gzSize", pair[1]); err == nil { m.GzSize = v } else { return nil, err } case "sha256": // check hash length if len(pair[1]) != 64 { return nil, fmt.Errorf("invalid sha256 hash length: %d", len(pair[1])) } // decode hex, check for error buf, err := hex.DecodeString(pair[1]) if err != nil { return nil, fmt.Errorf("invalid sha256 hash: %v", err) } // save to buffer, check for error len := copy(m.Sha256[:], buf[0:32]) if len != 32 { // difficult to test, but this basically doesn't happen, see here: // https://github.com/golang/go/blob/2ebe77a2fda1ee9ff6fd9a3e08933ad1ebaea039/src/runtime/slice.go#L247 return nil, fmt.Errorf("invalid copy length: %d", len) } default: // return error return nil, fmt.Errorf("unknown key: \"%s\"", pair[0]) } } // check for scanner error if err := scanner.Err(); err != nil { return nil, err } // return success return &m, nil }