diff options
Diffstat (limited to 'vendor/github.com/shazow/ssh-chat/sshd')
4 files changed, 93 insertions, 9 deletions
diff --git a/vendor/github.com/shazow/ssh-chat/sshd/auth.go b/vendor/github.com/shazow/ssh-chat/sshd/auth.go index afa7271a..a1404135 100644 --- a/vendor/github.com/shazow/ssh-chat/sshd/auth.go +++ b/vendor/github.com/shazow/ssh-chat/sshd/auth.go @@ -19,6 +19,7 @@ type Auth interface { } // MakeAuth makes an ssh.ServerConfig which performs authentication against an Auth implementation. +// TODO: Switch to using ssh.AuthMethod instead? func MakeAuth(auth Auth) *ssh.ServerConfig { config := ssh.ServerConfig{ NoClientAuth: false, diff --git a/vendor/github.com/shazow/ssh-chat/sshd/pty.go b/vendor/github.com/shazow/ssh-chat/sshd/pty.go index 4443fd33..d3ed9ca8 100644 --- a/vendor/github.com/shazow/ssh-chat/sshd/pty.go +++ b/vendor/github.com/shazow/ssh-chat/sshd/pty.go @@ -6,8 +6,8 @@ import "encoding/binary" // parsePtyRequest parses the payload of the pty-req message and extracts the // dimensions of the terminal. See RFC 4254, section 6.2. -func parsePtyRequest(s []byte) (width, height int, ok bool) { - _, s, ok = parseString(s) +func parsePtyRequest(s []byte) (term string, width, height int, ok bool) { + term, s, ok = parseString(s) if !ok { return } diff --git a/vendor/github.com/shazow/ssh-chat/sshd/terminal.go b/vendor/github.com/shazow/ssh-chat/sshd/terminal.go index 472b5c15..e8d99016 100644 --- a/vendor/github.com/shazow/ssh-chat/sshd/terminal.go +++ b/vendor/github.com/shazow/ssh-chat/sshd/terminal.go @@ -55,6 +55,29 @@ func (c sshConn) Name() string { return c.User() } +// EnvVar is an environment variable key-value pair +type EnvVar struct { + Key string + Value string +} + +func (v EnvVar) String() string { + return v.Key + "=" + v.Value +} + +// Env is a wrapper type around []EnvVar with some helper methods +type Env []EnvVar + +// Get returns the latest value for a given key, or empty string if not found +func (e Env) Get(key string) string { + for i := len(e) - 1; i >= 0; i-- { + if e[i].Key == key { + return e[i].Value + } + } + return "" +} + // Terminal extends ssh/terminal to include a close method type Terminal struct { terminal.Terminal @@ -63,9 +86,14 @@ type Terminal struct { done chan struct{} closeOnce sync.Once + + mu sync.Mutex + env []EnvVar + term string } // Make new terminal from a session channel +// TODO: For v2, make a separate `Serve(ctx context.Context) error` method to activate the Terminal func NewTerminal(conn *ssh.ServerConn, ch ssh.NewChannel) (*Terminal, error) { if ch.ChannelType() != "session" { return nil, ErrNotSessionChannel @@ -75,14 +103,15 @@ func NewTerminal(conn *ssh.ServerConn, ch ssh.NewChannel) (*Terminal, error) { return nil, err } term := Terminal{ - Terminal: *terminal.NewTerminal(channel, "Connecting..."), + Terminal: *terminal.NewTerminal(channel, ""), Conn: sshConn{conn}, Channel: channel, done: make(chan struct{}), } - go term.listen(requests) + ready := make(chan struct{}) + go term.listen(requests, ready) go func() { // Keep-Alive Ticker @@ -103,7 +132,18 @@ func NewTerminal(conn *ssh.ServerConn, ch ssh.NewChannel) (*Terminal, error) { } }() - return &term, nil + // We need to wait for term.ready to acquire a shell before we return, this + // gives the SSH session a chance to populate the env vars and other state. + // TODO: Make the timeout configurable + // TODO: Use context.Context for abort/timeout in the future, will need to change the API. + select { + case <-ready: // shell acquired + return &term, nil + case <-term.done: + return nil, errors.New("terminal aborted") + case <-time.NewTimer(time.Minute).C: + return nil, errors.New("timed out starting terminal") + } } // NewSession Finds a session channel and make a Terminal from it @@ -133,8 +173,9 @@ func (t *Terminal) Close() error { return err } -// Negotiate terminal type and settings -func (t *Terminal) listen(requests <-chan *ssh.Request) { +// listen negotiates the terminal type and state +// ready is closed when the terminal is ready. +func (t *Terminal) listen(requests <-chan *ssh.Request, ready chan<- struct{}) { hasShell := false for req := range requests { @@ -146,13 +187,19 @@ func (t *Terminal) listen(requests <-chan *ssh.Request) { if !hasShell { ok = true hasShell = true + close(ready) } case "pty-req": - width, height, ok = parsePtyRequest(req.Payload) + var term string + term, width, height, ok = parsePtyRequest(req.Payload) if ok { // TODO: Hardcode width to 100000? err := t.SetSize(width, height) ok = err == nil + // Save the term: + t.mu.Lock() + t.term = term + t.mu.Unlock() } case "window-change": width, height, ok = parseWinchRequest(req.Payload) @@ -161,6 +208,14 @@ func (t *Terminal) listen(requests <-chan *ssh.Request) { err := t.SetSize(width, height) ok = err == nil } + case "env": + var v EnvVar + if err := ssh.Unmarshal(req.Payload, &v); err == nil { + t.mu.Lock() + t.env = append(t.env, v) + t.mu.Unlock() + ok = true + } } if req.WantReply { @@ -168,3 +223,24 @@ func (t *Terminal) listen(requests <-chan *ssh.Request) { } } } + +// Env returns a list of environment key-values that have been set. They are +// returned in the order that they have been set, there is no deduplication or +// other pre-processing applied. +func (t *Terminal) Env() Env { + t.mu.Lock() + defer t.mu.Unlock() + return Env(t.env) +} + +// Term returns the terminal string value as set by the pty. +// If there was no pty request, it falls back to the TERM value passed in as an +// Env variable. +func (t *Terminal) Term() string { + t.mu.Lock() + defer t.mu.Unlock() + if t.term != "" { + return t.term + } + return Env(t.env).Get("TERM") +} diff --git a/vendor/github.com/shazow/ssh-chat/sshd/terminal/terminal.go b/vendor/github.com/shazow/ssh-chat/sshd/terminal/terminal.go index d6650bb1..ec04e8f1 100644 --- a/vendor/github.com/shazow/ssh-chat/sshd/terminal/terminal.go +++ b/vendor/github.com/shazow/ssh-chat/sshd/terminal/terminal.go @@ -10,6 +10,8 @@ import ( "strconv" "sync" "unicode/utf8" + + "golang.org/x/text/width" ) // EscapeCodes contains escape sequences that can be written to the terminal in @@ -262,7 +264,7 @@ func (t *Terminal) moveCursorToPos(pos int) { return } - x := visualLength(t.prompt) + pos + x := visualLength(t.prompt) + visualLength(t.line[:pos]) y := x / t.termWidth x = x % t.termWidth @@ -351,6 +353,7 @@ func (t *Terminal) setLine(newLine []rune, newPos int) { for i := len(newLine); i < len(t.line); i++ { t.writeLine(space) } + t.line = newLine t.moveCursorToPos(newPos) } t.line = newLine @@ -462,6 +465,10 @@ func visualLength(runes []rune) int { inEscapeSeq = true default: length++ + kind := width.LookupRune(r).Kind() + if kind == width.EastAsianFullwidth || kind == width.EastAsianWide { + length++ + } } } |