ircd/client.go

264 lines
5.0 KiB
Go

// vim:ts=4:sts=4:sw=4:noet:tw=72
package main
import (
"bufio"
"fmt"
"io"
"net"
"strings"
"time"
"code.dnix.de/an/irc"
"code.dnix.de/an/xlog"
)
type Client struct {
server *Server
name string
password string
modes string
isSSL bool
isRegistered bool
isAuthed bool
isClosed bool
receive chan *irc.Message
register chan bool
conn net.Conn
writeq chan string
}
func NewClient(srv *Server, conn net.Conn) *Client {
cl := new(Client)
cl.server = srv
cl.name = ""
cl.password = ""
cl.modes = ""
cl.receive = make(chan *irc.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 *Client) Name() string {
return cl.name
}
func (cl *Client) Send(msg *irc.Message) {
msg.Pre = cl.name
cl.server.Dispatch <- msg
}
func (cl *Client) Receive(msg *irc.Message) {
cl.receive <- msg
}
func (cl *Client) Register() chan bool {
return cl.register
}
func (cl *Client) AddMode(mode string) {
cl.modes = cl.modes + mode
}
func (cl *Client) DelMode(mode string) {
cl.modes = strings.Replace(cl.modes, mode, "", -1)
}
func (cl *Client) HasMode(mode string) bool {
return strings.IndexRune(cl.modes, rune(mode[0])) != -1
}
func (cl *Client) writeMsg(msg *irc.Message) {
cl.writeLine(msg.String())
}
func (cl *Client) loop() {
for {
time.Sleep(1 * time.Millisecond)
if cl.isClosed {
return
}
select {
case msg := <-cl.receive:
cl.writeMsg(msg)
default:
continue
}
}
}
func (cl *Client) 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 *Client) writeLine(format string, a ...interface{}) {
cl.writeq <- fmt.Sprintf(format, a...)
}
func (cl *Client) 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 *Client) 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(*Client, *irc.Message) bool{
"PASS": handleLinePass,
"NICK": handleLineNick,
"USER": handleLineUser,
// "OPER: handleLineOper,
// "QUIT: handleLineQuit,
"JOIN": handleLineJoin,
"PART": handleLinePart,
"MODE": handleLineMode,
// "TOPIC: handleLineTopic,
// "NAMES: handleLineNames,
// "LIST: handleLineList,
// "INVITE: handleLineInvite,
// "KICK: handleLineKick,
// "VERSION: handleLineVersion,
// "STATS: handleLineStats,
// "TIME: handleLineTime,
// "ADMIN: handleLineAdmin,
// "INFO: handleLineInfo,
"PRIVMSG": handleLinePrivmsg,
// "NOTICE: handleLineNotice,
// "WHO: handleLineWho,
// "WHOIS: handleLineWhois,
// "WHOWAS: handleLineWhowas,
// "KILL: handleLineKill,
"PING": handleLinePing,
// "PONG: handleLinePong,
// "ERROR: handleLineError,
// "AWAY: handleLineAway,
"REHASH": handleLineRehash,
// "RESTART: handleLineRestart,
// "SUMMON: handleLineSummon,
// "USERS: handleLineUsers,
// "USERHOST: handleLineUserhost,
// "ISON: handleLineIson,
}
func (cl *Client) handleLine(s string) {
xlog.Debug("Raw: [%s] '%s'.", cl.name, s)
cl.Send(irc.Parse(s))
}
func checkAuth(cl *Client) {
if cl.name == "" || cl.password == "" {
return
}
}
func handleLinePass(cl *Client, msg *irc.Message) bool {
cl.password = msg.Args[0]
return false
}
func handleLineNick(cl *Client, msg *irc.Message) bool {
if cl.name != "" {
return false
}
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 *Client, msg *irc.Message) bool {
return false
}
func handleLineJoin(cl *Client, msg *irc.Message) bool {
return true
}
func handleLinePart(cl *Client, msg *irc.Message) bool {
return true
}
func handleLineMode(cl *Client, msg *irc.Message) bool {
return true
}
func handleLinePrivmsg(cl *Client, msg *irc.Message) bool {
return true
}
func handleLinePing(cl *Client, msg *irc.Message) bool {
return true
}
func handleLineRehash(cl *Client, msg *irc.Message) bool {
return true
}