// 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 }