Skip to content

Commit

Permalink
implement client side reqs for #231
Browse files Browse the repository at this point in the history
  • Loading branch information
richardlehane committed Dec 15, 2023
1 parent dfc4bd5 commit 7aa402a
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 20 deletions.
49 changes: 30 additions & 19 deletions cmd/sf/update.go
Expand Up @@ -22,8 +22,8 @@ import (
"encoding/json"
"errors"
"fmt"
"io"
"io/fs"
"io/ioutil"
"net/http"
"os"
"path/filepath"
Expand All @@ -34,6 +34,8 @@ import (
"github.com/richardlehane/siegfried/pkg/config"
)

type Updates []Update

type Update struct {
Version [3]int `json:"sf"`
Created string `json:"created"`
Expand Down Expand Up @@ -74,7 +76,7 @@ func same(buf []byte, usize int, uhash string) bool {
}

func uptodate(utime, uhash string, usize int) bool {
fbuf, err := ioutil.ReadFile(config.Signature())
fbuf, err := os.ReadFile(config.Signature())
if err != nil {
return false
}
Expand All @@ -98,21 +100,31 @@ func location(base, sig string, args []string) string {
}

func updateSigs(sig string, args []string) (bool, string, error) {
return updateSigsDo(sig, args, getHttp)
}

func updateSigsDo(sig string, args []string, gf getHttpFn) (bool, string, error) {
url, _, _ := config.UpdateOptions()
if url == "" {
return false, "Update is not available for this distribution of siegfried", nil
}
response, err := getHttp(location(url, sig, args))
response, err := gf(location(url, sig, args))
if err != nil {
return false, "", err
}
var u Update
if err := json.Unmarshal(response, &u); err != nil {
var us Updates
if err := json.Unmarshal(response, &us); err != nil {
return false, "", err
}
version := config.Version()
if version[0] < u.Version[0] || (version[0] == u.Version[0] && version[1] < u.Version[1]) || // if the version is out of date
u.Version == [3]int{0, 0, 0} || u.Created == "" || u.Size == 0 || u.Path == "" { // or if the unmarshalling hasn't worked and we have blank values
var u Update
for _, v := range us {
if version[0] == v.Version[0] && version[1] == v.Version[1] {
u = v
break
}
}
if u.Version == [3]int{0, 0, 0} { // we didn't find an eligible update
return false, "Your version of siegfried is out of date; please install latest from http://www.itforarchivists.com/siegfried before continuing.", nil
}
if uptodate(u.Created, u.Hash, u.Size) {
Expand All @@ -123,28 +135,30 @@ func updateSigs(sig string, args []string) (bool, string, error) {
if errors.Is(err, fs.ErrNotExist) {
err = os.MkdirAll(config.Home(), os.ModePerm)
if err != nil {
return false, "", fmt.Errorf("Siegfried: cannot create home directory %s, %v", config.Home(), err)
return false, "", fmt.Errorf("siegfried: cannot create home directory %s, %v", config.Home(), err)
}
} else {
return false, "", fmt.Errorf("Siegfried: error opening directory %s, %v", config.Home(), err)
return false, "", fmt.Errorf("siegfried: error opening directory %s, %v", config.Home(), err)
}
}
fmt.Println("... downloading latest signature file ...")
response, err = getHttp(u.Path)
response, err = gf(u.Path)
if err != nil {
return false, "", fmt.Errorf("Siegfried: error retrieving %s.\nThis may be a network or firewall issue. See https://github.com/richardlehane/siegfried/wiki/Getting-started for manual instructions.\nSystem error: %v", config.SignatureBase(), err)
return false, "", fmt.Errorf("siegfried: retrieving %s.\nThis may be a network or firewall issue. See https://github.com/richardlehane/siegfried/wiki/Getting-started for manual instructions.\nSystem error: %v", config.SignatureBase(), err)
}
if !same(response, u.Size, u.Hash) {
return false, "", fmt.Errorf("Siegfried: error retrieving %s; SHA256 hash of response doesn't match %s", config.SignatureBase(), u.Hash)
return false, "", fmt.Errorf("siegfried: retrieving %s; SHA256 hash of response doesn't match %s", config.SignatureBase(), u.Hash)
}
err = ioutil.WriteFile(config.Signature(), response, os.ModePerm)
err = os.WriteFile(config.Signature(), response, os.ModePerm)
if err != nil {
return false, "", fmt.Errorf("Siegfried: error writing to directory, %v", err)
return false, "", fmt.Errorf("siegfried: error writing to directory, %v", err)
}
fmt.Printf("... writing %s ...\n", config.Signature())
return true, "Your signature file has been updated", nil
}

type getHttpFn func(string) ([]byte, error)

func getHttp(url string) ([]byte, error) {
req, err := http.NewRequest("GET", url, nil)
if err != nil {
Expand All @@ -153,17 +167,14 @@ func getHttp(url string) ([]byte, error) {
_, timeout, transport := config.UpdateOptions()
req.Header.Add("User-Agent", config.UserAgent())
req.Header.Add("Cache-Control", "no-cache")
timer := time.AfterFunc(timeout, func() {
transport.CancelRequest(req)
})
defer timer.Stop()
client := http.Client{
Transport: transport,
Timeout: timeout,
}
resp, err := client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
return ioutil.ReadAll(resp.Body)
return io.ReadAll(resp.Body)
}
79 changes: 79 additions & 0 deletions cmd/sf/update_test.go
@@ -0,0 +1,79 @@
package main

import (
"bytes"
"compress/flate"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"os"
"testing"
"time"

"github.com/richardlehane/siegfried/internal/persist"
"github.com/richardlehane/siegfried/pkg/config"
)

var testResp = `
[
{
"sf":[1,11,1],
"created":"2023-05-12T09:10:13Z",
"hash":"2ad9b1cb28370add9320473676e3a1ba9e0311fc22a058c0862f4fb68f890582",
"size":204689,
"path":"https://www.itforarchivists.com/siegfried/latest"
},
{
"sf":[1,10,1],
"created":"2023-05-12T09:10:13Z",
"hash":"2ad9b1cb28370add9320473676e3a1ba9e0311fc22a058c0862f4fb68f890582",
"size":204689,
"path":"https://www.itforarchivists.com/siegfried/latest"
}
]
`

func TestUpdate(t *testing.T) {
config.SetHome("../roy/data")
var us Updates
if err := json.Unmarshal([]byte(testResp), &us); err != nil {
t.Fatal(err)
}
// Edit the version, size, hash, and created time of the first response so that
// it always matches and we are always up-to-date
us[0].Version = config.Version()
fbuf, err := os.ReadFile(config.Signature())
if err != nil {
t.Fatal(err)
}
us[0].Size = len(fbuf)
h := sha256.New()
h.Write(fbuf)
us[0].Hash = hex.EncodeToString(h.Sum(nil))
byt, err := json.Marshal(us)
if err != nil {
t.Fatal(err)
}
if len(fbuf) < len(config.Magic())+2+15 {
t.Fatal("signature file too short!")
}
rc := flate.NewReader(bytes.NewBuffer(fbuf[len(config.Magic())+2:]))
nbuf := make([]byte, 15)
if n, _ := rc.Read(nbuf); n < 15 {
t.Fatal("bad signature")
}
rc.Close()
ls := persist.NewLoadSaver(nbuf)
tt := ls.LoadTime()
if ls.Err != nil {
t.Fatal(ls.Err)
}
us[0].Created = tt.Format(time.RFC3339)
tgf := func(url string) ([]byte, error) {
return byt, nil
}
_, str, err := updateSigsDo("http://www.example.com", nil, tgf)
if str != "You are already up to date!" {
t.Fatalf("got: %s; error: %v", str, err)
}
}
2 changes: 1 addition & 1 deletion pkg/config/siegfried.go
Expand Up @@ -56,7 +56,7 @@ var siegfried = struct {
choices: 128,
cost: 25600000,
repetition: 4,
updateURL: "https://www.itforarchivists.com/siegfried/update", // "http://localhost:8081/siegfried/update",
updateURL: "https://www.itforarchivists.com/siegfried/update/v2", // "http://localhost:8081/siegfried/update",
updateTimeout: 30 * time.Second,
updateTransport: &http.Transport{Proxy: http.ProxyFromEnvironment},
fpr: "/tmp/siegfried",
Expand Down

0 comments on commit 7aa402a

Please sign in to comment.