commit 564feb393e606b410d5cc88746c10398976035b3
parent 3daf15afb1cd6fdb99b08b8a46d9f46821070c1b
Author: Sean Enck <sean@ttypty.com>
Date: Sun, 29 Jun 2025 21:29:36 -0400
platform isn't required, this can all be figured at the clipboard
Diffstat:
11 files changed, 149 insertions(+), 162 deletions(-)
diff --git a/internal/app/showclip.go b/internal/app/showclip.go
@@ -19,7 +19,7 @@ func ShowClip(cmd CommandOptions, isShow bool) error {
clipboard := clip.Board{}
if !isShow {
var err error
- clipboard, err = clip.New()
+ clipboard, err = clip.New(clip.DefaultLoader{Full: false})
if err != nil {
return fmt.Errorf("unable to get clipboard: %w", err)
}
diff --git a/internal/app/totp.go b/internal/app/totp.go
@@ -121,7 +121,7 @@ func (args *TOTPArguments) display(opts TOTPOptions) error {
}
clipboard := clip.Board{}
if clipMode {
- clipboard, err = clip.New()
+ clipboard, err = clip.New(clip.DefaultLoader{Full: false})
if err != nil {
return err
}
diff --git a/internal/config/toml_test.go b/internal/config/toml_test.go
@@ -251,7 +251,7 @@ func TestDefaultTOMLToLoadFile(t *testing.T) {
if err := config.LoadConfigFile(file); err != nil {
t.Errorf("invalid error: %v", err)
}
- if len(store.List()) != 19 {
+ if len(store.List()) != 18 {
t.Errorf("invalid environment after load: %d", len(store.List()))
}
}
diff --git a/internal/config/vars.go b/internal/config/vars.go
@@ -6,7 +6,6 @@ import (
"strings"
"git.sr.ht/~enckse/lockbox/internal/output"
- "git.sr.ht/~enckse/lockbox/internal/platform"
)
var (
@@ -81,17 +80,6 @@ var (
}),
short: "max totp time",
})
- // EnvPlatform is the platform that the application is running on
- EnvPlatform = environmentRegister(EnvironmentString{
- environmentStrings: environmentStrings{
- environmentDefault: newDefaultedEnvironment(detectedValue,
- environmentBase{
- key: "PLATFORM",
- description: "Override the detected platform.",
- }),
- allowed: platform.Systems.List(),
- },
- })
// EnvStore is the location of the keepass file/store
EnvStore = environmentRegister(EnvironmentString{
environmentStrings: environmentStrings{
diff --git a/internal/config/vars_test.go b/internal/config/vars_test.go
@@ -157,7 +157,6 @@ func TestDefaultStrings(t *testing.T) {
func TestEmptyStrings(t *testing.T) {
store.Clear()
for _, v := range []config.EnvironmentString{
- config.EnvPlatform,
config.EnvStore,
config.EnvKeyFile,
config.EnvDefaultModTime,
diff --git a/internal/platform/clip/core.go b/internal/platform/clip/core.go
@@ -4,10 +4,12 @@ package clip
import (
"errors"
"fmt"
+ "os"
"os/exec"
+ "runtime"
+ "strings"
"git.sr.ht/~enckse/lockbox/internal/config"
- "git.sr.ht/~enckse/lockbox/internal/platform"
)
type (
@@ -18,8 +20,37 @@ type (
pasting []string
MaxTime int64
}
+ // Loader handles how the system is detected
+ Loader interface {
+ Name() (string, error)
+ Runtime() string
+ Complete() bool
+ }
+ // DefaultLoader is the default system detector
+ DefaultLoader struct {
+ Full bool
+ }
)
+// Name will get the uname results
+func (l DefaultLoader) Name() (string, error) {
+ b, err := exec.Command("uname", "-a").Output()
+ if err != nil {
+ return "", err
+ }
+ return string(b), nil
+}
+
+// Runtime will return the GOOS runtime
+func (l DefaultLoader) Runtime() string {
+ return runtime.GOOS
+}
+
+// Complete indicates if the loader needs a full complete
+func (l DefaultLoader) Complete() bool {
+ return l.Full
+}
+
func newBoard(copying, pasting []string) (Board, error) {
maximum, err := config.EnvClipTimeout.Get()
if err != nil {
@@ -30,7 +61,7 @@ func newBoard(copying, pasting []string) (Board, error) {
}
// New creates a new clipboard
-func New() (Board, error) {
+func New(loader Loader) (Board, error) {
if !config.EnvFeatureClip.Get() {
return Board{}, config.NewFeatureError("clip")
}
@@ -41,26 +72,37 @@ func New() (Board, error) {
if setPaste && setCopy {
return newBoard(overrideCopy, overridePaste)
}
- sys, err := platform.NewSystem(config.EnvPlatform.Get())
- if err != nil {
- return Board{}, err
+ if setCopy && !loader.Complete() {
+ return newBoard(overrideCopy, []string{})
}
var copying []string
var pasting []string
- switch sys {
- case platform.Systems.MacOSSystem:
+ switch loader.Runtime() {
+ case "darwin":
copying = []string{"pbcopy"}
pasting = []string{"pbpaste"}
- case platform.Systems.LinuxXSystem:
- copying = []string{"xclip"}
- pasting = []string{"xclip", "-o"}
- case platform.Systems.LinuxWaylandSystem:
- copying = []string{"wl-copy"}
- pasting = []string{"wl-paste"}
- case platform.Systems.WindowsLinuxSystem:
- copying = []string{"clip.exe"}
- pasting = []string{"powershell.exe", "-command", "Get-Clipboard"}
+ case "linux":
+ name, err := loader.Name()
+ if err != nil {
+ return Board{}, err
+ }
+ if strings.Contains(strings.ToLower(name), "microsoft") {
+ copying = []string{"clip.exe"}
+ pasting = []string{"powershell.exe", "-command", "Get-Clipboard"}
+ } else {
+ if strings.TrimSpace(os.Getenv("WAYLAND_DISPLAY")) != "" {
+ copying = []string{"wl-copy"}
+ pasting = []string{"wl-paste"}
+ } else {
+ if strings.TrimSpace(os.Getenv("DISPLAY")) != "" {
+ copying = []string{"xclip"}
+ pasting = []string{"xclip", "-o"}
+ } else {
+ return Board{}, errors.New("unable to detect linux clipboard")
+ }
+ }
+ }
default:
return Board{}, errors.New("clipboard is unavailable")
}
diff --git a/internal/platform/clip/core_test.go b/internal/platform/clip/core_test.go
@@ -1,17 +1,36 @@
package clip_test
import (
+ "fmt"
"testing"
"git.sr.ht/~enckse/lockbox/internal/config/store"
- "git.sr.ht/~enckse/lockbox/internal/platform"
"git.sr.ht/~enckse/lockbox/internal/platform/clip"
)
+type mockLoader struct {
+ err error
+ name string
+ runtime string
+ full bool
+}
+
+func (m mockLoader) Name() (string, error) {
+ return m.name, m.err
+}
+
+func (m mockLoader) Runtime() string {
+ return m.runtime
+}
+
+func (m mockLoader) Complete() bool {
+ return m.full
+}
+
func TestDisabled(t *testing.T) {
defer store.Clear()
store.SetBool("LOCKBOX_FEATURE_CLIP", false)
- if _, err := clip.New(); err == nil || err.Error() != "clip feature is disabled" {
+ if _, err := clip.New(mockLoader{}); err == nil || err.Error() != "clip feature is disabled" {
t.Errorf("invalid error: %v", err)
}
}
@@ -19,47 +38,84 @@ func TestDisabled(t *testing.T) {
func TestMaxTime(t *testing.T) {
store.Clear()
defer store.Clear()
- store.SetString("LOCKBOX_PLATFORM", string(platform.Systems.LinuxWaylandSystem))
- c, err := clip.New()
+ t.Setenv("WAYLAND_DISPLAY", "1")
+ loader := mockLoader{name: "linux", runtime: "linux"}
+ c, err := clip.New(loader)
if err != nil {
- t.Errorf("invalid clipboard: %v", err)
+ t.Errorf("invalid error: %v", err)
}
if c.MaxTime != 120 {
t.Error("invalid default")
}
store.SetInt64("LOCKBOX_CLIP_TIMEOUT", 1)
- c, err = clip.New()
+ c, err = clip.New(loader)
if err != nil {
- t.Errorf("invalid clipboard: %v", err)
+ t.Errorf("invalid error: %v", err)
}
if c.MaxTime != 1 {
t.Error("invalid default")
}
store.SetInt64("LOCKBOX_CLIP_TIMEOUT", -1)
- _, err = clip.New()
+ c, err = clip.New(loader)
if err == nil || err.Error() != "clipboard entry max time must be > 0" {
t.Errorf("invalid max time error: %v", err)
}
}
-func TestClipboardInstances(t *testing.T) {
+func TestInstance(t *testing.T) {
store.Clear()
defer store.Clear()
- for _, item := range platform.Systems.List() {
- store.SetString("LOCKBOX_PLATFORM", item)
- _, err := clip.New()
+ fxn := func(runtime, name, c, p, e string) {
+ l := mockLoader{runtime: runtime, name: name}
+ b, err := clip.New(l)
if err != nil {
- t.Errorf("invalid clipboard: %v", err)
+ if err.Error() != e {
+ t.Errorf("invalid error: %v", err)
+ }
+ return
+ }
+ cmd, args, _ := b.Args(true)
+ copying := fmt.Sprintf("%s (%v)", cmd, args)
+ cmd, args, _ = b.Args(false)
+ pasting := fmt.Sprintf("%s (%v)", cmd, args)
+ if copying != c {
+ t.Errorf("invalid copy: %s != %s", c, copying)
+ }
+ if pasting != p {
+ t.Errorf("invalid copy: %s != %s", p, pasting)
}
}
+ fxn("darwin", "", "pbcopy ([])", "pbpaste ([])", "")
+ fxn("linux", "microsoft", "clip.exe ([])", "powershell.exe ([-command Get-Clipboard])", "")
+ fxn("linux", "linux", "", "", "unable to detect linux clipboard")
+ t.Setenv("DISPLAY", "1")
+ t.Setenv("WAYLAND_DISPLAY", "1")
+ fxn("linux", "linux", "wl-copy ([])", "wl-paste ([])", "")
+ t.Setenv("WAYLAND_DISPLAY", "")
+ fxn("linux", "linux", "xclip ([])", "xclip ([-o])", "")
+}
+
+func TestFullPartial(t *testing.T) {
+ store.Clear()
+ defer store.Clear()
+ store.SetArray("LOCKBOX_CLIP_COPY_COMMAND", []string{"abc", "xyz", "111"})
+ if _, err := clip.New(mockLoader{}); err != nil {
+ t.Errorf("invalid error: %v", err)
+ }
+ if _, err := clip.New(mockLoader{full: true}); err == nil || err.Error() != "clipboard is unavailable" {
+ t.Errorf("invalid error: %v", err)
+ }
+ store.SetArray("LOCKBOX_CLIP_PASTE_COMMAND", []string{"abc", "xyz", "111"})
+ if _, err := clip.New(mockLoader{full: true}); err != nil {
+ t.Errorf("invalid error: %v", err)
+ }
}
func TestArgsOverride(t *testing.T) {
store.Clear()
defer store.Clear()
store.SetArray("LOCKBOX_CLIP_PASTE_COMMAND", []string{"abc", "xyz", "111"})
- store.SetString("LOCKBOX_PLATFORM", string(platform.Systems.WindowsLinuxSystem))
- c, err := clip.New()
+ c, err := clip.New(mockLoader{name: "microsoft", runtime: "linux"})
if err != nil {
t.Errorf("invalid error: %v", err)
}
@@ -72,7 +128,7 @@ func TestArgsOverride(t *testing.T) {
t.Error("invalid parse")
}
store.SetArray("LOCKBOX_CLIP_COPY_COMMAND", []string{"zzz", "lll", "123"})
- c, err = clip.New()
+ c, err = clip.New(mockLoader{})
if err != nil {
t.Errorf("invalid error: %v", err)
}
@@ -85,8 +141,7 @@ func TestArgsOverride(t *testing.T) {
t.Error("invalid parse")
}
store.Clear()
- store.SetString("LOCKBOX_PLATFORM", string(platform.Systems.WindowsLinuxSystem))
- c, err = clip.New()
+ c, err = clip.New(mockLoader{name: "microsoft", runtime: "linux"})
if err != nil {
t.Errorf("invalid error: %v", err)
}
diff --git a/internal/platform/clip/manager.go b/internal/platform/clip/manager.go
@@ -22,6 +22,7 @@ type (
Getpid() int
Copy(Board, string)
Sleep()
+ Loader() Loader
}
// DefaultDaemon is the default functioning daemon
DefaultDaemon struct{}
@@ -62,12 +63,17 @@ func (d DefaultDaemon) Sleep() {
time.Sleep(1 * time.Second)
}
+// Loader will get the backing loader to use
+func (d DefaultDaemon) Loader() Loader {
+ return DefaultLoader{Full: true}
+}
+
// Manager handles the daemon runner
func Manager(daemon bool, manager Daemon) error {
if manager == nil {
return errors.New("manager is nil")
}
- clipboard, err := New()
+ clipboard, err := New(manager.Loader())
if err != nil {
return err
}
diff --git a/internal/platform/clip/manager_test.go b/internal/platform/clip/manager_test.go
@@ -7,7 +7,6 @@ import (
"testing"
"git.sr.ht/~enckse/lockbox/internal/config/store"
- "git.sr.ht/~enckse/lockbox/internal/platform"
"git.sr.ht/~enckse/lockbox/internal/platform/clip"
)
@@ -59,10 +58,14 @@ func (d *mock) Copy(_ clip.Board, val string) {
func (d *mock) Sleep() {
}
+func (d *mock) Loader() clip.Loader {
+ return mockLoader{name: "linux", runtime: "linux"}
+}
+
func TestErrors(t *testing.T) {
store.Clear()
defer store.Clear()
- store.SetString("LOCKBOX_PLATFORM", string(platform.Systems.LinuxWaylandSystem))
+ t.Setenv("WAYLAND_DISPLAY", "1")
if err := clip.Manager(false, nil); err == nil || err.Error() != "manager is nil" {
t.Errorf("invalid error: %v", err)
}
@@ -80,8 +83,8 @@ func TestErrors(t *testing.T) {
func TestStart(t *testing.T) {
store.Clear()
defer store.Clear()
- store.SetString("LOCKBOX_PLATFORM", string(platform.Systems.LinuxWaylandSystem))
store.SetString("LOCKBOX_CLIP_PIDFILE", "a")
+ t.Setenv("WAYLAND_DISPLAY", "1")
m := &mock{}
if err := clip.Manager(false, m); err != nil {
t.Errorf("invalid error: %v", err)
@@ -94,8 +97,8 @@ func TestStart(t *testing.T) {
func TestPIDMismatch(t *testing.T) {
store.Clear()
defer store.Clear()
- store.SetString("LOCKBOX_PLATFORM", string(platform.Systems.LinuxWaylandSystem))
store.SetString("LOCKBOX_CLIP_PIDFILE", "falsepid")
+ t.Setenv("WAYLAND_DISPLAY", "1")
m := &mock{}
if err := clip.Manager(true, m); err != nil {
t.Errorf("invalid error: %v", err)
@@ -105,8 +108,8 @@ func TestPIDMismatch(t *testing.T) {
func TestChange(t *testing.T) {
store.Clear()
defer store.Clear()
- store.SetString("LOCKBOX_PLATFORM", string(platform.Systems.LinuxWaylandSystem))
store.SetString("LOCKBOX_CLIP_PIDFILE", "a")
+ t.Setenv("WAYLAND_DISPLAY", "1")
m := &mock{}
// NOTE: 100 (count before static) + 120 (default timeout) + 1 (caused break of loop)
if err := clip.Manager(true, m); err == nil || strings.Count(err.Error(), "copied: 221") != 6 {
diff --git a/internal/platform/core.go b/internal/platform/core.go
@@ -1,74 +0,0 @@
-// Package platform defines known platforms
-package platform
-
-import (
- "errors"
- "os"
- "os/exec"
- "strings"
-
- "git.sr.ht/~enckse/lockbox/internal/reflect"
-)
-
-// Systems are the known platforms for lockbox
-var Systems = SystemTypes{
- MacOSSystem: "macos",
- LinuxWaylandSystem: "linux-wayland",
- LinuxXSystem: "linux-x",
- WindowsLinuxSystem: "wsl",
-}
-
-const unknownSystem = ""
-
-type (
- // System represents the platform lockbox is running on.
- System string
-
- // SystemTypes defines systems lockbox is known to run on or can run on
- SystemTypes struct {
- MacOSSystem System
- LinuxWaylandSystem System
- LinuxXSystem System
- WindowsLinuxSystem System
- }
-)
-
-// List will list the platform types on the struct
-func (p SystemTypes) List() []string {
- return reflect.ListFields(p)
-}
-
-// NewSystem gets a new system platform.
-func NewSystem(candidate string) (System, error) {
- env := candidate
- if env != "" {
- for _, p := range Systems.List() {
- if p == env {
- return System(p), nil
- }
- }
- return unknownSystem, errors.New("unknown platform mode")
- }
- b, err := exec.Command("uname", "-a").Output()
- if err != nil {
- return unknownSystem, err
- }
- raw := strings.ToLower(strings.TrimSpace(string(b)))
- parts := strings.Split(raw, " ")
- switch parts[0] {
- case "darwin":
- return Systems.MacOSSystem, nil
- case "linux":
- if strings.Contains(raw, "microsoft-standard-wsl") {
- return Systems.WindowsLinuxSystem, nil
- }
- if strings.TrimSpace(os.Getenv("WAYLAND_DISPLAY")) == "" {
- if strings.TrimSpace(os.Getenv("DISPLAY")) == "" {
- return unknownSystem, errors.New("unable to detect linux system")
- }
- return Systems.LinuxXSystem, nil
- }
- return Systems.LinuxWaylandSystem, nil
- }
- return unknownSystem, errors.New("unable to detect system")
-}
diff --git a/internal/platform/core_test.go b/internal/platform/core_test.go
@@ -1,32 +0,0 @@
-package platform_test
-
-import (
- "testing"
-
- "git.sr.ht/~enckse/lockbox/internal/platform"
-)
-
-func TestPlatformList(t *testing.T) {
- if len(platform.Systems.List()) != 4 {
- t.Errorf("invalid list result")
- }
-}
-
-func TestNewPlatform(t *testing.T) {
- for _, item := range platform.Systems.List() {
- s, err := platform.NewSystem(item)
- if err != nil {
- t.Errorf("invalid clipboard: %v", err)
- }
- if s != platform.System(item) {
- t.Error("mismatch on input and resulting detection")
- }
- }
-}
-
-func TestNewPlatformUnknown(t *testing.T) {
- _, err := platform.NewSystem("ifjoajei")
- if err == nil || err.Error() != "unknown platform mode" {
- t.Errorf("error expected for platform: %v", err)
- }
-}