diff --git a/builtin.go b/builtin.go index 12257ca..fafa2a6 100644 --- a/builtin.go +++ b/builtin.go @@ -143,13 +143,13 @@ func handleJOIN(c *Client, e Event) { channel = c.state.lookupChannel(channelName) } - user := c.state.lookupUser(e.Source.Name) + user := c.state.lookupUser(e.Source.ID()) if user == nil { - if ok := c.state.createUser(e.Source.Name); !ok { + if ok := c.state.createUser(e.Source); !ok { c.state.Unlock() return } - user = c.state.lookupUser(e.Source.Name) + user = c.state.lookupUser(e.Source.ID()) } defer c.state.notify(c, UPDATE_STATE) @@ -169,7 +169,7 @@ func handleJOIN(c *Client, e Event) { } c.state.Unlock() - if e.Source.Name == c.GetNick() { + if e.Source.ID() == ToRFC1459(c.GetNick()) { // If it's us, don't just add our user to the list. Run a WHO which // will tell us who exactly is in the entire channel. c.Send(&Event{Command: WHO, Params: []string{channelName, "%tacuhnr,1"}}) @@ -209,7 +209,7 @@ func handlePART(c *Client, e Event) { defer c.state.notify(c, UPDATE_STATE) - if e.Source.Name == c.GetNick() { + if e.Source.ID() == ToRFC1459(c.GetNick()) { c.state.Lock() c.state.deleteChannel(channel) c.state.Unlock() @@ -217,7 +217,7 @@ func handlePART(c *Client, e Event) { } c.state.Lock() - c.state.deleteUser(channel, e.Source.Name) + c.state.deleteUser(channel, e.Source.ID()) c.state.Unlock() } @@ -327,9 +327,9 @@ func handleNICK(c *Client, e Event) { c.state.Lock() // renameUser updates the LastActive time automatically. if len(e.Params) == 1 { - c.state.renameUser(e.Source.Name, e.Params[0]) + c.state.renameUser(e.Source.ID(), e.Params[0]) } else if len(e.Trailing) > 0 { - c.state.renameUser(e.Source.Name, e.Trailing) + c.state.renameUser(e.Source.ID(), e.Trailing) } c.state.Unlock() c.state.notify(c, UPDATE_STATE) @@ -341,12 +341,12 @@ func handleQUIT(c *Client, e Event) { return } - if e.Source.Name == c.GetNick() { + if e.Source.ID() == ToRFC1459(c.GetNick()) { return } c.state.Lock() - c.state.deleteUser("", e.Source.Name) + c.state.deleteUser("", e.Source.ID()) c.state.Unlock() c.state.notify(c, UPDATE_STATE) } @@ -443,8 +443,9 @@ func handleNAMES(c *Client, e Event) { parts := strings.Split(e.Trailing, " ") - var host, ident, modes, nick string + var modes, nick string var ok bool + s := &Source{} c.state.Lock() for i := 0; i < len(parts); i++ { @@ -455,36 +456,29 @@ func handleNAMES(c *Client, e Event) { // If userhost-in-names. if strings.Contains(nick, "@") { - s := ParseSource(nick) + s = ParseSource(nick) if s == nil { continue } - host = s.Host - nick = s.Name - ident = s.Ident + } else { + s = &Source{ + Name: nick, + } + + if !IsValidNick(s.Name) { + continue + } } - if !IsValidNick(nick) { - continue - } - - c.state.createUser(nick) - user := c.state.lookupUser(nick) + c.state.createUser(s) + user := c.state.lookupUser(s.ID()) if user == nil { continue } user.addChannel(channel.Name) - channel.addUser(nick) - - // Add necessary userhost-in-names data into the user. - if host != "" { - user.Host = host - } - if ident != "" { - user.Ident = ident - } + channel.addUser(s.ID()) // Don't append modes, overwrite them. perms, _ := user.Perms.Lookup(channel.Name) @@ -507,7 +501,7 @@ func updateLastActive(c *Client, e Event) { c.state.Lock() // Update the users last active time, if they exist. - user := c.state.lookupUser(e.Source.Name) + user := c.state.lookupUser(e.Source.ID()) if user == nil { c.state.Unlock() return diff --git a/cap.go b/cap.go index 8914648..e627eec 100644 --- a/cap.go +++ b/cap.go @@ -193,7 +193,7 @@ func handleCHGHOST(c *Client, e Event) { } c.state.Lock() - user := c.state.lookupUser(e.Source.Name) + user := c.state.lookupUser(e.Source.ID()) if user != nil { user.Ident = e.Params[0] user.Host = e.Params[1] @@ -206,7 +206,7 @@ func handleCHGHOST(c *Client, e Event) { // when users are no longer away, or when they are away. func handleAWAY(c *Client, e Event) { c.state.Lock() - user := c.state.lookupUser(e.Source.Name) + user := c.state.lookupUser(e.Source.ID()) if user != nil { user.Extras.Away = e.Trailing } @@ -229,7 +229,7 @@ func handleACCOUNT(c *Client, e Event) { } c.state.Lock() - user := c.state.lookupUser(e.Source.Name) + user := c.state.lookupUser(e.Source.ID()) if user != nil { user.Extras.Account = account } diff --git a/cap_tags.go b/cap_tags.go index a4026a0..aff10f6 100644 --- a/cap_tags.go +++ b/cap_tags.go @@ -25,7 +25,7 @@ func handleTags(c *Client, e Event) { } c.state.Lock() - user := c.state.lookupUser(e.Source.Name) + user := c.state.lookupUser(e.Source.ID()) if user != nil { user.Extras.Account = account } diff --git a/conn.go b/conn.go index 77d8798..9fe6103 100644 --- a/conn.go +++ b/conn.go @@ -374,7 +374,7 @@ func (c *Client) readLoop(ctx context.Context, errs chan error, wg *sync.WaitGro // Check if it's an echo-message. if !c.Config.disableTracking { event.Echo = (event.Command == PRIVMSG || event.Command == NOTICE) && - event.Source != nil && event.Source.Name == c.GetNick() + event.Source != nil && event.Source.ID() == ToRFC1459(c.GetNick()) } c.rx <- event diff --git a/ctcp.go b/ctcp.go index 45fc6be..56f4c85 100644 --- a/ctcp.go +++ b/ctcp.go @@ -155,8 +155,8 @@ func (c *CTCP) call(client *Client, event *CTCPEvent) { } // Send a ERRMSG reply, if we know who sent it. - if event.Source != nil && IsValidNick(event.Source.Name) { - client.Cmd.SendCTCPReply(event.Source.Name, CTCP_ERRMSG, "that is an unknown CTCP query") + if event.Source != nil && IsValidNick(event.Source.ID()) { + client.Cmd.SendCTCPReply(event.Source.ID(), CTCP_ERRMSG, "that is an unknown CTCP query") } return } @@ -248,7 +248,7 @@ func handleCTCPPing(client *Client, ctcp CTCPEvent) { if ctcp.Reply { return } - client.Cmd.SendCTCPReply(ctcp.Source.Name, CTCP_PING, ctcp.Text) + client.Cmd.SendCTCPReply(ctcp.Source.ID(), CTCP_PING, ctcp.Text) } // handleCTCPPong replies with a pong. @@ -256,7 +256,7 @@ func handleCTCPPong(client *Client, ctcp CTCPEvent) { if ctcp.Reply { return } - client.Cmd.SendCTCPReply(ctcp.Source.Name, CTCP_PONG, "") + client.Cmd.SendCTCPReply(ctcp.Source.ID(), CTCP_PONG, "") } // handleCTCPVersion replies with the name of the client, Go version, as well @@ -264,12 +264,12 @@ func handleCTCPPong(client *Client, ctcp CTCPEvent) { // arm, etc). func handleCTCPVersion(client *Client, ctcp CTCPEvent) { if client.Config.Version != "" { - client.Cmd.SendCTCPReply(ctcp.Source.Name, CTCP_VERSION, client.Config.Version) + client.Cmd.SendCTCPReply(ctcp.Source.ID(), CTCP_VERSION, client.Config.Version) return } client.Cmd.SendCTCPReplyf( - ctcp.Source.Name, CTCP_VERSION, + ctcp.Source.ID(), CTCP_VERSION, "girc (github.com/lrstanley/girc) using %s (%s, %s)", runtime.Version(), runtime.GOOS, runtime.GOARCH, ) @@ -277,13 +277,13 @@ func handleCTCPVersion(client *Client, ctcp CTCPEvent) { // handleCTCPSource replies with the public git location of this library. func handleCTCPSource(client *Client, ctcp CTCPEvent) { - client.Cmd.SendCTCPReply(ctcp.Source.Name, CTCP_SOURCE, "https://github.com/lrstanley/girc") + client.Cmd.SendCTCPReply(ctcp.Source.ID(), CTCP_SOURCE, "https://github.com/lrstanley/girc") } // handleCTCPTime replies with a RFC 1123 (Z) formatted version of Go's // local time. func handleCTCPTime(client *Client, ctcp CTCPEvent) { - client.Cmd.SendCTCPReply(ctcp.Source.Name, CTCP_TIME, ":"+time.Now().Format(time.RFC1123Z)) + client.Cmd.SendCTCPReply(ctcp.Source.ID(), CTCP_TIME, ":"+time.Now().Format(time.RFC1123Z)) } // handleCTCPFinger replies with the realname and idle time of the user. This @@ -293,5 +293,5 @@ func handleCTCPFinger(client *Client, ctcp CTCPEvent) { active := client.conn.lastActive client.conn.mu.RUnlock() - client.Cmd.SendCTCPReply(ctcp.Source.Name, CTCP_FINGER, fmt.Sprintf("%s -- idle %s", client.Config.Name, time.Since(active))) + client.Cmd.SendCTCPReply(ctcp.Source.ID(), CTCP_FINGER, fmt.Sprintf("%s -- idle %s", client.Config.Name, time.Since(active))) } diff --git a/event.go b/event.go index 0b40b40..69f8214 100644 --- a/event.go +++ b/event.go @@ -516,7 +516,8 @@ const ( // Source represents the sender of an IRC event, see RFC1459 section 2.3.1. // | [ '!' ] [ '@' ] type Source struct { - // Name is the nickname, server name, or service name. + // Name is the nickname, server name, or service name, in its original + // non-rfc1459 form. Name string `json:"name"` // Ident is commonly known as the "user". Ident string `json:"ident"` @@ -525,6 +526,12 @@ type Source struct { Host string `json:"host"` } +// ID is the nickname, server name, or service name, in it's converted +// and comparable) form. +func (s *Source) ID() string { + return ToRFC1459(s.Name) +} + // Equals compares two Sources for equality. func (s *Source) Equals(ss *Source) bool { if s == nil && ss == nil { @@ -533,7 +540,7 @@ func (s *Source) Equals(ss *Source) bool { if s != nil && ss == nil || s == nil && ss != nil { return false } - if s.Name != ss.Name || s.Ident != ss.Ident || s.Host != ss.Host { + if s.ID() != ss.ID() || s.Ident != ss.Ident || s.Host != ss.Host { return false } return true diff --git a/state.go b/state.go index 36dcc82..0660a68 100644 --- a/state.go +++ b/state.go @@ -419,14 +419,16 @@ func (s *state) lookupUser(name string) *User { } // createUser creates the user in state, if not already done. -func (s *state) createUser(nick string) (ok bool) { - if _, ok := s.users[ToRFC1459(nick)]; ok { +func (s *state) createUser(src *Source) (ok bool) { + if _, ok := s.users[src.ID()]; ok { // User already exists. return false } - s.users[ToRFC1459(nick)] = &User{ - Nick: nick, + s.users[src.ID()] = &User{ + Nick: src.Name, + Host: src.Host, + Ident: src.Ident, FirstSeen: time.Now(), LastActive: time.Now(), Perms: &UserPerms{channels: make(map[string]Perms)},