commit a9ab6b7ac44a0531d297f72fb66388983e83ae8e
parent 886cdc69b720a4273dcb9f27d67a8a193d6a726f
Author: Sean Enck <sean@ttypty.com>
Date: Thu, 30 Mar 2023 18:55:50 -0400
use json instead of text parsing
Diffstat:
8 files changed, 56 insertions(+), 36 deletions(-)
diff --git a/cmd/vers.txt b/cmd/vers.txt
@@ -1 +1 @@
-6bf2506-1
-\ No newline at end of file
+76d0142
+\ No newline at end of file
diff --git a/internal/app/rekey.go b/internal/app/rekey.go
@@ -1,6 +1,7 @@
package app
import (
+ "encoding/json"
"errors"
"fmt"
"io"
@@ -17,7 +18,7 @@ type (
// Keyer defines how rekeying happens
Keyer interface {
List() ([]string, error)
- Stats(string) ([]string, error)
+ Stats(string) ([]byte, error)
Show(string) ([]byte, error)
Insert(ReKeyEntry) error
}
@@ -48,8 +49,8 @@ func (r DefaultKeyer) List() ([]string, error) {
}
// Stats will get stats for an entry
-func (r DefaultKeyer) Stats(entry string) ([]string, error) {
- return r.getCommandLines(cli.StatsCommand, entry)
+func (r DefaultKeyer) Stats(entry string) ([]byte, error) {
+ return exec.Command(r.exe, cli.StatsCommand, entry).Output()
}
func (r DefaultKeyer) getCommandLines(args ...string) ([]string, error) {
@@ -98,18 +99,14 @@ func ReKey(writer io.Writer, r Keyer) error {
}
stats, err := r.Stats(entry)
if err != nil {
- return fmt.Errorf("failed to get modtime, command failed: %w", err)
+ return fmt.Errorf("failed to get modtime: %w", err)
}
modTime := ""
- for _, stat := range stats {
- if strings.HasPrefix(stat, backend.ModTimeField) {
- if modTime != "" {
- return errors.New("unable to read modtime, too many values")
- }
- modTime = strings.TrimPrefix(stat, backend.ModTimeField)
- }
+ j := backend.Stats{}
+ if err := json.Unmarshal(stats, &j); err != nil {
+ return fmt.Errorf("invalid stats json: %w", err)
}
- modTime = strings.TrimSpace(modTime)
+ modTime = strings.TrimSpace(j.ModTime)
if modTime == "" {
return errors.New("did not read modtime")
}
diff --git a/internal/app/rekey_test.go b/internal/app/rekey_test.go
@@ -5,6 +5,7 @@ import (
"errors"
"fmt"
"os"
+ "strings"
"testing"
"github.com/enckse/lockbox/internal/app"
@@ -14,7 +15,7 @@ type (
mockKeyer struct {
entries []string
data map[string][]byte
- stats map[string][]string
+ stats map[string][]byte
err error
rekeys []app.ReKeyEntry
}
@@ -35,7 +36,7 @@ func (m *mockKeyer) Show(entry string) ([]byte, error) {
return val, nil
}
-func (m *mockKeyer) Stats(entry string) ([]string, error) {
+func (m *mockKeyer) Stats(entry string) ([]byte, error) {
val, ok := m.stats[entry]
if !ok {
return nil, errors.New("no stats")
@@ -66,20 +67,21 @@ func TestErrors(t *testing.T) {
}
m.err = nil
m.entries = []string{"test1", "error"}
- if err := app.ReKey(&buf, m); err == nil || err.Error() != "failed to get modtime, command failed: no stats" {
+ if err := app.ReKey(&buf, m); err == nil || err.Error() != "failed to get modtime: no stats" {
t.Errorf("invalid error: %v", err)
}
- m.stats = make(map[string][]string)
- m.stats["test1"] = []string{"modtime"}
- m.stats["error"] = []string{"modtime: 3"}
- if err := app.ReKey(&buf, m); err == nil || err.Error() != "did not read modtime" {
+ m.stats = make(map[string][]byte)
+ m.stats["test1"] = []byte("modtime")
+ if err := app.ReKey(&buf, m); err == nil || !strings.HasPrefix(err.Error(), "invalid stats json:") {
t.Errorf("invalid error: %v", err)
}
- m.stats["test1"] = []string{"modtime: 1", "modtime: 2"}
- if err := app.ReKey(&buf, m); err == nil || err.Error() != "unable to read modtime, too many values" {
+ m.stats = make(map[string][]byte)
+ m.stats["test1"] = []byte("{\"modtime\": \"\"}")
+ if err := app.ReKey(&buf, m); err == nil || err.Error() != "did not read modtime" {
t.Errorf("invalid error: %v", err)
}
- m.stats["test1"] = []string{"modtime: 1"}
+ m.stats["test1"] = []byte("{\"modtime\": \"1\"}")
+ m.stats["error"] = []byte("{\"modtime\": \"1\"}")
if err := app.ReKey(&buf, m); err == nil || err.Error() != "no data" {
t.Errorf("invalid error: %v", err)
}
@@ -105,9 +107,9 @@ func TestReKey(t *testing.T) {
m.data = make(map[string][]byte)
m.data["test1"] = []byte{1}
m.data["test2"] = []byte{2}
- m.stats = make(map[string][]string)
- m.stats["test1"] = []string{"modtime: 1", "modtime2"}
- m.stats["test2"] = []string{"moime: 1", "modtime: 2"}
+ m.stats = make(map[string][]byte)
+ m.stats["test1"] = []byte("{\"modtime\": \"1\"}")
+ m.stats["test2"] = []byte("{\"modtime\": \"2\"}")
if err := app.ReKey(&buf, m); err != nil {
t.Errorf("invalid error: %v", err)
}
diff --git a/internal/backend/query.go b/internal/backend/query.go
@@ -3,6 +3,7 @@ package backend
import (
"crypto/sha512"
+ "encoding/json"
"errors"
"fmt"
"sort"
@@ -131,12 +132,17 @@ func (t *Transaction) QueryCallback(args QueryOptions) ([]QueryEntity, error) {
switch args.Values {
case SecretValue:
entity.Value = val
- case HashedValue, StatsValue:
+ case StatsValue:
t := getValue(e.backing, modTimeKey)
- res := fmt.Sprintf("%s %s", ModTimeField, t)
- if args.Values == HashedValue {
- res = fmt.Sprintf("%s\nhash: %x", res, sha512.Sum512([]byte(val)))
+ s := Stats{Path: k, ModTime: t}
+ m, err := json.MarshalIndent(s, "", " ")
+ if err != nil {
+ return nil, err
}
+ entity.Value = string(m)
+ case HashedValue:
+ t := getValue(e.backing, modTimeKey)
+ res := fmt.Sprintf("modtime: %s\nhash: %x", t, sha512.Sum512([]byte(val)))
entity.Value = res
}
}
diff --git a/internal/backend/query_test.go b/internal/backend/query_test.go
@@ -1,6 +1,7 @@
package backend_test
import (
+ "encoding/json"
"fmt"
"os"
"strings"
@@ -106,8 +107,15 @@ func TestValueModes(t *testing.T) {
t.Errorf("invalid result value: %s", q.Value)
}
q, err = fullSetup(t, true).Get("test/test/abc", backend.StatsValue)
- if err != nil || !strings.HasPrefix(q.Value, "modtime: ") || len(strings.Split(q.Value, "\n")) != 1 {
- t.Errorf("invalid stats: %s", q.Value)
+ if err != nil {
+ t.Errorf("no error: %v", err)
+ }
+ r := backend.Stats{}
+ if err := json.Unmarshal([]byte(q.Value), &r); err != nil {
+ t.Errorf("json error: %v", err)
+ }
+ if len(r.ModTime) != 25 || r.Path != "test/test/abc" {
+ t.Errorf("invalid stats: %v", r)
}
}
diff --git a/internal/backend/types.go b/internal/backend/types.go
@@ -48,6 +48,11 @@ type (
enabled bool
scripts []string
}
+ // Stats shows information about the entry
+ Stats struct {
+ ModTime string `json:"modtime"`
+ Path string `json:"path"`
+ }
removal struct {
parts []string
title string
@@ -100,8 +105,6 @@ const (
pathSep = "/"
isGlob = pathSep + "*"
modTimeKey = "ModTime"
- // ModTimeField is the stats field for modification time
- ModTimeField = "modtime:"
)
var errPath = errors.New("input paths must contain at LEAST 2 components")
diff --git a/tests/expected.log b/tests/expected.log
@@ -15,7 +15,10 @@ keys2/k/three
test2
test3
test4
-modtime: XXXX-XX-XX
+{
+ "modtime": "XXXX-XX-XX",
+ "path": "keys2/k/three"
+}
test/k
XXXXXX
XXXXXX
diff --git a/tests/run.sh b/tests/run.sh
@@ -122,6 +122,7 @@ _clipboard() {
_logtest() {
_execute 2>&1 | \
sed 's/modtime: [0-9].*$/modtime: XXXX-XX-XX/g' | \
+ sed 's/\"modtime\": \"[0-9].*$/"modtime": "XXXX-XX-XX",/g' | \
sed 's/^[0-9][0-9][0-9][0-9][0-9][0-9]$/XXXXXX/g'
}