diff --git a/server.go b/server.go deleted file mode 100644 index c93c483..0000000 --- a/server.go +++ /dev/null @@ -1,440 +0,0 @@ -// vim:ts=4:sts=4:sw=4:noet:tw=72 - -package ircd - -import ( - "fmt" - "net" - "os" - "runtime" - "strings" - "time" - - "code.dnix.de/an/conf" - "code.dnix.de/an/irc" - "code.dnix.de/an/xlog" -) - -const ( - CHANLIMIT = 1024 - CHANNELLEN = 200 - TOPICLEN = 1024 -) - -var myinfo string = "%s %s/%s * *" -var isupport string = "CASEMAPPING=rfc1459 CHANTYPES=# NICKLEN=32 PREFIX=(aohv)&@%%+" - -type Server struct { - queue chan *irc.Message - addq chan Client - delq chan Client - - host string - info string - software string - version string - created string - motd string - - clients map[string]Client - - chUsers map[string]map[string]string - chTopics map[string]string - chModes map[string]map[string]bool - config *conf.ConfigFile - configPath string - - packetsTransferred float64 - connectionsCurrent float64 - connectionsCount float64 - queueLen float64 - - authCallback func(name, pass string) bool -} - -// Create a new server instance. -func NewServer(configPath, software, version string) *Server { - sv := &Server{software: software, version: version, - created: time.Now().String()} - - sv.queue = make(chan *irc.Message, 1024) - sv.addq = make(chan Client, 128) - sv.delq = make(chan Client, 128) - - sv.clients = make(map[string]Client) - sv.chUsers = make(map[string]map[string]string) - sv.chTopics = make(map[string]string) - sv.chModes = make(map[string]map[string]bool) - - sv.configPath = configPath - sv.loadConfig() - - loglevel, _ := sv.config.GetInt("system", "loglevel") - xlog.Init(loglevel) - - sv.host, _ = sv.config.GetString("server", "host") - sv.info, _ = sv.config.GetString("server", "info") - sv.motd, _ = sv.config.GetString("server", "motd") - - sv.packetsTransferred = 0 - sv.connectionsCurrent = 0 - sv.connectionsCount = 0 - sv.queueLen = 0 - - return sv -} - -func (sv *Server) SetAuthCallback(authCB func(name, pass string) bool) { - sv.authCallback = authCB -} - -// Open the listening port and start the main server loop. -func (sv *Server) Run() { - xlog.Info("%s/%s", sv.software, sv.version) - go monitoringRun(sv) - laddr, err := sv.config.GetString("net", "listen") - if err == nil { - go sv.listen(laddr) - } - laddr, err = sv.config.GetString("net", "listen_tls") - if err == nil { - go sv.listenTls(laddr) - } - sv.dispatcher() -} - -func (sv *Server) Dispatch(msg *irc.Message) { - sv.queue <- msg -} - -func (sv *Server) AddClient(cl Client) { - sv.addq <- cl -} - -func (sv *Server) DelClient(cl Client) { - sv.delq <- cl -} - -func (sv *Server) listen(laddr string) { - listen, err := net.Listen("tcp", laddr) - if err != nil { - xlog.Fatal(err.Error()) - os.Exit(-1) - } - for { - time.Sleep(1 * time.Millisecond) - conn, err := listen.Accept() - if err != nil { - xlog.Error(err.Error()) - } else { - NewRemoteClient(sv, conn) - sv.connectionsCount++ - } - } -} - -func (sv *Server) listenTls(laddr string) { -} - -func (sv *Server) dispatcher() { - for { - time.Sleep(1 * time.Microsecond) - sv.queueLen = float64(len(sv.queue)) - select { - case msg := <-sv.queue: - sv.recvMsg(msg) - sv.packetsTransferred++ - case cl := <-sv.addq: - clid := strings.ToLower(cl.Name()) - if _, exists := sv.clients[clid]; exists { - sv.sendReply(cl.Name(), ERR_NICKNAMEINUSE, "", "Nickname is already in use") - go func() { - time.Sleep(5 * time.Second) - cl.Register(false) - }() - xlog.Info("Client registration failed: '%s' (client exists)", clid) - continue - } - if !sv.authCallback(cl.Name(), cl.Password()) { - sv.sendReply(cl.Name(), ERR_PASSWDMISMATCH, "", "Password incorrect") - go func() { - time.Sleep(5 * time.Second) - cl.Register(false) - }() - xlog.Info("Client registration failed: '%s' (wrong password)", clid) - continue - } - sv.clients[clid] = cl - sv.clients[clid] = cl - sv.sendLogon(cl.Name()) - sv.connectionsCurrent = float64(len(sv.clients)) - cl.Register(true) - xlog.Info("Client registered: '%s'", clid) - xlog.Info("Server has %d client(s)", len(sv.clients)) - xlog.Debug("Goroutines running: %d", runtime.NumGoroutine()) - case cl := <-sv.delq: - clid := strings.ToLower(cl.Name()) - cl.Destroy() - for chname, ch := range sv.chUsers { - if _, exists := ch[clid]; exists { - delete(ch, clid) - sv.sendMsg(irc.M(cl.Name(), "PART", chname, "quit")) - } - } - delete(sv.clients, clid) - sv.connectionsCurrent = float64(len(sv.clients)) - xlog.Info("Client deleted: '%s'", clid) - xlog.Info("Server has %d client(s)", len(sv.clients)) - xlog.Debug("Goroutines running: %d", runtime.NumGoroutine()) - default: - } - } -} - -func (sv *Server) loadConfig() { - cfg, err := conf.ReadConfigFile(sv.configPath) - if err != nil { - xlog.Fatal("Can't read config file (%s)", err.Error()) - os.Exit(-1) - } - sv.config = cfg -} - -func (sv *Server) recvMsg(msg *irc.Message) { - cmd := msg.Cmd - hook, exists := svCommandHooks[cmd] - if !exists { - sv.sendReply(msg.Pre, ERR_UNKNOWNCOMMAND, cmd, "Unknown command") - return - } - argc := len(msg.Args) - if argc < hook.MinArgs { - sv.sendReply(msg.Pre, ERR_NEEDMOREPARAMS, cmd, "Not enough parameters") - return - } - if hook.NeedTrail && msg.Trail == "" { - sv.sendReply(msg.Pre, ERR_NEEDMOREPARAMS, cmd, "Not enough parameters") - return - } - hook.HookFn(sv, msg) -} - -func (sv *Server) sendMsg(msg *irc.Message) { - if strings.HasPrefix(msg.Args[0], "#") { - chid := strings.ToLower(msg.Args[0]) - if _, exists := sv.chUsers[chid]; !exists { - sv.sendReply(msg.Pre, ERR_NOSUCHNICK, msg.Args[0], "No such nick/channel") - return - } - for clid, _ := range sv.chUsers[chid] { - if strings.ToLower(msg.Pre) == clid && msg.Cmd == "PRIVMSG" { - continue - } - if cl, exists := sv.clients[clid]; exists { - cl.Receive(msg) - } - } - } else { - clid := strings.ToLower(msg.Args[0]) - if _, exists := sv.clients[clid]; !exists { - sv.sendReply(msg.Pre, ERR_NOSUCHNICK, msg.Args[0], "No such nick/channel") - return - } - cl := sv.clients[clid] - cl.Receive(msg) - } -} - -func (sv *Server) sendReply(nick, cmd, args, trail string) { - clid := strings.ToLower(nick) - if _, exists := sv.clients[clid]; !exists { - return - } - cl := sv.clients[clid] - if args != "" { - args = nick + " " + args - } else { - args = nick - } - cl.Receive(irc.M(sv.host, cmd, args, trail)) -} - -func (sv *Server) sendLogon(nick string) { - sv.sendReply(nick, RPL_WELCOME, "", "Willkommen!") - sv.sendReply(nick, RPL_YOURHOST, "", - fmt.Sprintf("Your host is %s, running on %s/%s", - sv.host, sv.software, sv.version)) - sv.sendReply(nick, RPL_CREATED, "", - fmt.Sprintf("This server was created %s", sv.created)) - sv.sendReply(nick, RPL_MYINFO, "", - fmt.Sprintf(myinfo, sv.host, sv.software, sv.version)) - sv.sendReply(nick, RPL_ISUPPORT, "", - isupport+" are supported by this server") - sv.sendReply(nick, RPL_MOTDSTART, "", - fmt.Sprintf("- %s Message of the day -", sv.host)) - for _, line := range strings.Split(sv.motd, "\n") { - sv.sendReply(nick, RPL_MOTD, "", fmt.Sprintf("- %s", line)) - } - sv.sendReply(nick, RPL_ENDOFMOTD, "", "End of MOTD command") -} - -func (sv *Server) channelNames(nick, ch string) { - chid := strings.ToLower(ch) - if _, exists := sv.chUsers[chid]; !exists { - return - } - names := "" - for clid, mode := range sv.chUsers[chid] { - name := sv.clients[clid].Name() - if names != "" { - names += " " - } - names = names + mode + name - } - sv.sendReply(nick, RPL_NAMEREPLY, "= "+ch, names) - sv.sendReply(nick, RPL_ENDOFNAMES, ch, "End of /NAMES list") -} - -type commandHook struct { - HookFn func(sv *Server, msg *irc.Message) - MinArgs int - NeedTrail bool - NeedOper bool - NeedAuth bool -} - -var svCommandHooks = map[string]commandHook{ - "PRIVMSG": {handleCmdPrivmsg, 1, true, false, false}, - "JOIN": {handleCmdJoin, 1, false, false, false}, - "PART": {handleCmdPart, 1, false, false, false}, - "QUIT": {handleCmdQuit, 0, false, false, false}, - "MODE": {handleCmdMode, 1, false, false, false}, - "TOPIC": {handleCmdTopic, 1, false, false, false}, - "NAMES": {handleCmdNames, 1, false, false, false}, - "WHOIS": {handleCmdWhois, 0, false, false, false}, - "PING": {handleCmdPing, 1, false, false, false}, - "REHASH": {handleCmdRehash, 0, false, false, false}, - /* - "LIST": {handleCmdList, 0, false, false}, - "VERSION": {handleCmdVersion, 0, false, false}, - "STATS": {handleCmdStats, 0, false, false}, - "TIME": {handleCmdTime, 0, false, false}, - "OPER": {handleCmdOper, 1, false, false}, - "ADMIN": {handleCmdAdmin, 0, false, false}, - "INFO": {handleCmdInfo, 0, false, false}, - "WHO": {handleCmdWho, 0, false, false}, - "WHOWAS": {handleCmdWhowas, 0, false, false}, - "KILL": {handleCmdKill, 0, false, false}, - "PONG": {handleCmdPong, 0, false, false}, - "ERROR": {handleCmdError, 0, false, false}, - "AWAY": {handleCmdAway, 0, false, false}, - "RESTART": {handleCmdRestart, 0, false, false}, - "SUMMON": {handleCmdSummon, 0, false, false}, - "USERS": {handleCmdUsers, 0, false, false}, - "USERHOST": {handleCmdUserhost, 0, false, false}, - "ISON": {handleCmdIson, 0, false, false}, - */ -} - -func handleCmdPrivmsg(sv *Server, msg *irc.Message) { - sv.sendMsg(msg) -} - -func handleCmdJoin(sv *Server, msg *irc.Message) { - clid := strings.ToLower(msg.Pre) - chid := strings.ToLower(msg.Args[0]) - if _, exists := sv.chUsers[chid]; !exists { - sv.chUsers[chid] = make(map[string]string) - sv.chTopics[chid] = "" - sv.chModes[chid] = make(map[string]bool) - } - if _, exists := sv.chUsers[chid][clid]; exists { - return - } - sv.chUsers[chid][clid] = "" - sv.sendMsg(msg) - sv.sendReply(msg.Pre, RPL_TOPIC, msg.Args[0], sv.chTopics[msg.Args[0]]) - sv.channelNames(msg.Pre, msg.Args[0]) -} - -func handleCmdPart(sv *Server, msg *irc.Message) { - clid := strings.ToLower(msg.Pre) - chid := strings.ToLower(msg.Args[0]) - if _, exists := sv.chUsers[chid]; !exists { - return - } - if _, exists := sv.chUsers[chid][clid]; !exists { - return - } - sv.sendMsg(msg) - delete(sv.chUsers[chid], clid) - -} - -func handleCmdQuit(sv *Server, msg *irc.Message) { - -} - -func handleCmdMode(sv *Server, msg *irc.Message) { - if strings.HasPrefix(msg.Args[0], "#") { - chid := strings.ToLower(msg.Args[0]) - if _, exists := sv.chUsers[chid]; !exists { - return - } - if len(msg.Args) < 2 { - return - } - modeFlag := strings.ToLower(msg.Args[1]) - if len(msg.Args) < 3 { - //modeTar := "" - } else { - //modeTar := strings.ToLower(msg.Args[2]) - } - switch modeFlag { - case "+o": - case "-o": - case "+h": - case "-h": - case "+b": - case "-b": - case "+v": - case "-v": - } - sv.sendMsg(msg) - } -} - -func handleCmdTopic(sv *Server, msg *irc.Message) { - ch := msg.Args[0] - if _, exists := sv.chUsers[ch]; !exists { - sv.sendReply(msg.Pre, ERR_NOSUCHCHANNEL, ch, "No such channel") - } - if msg.Trail == "" { - sv.sendReply(msg.Pre, RPL_TOPIC, ch, sv.chTopics[ch]) - } else { - sv.chTopics[ch] = msg.Trail - sv.sendMsg(msg) - //sv.sendReply(msg.Pre, RPL_TOPIC, ch, msg.Trail) - } -} -func handleCmdNames(sv *Server, msg *irc.Message) { - ch := msg.Args[0] - if _, exists := sv.chUsers[ch]; !exists { - sv.sendReply(msg.Pre, ERR_NOSUCHCHANNEL, ch, "No such channel") - } - sv.channelNames(msg.Pre, ch) -} - -func handleCmdWhois(sv *Server, msg *irc.Message) { - sv.sendReply(msg.Pre, RPL_WHOISUSER, "nick user host *", "real name") -} - -func handleCmdPing(sv *Server, msg *irc.Message) { - sv.sendReply(msg.Pre, "PONG", msg.Args[0], "") -} - -func handleCmdRehash(sv *Server, msg *irc.Message) { - sv.loadConfig() - sv.sendReply(msg.Pre, RPL_REHASHING, "", "Rehashing.") - xlog.Info("Rehashing") -}