add input validation to cli
This commit is contained in:
parent
f2c45d379d
commit
405bb64850
1
go.mod
1
go.mod
|
@ -39,6 +39,7 @@ require (
|
|||
golang.org/x/text v0.9.0
|
||||
gopkg.in/square/go-jose.v2 v2.6.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
golang.org/x/term v0.7.0
|
||||
)
|
||||
|
||||
require (
|
||||
|
|
2
go.sum
2
go.sum
|
@ -184,6 +184,8 @@ golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.7.0 h1:BEvjmm5fURWqcfbSKTdpkDXYBrUS1c0m8agp14W48vQ=
|
||||
golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
package seconduser
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/df-mc/dragonfly/server/block/cube"
|
||||
"github.com/df-mc/dragonfly/server/world"
|
||||
"github.com/google/uuid"
|
||||
|
@ -14,7 +12,6 @@ type provider struct {
|
|||
|
||||
func (p *provider) Settings() *world.Settings {
|
||||
return &world.Settings{
|
||||
Mutex: sync.Mutex{},
|
||||
Name: "world",
|
||||
Spawn: cube.Pos{0, 0, 0},
|
||||
DefaultGameMode: world.GameModeCreative,
|
||||
|
|
14
ui/gui.go
14
ui/gui.go
|
@ -1,4 +1,4 @@
|
|||
//go:build gui || android
|
||||
//go:build gui
|
||||
|
||||
package ui
|
||||
|
||||
|
@ -32,7 +32,6 @@ type GUI struct {
|
|||
}
|
||||
|
||||
func (g *GUI) Init() bool {
|
||||
utils.SetCurrentUI(g)
|
||||
return true
|
||||
}
|
||||
|
||||
|
@ -54,7 +53,7 @@ func (g *GUI) Start(ctx context.Context, cancel context.CancelFunc) (err error)
|
|||
g.cancel = cancel
|
||||
|
||||
w := app.NewWindow(
|
||||
app.Title("Bedrocktool"),
|
||||
app.Title("Bedrocktool " + utils.Version),
|
||||
)
|
||||
|
||||
th := material.NewTheme(gofont.Collection())
|
||||
|
@ -71,7 +70,6 @@ func (g *GUI) Start(ctx context.Context, cancel context.CancelFunc) (err error)
|
|||
}
|
||||
|
||||
g.router = pages.NewRouter(ctx, w.Invalidate, th)
|
||||
|
||||
g.router.Register("Settings", settings.New(&g.router))
|
||||
g.router.Register("worlds", worlds.New(&g.router))
|
||||
g.router.Register("skins", skins.New(&g.router))
|
||||
|
@ -80,17 +78,11 @@ func (g *GUI) Start(ctx context.Context, cancel context.CancelFunc) (err error)
|
|||
|
||||
g.router.SwitchTo("Settings")
|
||||
|
||||
go func() {
|
||||
err = g.run(w)
|
||||
}()
|
||||
|
||||
go func() {
|
||||
app.Main()
|
||||
}()
|
||||
|
||||
<-ctx.Done()
|
||||
|
||||
return err
|
||||
return g.run(w)
|
||||
}
|
||||
|
||||
func (g *GUI) run(w *app.Window) error {
|
||||
|
|
|
@ -196,14 +196,12 @@ func (p *Page) layoutFinished(gtx C, th *material.Theme) D {
|
|||
}
|
||||
|
||||
func (p *Page) Layout(gtx C, th *material.Theme) D {
|
||||
margin := layout.Inset{
|
||||
return layout.Inset{
|
||||
Top: unit.Dp(25),
|
||||
Bottom: unit.Dp(25),
|
||||
Right: unit.Dp(35),
|
||||
Left: unit.Dp(35),
|
||||
}
|
||||
|
||||
return margin.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
|
||||
}.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
|
||||
switch p.State {
|
||||
case messages.UIStateConnecting:
|
||||
return layout.Center.Layout(gtx, material.Label(th, 100, "Connecting").Layout)
|
||||
|
|
|
@ -55,27 +55,24 @@ func (p *Page) NavItem() component.NavItem {
|
|||
}
|
||||
|
||||
func (p *Page) Layout(gtx C, th *material.Theme) D {
|
||||
margin := layout.Inset{
|
||||
return layout.Inset{
|
||||
Top: unit.Dp(25),
|
||||
Bottom: unit.Dp(25),
|
||||
Right: unit.Dp(35),
|
||||
Left: unit.Dp(35),
|
||||
}
|
||||
|
||||
switch p.State {
|
||||
case messages.UIStateConnect:
|
||||
// display login page
|
||||
return margin.Layout(gtx, material.Label(th, 100, "connect Client").Layout)
|
||||
case messages.UIStateConnecting:
|
||||
// display connecting to server
|
||||
return margin.Layout(gtx, material.Label(th, 100, "Connecting").Layout)
|
||||
case messages.UIStateMain:
|
||||
// show the main ui
|
||||
return margin.Layout(gtx, func(gtx C) D {
|
||||
}.Layout(gtx, func(gtx C) D {
|
||||
switch p.State {
|
||||
case messages.UIStateConnect:
|
||||
// display login page
|
||||
return material.Label(th, 100, "connect Client").Layout(gtx)
|
||||
case messages.UIStateConnecting:
|
||||
// display connecting to server
|
||||
return material.Label(th, 100, "Connecting").Layout(gtx)
|
||||
case messages.UIStateMain:
|
||||
// show the main ui
|
||||
return layout.Flex{
|
||||
Axis: layout.Vertical,
|
||||
}.Layout(gtx,
|
||||
layout.Rigid(material.Label(th, 20, "Skin Basic UI").Layout),
|
||||
layout.Flexed(1, func(gtx C) D {
|
||||
p.l.Lock()
|
||||
defer p.l.Unlock()
|
||||
|
@ -89,10 +86,9 @@ func (p *Page) Layout(gtx C, th *material.Theme) D {
|
|||
})
|
||||
}),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
return layout.Flex{}.Layout(gtx)
|
||||
}
|
||||
return D{}
|
||||
})
|
||||
}
|
||||
|
||||
func (p *Page) Handler(data interface{}) messages.MessageResponse {
|
||||
|
|
103
utils/input.go
103
utils/input.go
|
@ -1,26 +1,84 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/bedrock-tool/bedrocktool/locale"
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/term"
|
||||
)
|
||||
|
||||
func UserInput(ctx context.Context, q string) (string, bool) {
|
||||
func UserInput(ctx context.Context, q string, validator func(string) bool) (string, bool) {
|
||||
c := make(chan string)
|
||||
oldState, _ := term.MakeRaw(int(os.Stdin.Fd()))
|
||||
defer term.Restore(int(os.Stdin.Fd()), oldState)
|
||||
go func() {
|
||||
fmt.Print(q)
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
answer, _ := reader.ReadString('\n')
|
||||
r, _ := regexp.Compile(`[\n\r]`)
|
||||
answer = string(r.ReplaceAll([]byte(answer), []byte("")))
|
||||
|
||||
var answerb []byte
|
||||
var b [1]byte
|
||||
|
||||
var valid bool
|
||||
var validatorRunning atomic.Bool
|
||||
var validatorQueued atomic.Bool
|
||||
|
||||
for {
|
||||
os.Stdin.Read(b[:])
|
||||
|
||||
done := false
|
||||
switch b[0] {
|
||||
case 0x3:
|
||||
c <- ""
|
||||
return
|
||||
case 0xA:
|
||||
fallthrough
|
||||
case 0xD:
|
||||
done = true
|
||||
case 0x8:
|
||||
fallthrough
|
||||
case 0x7F:
|
||||
if len(answerb) > 0 {
|
||||
answerb = answerb[:len(answerb)-1]
|
||||
}
|
||||
default:
|
||||
if b[0] >= 0x20 {
|
||||
answerb = append(answerb, b[0])
|
||||
}
|
||||
}
|
||||
|
||||
if done {
|
||||
break
|
||||
}
|
||||
|
||||
fmt.Printf("\r%s%s\033[K", q, string(answerb))
|
||||
|
||||
if validator != nil {
|
||||
validatorQueued.Store(true)
|
||||
if validatorRunning.CompareAndSwap(false, true) {
|
||||
go func() {
|
||||
for validatorQueued.Load() {
|
||||
validatorQueued.Store(false)
|
||||
valid = validator(string(answerb))
|
||||
validatorRunning.Store(false)
|
||||
var st = "❌"
|
||||
if valid {
|
||||
st = "✅"
|
||||
}
|
||||
fmt.Printf("\r%s%s %s\033[K\033[%dD", q, string(answerb), st, 4)
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
print("\n\r")
|
||||
answer := string(answerb)
|
||||
c <- answer
|
||||
}()
|
||||
|
||||
|
@ -28,6 +86,9 @@ func UserInput(ctx context.Context, q string) (string, bool) {
|
|||
case <-ctx.Done():
|
||||
return "", true
|
||||
case a := <-c:
|
||||
if a == "" {
|
||||
return a, true
|
||||
}
|
||||
return a, false
|
||||
}
|
||||
}
|
||||
|
@ -60,7 +121,7 @@ func ServerInput(ctx context.Context, server string) (string, string, error) {
|
|||
// no arg provided, interactive input
|
||||
if server == "" {
|
||||
var cancelled bool
|
||||
server, cancelled = UserInput(ctx, locale.Loc("enter_server", nil))
|
||||
server, cancelled = UserInput(ctx, locale.Loc("enter_server", nil), ValidateServerInput)
|
||||
if cancelled {
|
||||
return "", "", context.Canceled
|
||||
}
|
||||
|
@ -94,3 +155,31 @@ func ServerInput(ctx context.Context, server string) (string, string, error) {
|
|||
}
|
||||
return server, serverGetHostname(server), nil
|
||||
}
|
||||
|
||||
func ValidateServerInput(server string) bool {
|
||||
if pcapRegex.MatchString(server) {
|
||||
return true
|
||||
}
|
||||
|
||||
if realmRegex.MatchString(server) {
|
||||
return true // todo
|
||||
}
|
||||
|
||||
host, _, err := net.SplitHostPort(server)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "missing port in address") {
|
||||
host = server
|
||||
}
|
||||
}
|
||||
|
||||
ip := net.ParseIP(host)
|
||||
if ip != nil {
|
||||
return true
|
||||
}
|
||||
|
||||
ips, err := net.LookupIP(host)
|
||||
if len(ips) > 0 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -63,7 +63,14 @@ func (c *InteractiveCLI) Start(ctx context.Context, cancel context.CancelFunc) e
|
|||
}
|
||||
fmt.Println(locale.Loc("use_to_run_command", nil))
|
||||
|
||||
cmd, cancelled := UserInput(ctx, locale.Loc("input_command", nil))
|
||||
cmd, cancelled := UserInput(ctx, locale.Loc("input_command", nil), func(s string) bool {
|
||||
for k := range ValidCMDs {
|
||||
if s == k {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
})
|
||||
if cancelled {
|
||||
cancel()
|
||||
return nil
|
||||
|
|
Loading…
Reference in New Issue