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
|
golang.org/x/text v0.9.0
|
||||||
gopkg.in/square/go-jose.v2 v2.6.0
|
gopkg.in/square/go-jose.v2 v2.6.0
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
|
golang.org/x/term v0.7.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
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-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.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.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.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.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
package seconduser
|
package seconduser
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/df-mc/dragonfly/server/block/cube"
|
"github.com/df-mc/dragonfly/server/block/cube"
|
||||||
"github.com/df-mc/dragonfly/server/world"
|
"github.com/df-mc/dragonfly/server/world"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
|
@ -14,7 +12,6 @@ type provider struct {
|
||||||
|
|
||||||
func (p *provider) Settings() *world.Settings {
|
func (p *provider) Settings() *world.Settings {
|
||||||
return &world.Settings{
|
return &world.Settings{
|
||||||
Mutex: sync.Mutex{},
|
|
||||||
Name: "world",
|
Name: "world",
|
||||||
Spawn: cube.Pos{0, 0, 0},
|
Spawn: cube.Pos{0, 0, 0},
|
||||||
DefaultGameMode: world.GameModeCreative,
|
DefaultGameMode: world.GameModeCreative,
|
||||||
|
|
14
ui/gui.go
14
ui/gui.go
|
@ -1,4 +1,4 @@
|
||||||
//go:build gui || android
|
//go:build gui
|
||||||
|
|
||||||
package ui
|
package ui
|
||||||
|
|
||||||
|
@ -32,7 +32,6 @@ type GUI struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *GUI) Init() bool {
|
func (g *GUI) Init() bool {
|
||||||
utils.SetCurrentUI(g)
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,7 +53,7 @@ func (g *GUI) Start(ctx context.Context, cancel context.CancelFunc) (err error)
|
||||||
g.cancel = cancel
|
g.cancel = cancel
|
||||||
|
|
||||||
w := app.NewWindow(
|
w := app.NewWindow(
|
||||||
app.Title("Bedrocktool"),
|
app.Title("Bedrocktool " + utils.Version),
|
||||||
)
|
)
|
||||||
|
|
||||||
th := material.NewTheme(gofont.Collection())
|
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 = pages.NewRouter(ctx, w.Invalidate, th)
|
||||||
|
|
||||||
g.router.Register("Settings", settings.New(&g.router))
|
g.router.Register("Settings", settings.New(&g.router))
|
||||||
g.router.Register("worlds", worlds.New(&g.router))
|
g.router.Register("worlds", worlds.New(&g.router))
|
||||||
g.router.Register("skins", skins.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")
|
g.router.SwitchTo("Settings")
|
||||||
|
|
||||||
go func() {
|
|
||||||
err = g.run(w)
|
|
||||||
}()
|
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
app.Main()
|
app.Main()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
<-ctx.Done()
|
return g.run(w)
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *GUI) run(w *app.Window) error {
|
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 {
|
func (p *Page) Layout(gtx C, th *material.Theme) D {
|
||||||
margin := layout.Inset{
|
return layout.Inset{
|
||||||
Top: unit.Dp(25),
|
Top: unit.Dp(25),
|
||||||
Bottom: unit.Dp(25),
|
Bottom: unit.Dp(25),
|
||||||
Right: unit.Dp(35),
|
Right: unit.Dp(35),
|
||||||
Left: unit.Dp(35),
|
Left: unit.Dp(35),
|
||||||
}
|
}.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
|
||||||
|
|
||||||
return margin.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
|
|
||||||
switch p.State {
|
switch p.State {
|
||||||
case messages.UIStateConnecting:
|
case messages.UIStateConnecting:
|
||||||
return layout.Center.Layout(gtx, material.Label(th, 100, "Connecting").Layout)
|
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 {
|
func (p *Page) Layout(gtx C, th *material.Theme) D {
|
||||||
margin := layout.Inset{
|
return layout.Inset{
|
||||||
Top: unit.Dp(25),
|
Top: unit.Dp(25),
|
||||||
Bottom: unit.Dp(25),
|
Bottom: unit.Dp(25),
|
||||||
Right: unit.Dp(35),
|
Right: unit.Dp(35),
|
||||||
Left: unit.Dp(35),
|
Left: unit.Dp(35),
|
||||||
}
|
}.Layout(gtx, func(gtx C) D {
|
||||||
|
switch p.State {
|
||||||
switch p.State {
|
case messages.UIStateConnect:
|
||||||
case messages.UIStateConnect:
|
// display login page
|
||||||
// display login page
|
return material.Label(th, 100, "connect Client").Layout(gtx)
|
||||||
return margin.Layout(gtx, material.Label(th, 100, "connect Client").Layout)
|
case messages.UIStateConnecting:
|
||||||
case messages.UIStateConnecting:
|
// display connecting to server
|
||||||
// display connecting to server
|
return material.Label(th, 100, "Connecting").Layout(gtx)
|
||||||
return margin.Layout(gtx, material.Label(th, 100, "Connecting").Layout)
|
case messages.UIStateMain:
|
||||||
case messages.UIStateMain:
|
// show the main ui
|
||||||
// show the main ui
|
|
||||||
return margin.Layout(gtx, func(gtx C) D {
|
|
||||||
return layout.Flex{
|
return layout.Flex{
|
||||||
Axis: layout.Vertical,
|
Axis: layout.Vertical,
|
||||||
}.Layout(gtx,
|
}.Layout(gtx,
|
||||||
layout.Rigid(material.Label(th, 20, "Skin Basic UI").Layout),
|
|
||||||
layout.Flexed(1, func(gtx C) D {
|
layout.Flexed(1, func(gtx C) D {
|
||||||
p.l.Lock()
|
p.l.Lock()
|
||||||
defer p.l.Unlock()
|
defer p.l.Unlock()
|
||||||
|
@ -89,10 +86,9 @@ func (p *Page) Layout(gtx C, th *material.Theme) D {
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
})
|
}
|
||||||
}
|
return D{}
|
||||||
|
})
|
||||||
return layout.Flex{}.Layout(gtx)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Page) Handler(data interface{}) messages.MessageResponse {
|
func (p *Page) Handler(data interface{}) messages.MessageResponse {
|
||||||
|
|
103
utils/input.go
103
utils/input.go
|
@ -1,26 +1,84 @@
|
||||||
package utils
|
package utils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync/atomic"
|
||||||
|
|
||||||
"github.com/bedrock-tool/bedrocktool/locale"
|
"github.com/bedrock-tool/bedrocktool/locale"
|
||||||
"github.com/sirupsen/logrus"
|
"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)
|
c := make(chan string)
|
||||||
|
oldState, _ := term.MakeRaw(int(os.Stdin.Fd()))
|
||||||
|
defer term.Restore(int(os.Stdin.Fd()), oldState)
|
||||||
go func() {
|
go func() {
|
||||||
fmt.Print(q)
|
fmt.Print(q)
|
||||||
reader := bufio.NewReader(os.Stdin)
|
|
||||||
answer, _ := reader.ReadString('\n')
|
var answerb []byte
|
||||||
r, _ := regexp.Compile(`[\n\r]`)
|
var b [1]byte
|
||||||
answer = string(r.ReplaceAll([]byte(answer), []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
|
c <- answer
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
@ -28,6 +86,9 @@ func UserInput(ctx context.Context, q string) (string, bool) {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
return "", true
|
return "", true
|
||||||
case a := <-c:
|
case a := <-c:
|
||||||
|
if a == "" {
|
||||||
|
return a, true
|
||||||
|
}
|
||||||
return a, false
|
return a, false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -60,7 +121,7 @@ func ServerInput(ctx context.Context, server string) (string, string, error) {
|
||||||
// no arg provided, interactive input
|
// no arg provided, interactive input
|
||||||
if server == "" {
|
if server == "" {
|
||||||
var cancelled bool
|
var cancelled bool
|
||||||
server, cancelled = UserInput(ctx, locale.Loc("enter_server", nil))
|
server, cancelled = UserInput(ctx, locale.Loc("enter_server", nil), ValidateServerInput)
|
||||||
if cancelled {
|
if cancelled {
|
||||||
return "", "", context.Canceled
|
return "", "", context.Canceled
|
||||||
}
|
}
|
||||||
|
@ -94,3 +155,31 @@ func ServerInput(ctx context.Context, server string) (string, string, error) {
|
||||||
}
|
}
|
||||||
return server, serverGetHostname(server), nil
|
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))
|
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 {
|
if cancelled {
|
||||||
cancel()
|
cancel()
|
||||||
return nil
|
return nil
|
||||||
|
|
Loading…
Reference in New Issue