From 3d621f5e389dd004abdca2b81e8379b98d80c1e9 Mon Sep 17 00:00:00 2001 From: Randall Winkhart Date: Sat, 2 Mar 2024 00:12:55 -0500 Subject: [PATCH] Implement clipboard clearing functionality on Wayland --- main.go | 41 ++++++++++++++++++++++--------------- src/offline/copy.go | 50 ++++++++++++++++++++++++++++++++++++++------- 2 files changed, 68 insertions(+), 23 deletions(-) diff --git a/main.go b/main.go index a24bbee..0c4e9d3 100644 --- a/main.go +++ b/main.go @@ -1,6 +1,7 @@ package main import ( + "bufio" "github.com/rwinkhart/MUTN/src/cli" "github.com/rwinkhart/MUTN/src/offline" "os" @@ -9,28 +10,28 @@ import ( func main() { - args := os.Args[1:] + args := os.Args argsCount := len(args) // if no arguments are supplied... - if argsCount == 0 { + if argsCount == 1 { // list all entries cli.EntryListGen() } else { // if the first argument is an entry... - if strings.HasPrefix(args[0], "/") { + if strings.HasPrefix(args[1], "/") { // store location of target entry - targetLocation := offline.EntryRoot + offline.PathSeparator + args[0][1:] + targetLocation := offline.EntryRoot + offline.PathSeparator + args[1][1:] // entry reader shortcut (if no other arguments are supplied) - if argsCount == 1 { + if argsCount == 2 { cli.EntryReaderShortcut(targetLocation, true) // perform other operations on the entry (if other arguments are supplied) - } else if argsCount == 2 { + } else if argsCount == 3 { - switch args[1] { + switch args[2] { case "show", "-s": cli.EntryReaderShortcut(targetLocation, false) case "shear": // TODO offline.Shear(targetLocation), exit after run @@ -45,14 +46,14 @@ func main() { cli.HelpMain() } - } else if argsCount == 3 { + } else if argsCount == 4 { // declare entry field to perform target operation on - switch args[1] { + switch args[2] { case "copy": var field uint8 - switch args[2] { + switch args[3] { case "password", "-p": field = 0 case "username", "-u": @@ -64,11 +65,11 @@ func main() { default: cli.HelpCopy() } - offline.CopyField(targetLocation, field) + offline.CopyField(targetLocation, field, args[0]) case "edit": var field rune field = field // TODO temporary to avoid unused variable error - switch args[2] { + switch args[3] { case "password", "-p": field = 'p' case "username", "-u": @@ -85,7 +86,7 @@ func main() { case "add": var field rune field = field // TODO temporary to avoid unused variable error - switch args[2] { + switch args[3] { case "password", "-p": field = 'p' case "note", "-n": @@ -98,7 +99,7 @@ func main() { case "gen": var field rune field = field // TODO temporary to avoid unused variable error - switch args[2] { + switch args[3] { case "update", "-u": // TODO offline.GenUpdate(targetLocation), exit after run default: cli.HelpGen() @@ -110,11 +111,19 @@ func main() { // if the first argument is not an entry... } else { - switch args[0] { + switch args[1] { + case "clipclear": + // read previous clipboard contents from stdin + clipScanner := bufio.NewScanner(os.Stdin) + if clipScanner.Scan() { + oldContents := clipScanner.Text() + offline.ClipClear(oldContents) + } else { + os.Exit(0) + } case "sync": // TODO online.Sync(), exit after run case "init": // TODO offline.Init(), exit after run case "tweak": // TODO offline.Tweak(), exit after run - case "add": cli.HelpAdd() case "edit": diff --git a/src/offline/copy.go b/src/offline/copy.go index ad0d183..af88cec 100644 --- a/src/offline/copy.go +++ b/src/offline/copy.go @@ -5,24 +5,60 @@ import ( "io" "os" "os/exec" + "strings" + "time" ) -func CopyField(targetLocation string, field uint8) { +// TODO Handle cmd.Run errors, especially those due to missing clipboard utilities + +// CopyField copies a field from an entry to the clipboard +// TODO Implement support for Windows via powershell, MacOS via pbcopy, Termux via termux-clipboard-set, X11 via xclip +// TODO Monitor Go support for Haiku, if possible implement support via clipboard command +func CopyField(targetLocation string, field uint8, executableName string) { if isFile, _ := TargetIsFile(targetLocation, true); isFile { copySubject := DecryptGPG(targetLocation)[field] + + //hash := getSha512Sum(copySubject) + cmd := exec.Command("wl-copy") - stdin, _ := cmd.StdinPipe() - go func() { - defer stdin.Close() - io.WriteString(stdin, copySubject) - }() + writeToStdin(cmd, copySubject) cmd.Run() + + // TODO Unpair from mutn, as this breaks library functionality + // TODO Maybe use this method for mutn and strongly encourage other implementations to support a clipclear argument + // TODO If an implementation opts out of doing this, this may error out + //cmd = exec.Command(executableName, "clipclear", hash) + cmd = exec.Command(executableName, "clipclear") + writeToStdin(cmd, copySubject) + cmd.Start() + } else { fmt.Println(AnsiError + "Failed to read \"" + targetLocation + "\" - it is a directory" + AnsiReset) } os.Exit(0) } -func clipClear() { +// ClipClear is called in a separate process to clear the clipboard after 30 seconds +// TODO Implement support for Windows via powershell, MacOS via pbcopy, Termux via termux-clipboard-set, X11 via xclip +// TODO Make clear time period configurable +func ClipClear(oldContents string) { + time.Sleep(30 * time.Second) + cmd := exec.Command("wl-paste") + newContents, _ := cmd.Output() + + if oldContents == strings.TrimRight(string(newContents), "\n") { + cmd := exec.Command("wl-copy", "-c") + cmd.Run() + } + os.Exit(0) +} + +// writeToStdin writes a string to a command's stdin +func writeToStdin(cmd *exec.Cmd, input string) { + stdin, _ := cmd.StdinPipe() + go func() { + defer stdin.Close() + io.WriteString(stdin, input) + }() }