github.com/Finschia/finschia-sdk@v0.48.1/client/input/input.go (about) 1 package input 2 3 import ( 4 "bufio" 5 "errors" 6 "fmt" 7 "io" 8 "os" 9 "strings" 10 11 "github.com/bgentry/speakeasy" 12 isatty "github.com/mattn/go-isatty" 13 ) 14 15 // MinPassLength is the minimum acceptable password length 16 const MinPassLength = 8 17 18 // GetPassword will prompt for a password one-time (to sign a tx) 19 // It enforces the password length 20 func GetPassword(prompt string, buf *bufio.Reader) (pass string, err error) { 21 if inputIsTty() { 22 pass, err = speakeasy.FAsk(os.Stderr, prompt) 23 } else { 24 pass, err = readLineFromBuf(buf) 25 } 26 27 if err != nil { 28 return "", err 29 } 30 31 if len(pass) < MinPassLength { 32 // Return the given password to the upstream client so it can handle a 33 // non-STDIN failure gracefully. 34 return pass, fmt.Errorf("password must be at least %d characters", MinPassLength) 35 } 36 37 return pass, nil 38 } 39 40 // GetConfirmation will request user give the confirmation from stdin. 41 // "y", "Y", "yes", "YES", and "Yes" all count as confirmations. 42 // If the input is not recognized, it returns false and a nil error. 43 func GetConfirmation(prompt string, r *bufio.Reader, w io.Writer) (bool, error) { 44 if inputIsTty() { 45 fmt.Fprintf(w, "%s [y/N]: ", prompt) 46 } 47 48 response, err := readLineFromBuf(r) 49 if err != nil { 50 return false, err 51 } 52 53 response = strings.TrimSpace(response) 54 if len(response) == 0 { 55 return false, nil 56 } 57 58 response = strings.ToLower(response) 59 if response[0] == 'y' { 60 return true, nil 61 } 62 63 return false, nil 64 } 65 66 // GetString simply returns the trimmed string output of a given reader. 67 func GetString(prompt string, buf *bufio.Reader) (string, error) { 68 if inputIsTty() && prompt != "" { 69 fmt.Fprintf(os.Stderr, "> %s\n", prompt) 70 } 71 72 out, err := readLineFromBuf(buf) 73 if err != nil { 74 return "", err 75 } 76 77 return strings.TrimSpace(out), nil 78 } 79 80 // inputIsTty returns true iff we have an interactive prompt, 81 // where we can disable echo and request to repeat the password. 82 // If false, we can optimize for piped input from another command 83 func inputIsTty() bool { 84 return isatty.IsTerminal(os.Stdin.Fd()) || isatty.IsCygwinTerminal(os.Stdin.Fd()) 85 } 86 87 // readLineFromBuf reads one line from reader. 88 // Subsequent calls reuse the same buffer, so we don't lose 89 // any input when reading a password twice (to verify) 90 func readLineFromBuf(buf *bufio.Reader) (string, error) { 91 pass, err := buf.ReadString('\n') 92 93 switch { 94 case errors.Is(err, io.EOF): 95 // If by any chance the error is EOF, but we were actually able to read 96 // something from the reader then don't return the EOF error. 97 // If we didn't read anything from the reader and got the EOF error, then 98 // it's safe to return EOF back to the caller. 99 if len(pass) > 0 { 100 // exit the switch statement 101 break 102 } 103 return "", err 104 105 case err != nil: 106 return "", err 107 } 108 109 return strings.TrimSpace(pass), nil 110 }