ssh-chat/host.go
2014-12-25 00:37:50 -08:00

105 lines
2.1 KiB
Go

package main
import (
"fmt"
"io"
"strings"
"github.com/shazow/ssh-chat/chat"
"github.com/shazow/ssh-chat/sshd"
)
// Host is the bridge between sshd and chat modules
// TODO: Should be easy to add support for multiple channels, if we want.
type Host struct {
listener *sshd.SSHListener
channel *chat.Channel
}
// NewHost creates a Host on top of an existing listener
func NewHost(listener *sshd.SSHListener) *Host {
h := Host{
listener: listener,
channel: chat.NewChannel(),
}
return &h
}
// Connect a specific Terminal to this host and its channel
func (h *Host) Connect(term *sshd.Terminal) {
defer term.Close()
name := term.Conn.User()
term.SetPrompt(fmt.Sprintf("[%s] ", name))
term.AutoCompleteCallback = h.AutoCompleteFunction
user := chat.NewUserScreen(name, term)
defer user.Close()
err := h.channel.Join(user)
if err != nil {
logger.Errorf("Failed to join: %s", err)
return
}
for {
// TODO: Handle commands etc?
line, err := term.ReadLine()
if err == io.EOF {
// Closed
break
} else if err != nil {
logger.Errorf("Terminal reading error: %s", err)
break
}
m := chat.NewMessage(line).From(user)
h.channel.Send(*m)
}
err = h.channel.Leave(user)
if err != nil {
logger.Errorf("Failed to leave: %s", err)
return
}
}
// Serve our chat channel onto the listener
func (h *Host) Serve() {
terminals := h.listener.ServeTerminal()
for term := range terminals {
go h.Connect(term)
}
}
// AutoCompleteFunction is a callback for terminal autocompletion
func (h *Host) AutoCompleteFunction(line string, pos int, key rune) (newLine string, newPos int, ok bool) {
if key != 9 {
return
}
fields := strings.Fields(line[:pos])
partial := fields[len(fields)-1]
names := h.channel.NamesPrefix(partial)
if len(names) == 0 {
// Didn't find anything
return
}
name := names[len(names)-1]
posPartial := pos - len(partial)
// Append suffix separator
if len(fields) < 2 {
name += ": "
} else {
name += " "
}
// Reposition the cursor
newLine = strings.Replace(line[posPartial:], partial, name, 1)
newLine = line[:posPartial] + newLine
newPos = pos + (len(name) - len(partial))
ok = true
return
}