Go 96 lines
package selfupdate
import (
"crypto/sha256"
"encoding/hex"
"fmt"
"io"
"net/http"
"os"
"strings"
)
func download(client *http.Client, url, dst string) error {
req, err := http.NewRequest(http.MethodGet, url, nil)
if err != nil {
return err
}
req.Header.Set("User-Agent", "eeco-update/1")
resp, err := client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("http %d", resp.StatusCode)
}
tmp, err := os.CreateTemp(parentDir(dst), ".eeco-dl-*")
if err != nil {
return err
}
tmpName := tmp.Name()
if _, err := io.Copy(tmp, resp.Body); err != nil {
tmp.Close()
os.Remove(tmpName)
return err
}
if err := tmp.Close(); err != nil {
os.Remove(tmpName)
return err
}
return os.Rename(tmpName, dst)
}
func parentDir(path string) string {
i := strings.LastIndexAny(path, `/\`)
if i < 0 {
return "."
}
return path[:i]
}
func sha256File(path string) (string, error) {
f, err := os.Open(path)
if err != nil {
return "", err
}
defer f.Close()
h := sha256.New()
if _, err := io.Copy(h, f); err != nil {
return "", err
}
return hex.EncodeToString(h.Sum(nil)), nil
}
// checksumFor reads sumsPath (the `shasum -a 256` SHA256SUMS file) and
// returns the recorded hash for archiveBasename. Each line is:
//
// <64-hex> <basename>
//
// (two spaces between, per shasum's default text mode).
func checksumFor(sumsPath, archiveBasename string) (string, error) {
b, err := os.ReadFile(sumsPath)
if err != nil {
return "", err
}
for _, raw := range strings.Split(string(b), "\n") {
line := strings.TrimSpace(raw)
if line == "" {
continue
}
fields := strings.Fields(line)
if len(fields) < 2 {
continue
}
hash := fields[0]
name := strings.TrimPrefix(fields[len(fields)-1], "*")
if name == archiveBasename {
if len(hash) != 64 {
return "", fmt.Errorf("SHA256SUMS: bad hash for %s", archiveBasename)
}
return strings.ToLower(hash), nil
}
}
return "", fmt.Errorf("SHA256SUMS: no entry for %s", archiveBasename)
}