1/*2Maddy Mail Server - Composable all-in-one email server.3Copyright © 2019-2020 Max Mazurov <fox.cpp@disroot.org>, Maddy Mail Server contributors45This program is free software: you can redistribute it and/or modify6it under the terms of the GNU General Public License as published by7the Free Software Foundation, either version 3 of the License, or8(at your option) any later version.910This program is distributed in the hope that it will be useful,11but WITHOUT ANY WARRANTY; without even the implied warranty of12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the13GNU General Public License for more details.1415You should have received a copy of the GNU General Public License16along with this program. If not, see <https://www.gnu.org/licenses/>.17*/1819package clitools2021import (22 "bufio"23 "errors"24 "fmt"25 "os"26)2728var stdinScanner = bufio.NewScanner(os.Stdin)2930func Confirmation(prompt string, def bool) bool {31 selection := "y/N"32 if def {33 selection = "Y/n"34 }3536 fmt.Fprintf(os.Stderr, "%s [%s]: ", prompt, selection)37 if !stdinScanner.Scan() {38 fmt.Fprintln(os.Stderr, stdinScanner.Err())39 return false40 }4142 switch stdinScanner.Text() {43 case "Y", "y":44 return true45 case "N", "n":46 return false47 default:48 return def49 }50}5152func readPass(tty *os.File, output []byte) ([]byte, error) {53 cursor := output[0:1]54 readen := 055 for {56 n, err := tty.Read(cursor)57 if n != 1 {58 return nil, errors.New("ReadPassword: invalid read size when not in canonical mode")59 }60 if err != nil {61 return nil, errors.New("ReadPassword: " + err.Error())62 }63 if cursor[0] == '\n' {64 break65 }66 // Esc or Ctrl+D or Ctrl+C.67 if cursor[0] == '\x1b' || cursor[0] == '\x04' || cursor[0] == '\x03' {68 return nil, errors.New("ReadPassword: prompt rejected")69 }70 if cursor[0] == '\x7F' /* DEL */ {71 if readen != 0 {72 readen--73 cursor = output[readen : readen+1]74 }75 continue76 }7778 if readen == cap(output) {79 return nil, errors.New("ReadPassword: too long password")80 }8182 readen++83 cursor = output[readen : readen+1]84 }8586 return output[0:readen], nil87}8889func ReadPassword(prompt string) (string, error) {90 termios, err := TurnOnRawIO(os.Stdin)91 hiddenPass := true92 if err != nil {93 hiddenPass = false94 fmt.Fprintln(os.Stderr, "Failed to disable terminal output:", err)95 }9697 // There is no meaningful way to handle error here.98 //nolint:errcheck99 defer TcSetAttr(os.Stdin.Fd(), &termios)100101 fmt.Fprintf(os.Stderr, "%s: ", prompt)102103 if hiddenPass {104 buf := make([]byte, 512)105 buf, err = readPass(os.Stdin, buf)106 if err != nil {107 return "", err108 }109 fmt.Println()110111 return string(buf), nil112 }113 if !stdinScanner.Scan() {114 return "", stdinScanner.Err()115 }116117 return stdinScanner.Text(), nil118}