From 0e6210f1af81d025c79970fdc4f5a56b078ed9d0 Mon Sep 17 00:00:00 2001 From: Randall Winkhart Date: Sun, 3 Mar 2024 22:36:20 -0500 Subject: [PATCH] Implement untested MacOS clipboard support via pbcopy --- src/offline/copyDARWIN.go | 75 ++++++++++++++++++++++++++++++++++ src/offline/copyUNIXGeneric.go | 4 +- src/offline/copyWIN.go | 4 +- src/offline/gpg.go | 2 +- 4 files changed, 79 insertions(+), 6 deletions(-) create mode 100644 src/offline/copyDARWIN.go diff --git a/src/offline/copyDARWIN.go b/src/offline/copyDARWIN.go new file mode 100644 index 0000000..5b4f088 --- /dev/null +++ b/src/offline/copyDARWIN.go @@ -0,0 +1,75 @@ +//go:build darwin + +package offline + +import ( + "fmt" + "os" + "os/exec" + "strings" + "time" +) + +// TODO MacOS support is entirely untested - I would appreciate feedback on this implementation + +// CopyField copies a field from an entry to the clipboard +func CopyField(targetLocation string, field int, executableName string) { + if isFile, _ := TargetIsFile(targetLocation, true); isFile { + decryptedEntry := DecryptGPG(targetLocation) + var copySubject string // will store data to be copied + + // ensure field exists in entry + if len(decryptedEntry) > field { + copySubject = decryptedEntry[field] + } else { + fmt.Println(AnsiError + "Field does not exist in entry" + AnsiReset) + os.Exit(1) + } + + // ensure field is not blank + if copySubject == "" { + fmt.Println(AnsiError + "Field is empty" + AnsiReset) + os.Exit(1) + } + + cmd := exec.Command("pbcopy") + writeToStdin(cmd, copySubject) + err := cmd.Run() + if err != nil { + fmt.Println(AnsiError + "Failed to copy to clipboard: " + err.Error() + AnsiReset) + os.Exit(1) + } + + cmd = exec.Command(executableName, "clipclear") + writeToStdin(cmd, copySubject) + err = cmd.Start() + if err != nil { + fmt.Println(AnsiError + "Failed to launch automated clipboard clearing process - does this libmutton implementation support the \"clipclear\" argument?" + AnsiReset) + os.Exit(1) + } + + } else { + fmt.Println(AnsiError + "Failed to read \"" + targetLocation + "\" - it is a directory" + AnsiReset) + os.Exit(1) + } + os.Exit(0) +} + +// ClipClear is called in a separate process to clear the clipboard after 30 seconds +func ClipClear(oldContents string) { + time.Sleep(30 * time.Second) + + cmd := exec.Command("pbpaste") + newContents, _ := cmd.Output() + + if oldContents == strings.TrimRight(string(newContents), "\r\n") { + cmd = exec.Command("pbcopy") + writeToStdin(cmd, "") + err := cmd.Run() + if err != nil { + fmt.Println(AnsiError + "Failed to clear clipboard: " + err.Error() + AnsiReset) + os.Exit(1) + } + } + os.Exit(0) +} diff --git a/src/offline/copyUNIXGeneric.go b/src/offline/copyUNIXGeneric.go index 28c1ac5..e4d6b30 100644 --- a/src/offline/copyUNIXGeneric.go +++ b/src/offline/copyUNIXGeneric.go @@ -10,7 +10,7 @@ import ( "time" ) -// TODO Implement support for MacOS via pbcopy, Termux via termux-clipboard-set (in separate files) +// TODO Implement support for Termux via termux-clipboard-set (in separate file) // CopyField copies a field from an entry to the clipboard func CopyField(targetLocation string, field int, executableName string) { @@ -61,12 +61,12 @@ func CopyField(targetLocation string, field int, executableName string) { } else { fmt.Println(AnsiError + "Failed to read \"" + targetLocation + "\" - it is a directory" + AnsiReset) + os.Exit(1) } os.Exit(0) } // ClipClear is called in a separate process to clear the clipboard after 30 seconds -// TODO Make clear time period configurable func ClipClear(oldContents string) { time.Sleep(30 * time.Second) diff --git a/src/offline/copyWIN.go b/src/offline/copyWIN.go index f0404d7..7ce8fbf 100644 --- a/src/offline/copyWIN.go +++ b/src/offline/copyWIN.go @@ -10,8 +10,6 @@ import ( "time" ) -// TODO Avoid index out of range errors when copying fields that do not exist - // CopyField copies a field from an entry to the clipboard func CopyField(targetLocation string, field int, executableName string) { if isFile, _ := TargetIsFile(targetLocation, true); isFile { @@ -49,12 +47,12 @@ func CopyField(targetLocation string, field int, executableName string) { } else { fmt.Println(AnsiError + "Failed to read \"" + targetLocation + "\" - it is a directory" + AnsiReset) + os.Exit(1) } os.Exit(0) } // ClipClear is called in a separate process to clear the clipboard after 30 seconds -// TODO Make clear time period configurable func ClipClear(oldContents string) { time.Sleep(30 * time.Second) diff --git a/src/offline/gpg.go b/src/offline/gpg.go index 2a8025b..14592ba 100644 --- a/src/offline/gpg.go +++ b/src/offline/gpg.go @@ -13,7 +13,7 @@ import ( // DecryptGPG decrypts a GPG-encrypted file and returns the contents as a slice of (trimmed) strings func DecryptGPG(targetLocation string) []string { cmd := exec.Command("gpg", "--pinentry-mode", "loopback", "-q", "-d", targetLocation) - output, err := cmd.CombinedOutput() + output, err := cmd.Output() if err != nil { fmt.Println(AnsiError + "Failed to decrypt \"" + targetLocation + "\" - ensure it is a valid GPG-encrypted file and that you entered your passphrase correctly" + AnsiReset) os.Exit(1)