func Uid(hook string, msg *parser.Message, ircd *IRCd) { nickname, hopcount, nickTS := msg.Args[0], msg.Args[1], msg.Args[2] umode, username, hostname := msg.Args[3], msg.Args[4], msg.Args[5] ip, uid, name := msg.Args[6], msg.Args[7], msg.Args[8] _ = umode err := user.Import(uid, nickname, username, hostname, ip, hopcount, nickTS, name) if err != nil { // TODO: TS check - Kill remote or local? For now, we kill remote. ircd.ToServer <- &parser.Message{ Prefix: Config.SID, Command: parser.CMD_SQUIT, Args: []string{ uid, err.String(), }, DestIDs: []string{msg.SenderID}, } } for fwd := range server.Iter() { if fwd != msg.SenderID { log.Debug.Printf("Forwarding UID from %s to %s", msg.SenderID, fwd) fmsg := msg.Dup() fmsg.DestIDs = []string{fwd} } } }
func (c *Conn) WriteMessage(message *parser.Message) { bytes := message.Bytes() bytes = append(bytes, '\r', '\n') n, err := c.Write(bytes) if err != nil || n != len(bytes) { c.Error = err c.active = false c.Close() } }
func SQuit(hook string, msg *parser.Message, ircd *IRCd) { split, reason := msg.Args[0], msg.Args[1] if split == Config.SID { split = msg.SenderID } // Forward for sid := range server.Iter() { if sid != msg.SenderID { msg := msg.Dup() msg.DestIDs = []string{sid} ircd.ToServer <- msg } } if server.IsLocal(split) { ircd.ToServer <- &parser.Message{ Command: parser.CMD_ERROR, Args: []string{ "SQUIT: " + reason, }, } } sids := server.Unlink(split) peers := user.Netsplit(sids) notify := channel.Netsplit(Config.SID, peers) log.Debug.Printf("NET SPLIT: %s", split) log.Debug.Printf(" - SIDs: %v", sids) log.Debug.Printf(" - Peers: %v", peers) log.Debug.Printf(" - Notify: %v", notify) for uid, peers := range notify { if len(peers) > 0 { ircd.ToClient <- &parser.Message{ Prefix: uid, Command: parser.CMD_QUIT, Args: []string{ "*.net *.split", }, DestIDs: peers, } } } // Delete all of the peers if len(peers) > 0 { ircd.ToClient <- &parser.Message{ Command: parser.INT_DELUSER, DestIDs: peers, } } }
// Server PART func SPart(hook string, msg *parser.Message, ircd *IRCd) { chanlist, reason := strings.Split(msg.Args[0], ","), msg.Args[1] // Forward on to other servers for fwd := range server.Iter() { if fwd != msg.SenderID { log.Debug.Printf("Forwarding PART from %s to %s", msg.SenderID, fwd) fmsg := msg.Dup() fmsg.DestIDs = []string{fwd} } } for _, channame := range chanlist { channel, err := channel.Get(channame, false) if num, ok := err.(*parser.Numeric); ok { ircd.ToServer <- num.Error(msg.SenderID) return } chanusers, err := channel.Part(msg.Prefix) if num, ok := err.(*parser.Numeric); ok { ircd.ToServer <- num.Error(msg.SenderID) return } notify := []string{} for _, uid := range chanusers { if uid[:3] == Config.SID { notify = append(notify, uid) } } if len(notify) > 0 { ircd.ToClient <- &parser.Message{ Prefix: msg.Prefix, Command: parser.CMD_PART, Args: []string{ channel.Name(), reason, }, DestIDs: notify, } } } }
func Sid(hook string, msg *parser.Message, ircd *IRCd) { servname, hopcount, sid, desc := msg.Args[0], msg.Args[1], msg.Args[2], msg.Args[3] err := server.Link(msg.Prefix, sid, servname, hopcount, desc) if err != nil { ircd.ToServer <- &parser.Message{ Prefix: Config.SID, Command: parser.CMD_SQUIT, Args: []string{ sid, err.String(), }, DestIDs: []string{msg.SenderID}, } } for fwd := range server.Iter() { if fwd != msg.SenderID { log.Debug.Printf("Forwarding SID from %s to %s", msg.SenderID, fwd) fmsg := msg.Dup() fmsg.DestIDs = []string{fwd} } } }
// Server JOIN and SJOIN func SJoin(hook string, msg *parser.Message, ircd *IRCd) { chanTS, channame, mode := msg.Args[0], msg.Args[1], msg.Args[2] uids := []string{msg.Prefix} if len(msg.Prefix) == 3 { if len(msg.Args) == 3 { return } uids = strings.Split(msg.Args[3], " ") } _ = chanTS _ = mode // Forward on to other servers for fwd := range server.Iter() { if fwd != msg.SenderID { log.Debug.Printf("Forwarding SJOIN from %s to %s", msg.SenderID, fwd) fmsg := msg.Dup() fmsg.DestIDs = []string{fwd} } } for i, uid := range uids { uids[i] = uid[len(uid)-9:] } channel, err := channel.Get(channame, true) if num, ok := err.(*parser.Numeric); ok { ircd.ToServer <- num.Error(msg.SenderID) return } if len(uids) == 0 && len(msg.Prefix) == 9 { uids = []string{msg.Prefix} } chanusers, err := channel.Join(uids...) if num, ok := err.(*parser.Numeric); ok { ircd.ToServer <- num.Error(msg.SenderID) return } notify := []string{} for _, uid := range chanusers { if uid[:3] == Config.SID { notify = append(notify, uid) } } if len(notify) > 0 { for _, joiner := range uids { ircd.ToClient <- &parser.Message{ Prefix: joiner, Command: parser.CMD_JOIN, Args: []string{ channel.Name(), }, DestIDs: notify, } } } }
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 } } }
// Handle the NICK, USER, SERVER, and PASS messages func ConnReg(hook string, msg *parser.Message, ircd *IRCd) { var err os.Error var u *user.User var s *server.Server switch len(msg.SenderID) { case 3: s = server.Get(msg.SenderID, true) case 9: u = user.Get(msg.SenderID) } switch msg.Command { case parser.CMD_NICK: // NICK <nick> if u != nil { nick := msg.Args[0] err = u.SetNick(nick) } case parser.CMD_USER: // USER <user> . . :<real name> if u != nil { username, realname := msg.Args[0], msg.Args[3] err = u.SetUser(username, realname) } case parser.CMD_PASS: if s != nil { if len(msg.Args) != 4 { return } // PASS <password> TS <ver> <pfx> err = s.SetPass(msg.Args[0], msg.Args[2], msg.Args[3]) } case parser.CMD_CAPAB: if s != nil { err = s.SetCapab(msg.Args[0]) } case parser.CMD_SERVER: if s != nil { err = s.SetServer(msg.Args[0], msg.Args[1]) } default: log.Warn.Printf("Unknown command %q", msg) } if u != nil { if err != nil { switch err := err.(type) { case *parser.Numeric: msg := err.Message() msg.DestIDs = append(msg.DestIDs, u.ID()) ircd.ToClient <- msg return default: msg := &parser.Message{ Command: parser.CMD_ERROR, Args: []string{err.String()}, DestIDs: []string{u.ID()}, } ircd.ToClient <- msg return } } nickname, username, realname, _ := u.Info() if nickname != "*" && username != "" { // Notify servers for sid := range server.Iter() { ircd.ToServer <- &parser.Message{ Prefix: Config.SID, Command: parser.CMD_UID, Args: []string{ nickname, "1", u.TS(), "+i", username, "some.host", "127.0.0.1", u.ID(), realname, }, DestIDs: []string{sid}, } } // Process signon sendSignon(u, ircd) return } } if s != nil { if err != nil { switch err := err.(type) { case *parser.Numeric: msg := err.Message() msg.DestIDs = append(msg.DestIDs, s.ID()) ircd.ToServer <- msg return default: msg := &parser.Message{ Command: parser.CMD_ERROR, Args: []string{err.String()}, DestIDs: []string{s.ID()}, } ircd.ToServer <- msg return } } sid, serv, pass, capab := s.Info() if sid != "" && serv != "" && pass != "" && len(capab) > 0 { // Notify servers for sid := range server.Iter() { ircd.ToServer <- &parser.Message{ Prefix: Config.SID, Command: parser.CMD_SID, Args: []string{ serv, "2", sid, "some server", }, DestIDs: []string{sid}, } } sendServerSignon(s, ircd) Burst(s, ircd) } } }