commit 12a28cec60fb45fd70b6233c50b5afc015318da3
parent e16ccb77d092c455a30cfe40247bc9db47fc5a07
Author: Sean Enck <sean@ttypty.com>
Date: Sun, 9 Oct 2022 10:29:06 -0400
allow override clipboard commands
Diffstat:
3 files changed, 80 insertions(+), 21 deletions(-)
diff --git a/internal/inputs/env.go b/internal/inputs/env.go
@@ -21,6 +21,7 @@ const (
interactiveEnv = prefixKey + "INTERACTIVE"
readOnlyEnv = prefixKey + "READONLY"
fieldTOTPEnv = prefixKey + "TOTP"
+ clipBaseEnv = prefixKey + "CLIP_"
formatTOTPEnv = fieldTOTPEnv + "_FORMAT"
keyModeEnv = prefixKey + "KEYMODE"
keyEnv = prefixKey + "KEY"
@@ -31,9 +32,13 @@ const (
// StoreEnv is the location of the filesystem store that lb is operating on.
StoreEnv = prefixKey + "STORE"
// ClipMaxEnv is the max time a value should be stored in the clipboard.
- ClipMaxEnv = prefixKey + "CLIPMAX"
+ ClipMaxEnv = clipBaseEnv + "MAX"
// ColorBetweenEnv is a comma-delimited list of times to color totp outputs (e.g. 0:5,30:35 which is the default).
ColorBetweenEnv = fieldTOTPEnv + "_BETWEEN"
+ // ClipPasteEnv allows overriding the clipboard paste command
+ ClipPasteEnv = clipBaseEnv + "PASTE"
+ // ClipCopyEnv allows overriding the clipboard copy command
+ ClipCopyEnv = clipBaseEnv + "COPY"
)
// EnvOrDefault will get the environment value OR default if env is not set.
diff --git a/internal/platform/clipboard.go b/internal/platform/clipboard.go
@@ -7,8 +7,10 @@ import (
"os"
"os/exec"
"strconv"
+ "strings"
"github.com/enckse/lockbox/internal/inputs"
+ "github.com/google/shlex"
)
const (
@@ -24,6 +26,30 @@ type (
}
)
+func newClipboard(copying, pasting []string) (Clipboard, error) {
+ max := maxTime
+ useMax := os.Getenv(inputs.ClipMaxEnv)
+ if useMax != "" {
+ i, err := strconv.Atoi(useMax)
+ if err != nil {
+ return Clipboard{}, err
+ }
+ if i < 1 {
+ return Clipboard{}, errors.New("clipboard max time must be greater than 0")
+ }
+ max = i
+ }
+ return Clipboard{copying: copying, pasting: pasting, MaxTime: max}, nil
+}
+
+func overrideCommand(v string) ([]string, error) {
+ value := inputs.EnvOrDefault(v, "")
+ if strings.TrimSpace(value) == "" {
+ return nil, nil
+ }
+ return shlex.Split(value)
+}
+
// NewClipboard will retrieve the commands to use for clipboard operations.
func NewClipboard() (Clipboard, error) {
noClip, err := inputs.IsNoClipEnabled()
@@ -33,22 +59,22 @@ func NewClipboard() (Clipboard, error) {
if noClip {
return Clipboard{}, errors.New("clipboard is off")
}
- sys, err := NewPlatform()
+ overridePaste, err := overrideCommand(inputs.ClipPasteEnv)
if err != nil {
return Clipboard{}, err
}
- max := maxTime
- useMax := os.Getenv(inputs.ClipMaxEnv)
- if useMax != "" {
- i, err := strconv.Atoi(useMax)
- if err != nil {
- return Clipboard{}, err
- }
- if i < 1 {
- return Clipboard{}, errors.New("clipboard max time must be greater than 0")
- }
- max = i
+ overrideCopy, err := overrideCommand(inputs.ClipCopyEnv)
+ if err != nil {
+ return Clipboard{}, err
+ }
+ if overrideCopy != nil && overridePaste != nil {
+ return newClipboard(overrideCopy, overridePaste)
+ }
+ sys, err := NewPlatform()
+ if err != nil {
+ return Clipboard{}, err
}
+
var copying []string
var pasting []string
switch sys {
@@ -67,7 +93,13 @@ func NewClipboard() (Clipboard, error) {
default:
return Clipboard{}, errors.New("clipboard is unavailable")
}
- return Clipboard{copying: copying, pasting: pasting, MaxTime: max}, nil
+ if overridePaste != nil {
+ pasting = overridePaste
+ }
+ if overrideCopy != nil {
+ copying = overrideCopy
+ }
+ return newClipboard(copying, pasting)
}
// CopyTo will copy to clipboard, if non-empty will clear later.
diff --git a/internal/platform/clipboard_test.go b/internal/platform/clipboard_test.go
@@ -9,7 +9,7 @@ import (
)
func TestNoClipboard(t *testing.T) {
- os.Setenv("LOCKBOX_CLIPMAX", "")
+ os.Setenv("LOCKBOX_CLIP_MAX", "")
os.Setenv("LOCKBOX_NOCLIP", "yes")
_, err := platform.NewClipboard()
if err == nil || err.Error() != "clipboard is off" {
@@ -20,7 +20,7 @@ func TestNoClipboard(t *testing.T) {
func TestMaxTime(t *testing.T) {
os.Setenv("LOCKBOX_NOCLIP", "no")
os.Setenv("LOCKBOX_PLATFORM", string(platform.LinuxWayland))
- os.Setenv("LOCKBOX_CLIPMAX", "")
+ os.Setenv("LOCKBOX_CLIP_MAX", "")
c, err := platform.NewClipboard()
if err != nil {
t.Errorf("invalid clipboard: %v", err)
@@ -28,7 +28,7 @@ func TestMaxTime(t *testing.T) {
if c.MaxTime != 45 {
t.Error("invalid default")
}
- os.Setenv("LOCKBOX_CLIPMAX", "1")
+ os.Setenv("LOCKBOX_CLIP_MAX", "1")
c, err = platform.NewClipboard()
if err != nil {
t.Errorf("invalid clipboard: %v", err)
@@ -36,12 +36,12 @@ func TestMaxTime(t *testing.T) {
if c.MaxTime != 1 {
t.Error("invalid default")
}
- os.Setenv("LOCKBOX_CLIPMAX", "-1")
+ os.Setenv("LOCKBOX_CLIP_MAX", "-1")
_, err = platform.NewClipboard()
if err == nil || err.Error() != "clipboard max time must be greater than 0" {
t.Errorf("invalid max time error: %v", err)
}
- os.Setenv("LOCKBOX_CLIPMAX", "$&(+")
+ os.Setenv("LOCKBOX_CLIP_MAX", "$&(+")
_, err = platform.NewClipboard()
if err == nil || err.Error() != "strconv.Atoi: parsing \"$&(+\": invalid syntax" {
t.Errorf("invalid max time error: %v", err)
@@ -50,7 +50,7 @@ func TestMaxTime(t *testing.T) {
func TestClipboardInstances(t *testing.T) {
os.Setenv("LOCKBOX_NOCLIP", "no")
- os.Setenv("LOCKBOX_CLIPMAX", "")
+ os.Setenv("LOCKBOX_CLIP_MAX", "")
for _, item := range []platform.System{platform.MacOS, platform.LinuxWayland, platform.LinuxX, platform.WindowsLinux} {
os.Setenv("LOCKBOX_PLATFORM", string(item))
_, err := platform.NewClipboard()
@@ -60,7 +60,8 @@ func TestClipboardInstances(t *testing.T) {
}
}
-func TestArgs(t *testing.T) {
+func TestArgsOverride(t *testing.T) {
+ os.Setenv("LOCKBOX_CLIP_PASTE", "abc xyz 111")
os.Setenv("LOCKBOX_PLATFORM", string(platform.WindowsLinux))
c, _ := platform.NewClipboard()
cmd, args := c.Args(true)
@@ -68,6 +69,27 @@ func TestArgs(t *testing.T) {
t.Error("invalid parse")
}
cmd, args = c.Args(false)
+ if cmd != "abc" || len(args) != 2 || args[0] != "xyz" || args[1] != "111" {
+ t.Error("invalid parse")
+ }
+ os.Setenv("LOCKBOX_CLIP_COPY", "zzz lll 123")
+ c, _ = platform.NewClipboard()
+ cmd, args = c.Args(true)
+ if cmd != "zzz" || len(args) != 2 || args[0] != "lll" || args[1] != "123" {
+ t.Error("invalid parse")
+ }
+ cmd, args = c.Args(false)
+ if cmd != "abc" || len(args) != 2 || args[0] != "xyz" || args[1] != "111" {
+ t.Error("invalid parse")
+ }
+ os.Setenv("LOCKBOX_CLIP_PASTE", "")
+ os.Setenv("LOCKBOX_CLIP_COPY", "")
+ c, _ = platform.NewClipboard()
+ cmd, args = c.Args(true)
+ if cmd != "clip.exe" || len(args) != 0 {
+ t.Error("invalid parse")
+ }
+ cmd, args = c.Args(false)
if cmd != "powershell.exe" || len(args) != 2 || args[0] != "-command" || args[1] != "Get-Clipboard" {
fmt.Println(args)
t.Error("invalid parse")