diff --git a/client.go b/client.go index a8c8365..4e56ced 100644 --- a/client.go +++ b/client.go @@ -107,19 +107,18 @@ func (cl *Client) loop() { } } -func (cl *Client) destroy(s string) { +func (cl *Client) destroy() { if cl.isClosed { return } cl.isClosed = true close(cl.writeq) - cl.conn.Write([]byte("ERROR :Closing link (" + s + ")")) + cl.conn.Write([]byte("ERROR :Closing link")) cl.conn.Close() - cl.server.DelClient <- cl if cl.name != "" { - xlog.Info("Client '%s' disconnected: %s", cl.name, s) + xlog.Info("Client '%s' disconnected", cl.name) } else { - xlog.Info("Client disconnected: %s", s) + xlog.Info("Client disconnected") } } @@ -130,13 +129,19 @@ func (cl *Client) writeLine(format string, a ...interface{}) { func (cl *Client) connReader() { input := bufio.NewReader(cl.conn) for { + if cl.isClosed { + xlog.Debug("connReader: exiting") + return + } s, err := input.ReadString('\n') if err == io.EOF { - cl.destroy("Connection lost") + xlog.Info("connReader: Connection closed by peer") + cl.server.DelClient <- cl return } if err != nil { - cl.destroy(fmt.Sprintf("Read error (%s)", err.Error())) + xlog.Error("connReader: %s", err.Error()) + cl.server.DelClient <- cl return } s = strings.Trim(s, "\r\n") @@ -151,57 +156,37 @@ func (cl *Client) connWriter() { for written < len(line) { n, err := cl.conn.Write(bytes[written:]) if err == io.EOF { - cl.destroy("Connection lost") + xlog.Info("connWriter: Connection closed by peer") + cl.server.DelClient <- cl return } else if err != nil { - cl.destroy(fmt.Sprintf("Write error (%s)", err.Error())) + xlog.Error("connWriter: %s", err.Error()) + cl.server.DelClient <- cl return } written += n } } + xlog.Debug("connWriter: exiting") } 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)) + xlog.Debug("handleLine: [%s] '%s'", cl.name, s) + msg := irc.Parse(s) + hook, exists := lineFuncs[msg.Cmd] + forward := true + if exists { + forward = hook(cl, msg) + } + if forward { + cl.Send(msg) + } } func checkAuth(cl *Client) { @@ -227,9 +212,10 @@ func handleLineNick(cl *Client, msg *irc.Message) bool { cl.server.AddClient <- cl if registered := <-cl.register; registered { cl.isRegistered = true - xlog.Info("User '%s' registered.", msg.Args[0]) + xlog.Info("User '%s' registered", msg.Args[0]) } else { - cl.destroy("User '" + msg.Args[0] + "' already connected.") + xlog.Error("User '%s' already connected", msg.Args[0]) + cl.server.DelClient <- cl } return false } @@ -237,27 +223,3 @@ func handleLineNick(cl *Client, msg *irc.Message) bool { 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 -} diff --git a/main.go b/main.go new file mode 100644 index 0000000..a2de514 --- /dev/null +++ b/main.go @@ -0,0 +1,8 @@ +// vim:ts=4:sts=4:sw=4:noet:tw=72 + +package main + +func main() { + srv := NewServer("server.conf", "ircd", "0.0.1") + srv.Run() +} diff --git a/server.go b/server.go index 138d953..271aab3 100644 --- a/server.go +++ b/server.go @@ -36,8 +36,8 @@ type Server struct { Dispatch chan *irc.Message AddClient chan *Client DelClient chan *Client - Host string + host string info string software string version string @@ -60,15 +60,15 @@ func NewServer(configPath, software, version string) *Server { srv.Dispatch = make(chan *irc.Message, 1024) srv.AddClient = make(chan *Client, 1024) srv.DelClient = make(chan *Client, 1024) - srv.Host = "dnix.de" srv.clients = make(map[string]*Client) srv.subscriptions = make(map[string][]string) - srv.info = "" - srv.motd = `foo bar baz.` srv.configPath = configPath srv.loadConfig() loglevel, _ := srv.config.GetInt("system", "loglevel") + srv.host, _ = srv.config.GetString("server", "host") + srv.info, _ = srv.config.GetString("server", "info") + srv.motd, _ = srv.config.GetString("server", "motd") xlog.Init(loglevel) return srv @@ -77,13 +77,36 @@ func NewServer(configPath, software, version string) *Server { // Open the listening port and start the main server loop. func (srv *Server) Run() { xlog.Info("%s/%s", srv.software, srv.version) - srv.Host, _ = srv.config.GetString("server", "host") - srv.info, _ = srv.config.GetString("server", "info") + address, _ := srv.config.GetString("net", "address") port, _ := srv.config.GetInt("net", "port") - go srv.listen(port) + go srv.listen(address, port) srv.dispatch() } +func (srv *Server) listen(address string, port int) { + if _, exists := srv.ports[port]; exists { + xlog.Warning("Port %i already opened", port) + } + listen, err := net.Listen("tcp", address+":"+strconv.Itoa(port)) + if err != nil { + xlog.Fatal("Cannot listen on port %i (%s), exiting", + port, err.Error()) + os.Exit(-1) + } + + xlog.Info("Start listening on port %d", port) + + for { + time.Sleep(1e6) + conn, err := listen.Accept() + if err != nil { + xlog.Error(err.Error()) + } else { + NewClient(srv, conn) + } + } +} + func (srv *Server) dispatch() { xlog.Debug("Entering msg dispatcher") for { @@ -107,7 +130,11 @@ func (srv *Server) dispatch() { srv.clientMotd(cl) case cl := <-srv.DelClient: name := cl.Name() + cl.destroy() + //go func() { + // time.Sleep(10 * time.Second) delete(srv.clients, name) + //}() xlog.Info("Client deleted: '%s'", name) xlog.Info("Server has %d client(s)", len(srv.clients)) xlog.Debug("Goroutines running: %d", runtime.NumGoroutine()) @@ -116,30 +143,6 @@ func (srv *Server) dispatch() { } } -func (srv *Server) listen(port int) { - if _, exists := srv.ports[port]; exists { - xlog.Warning("Port %i already opened", port) - } - listen, err := net.Listen("tcp", ":"+strconv.Itoa(port)) - if err != nil { - xlog.Fatal("Cannot listen on port %i (%s), exiting", - port, err.Error()) - os.Exit(-1) - } - - xlog.Info("Start listening on port %d", port) - - for { - time.Sleep(1e6) - conn, err := listen.Accept() - if err != nil { - xlog.Error(err.Error()) - } else { - NewClient(srv, conn) - } - } -} - func (srv *Server) loadConfig() { cfg, err := conf.ReadConfigFile(srv.configPath) if err != nil { @@ -150,20 +153,15 @@ func (srv *Server) loadConfig() { } func (srv *Server) recvMsg(msg *irc.Message) { - if msg.Args[0] != "" { - srv.sendMsg(msg) - return - } cmd := msg.Cmd hook, exists := srvCommandHooks[cmd] if !exists { - srv.sendCommand(msg.Pre, ERR_UNKNOWNCOMMAND, cmd, "Unknown command") + srv.sendReply(msg.Pre, ERR_UNKNOWNCOMMAND, cmd, "Unknown command") return } argc := len(msg.Args) if argc < hook.MinArgs { - srv.sendCommand(msg.Pre, ERR_NEEDMOREPARAMS, cmd, - "Not enough parameters") + srv.sendReply(msg.Pre, ERR_NEEDMOREPARAMS, cmd, "Not enough parameters") return } hook.HookFn(srv, msg) @@ -194,39 +192,42 @@ func (srv *Server) sendMsgToChannel(msg *irc.Message) { func (srv *Server) sendMsgToClient(msg *irc.Message) { if _, exists := srv.clients[msg.Args[0]]; !exists { - xlog.Error("Client '%s' does not exist", msg.Args[0]) + xlog.Error("sendMsgToClient: Client '%s' does not exist", msg.Args[0]) return } cl := srv.clients[msg.Args[0]] cl.Receive(msg) } -func (srv *Server) sendCommand(dst, cmd, args, trail string) { - srv.sendMsg(irc.M(srv.Host, cmd, args, trail)) -} - -func (srv *Server) sendClient(cl *Client, cmd, args, trail string) { - srv.sendMsg(irc.M(srv.Host, cmd, cl.Name()+" "+args, trail)) +func (srv *Server) sendReply(tar, cmd, args, trail string) { + if _, exists := srv.clients[tar]; !exists { + xlog.Error("sendReply: Client '%s' does not exist", tar) + return + } + cl := srv.clients[tar] + cl.Receive(irc.M(srv.host, cmd, tar+" "+args, trail)) } func (srv *Server) clientLogon(cl *Client) { - srv.sendClient(cl, RPL_WELCOME, "", "Willkommen!") - srv.sendClient(cl, RPL_YOURHOST, "", + srv.sendReply(cl.Name(), RPL_WELCOME, "", "Willkommen!") + srv.sendReply(cl.Name(), RPL_YOURHOST, "", fmt.Sprintf("Your host is %s, running on %s/%s", - srv.Host, srv.software, srv.version)) - srv.sendClient(cl, RPL_CREATED, "", fmt.Sprintf("Created: %s", srv.created)) - srv.sendClient(cl, RPL_MYINFO, - fmt.Sprintf(myinfo, srv.Host, srv.software, srv.version), "") - srv.sendClient(cl, RPL_ISUPPORT, isupport, "are supported by this server") + srv.host, srv.software, srv.version)) + srv.sendReply(cl.Name(), RPL_CREATED, "", + fmt.Sprintf("Created: %s", srv.created)) + srv.sendReply(cl.Name(), RPL_MYINFO, "", + fmt.Sprintf(myinfo, srv.host, srv.software, srv.version)) + srv.sendReply(cl.Name(), RPL_ISUPPORT, "", + isupport+" are supported by this server") } func (srv *Server) clientMotd(cl *Client) { - srv.sendClient(cl, RPL_MOTDSTART, "", - fmt.Sprintf("- %s Message of the day -", srv.Host)) + srv.sendReply(cl.Name(), RPL_MOTDSTART, "", + fmt.Sprintf("- %s Message of the day -", srv.host)) for _, line := range strings.Split(srv.motd, "\n") { - srv.sendClient(cl, RPL_MOTD, "", fmt.Sprintf("- %s", line)) + srv.sendReply(cl.Name(), RPL_MOTD, "", fmt.Sprintf("- %s", line)) } - srv.sendClient(cl, RPL_ENDOFMOTD, "", "End of MOTD command") + srv.sendReply(cl.Name(), RPL_ENDOFMOTD, "", "End of MOTD command") } type SrvCommandHook struct { @@ -237,13 +238,16 @@ type SrvCommandHook struct { } var srvCommandHooks = map[string]SrvCommandHook{ - "OPER": {srvHandleCmdOper, 1, false, false}, + "PRIVMSG": {srvHandleCmdPrivmsg, 1, false, false}, + "JOIN": {srvHandleCmdPrivmsg, 1, false, false}, + "PART": {srvHandleCmdPrivmsg, 1, false, false}, "QUIT": {srvHandleCmdQuit, 0, false, false}, "MODE": {srvHandleCmdMode, 1, false, false}, "LIST": {srvHandleCmdList, 0, false, false}, "VERSION": {srvHandleCmdVersion, 0, false, false}, "STATS": {srvHandleCmdStats, 0, false, false}, "TIME": {srvHandleCmdTime, 0, false, false}, + "OPER": {srvHandleCmdOper, 1, false, false}, "ADMIN": {srvHandleCmdAdmin, 0, false, false}, "INFO": {srvHandleCmdInfo, 0, false, false}, "WHO": {srvHandleCmdWho, 0, false, false}, @@ -262,6 +266,16 @@ var srvCommandHooks = map[string]SrvCommandHook{ "ISON": {srvHandleCmdIson, 0, false, false}, } +func srvHandleCmdPrivmsg(srv *Server, msg *irc.Message) { + srv.sendMsg(msg) +} + +func srvHandleCmdJoin(srv *Server, msg *irc.Message) { +} + +func srvHandleCmdPart(srv *Server, msg *irc.Message) { +} + func srvHandleCmdOper(srv *Server, msg *irc.Message) { } @@ -293,6 +307,8 @@ func srvHandleCmdWho(srv *Server, msg *irc.Message) { } func srvHandleCmdWhois(srv *Server, msg *irc.Message) { + println("in whois") + srv.sendReply(msg.Pre, RPL_WHOISUSER, "nick user host *", "real name") } func srvHandleCmdWhowas(srv *Server, msg *irc.Message) { @@ -302,7 +318,7 @@ func srvHandleCmdKill(srv *Server, msg *irc.Message) { } func srvHandleCmdPing(srv *Server, msg *irc.Message) { - srv.sendCommand(msg.Pre, "PONG", "", msg.Args[0]) + srv.sendReply(msg.Pre, "PONG", msg.Args[0], "") } func srvHandleCmdPong(srv *Server, msg *irc.Message) { @@ -316,8 +332,8 @@ func srvHandleCmdAway(srv *Server, msg *irc.Message) { func srvHandleCmdRehash(srv *Server, msg *irc.Message) { srv.loadConfig() - srv.sendCommand(msg.Pre, RPL_REHASHING, "", "Rehashing.") - xlog.Info("Rehashing.") + srv.sendReply(msg.Pre, RPL_REHASHING, "", "Rehashing.") + xlog.Info("Rehashing") } func srvHandleCmdRestart(srv *Server, msg *irc.Message) {