func (s *IRCd) manageIncoming() { defer s.running.Done() quit := false defer func() { quit = true }() manage := func(conn *conn.Conn) { inc := make(chan *parser.Message) stop := make(chan string) conn.Subscribe(inc) conn.SubscribeClose(stop) user, nick := false, false pass, server, capab := false, false, false sid := "" queued := make([]*parser.Message, 0, 3) for !quit { select { case msg := <-inc: log.Debug.Printf(" %s %s", msg.SenderID, msg) queued = append(queued, msg) switch msg.Command { case parser.CMD_PASS: if len(msg.Args) == 4 { pass = true sid = msg.Args[3] } case parser.CMD_USER: user = true case parser.CMD_NICK: nick = true case parser.CMD_CAPAB: capab = true case parser.CMD_SERVER: server = true } case <-stop: return } if !quit && nick && user { conn.Unsubscribe(inc) conn.UnsubscribeClose(stop) s.newClient <- conn for _, msg := range queued { s.fromClient <- msg } return } if !quit && pass && server && capab { conn.SetServer(sid) conn.Unsubscribe(inc) conn.UnsubscribeClose(stop) s.newServer <- conn for _, msg := range queued { msg.SenderID = sid s.fromServer <- msg } return } } } for { select { // Connecting clients case conn := <-s.Incoming: go manage(conn) } } }
func (s *IRCd) manageClients() { defer s.running.Done() uid2conn := make(map[string]*conn.Conn) var open bool = true var msg *parser.Message for open { // anything to do here? select { //// Incoming and outgoing messages to and from clients // Messages directly from connections // TODO(kevlar): Collapse into one case (I don't like that server gets split) case msg = <-s.fromClient: u := user.Get(msg.SenderID) uid := u.ID() if msg.Command == parser.CMD_ERROR { user.Delete(uid) conn := uid2conn[uid] if conn != nil { log.Debug.Printf("[%s] ** Connection terminated remotely", uid) user.Delete(uid) uid2conn[uid] = nil, false conn.UnsubscribeClose(s.clientClosing) conn.Close() } continue } log.Debug.Printf("[%s] >> %s", uid, msg) DispatchClient(msg, s) // Messages from hooks case msg, open = <-s.ToClient: // Handle internal messages switch msg.Command { case parser.INT_DELUSER: // This is sent over the channel to ensure that all user messages // which may need to query these UIDs are sent before they are // removed. for _, uid := range msg.DestIDs { log.Debug.Printf("[%s] Netsplit", uid) user.Delete(uid) } continue } // Count the number of messages sent sentcount := 0 // For simplicity, * in the prefix or as the first argument // is replaced by the nick of the user the message is sent to setnick := len(msg.Args) > 0 && msg.Args[0] == "*" setprefix := msg.Prefix == "*" closeafter := false if msg.Command == parser.CMD_ERROR { // Close the connection and remove the prefix if we are sending an ERROR closeafter = true msg.Prefix = "" } else if len(msg.Prefix) == 0 { // Make sure a prefix is specified (use the server name) msg.Prefix = Config.Name } local := make([]string, 0, len(msg.DestIDs)) remote := make([]string, 0, len(msg.DestIDs)) for _, id := range msg.DestIDs { if id[:3] != Config.SID { remote = append(remote, id) continue } local = append(local, id) } // Pass the message to the server goroutine if len(remote) > 0 { if closeafter { for _, id := range remote { user.Delete(id) } } else { log.Warn.Printf("Dropping non-local: %s", len(remote), msg) } // Short circuit if there are no local recipients if len(local) == 0 { continue } } // Examine all arguments for UIDs and replace them if isuid(msg.Prefix) { nick, user, _, _, ok := user.GetInfo(msg.Prefix) if !ok { log.Warn.Printf("Nonexistent ID %s as prefix", msg.Prefix) } else { msg.Prefix = nick + "!" + user + "@host" // TODO(kevlar): hostname } } for i := range msg.Args { if isuid(msg.Args[i]) { nick, _, _, _, ok := user.GetInfo(msg.Args[i]) if !ok { log.Warn.Printf("Nonexistent ID %s as argument", msg.Args[i]) continue } msg.Args[i] = nick } } for _, id := range msg.DestIDs { conn, ok := uid2conn[id] if !ok { log.Warn.Printf("Nonexistent ID %s in send", id) continue } if setnick || setprefix { nick, _, _, _, _ := user.GetInfo(id) if setnick { msg.Args[0] = nick } if setprefix { msg.Prefix = nick } } conn.WriteMessage(msg) log.Debug.Printf("[%s] << %s\n", id, msg) sentcount++ if closeafter { log.Debug.Printf("[%s] ** Connection terminated", id) user.Delete(id) uid2conn[id] = nil, false conn.UnsubscribeClose(s.clientClosing) conn.Close() } } if sentcount == 0 { log.Warn.Printf("Dropped outgoing client message: %s", msg) } // Connecting clients case conn := <-s.newClient: id := conn.ID() uid2conn[id] = conn user.Get(id) conn.Subscribe(s.fromClient) conn.SubscribeClose(s.clientClosing) // Disconnecting clients case closeid := <-s.clientClosing: log.Debug.Printf("[%s] ** Connection closed", closeid) user.Delete(closeid) uid2conn[closeid] = nil, false } } }