|
@@ -1,4 +1,210 @@
|
|
package server
|
|
package server
|
|
|
|
|
|
-/* This file contains dialog helpers fort the client. */
|
|
|
|
|
|
+/* This file contains dialog helpers for the client. */
|
|
|
|
+
|
|
|
|
+// import "github.com/beoran/woe/monolog"
|
|
|
|
+import t "github.com/beoran/woe/telnet"
|
|
|
|
+import "github.com/beoran/woe/telnet"
|
|
|
|
+import "github.com/beoran/woe/world"
|
|
|
|
+import "github.com/beoran/woe/monolog"
|
|
|
|
+import "bytes"
|
|
|
|
+import "regexp"
|
|
|
|
+import "fmt"
|
|
|
|
+// import "strconv"
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+// Switches to "password" mode.
|
|
|
|
+func (me * Client) PasswordMode() telnet.Event {
|
|
|
|
+// The server sends "IAC WILL ECHO", meaning "I, the server, will do any
|
|
|
|
+// echoing from now on." The client should acknowledge this with an IAC DO
|
|
|
|
+// ECHO, and then stop putting echoed text in the input buffer.
|
|
|
|
+// It should also do whatever is appropriate for password entry to the input
|
|
|
|
+// box thing - for example, it might * it out. Text entered in server-echoes
|
|
|
|
+// mode should also not be placed any command history.
|
|
|
|
+// don't use the Q state machne for echos
|
|
|
|
+ me.telnet.TelnetSendBytes(t.TELNET_IAC, t.TELNET_WILL, t.TELNET_TELOPT_ECHO)
|
|
|
|
+ tev, _, _:= me.TryReadEvent(100)
|
|
|
|
+ if tev != nil && !telnet.IsEventType(tev, t.TELNET_DO_EVENT) {
|
|
|
|
+ return tev
|
|
|
|
+ }
|
|
|
|
+ return nil
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// Switches to "normal, or non-password mode.
|
|
|
|
+func (me * Client) NormalMode() telnet.Event {
|
|
|
|
+// When the server wants the client to start local echoing again, it s}s
|
|
|
|
+// "IAC WONT ECHO" - the client must respond to this with "IAC DONT ECHO".
|
|
|
|
+// Again don't use Q state machine.
|
|
|
|
+ me.telnet.TelnetSendBytes(t.TELNET_IAC, t.TELNET_WONT, t.TELNET_TELOPT_ECHO)
|
|
|
|
+ tev, _, _ := me.TryReadEvent(100)
|
|
|
|
+ if tev != nil && !telnet.IsEventType(tev, t.TELNET_DONT_EVENT) {
|
|
|
|
+ return tev
|
|
|
|
+ }
|
|
|
|
+ return nil
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+func (me * Client) Printf(format string, args ...interface{}) {
|
|
|
|
+ me.telnet.TelnetPrintf(format, args...)
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+func (me * Client) ColorTest() {
|
|
|
|
+ me.Printf("\033[1mBold\033[0m\r\n")
|
|
|
|
+ me.Printf("\033[3mItalic\033[0m\r\n")
|
|
|
|
+ me.Printf("\033[4mUnderline\033[0m\r\n")
|
|
|
|
+ for fg := 30; fg < 38; fg++ {
|
|
|
|
+ me.Printf("\033[%dmForeground Color %d\033[0m\r\n", fg, fg)
|
|
|
|
+ me.Printf("\033[1;%dmBold Foreground Color %d\033[0m\r\n", fg, fg)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ for bg := 40; bg < 48; bg++ {
|
|
|
|
+ me.Printf("\033[%dmBackground Color %d\033[0m\r\n", bg, bg)
|
|
|
|
+ me.Printf("\033[1;%dmBold Background Color %d\033[0m\r\n", bg, bg)
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+// Blockingly reads a single command from the client
|
|
|
|
+func (me * Client) ReadCommand() (something []byte) {
|
|
|
|
+ something = nil
|
|
|
|
+ for something == nil {
|
|
|
|
+ something, _, _ = me.TryRead(-1)
|
|
|
|
+ if something != nil {
|
|
|
|
+ something = bytes.TrimRight(something, "\r\n")
|
|
|
|
+ return something
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ return nil
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+func (me * Client) AskSomething(prompt string, re string, nomatch_prompt string, noecho bool) (something []byte) {
|
|
|
|
+ something = nil
|
|
|
|
+
|
|
|
|
+ if noecho {
|
|
|
|
+ me.PasswordMode()
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ for something == nil || len(something) == 0 {
|
|
|
|
+ me.Printf("%s:", prompt)
|
|
|
|
+ something, _, _ = me.TryRead(-1)
|
|
|
|
+ if something != nil {
|
|
|
|
+ something = bytes.TrimRight(something, "\r\n")
|
|
|
|
+ if len(re) > 0 {
|
|
|
|
+ ok, _ := regexp.Match(re, something)
|
|
|
|
+ if !ok {
|
|
|
|
+ me.Printf("\n%s\n", nomatch_prompt)
|
|
|
|
+ something = nil
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if noecho {
|
|
|
|
+ me.NormalMode()
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return something
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+const LOGIN_RE = "^[A-Za-z][A-Za-z0-9]+$"
|
|
|
|
+
|
|
|
|
+func (me * Client) AskLogin() []byte {
|
|
|
|
+ return me.AskSomething("Login", LOGIN_RE, "Login must consist of a letter followed by letters or numbers.", false)
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+const EMAIL_RE = "@"
|
|
|
|
+
|
|
|
|
+func (me * Client) AskEmail() []byte {
|
|
|
|
+ return me.AskSomething("E-mail", EMAIL_RE, "Email must have at least an @ in there somewhere.", false)
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+func (me * Client) AskPassword() []byte {
|
|
|
|
+ return me.AskSomething("Password", "", "", true)
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+func (me * Client) AskRepeatPassword() []byte {
|
|
|
|
+ return me.AskSomething("Repeat Password", "", "", true)
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+func (me * Client) HandleCommand() {
|
|
|
|
+ command := me.ReadCommand()
|
|
|
|
+ if bytes.HasPrefix(command, []byte("/quit")) {
|
|
|
|
+ me.Printf("Byebye!\n")
|
|
|
|
+ me.alive = false
|
|
|
|
+ } else {
|
|
|
|
+ bro := fmt.Sprintf("Client %d said %s\r\n", me.id, command)
|
|
|
|
+ me.server.Broadcast(bro)
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+func (me * Client) ExistingAccountDialog() bool {
|
|
|
|
+ pass := me.AskPassword()
|
|
|
|
+ for pass == nil {
|
|
|
|
+ me.Printf("Password may not be empty!\n")
|
|
|
|
+ pass = me.AskPassword()
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if !me.account.Challenge(string(pass)) {
|
|
|
|
+ me.Printf("Password not correct!\n")
|
|
|
|
+ me.Printf("Disconnecting!\n")
|
|
|
|
+ return false
|
|
|
|
+ }
|
|
|
|
+ return true
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+func (me * Client) NewAccountDialog(login string) bool {
|
|
|
|
+ for me.account == nil {
|
|
|
|
+ me.Printf("\nWelcome, %s! Creating new account...\n", login)
|
|
|
|
+ pass1 := me.AskPassword()
|
|
|
|
+
|
|
|
|
+ if pass1 == nil {
|
|
|
|
+ return false
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ pass2 := me.AskRepeatPassword()
|
|
|
|
+
|
|
|
|
+ if pass1 == nil {
|
|
|
|
+ return false
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if string(pass1) != string(pass2) {
|
|
|
|
+ me.Printf("\nPasswords do not match! Please try again!\n")
|
|
|
|
+ continue
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ email := me.AskEmail()
|
|
|
|
+ if email == nil { return false }
|
|
|
|
+
|
|
|
|
+ me.account = world.NewAccount(login, string(pass1), string(email), 7)
|
|
|
|
+ err := me.account.Save(me.server.DataPath())
|
|
|
|
+
|
|
|
|
+ if err != nil {
|
|
|
|
+ monolog.Error("Could not save account %s: %v", login, err)
|
|
|
|
+ me.Printf("\nFailed to save your account!\nPlease contact a WOE administrator!\n")
|
|
|
|
+ return false
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ monolog.Info("Created new account %s", login)
|
|
|
|
+ me.Printf("\nSaved your account.\n")
|
|
|
|
+ return true
|
|
|
|
+ }
|
|
|
|
+ return false
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+func (me * Client) AccountDialog() bool {
|
|
|
|
+ login := me.AskLogin()
|
|
|
|
+ if login == nil { return false }
|
|
|
|
+ var err error
|
|
|
|
+ me.account, err = world.LoadAccount(me.server.DataPath(), string(login))
|
|
|
|
+ if err != nil {
|
|
|
|
+ monolog.Warning("Could not load account %s: %v", login, err)
|
|
|
|
+ }
|
|
|
|
+ if me.account != nil {
|
|
|
|
+ return me.ExistingAccountDialog()
|
|
|
|
+ } else {
|
|
|
|
+ return me.NewAccountDialog(string(login))
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
|