Files
libmutton/core/copy.go
T

121 lines
3.4 KiB
Go

package core
import (
"bufio"
"fmt"
"os"
"strings"
"time"
steamtotp "github.com/fortis/go-steam-totp"
"github.com/pquerna/otp/totp"
)
// CopyArgument copies a field from an entry to the clipboard.
func CopyArgument(targetLocation string, field int) {
if isFile, _ := TargetIsFile(targetLocation, true, 2); isFile {
decryptedEntry := DecryptGPG(targetLocation)
var copySubject string // will store data to be copied
// ensure field exists in entry
if len(decryptedEntry) > field {
// ensure field is not empty
if decryptedEntry[field] == "" {
PrintError("Field is empty", ErrorTargetNotFound, true)
}
if field != 2 {
copySubject = decryptedEntry[field]
} else { // TOTP mode
var secret string // stores secret for TOTP generation
var forSteam bool // indicates whether to generate TOTP in Steam format
if strings.HasPrefix(decryptedEntry[2], "steam@") {
secret = decryptedEntry[2][6:]
forSteam = true
} else {
secret = decryptedEntry[2]
}
fmt.Println("Clipboard will be kept up to date with the current TOTP code until this process is closed")
for { // keep token copied to clipboard, refresh on 30-second intervals
currentTime := time.Now()
copyString(true, GenTOTP(secret, currentTime, forSteam))
// sleep until next 30-second interval
time.Sleep(time.Duration(30-(currentTime.Second()%30)) * time.Second)
}
}
} else {
PrintError("Field does not exist in entry", ErrorTargetNotFound, true)
}
// copy field to clipboard, launch clipboard clearing process
copyString(false, copySubject)
}
}
// ClipClearArgument reads the assigned clipboard contents from stdin and passes them to clipClearProcess.
func ClipClearArgument() {
// read previous clipboard contents from stdin
clipScanner := bufio.NewScanner(os.Stdin)
if clipScanner.Scan() {
assignedContents := clipScanner.Text()
clipClearProcess(assignedContents)
} else {
os.Exit(0) // use os.Exit instead of core.Exit, as this function runs out of a background subprocess that is invisible to the user (will never appear in GUI/TUI environment)
}
}
// clipClearProcess clears the clipboard after 30 seconds if the clipboard contents have not changed.
// assignedContents can be omitted to clear the clipboard immediately and unconditionally.
func clipClearProcess(assignedContents string) {
cmdPaste, cmdClear := getClipCommands()
clearClipboard := func() {
err := cmdClear.Run()
if err != nil {
PrintError("Failed to clear clipboard", ErrorClipboard, true)
}
Exit(0)
}
// if assignedContents is empty, clear the clipboard immediately and unconditionally
if assignedContents == "" {
clearClipboard()
return
}
// wait 30 seconds before checking clipboard contents
time.Sleep(30 * time.Second)
newContents, err := cmdPaste.Output()
if err != nil {
PrintError("Failed to read clipboard contents", ErrorClipboard, true)
}
if assignedContents == strings.TrimRight(string(newContents), "\r\n") {
clearClipboard()
}
}
// GenTOTP generates a TOTP token from a secret (supports standard and Steam TOTP).
func GenTOTP(secret string, time time.Time, forSteam bool) string {
var totpToken string
var err error
if forSteam {
totpToken, err = steamtotp.GenerateAuthCode(secret, time)
} else {
totpToken, err = totp.GenerateCode(secret, time)
}
if err != nil {
PrintError("Error generating TOTP code", ErrorOther, true)
}
return totpToken
}