324 lines
6.7 KiB
Go
324 lines
6.7 KiB
Go
package ircd
|
|
|
|
import (
|
|
"bufio"
|
|
"code.dnix.de/xlog"
|
|
"fmt"
|
|
"io"
|
|
"net"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
type Client interface {
|
|
Name() string
|
|
Send(*Message)
|
|
Receive(*Message)
|
|
Register() chan bool
|
|
AddMode(string)
|
|
DelMode(string)
|
|
HasMode(string) bool
|
|
}
|
|
|
|
type RemoteClient struct {
|
|
server *Server
|
|
|
|
name string
|
|
password string
|
|
modes string
|
|
|
|
isSSL bool
|
|
isRegistered bool
|
|
isAuthed bool
|
|
isClosed bool
|
|
|
|
receive chan *Message
|
|
register chan bool
|
|
|
|
conn net.Conn
|
|
writeq chan string
|
|
channels map[*Channel]bool
|
|
}
|
|
|
|
func NewRemoteClient(srv *Server, conn net.Conn) *RemoteClient {
|
|
cl := new(RemoteClient)
|
|
cl.server = srv
|
|
|
|
cl.name = ""
|
|
cl.password = ""
|
|
cl.modes = ""
|
|
|
|
cl.receive = make(chan *Message)
|
|
cl.register = make(chan bool)
|
|
|
|
cl.isRegistered = false
|
|
cl.isAuthed = false
|
|
cl.isClosed = false
|
|
cl.conn = conn
|
|
cl.writeq = make(chan string, 256)
|
|
|
|
go cl.connReader()
|
|
go cl.connWriter()
|
|
go cl.loop()
|
|
|
|
xlog.Info("Client connected.")
|
|
|
|
return cl
|
|
}
|
|
|
|
func (cl *RemoteClient) Name() string {
|
|
return cl.name
|
|
}
|
|
|
|
func (cl *RemoteClient) Send(msg *Message) {
|
|
cl.server.Receive <- msg
|
|
}
|
|
|
|
func (cl *RemoteClient) Receive(msg *Message) {
|
|
cl.receive <- msg
|
|
}
|
|
|
|
func (cl *RemoteClient) Register() chan bool {
|
|
return cl.register
|
|
}
|
|
|
|
func (cl *RemoteClient) AddMode(mode string) {
|
|
cl.modes = cl.modes + mode
|
|
}
|
|
|
|
func (cl *RemoteClient) DelMode(mode string) {
|
|
cl.modes = strings.Replace(cl.modes, mode, "", -1)
|
|
}
|
|
|
|
func (cl *RemoteClient) HasMode(mode string) bool {
|
|
return strings.IndexRune(cl.modes, rune(mode[0])) != -1
|
|
}
|
|
|
|
func (cl *RemoteClient) writeMsg(msg *Message) {
|
|
var src, ctx, cmd, args, text string
|
|
|
|
src = fmt.Sprintf("%s!%s@%s", msg.Src, msg.Src, cl.server.Host)
|
|
ctx = msg.Ctx
|
|
cmd = msg.Cmd
|
|
text = msg.Text
|
|
args = ""
|
|
for _, arg := range msg.Args {
|
|
args += " " + arg
|
|
}
|
|
if text != "" {
|
|
cl.writeLine(fmt.Sprintf(":%s %s %s%s :%s", src, cmd, ctx, args, text))
|
|
} else {
|
|
cl.writeLine(fmt.Sprintf(":%s %s %s%s", src, cmd, ctx, args))
|
|
}
|
|
}
|
|
|
|
func (cl *RemoteClient) loop() {
|
|
for {
|
|
time.Sleep(1 * time.Millisecond)
|
|
if cl.isClosed {
|
|
return
|
|
}
|
|
select {
|
|
case msg := <-cl.receive:
|
|
cl.writeMsg(msg)
|
|
default:
|
|
continue
|
|
}
|
|
}
|
|
}
|
|
|
|
func (cl *RemoteClient) destroy(s string) {
|
|
if cl.isClosed {
|
|
return
|
|
}
|
|
cl.isClosed = true
|
|
close(cl.writeq)
|
|
cl.conn.Write([]byte("ERROR :Closing link (" + s + ")"))
|
|
cl.conn.Close()
|
|
cl.server.DelClient <- cl
|
|
if cl.name != "" {
|
|
xlog.Info("Client '%s' disconnected: %s", cl.name, s)
|
|
} else {
|
|
xlog.Info("Client disconnected: %s", s)
|
|
}
|
|
}
|
|
|
|
func (cl *RemoteClient) writeLine(format string, a ...interface{}) {
|
|
cl.writeq <- fmt.Sprintf(format, a...)
|
|
}
|
|
|
|
func (cl *RemoteClient) connReader() {
|
|
input := bufio.NewReader(cl.conn)
|
|
for {
|
|
s, err := input.ReadString('\n')
|
|
if err == io.EOF {
|
|
cl.destroy("Connection lost.")
|
|
return
|
|
}
|
|
if err != nil {
|
|
cl.destroy(fmt.Sprintf("Read error (%s).", err.Error()))
|
|
return
|
|
}
|
|
s = strings.Trim(s, "\r\n")
|
|
cl.handleLine(s)
|
|
}
|
|
}
|
|
|
|
func (cl *RemoteClient) connWriter() {
|
|
for line := range cl.writeq {
|
|
written := 0
|
|
bytes := []byte(line + "\r\n")
|
|
for written < len(line) {
|
|
n, err := cl.conn.Write(bytes[written:])
|
|
if err == io.EOF {
|
|
cl.destroy("Connection lost.")
|
|
return
|
|
} else if err != nil {
|
|
cl.destroy(fmt.Sprintf("Write error (%s).", err.Error()))
|
|
return
|
|
}
|
|
written += n
|
|
}
|
|
}
|
|
}
|
|
|
|
var lineFuncs = map[string]func(*RemoteClient, *Message) bool{
|
|
CMD_PASS: handleLinePass,
|
|
CMD_NICK: handleLineNick,
|
|
CMD_USER: handleLineUser,
|
|
// CMD_OPER: handleLineOper,
|
|
// CMD_QUIT: handleLineQuit,
|
|
CMD_JOIN: handleLineJoin,
|
|
CMD_PART: handleLinePart,
|
|
CMD_MODE: handleLineMode,
|
|
// CMD_TOPIC: handleLineTopic,
|
|
// CMD_NAMES: handleLineNames,
|
|
// CMD_LIST: handleLineList,
|
|
// CMD_INVITE: handleLineInvite,
|
|
// CMD_KICK: handleLineKick,
|
|
// CMD_VERSION: handleLineVersion,
|
|
// CMD_STATS: handleLineStats,
|
|
// CMD_TIME: handleLineTime,
|
|
// CMD_ADMIN: handleLineAdmin,
|
|
// CMD_INFO: handleLineInfo,
|
|
CMD_PRIVMSG: handleLinePrivmsg,
|
|
// CMD_NOTICE: handleLineNotice,
|
|
// CMD_WHO: handleLineWho,
|
|
// CMD_WHOIS: handleLineWhois,
|
|
// CMD_WHOWAS: handleLineWhowas,
|
|
// CMD_KILL: handleLineKill,
|
|
CMD_PING: handleLinePing,
|
|
// CMD_PONG: handleLinePong,
|
|
// CMD_ERROR: handleLineError,
|
|
// CMD_AWAY: handleLineAway,
|
|
CMD_REHASH: handleLineRehash,
|
|
// CMD_RESTART: handleLineRestart,
|
|
// CMD_SUMMON: handleLineSummon,
|
|
// CMD_USERS: handleLineUsers,
|
|
// CMD_USERHOST: handleLineUserhost,
|
|
// CMD_ISON: handleLineIson,
|
|
}
|
|
|
|
func (cl *RemoteClient) handleLine(s string) {
|
|
xlog.Debug("Raw: [%s] '%s'.", cl.name, s)
|
|
msg := M("", "", "", "", "", "")
|
|
args := strings.SplitN(s, " :", 2)
|
|
if len(args) > 1 {
|
|
msg.Text = args[1]
|
|
}
|
|
args = strings.Fields(args[0])
|
|
msg.Cmd = strings.ToUpper(args[0])
|
|
if len(args) > 1 {
|
|
msg.Args = args[1:]
|
|
}
|
|
msg.Src = cl.name
|
|
msg.Dst = ""
|
|
if _, exists := lineFuncs[msg.Cmd]; !exists {
|
|
// xlog.Warning("No such command (%s).", msg.Cmd)
|
|
return
|
|
}
|
|
if route := lineFuncs[msg.Cmd](cl, msg); route {
|
|
// xlog.Warning("Routing to server (%s).", msg.Cmd)
|
|
cl.Send(msg)
|
|
}
|
|
}
|
|
|
|
func checkAuth(cl *RemoteClient) {
|
|
if cl.name == "" || cl.password == "" {
|
|
return
|
|
}
|
|
}
|
|
|
|
func handleLinePass(cl *RemoteClient, msg *Message) bool {
|
|
cl.password = msg.Args[0]
|
|
return false
|
|
}
|
|
|
|
func handleLineNick(cl *RemoteClient, msg *Message) bool {
|
|
if cl.name != "" {
|
|
// TODO multiple registration not possible
|
|
return false
|
|
}
|
|
/*
|
|
if _, exists := cl.Server.Clients[msg.Args[0]]; exists {
|
|
cl.destroy("User '" + msg.Args[0] + "' already connected.")
|
|
} else {
|
|
cl.name = msg.Args[0]
|
|
cl.isRegistered = true
|
|
cl.Server.AddClient <- cl
|
|
xlog.Info("User '%s' registered.", msg.Args[0])
|
|
}
|
|
*/
|
|
if len(msg.Args) < 1 {
|
|
xlog.Warning("Nicksalat!")
|
|
return false
|
|
}
|
|
cl.name = msg.Args[0]
|
|
cl.server.AddClient <- cl
|
|
if registered := <-cl.register; registered {
|
|
cl.isRegistered = true
|
|
xlog.Info("User '%s' registered.", msg.Args[0])
|
|
} else {
|
|
cl.destroy("User '" + msg.Args[0] + "' already connected.")
|
|
}
|
|
return false
|
|
}
|
|
|
|
func handleLineUser(cl *RemoteClient, msg *Message) bool {
|
|
return false
|
|
}
|
|
|
|
func handleLineJoin(cl *RemoteClient, msg *Message) bool {
|
|
msg.Dst = msg.Args[0]
|
|
msg.Args = make([]string, 0)
|
|
return true
|
|
}
|
|
|
|
func handleLinePart(cl *RemoteClient, msg *Message) bool {
|
|
msg.Dst = msg.Args[0]
|
|
msg.Args = make([]string, 0)
|
|
return true
|
|
}
|
|
|
|
func handleLineMode(cl *RemoteClient, msg *Message) bool {
|
|
msg.Dst = msg.Args[0]
|
|
msg.Args = make([]string, 0)
|
|
return true
|
|
}
|
|
|
|
func handleLinePrivmsg(cl *RemoteClient, msg *Message) bool {
|
|
msg.Dst = msg.Args[0]
|
|
msg.Args = make([]string, 0)
|
|
return true
|
|
}
|
|
|
|
func handleLinePing(cl *RemoteClient, msg *Message) bool {
|
|
return true
|
|
}
|
|
|
|
func handleLineRehash(cl *RemoteClient, msg *Message) bool {
|
|
return true
|
|
}
|
|
|
|
// vim:ts=4:sts=4:sw=4:noet:tw=72
|