github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/release/update/update.go (about)

     1  // Copyright 2015 Keybase, Inc. All rights reserved. Use of
     2  // this source code is governed by the included BSD license.
     3  
     4  package update
     5  
     6  import (
     7  	"crypto/sha256"
     8  	"encoding/hex"
     9  	"encoding/json"
    10  	"fmt"
    11  	"io"
    12  	"net/url"
    13  	"os"
    14  	"path"
    15  	"strings"
    16  
    17  	releaseVersion "github.com/keybase/client/go/release/version"
    18  )
    19  
    20  // EncodeJSON returns JSON (as bytes) for an update
    21  func EncodeJSON(version string, name string, descriptionPath string, props []string, src string, uri fmt.Stringer, signaturePath string) ([]byte, error) {
    22  	upd := Update{
    23  		Version: version,
    24  		Name:    name,
    25  	}
    26  
    27  	// Get published at from version string
    28  	_, _, date, _, err := releaseVersion.Parse(version)
    29  	if err == nil {
    30  		t := ToTime(date)
    31  		upd.PublishedAt = &t
    32  	}
    33  
    34  	if src != "" && uri != nil {
    35  		fileName := path.Base(src)
    36  
    37  		// Or if we can't parse use the src file modification time
    38  		if upd.PublishedAt == nil {
    39  			var srcInfo os.FileInfo
    40  			srcInfo, err = os.Stat(src)
    41  			if err != nil {
    42  				return nil, err
    43  			}
    44  			t := ToTime(srcInfo.ModTime())
    45  			upd.PublishedAt = &t
    46  		}
    47  
    48  		urlString := fmt.Sprintf("%s/%s", uri.String(), url.QueryEscape(fileName))
    49  		asset := Asset{
    50  			Name: fileName,
    51  			URL:  urlString,
    52  		}
    53  
    54  		digest, err := digest(src)
    55  		if err != nil {
    56  			return nil, fmt.Errorf("Error creating digest: %s", err)
    57  		}
    58  		asset.Digest = digest
    59  
    60  		if signaturePath != "" {
    61  			sig, err := readFile(signaturePath)
    62  			if err != nil {
    63  				return nil, err
    64  			}
    65  			asset.Signature = sig
    66  		}
    67  
    68  		if descriptionPath != "" {
    69  			desc, err := readFile(descriptionPath)
    70  			if err != nil {
    71  				return nil, err
    72  			}
    73  			upd.Description = desc
    74  		}
    75  
    76  		upd.Asset = &asset
    77  	}
    78  
    79  	if props != nil {
    80  		uprops := []Property{}
    81  		for _, p := range props {
    82  			splitp := strings.SplitN(p, ":", 2)
    83  			if len(splitp) == 2 {
    84  				uprops = append(uprops, Property{Name: splitp[0], Value: splitp[1]})
    85  			}
    86  		}
    87  		if len(uprops) > 0 {
    88  			upd.Props = uprops
    89  		}
    90  	}
    91  
    92  	return json.MarshalIndent(upd, "", "  ")
    93  }
    94  
    95  // DecodeJSON returns an update object from JSON (bytes)
    96  func DecodeJSON(r io.Reader) (*Update, error) {
    97  	var obj Update
    98  	if err := json.NewDecoder(r).Decode(&obj); err != nil {
    99  		return nil, err
   100  	}
   101  	return &obj, nil
   102  }
   103  
   104  func readFile(path string) (string, error) {
   105  	sigFile, err := os.Open(path)
   106  	if err != nil {
   107  		return "", err
   108  	}
   109  	defer func() { _ = sigFile.Close() }()
   110  	data, err := io.ReadAll(sigFile)
   111  	if err != nil {
   112  		return "", err
   113  	}
   114  	return string(data), nil
   115  }
   116  
   117  func digest(p string) (digest string, err error) {
   118  	hasher := sha256.New()
   119  	f, err := os.Open(p)
   120  	if err != nil {
   121  		return
   122  	}
   123  	defer func() { _ = f.Close() }()
   124  	if _, ioerr := io.Copy(hasher, f); ioerr != nil {
   125  		err = ioerr
   126  		return
   127  	}
   128  	digest = hex.EncodeToString(hasher.Sum(nil))
   129  	return
   130  }