lockbox

password manager
Log | Files | Refs | README | LICENSE

commit c4975578c27f6d00e4147edf81e0cceafeab8c91
parent 38c784898cf1af0597482f9e1ff4aa3f288f7746
Author: Sean Enck <sean@ttypty.com>
Date:   Tue, 25 Jul 2023 19:54:45 -0400

move stdin to platform

Diffstat:
Mcmd/main.go | 3+--
Minternal/app/core.go | 8++++----
Ainternal/platform/stdin.go | 166+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dinternal/system/stdin.go | 166-------------------------------------------------------------------------------
4 files changed, 171 insertions(+), 172 deletions(-)

diff --git a/cmd/main.go b/cmd/main.go @@ -12,7 +12,6 @@ import ( "github.com/enckse/lockbox/internal/app" "github.com/enckse/lockbox/internal/inputs" "github.com/enckse/lockbox/internal/platform" - "github.com/enckse/lockbox/internal/system" "github.com/enckse/lockbox/internal/totp" ) @@ -102,7 +101,7 @@ func run() error { func clearClipboard() error { idx := 0 - val, err := system.Stdin(false) + val, err := platform.Stdin(false) if err != nil { return err } diff --git a/internal/app/core.go b/internal/app/core.go @@ -14,7 +14,7 @@ import ( "github.com/enckse/lockbox/internal/backend" "github.com/enckse/lockbox/internal/inputs" - "github.com/enckse/lockbox/internal/system" + "github.com/enckse/lockbox/internal/platform" ) const ( @@ -143,7 +143,7 @@ func (a *DefaultCommand) Transaction() *backend.Transaction { // Confirm will confirm with the user (dying if something abnormal happens) func (a *DefaultCommand) Confirm(prompt string) bool { - yesNo, err := system.ConfirmYesNoPrompt(prompt) + yesNo, err := platform.ConfirmYesNoPrompt(prompt) if err != nil { Die(fmt.Sprintf("failed to read stdin for confirmation: %v", err)) } @@ -163,12 +163,12 @@ func (a *DefaultCommand) SetArgs(args ...string) { // IsPipe will indicate if we're receiving pipe input func (a *DefaultCommand) IsPipe() bool { - return system.IsInputFromPipe() + return platform.IsInputFromPipe() } // Input will read user input func (a *DefaultCommand) Input(pipe, multi bool) ([]byte, error) { - return system.GetUserInputPassword(pipe, multi) + return platform.GetUserInputPassword(pipe, multi) } func subCommand(parent, name, args, desc string) string { diff --git a/internal/platform/stdin.go b/internal/platform/stdin.go @@ -0,0 +1,166 @@ +// Package platform handles stdin processing +package platform + +import ( + "bufio" + "bytes" + "errors" + "fmt" + "os" + "strings" + "syscall" +) + +type ( + stdinReaderFunc func(string) (bool, error) +) + +func termEcho(on bool) { + // Common settings and variables for both stty calls. + attrs := syscall.ProcAttr{ + Dir: "", + Env: []string{}, + Files: []uintptr{os.Stdin.Fd(), os.Stdout.Fd(), os.Stderr.Fd()}, + Sys: nil, + } + var ws syscall.WaitStatus + cmd := "echo" + if !on { + cmd = "-echo" + } + + // Enable/disable echoing. + pid, err := syscall.ForkExec( + "/bin/stty", + []string{"stty", cmd}, + &attrs) + if err != nil { + panic(err) + } + + // Wait for the stty process to complete. + _, err = syscall.Wait4(pid, &ws, 0, nil) + if err != nil { + panic(err) + } +} + +// GetUserInputPassword will read the user's input from stdin via multiple means. +func GetUserInputPassword(piping, multiLine bool) ([]byte, error) { + var password string + if !multiLine && !piping { + input, err := confirmInputsMatch() + if err != nil { + return nil, err + } + password = input + } else { + input, err := Stdin(false) + if err != nil { + return nil, err + } + password = input + } + if password == "" { + return nil, errors.New("password can NOT be empty") + } + return []byte(password), nil +} + +func confirmInputsMatch() (string, error) { + termEcho(false) + defer func() { + termEcho(true) + }() + fmt.Print("please enter password: ") + first, err := Stdin(true) + if err != nil { + return "", err + } + fmt.Print("\nplease re-enter password: ") + second, err := Stdin(true) + if err != nil { + return "", err + } + if first != second { + return "", errors.New("passwords do NOT match") + } + return first, nil +} + +// Stdin will get one (or more) lines of stdin as string. +func Stdin(one bool) (string, error) { + var b []byte + var err error + if one { + b, err = readLine() + } else { + b, err = readAll() + } + if err != nil { + return "", err + } + return strings.TrimSpace(string(b)), nil +} + +// IsInputFromPipe will indicate if connected to stdin pipe. +func IsInputFromPipe() bool { + fileInfo, _ := os.Stdin.Stat() + return fileInfo.Mode()&os.ModeCharDevice == 0 +} + +// ConfirmYesNoPrompt will ask a yes/no question. +func ConfirmYesNoPrompt(prompt string) (bool, error) { + fmt.Printf("%s? (y/N) ", prompt) + resp, err := Stdin(true) + if err != nil { + return false, err + } + return resp == "Y" || resp == "y", nil +} + +func readAll() ([]byte, error) { + return read(false) +} + +func readLine() ([]byte, error) { + return read(true) +} + +// ReadFunc will read stdin and execute the given function +func ReadFunc(reader stdinReaderFunc) error { + if reader == nil { + return errors.New("invalid reader, nil") + } + scanner := bufio.NewScanner(os.Stdin) + for scanner.Scan() { + ok, err := reader(scanner.Text()) + if err != nil { + return err + } + if !ok { + break + } + } + return scanner.Err() +} + +func read(one bool) ([]byte, error) { + var b bytes.Buffer + err := ReadFunc(func(line string) (bool, error) { + if _, err := b.WriteString(line); err != nil { + return false, err + } + if _, err := b.WriteString("\n"); err != nil { + return false, err + } + if one { + return false, nil + } + return true, nil + }) + if err != nil { + return nil, err + } + return b.Bytes(), nil +} diff --git a/internal/system/stdin.go b/internal/system/stdin.go @@ -1,166 +0,0 @@ -// Package system handles stdin processing -package system - -import ( - "bufio" - "bytes" - "errors" - "fmt" - "os" - "strings" - "syscall" -) - -type ( - stdinReaderFunc func(string) (bool, error) -) - -func termEcho(on bool) { - // Common settings and variables for both stty calls. - attrs := syscall.ProcAttr{ - Dir: "", - Env: []string{}, - Files: []uintptr{os.Stdin.Fd(), os.Stdout.Fd(), os.Stderr.Fd()}, - Sys: nil, - } - var ws syscall.WaitStatus - cmd := "echo" - if !on { - cmd = "-echo" - } - - // Enable/disable echoing. - pid, err := syscall.ForkExec( - "/bin/stty", - []string{"stty", cmd}, - &attrs) - if err != nil { - panic(err) - } - - // Wait for the stty process to complete. - _, err = syscall.Wait4(pid, &ws, 0, nil) - if err != nil { - panic(err) - } -} - -// GetUserInputPassword will read the user's input from stdin via multiple means. -func GetUserInputPassword(piping, multiLine bool) ([]byte, error) { - var password string - if !multiLine && !piping { - input, err := confirmInputsMatch() - if err != nil { - return nil, err - } - password = input - } else { - input, err := Stdin(false) - if err != nil { - return nil, err - } - password = input - } - if password == "" { - return nil, errors.New("password can NOT be empty") - } - return []byte(password), nil -} - -func confirmInputsMatch() (string, error) { - termEcho(false) - defer func() { - termEcho(true) - }() - fmt.Print("please enter password: ") - first, err := Stdin(true) - if err != nil { - return "", err - } - fmt.Print("\nplease re-enter password: ") - second, err := Stdin(true) - if err != nil { - return "", err - } - if first != second { - return "", errors.New("passwords do NOT match") - } - return first, nil -} - -// Stdin will get one (or more) lines of stdin as string. -func Stdin(one bool) (string, error) { - var b []byte - var err error - if one { - b, err = readLine() - } else { - b, err = readAll() - } - if err != nil { - return "", err - } - return strings.TrimSpace(string(b)), nil -} - -// IsInputFromPipe will indicate if connected to stdin pipe. -func IsInputFromPipe() bool { - fileInfo, _ := os.Stdin.Stat() - return fileInfo.Mode()&os.ModeCharDevice == 0 -} - -// ConfirmYesNoPrompt will ask a yes/no question. -func ConfirmYesNoPrompt(prompt string) (bool, error) { - fmt.Printf("%s? (y/N) ", prompt) - resp, err := Stdin(true) - if err != nil { - return false, err - } - return resp == "Y" || resp == "y", nil -} - -func readAll() ([]byte, error) { - return read(false) -} - -func readLine() ([]byte, error) { - return read(true) -} - -// ReadFunc will read stdin and execute the given function -func ReadFunc(reader stdinReaderFunc) error { - if reader == nil { - return errors.New("invalid reader, nil") - } - scanner := bufio.NewScanner(os.Stdin) - for scanner.Scan() { - ok, err := reader(scanner.Text()) - if err != nil { - return err - } - if !ok { - break - } - } - return scanner.Err() -} - -func read(one bool) ([]byte, error) { - var b bytes.Buffer - err := ReadFunc(func(line string) (bool, error) { - if _, err := b.WriteString(line); err != nil { - return false, err - } - if _, err := b.WriteString("\n"); err != nil { - return false, err - } - if one { - return false, nil - } - return true, nil - }) - if err != nil { - return nil, err - } - return b.Bytes(), nil -}