// Read or write text data to or from the clipboard. // // Check out a usage example at https://github.com/sven-seyfert/gomisc/blob/main/examples/clipboard/main.go package clipboard // Read returns the current text data of the clipboard. In case a file was // copied to the clipboard, the file path will be returned. // If there is an error, the error will be returned. func Read() (string, error) { return read() } // Write sets the given text data to the clipboard. If there is an error, // the error will be returned. func Write(text string) error { return write(text) }
package clipboard import ( "fmt" "os/exec" "strings" ) func read() (string, error) { // Command looks for a file name, in case a file was copied to the clipboard. command := "Get-Clipboard -Format FileDropList -RAW" cmd := exec.Command("powershell", command) content, err := getClipboardText(cmd) if err != nil { return "", err } if content != "" { // Return found file path. return removeTrailingNewline(content), nil } // Command simply gets the clipboard text. command = "Get-Clipboard -Format text" cmd = exec.Command("powershell", command) content, err = getClipboardText(cmd) if err != nil { return "", err } // Return found text. return removeTrailingNewline(content), nil } func getClipboardText(cmd *exec.Cmd) (string, error) { stdin, err := cmd.StdinPipe() if err != nil { return "", err } defer stdin.Close() output, err := cmd.CombinedOutput() if err != nil { return "", err } return string(output), nil } func removeTrailingNewline(text string) string { return strings.TrimSuffix(text, "\r\n") } func write(text string) error { command := fmt.Sprintf("$rawString = @'\n%s\n'@; Set-Clipboard -Value $rawString", text) cmd := exec.Command("powershell", command) _, err := cmd.CombinedOutput() if err != nil { return err } return nil }
// Encrypt and decrypt data (strings) by the usage of a secret and GCM. // // Check out a usage example at https://github.com/sven-seyfert/gomisc/blob/main/examples/crypt/main.go package crypt import ( "crypto/aes" "crypto/cipher" "crypto/rand" "encoding/hex" "fmt" "io" ) // GenerateSecretKey generates a encoded string based on a 32 byte key for // AES-256. This secret key will be used in Encrypt and Decypt functions. // If there is an error, the error will be returned. func GenerateSecretKey() (string, error) { bytes, err := generate32ByteKeyForAes256() if err != nil { return "", err } return encodeByteToString(bytes), nil } func generate32ByteKeyForAes256() ([]byte, error) { length := 32 bytes := make([]byte, length) // Check the correct number of bytes _, err := rand.Read(bytes) if err != nil { return nil, err } return bytes, nil } func encodeByteToString(bytes []byte) string { return hex.EncodeToString(bytes) } // Encrypt encrypts a string by the usage of a secret and the GCM // cryptography mode. If there is an error, the error will be returned. func Encrypt(stringToEncrypt, secret string) (string, error) { key, err := decodeStringToBytes(secret) if err != nil { return "", err } plainText := []byte(stringToEncrypt) block, err := createCipherBlock(key) if err != nil { return "", err } // Create new GCM (https://en.wikipedia.org/wiki/Galois/Counter_Mode) aesGCM, err := cipher.NewGCM(block) if err != nil { return "", err } // Create a nonce from GCM nonce := make([]byte, aesGCM.NonceSize()) // Check the correct number of bytes _, err = io.ReadFull(rand.Reader, nonce) if err != nil { return "", err } // Encrypt data cipherText := aesGCM.Seal(nonce, nonce, plainText, nil) return fmt.Sprintf("%x", cipherText), nil } func decodeStringToBytes(data string) ([]byte, error) { key, err := hex.DecodeString(data) if err != nil { return nil, err } return key, nil } func createCipherBlock(key []byte) (cipher.Block, error) { block, err := aes.NewCipher(key) if err != nil { return nil, err } return block, nil } // Decrypt decrypts a encrypted string by the usage of a secret and the // GCM cryptography mode. If there is an error, the error will be returned. func Decrypt(encryptedString, secret string) (string, error) { key, err := decodeStringToBytes(secret) if err != nil { return "", err } enc, err := decodeStringToBytes(encryptedString) if err != nil { return "", err } block, err := createCipherBlock(key) if err != nil { return "", err } // Create new GCM (https://en.wikipedia.org/wiki/Galois/Counter_Mode) aesGCM, err := cipher.NewGCM(block) if err != nil { return "", err } // Get the nonce size nonceSize := aesGCM.NonceSize() // Extract the nonce from the encrypted data nonce, cipherText := enc[:nonceSize], enc[nonceSize:] // Decrypt data plainText, err := aesGCM.Open(nil, nonce, cipherText, nil) if err != nil { return "", err } return string(plainText), nil }
// Ensures only a single instance of the program runs at the same time. // // Check out a usage example at https://github.com/sven-seyfert/gomisc/blob/main/examples/singleinstance/main.go package singleinstance import ( "errors" "fmt" "os" "time" ) const instanceFile = "instance_indicator.lock" // CreateInstanceFile creates a instance indicator file for this instance of // the program. To ensure this current instance of the program is the only one, // the function checks for a existing instance indicator file. If there is an // error, the error will be returned. func CreateInstanceFile() error { if existsInstanceFile() { message := `instance indicator file "%s" could not be created because it already exist` return fmt.Errorf(message, instanceFile) //nolint:goerr113 } file, err := os.Create(instanceFile) if err != nil { return err } defer file.Close() // This 100 milliseconds delay ensures a robust file handling time.Sleep(time.Millisecond * 100) //nolint:gomnd return nil } func existsInstanceFile() bool { _, err := os.Stat(instanceFile) return !errors.Is(err, os.ErrNotExist) } // RemoveInstanceFile removes the previously created instance indicator file. // Always set this to ensure the next single instance of the program can be run. // If there is an error, the error will be returned. func RemoveInstanceFile() error { err := os.Remove(instanceFile) if err != nil { return err } return nil }