123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289 |
- package server
- import "github.com/beoran/woe/monolog"
- import t "github.com/beoran/woe/telnet"
- import "github.com/beoran/woe/telnet"
- import "strings"
- import "strconv"
- /* This file contains telnet setup helpers for the client. */
- // generic negotiation
- func (me *Client) SetupNegotiate(millis int, command byte, option byte, yes_event telnet.EventType, no_event telnet.EventType) (bool, telnet.Event) {
- me.telnet.TelnetSendNegotiate(command, option)
- tev, timeout, close := me.TryReadEvent(millis)
- if tev == nil || timeout || close {
- monolog.Info("Timeout or close in TryReadEvent")
- return false, nil
- }
- evtype := telnet.EventTypeOf(tev)
- if evtype == no_event {
- monolog.Info("Negative event no_event %v %v %v", tev, evtype, no_event)
- return false, tev
- }
- if evtype != yes_event {
- monolog.Info("Unexpected event yes_event %v %v %v", tev, evtype, yes_event)
- return false, tev
- }
- if tev == nil {
- return false, tev
- }
- return true, tev
- }
- // Negotiate BINARY support, for sending UTF-8. This is largely a courtesy to the cliennt,
- // since woe will send UTF-8 anyway.
- func (me *Client) SetupBinary() telnet.Event {
- ok, tev := me.SetupNegotiate(1000, t.TELNET_WILL, t.TELNET_TELOPT_BINARY, t.TELNET_DO_EVENT, t.TELNET_DONT_EVENT)
- if !ok {
- me.info.binary = false
- return tev
- }
- monolog.Info("Client #{@id} accepts BINARY")
- me.info.binary = true
- return tev
- }
- // negotiate suppress go ahead
- func (me *Client) SetupSupressGA() telnet.Event {
- ok, tev := me.SetupNegotiate(1000, t.TELNET_WILL, t.TELNET_TELOPT_SGA, t.TELNET_DO_EVENT, t.TELNET_DONT_EVENT)
- if !ok {
- me.info.binary = false
- return tev
- }
- monolog.Info("Client #{@id} will suppress GA")
- me.info.sga = true
- return tev
- }
- // Negotiate COMPRESS2 support
- func (me *Client) SetupCompress2() telnet.Event {
- ok, tev := me.SetupNegotiate(1000, t.TELNET_WILL, t.TELNET_TELOPT_COMPRESS2, t.TELNET_DO_EVENT, t.TELNET_DONT_EVENT)
- if !ok {
- return tev
- }
- me.telnet.TelnetBeginCompress2()
- monolog.Info("Client #{@id} started COMPRESS2 compression")
- me.info.compress2 = true
- return tev
- }
- // Negotiate NAWS (window size) support
- func (me *Client) SetupNAWS() telnet.Event {
- ok, tev := me.SetupNegotiate(1000, t.TELNET_DO, t.TELNET_TELOPT_NAWS, t.TELNET_WILL_EVENT, t.TELNET_WONT_EVENT)
- if !ok {
- return tev
- }
- tev2, _, _ := me.TryReadEvent(1000)
- if (tev2 == nil) || (!telnet.IsEventType(tev2, t.TELNET_NAWS_EVENT)) {
- return tev2
- }
- nawsevent, ok := tev2.(*telnet.NAWSEvent)
- if ok {
- me.info.w = nawsevent.W
- me.info.h = nawsevent.H
- monolog.Info("Client %d window size #{%d}x#{%d}", me.id, me.info.w, me.info.h)
- me.info.naws = true
- }
- return nil
- }
- func (me *Client) SetupMSSP() telnet.Event {
- ok, tev := me.SetupNegotiate(1000, t.TELNET_WILL, t.TELNET_TELOPT_MSSP, t.TELNET_DO_EVENT, t.TELNET_DONT_EVENT)
- if !ok {
- return tev
- }
- me.telnet.TelnetSendMSSP(MSSP)
- monolog.Info("Client %d accepts MSSP", me.id)
- me.info.mssp = true
- return nil
- }
- // Check for MXP (html-like) support (but don't implement it yet)
- func (me *Client) SetupMXP() telnet.Event {
- ok, tev := me.SetupNegotiate(1000, t.TELNET_DO, t.TELNET_TELOPT_MXP, t.TELNET_WILL_EVENT, t.TELNET_WONT_EVENT)
- if !ok {
- return tev
- }
- monolog.Info("Client %d accepts MXP", me.id)
- me.info.mxp = true
- return nil
- }
- // Check for MSP (sound) support (but don't implement it yet)
- func (me *Client) SetupMSP() telnet.Event {
- ok, tev := me.SetupNegotiate(1000, t.TELNET_DO, t.TELNET_TELOPT_MSP, t.TELNET_WILL_EVENT, t.TELNET_WONT_EVENT)
- if !ok {
- return tev
- }
- monolog.Info("Client %d accepts MSP", me.id)
- me.info.msp = true
- return nil
- }
- // Check for MSDP (two way MSSP) support (but don't implement it yet)
- func (me *Client) SetupMSDP() telnet.Event {
- ok, tev := me.SetupNegotiate(1000, t.TELNET_WILL, t.TELNET_TELOPT_MSDP, t.TELNET_DO_EVENT, t.TELNET_DONT_EVENT)
- if !ok {
- return tev
- }
- monolog.Info("Client %d accepts MSDP", me.id)
- me.info.msdp = true
- return nil
- }
- func (me *Client) HasTerminal(name string) bool {
- monolog.Debug("Client %d supports terminals? %s %v", me.id, name, me.info.terminals)
- for index := range me.info.terminals {
- if me.info.terminals[index] == name {
- return true
- }
- }
- return false
- }
- // Negotiate MTTS/TTYPE (TERMINAL TYPE) support
- func (me *Client) SetupTType() telnet.Event {
- me.info.terminals = nil
- ok, tev := me.SetupNegotiate(1000, t.TELNET_DO, t.TELNET_TELOPT_TTYPE, t.TELNET_WILL_EVENT, t.TELNET_WONT_EVENT)
- if !ok {
- return tev
- }
- var last string = "none"
- var now string = ""
- for last != now {
- last = now
- me.telnet.TelnetTTypeSend()
- var tev2 telnet.Event = nil
- // Some clients (like KildClient, but not TinTin or telnet),
- // insist on spamming useless NUL characters
- // here... So we have to retry a few times to get a ttype_is
- // throwing away any undesirable junk in between.
- GET_TTYPE:
- for index := 0; index < 3; index++ {
- tev2, _, _ = me.TryReadEvent(1000)
- if tev2 != nil {
- etyp := telnet.EventTypeOf(tev2)
- monolog.Info("Waiting for TTYPE: %T %v %d", tev2, tev2, etyp)
- if telnet.IsEventType(tev2, t.TELNET_TTYPE_EVENT) {
- monolog.Info("TTYPE received: %T %v %d", tev2, tev2, etyp)
- break GET_TTYPE
- }
- } else { // and some clients don't respond, even
- monolog.Info("Waiting for TTYPE: %T", tev2)
- }
- }
- if tev2 == nil || !telnet.IsEventType(tev2, t.TELNET_TTYPE_EVENT) {
- etyp := telnet.EventTypeOf(tev2)
- monolog.Warning("Received no TTYPE: %T %v %d", tev2, tev2, etyp)
- return tev2
- }
- ttypeevent := tev2.(*telnet.TTypeEvent)
- now = ttypeevent.Name
- if !me.HasTerminal(now) {
- me.info.terminals = append(me.info.terminals, now)
- }
- me.info.terminal = now
- }
- monolog.Info("Client %d supports terminals %v", me.id, me.info.terminals)
- monolog.Info("Client %d active terminal %v", me.id, me.info.terminal)
- // MTTS support
- for i := range me.info.terminals {
- term := me.info.terminals[i]
- monolog.Info("Checking MTTS support: %s", term)
- if strings.HasPrefix(term, "MTTS ") {
- // it's an mtts terminal
- strnum := strings.TrimPrefix(term, "MTTS ")
- num, err := strconv.Atoi(strnum)
- if err == nil {
- me.info.mtts = num
- monolog.Info("Client %d supports mtts %d", me.id, me.info.mtts)
- } else {
- monolog.Warning("Client %d could not parse mtts %s %v", me.id, strnum, err)
- }
- }
- }
- me.info.ttype = true
- return nil
- }
- /*
- Although most clients don't do this, some clients, like tinyfuge actively initiate telnet setup.
- Deal with that first, then do our own setup. It seems like tinyfuge is "waiting" for something
- before performing the setup, which messes up TryReadEvent...
- */
- func (me *Client) ReadTelnetSetup() {
- /* First send a NOP to prod the client into activity. This shouldn't be neccesary,
- but some clients, in particular Tinyfugue waits for server activity before it sends it's own
- telnet setup messages. If the NOP is not sent, those setup messages will then end up messing up the
- telnet setup the WOE server is trying to do. Arguably this should perhaps be an AYT, but a NOP will
- hopefully do the least damage.
- */
- msg := []byte{telnet.TELNET_IAC, telnet.TELNET_NOP}
- me.telnet.SendRaw(msg)
- for {
- // wait for incoming events.
- event, _, _ := me.TryReadEvent(500)
- if event == nil {
- monolog.Info("Client %d no telnet setup received", me.id)
- return
- }
- monolog.Info("Client %d telnet setup received: %v", me.id, event)
- switch event := event.(type) {
- case *telnet.DataEvent:
- monolog.Info("Unexpected DATA event %T : %d.", event, len(event.Data))
- // should do something with the data, maybe?
- return
- case *telnet.NAWSEvent:
- monolog.Info("Received Telnet NAWS event %T.", event)
- me.HandleNAWSEvent(event)
- case *telnet.WillEvent:
- monolog.Info("Received Telnet WILL event %T.", event)
- // XXX do something with it!
- default:
- monolog.Info("Unexpected event in setup phase %T : %v. Ignored for now.", event, event)
- }
- }
- }
- func (me *Client) SetupTelnet() {
- me.ReadTelnetSetup()
- me.SetupSupressGA()
- // me.SetupBinary()
- me.SetupMSSP()
- // me.SetupCompress2
- me.SetupNAWS()
- me.SetupTType()
- me.SetupMXP()
- me.SetupMSP()
- me.SetupMSDP()
- // me.ColorTest()
- }
|