From f8b39cfe61fc95d0bbb5a7e008541ff21aaafe37 Mon Sep 17 00:00:00 2001 From: Daniel Oaks Date: Sun, 21 Jan 2018 13:13:20 +1000 Subject: [PATCH] Send fake channel joins on RESUME --- irc/channel.go | 26 +++++++++++++++----------- irc/client.go | 14 ++++---------- irc/server.go | 23 +++++++++++++++++++++-- 3 files changed, 40 insertions(+), 23 deletions(-) diff --git a/irc/channel.go b/irc/channel.go index 6c4c77f8..54163c89 100644 --- a/irc/channel.go +++ b/irc/channel.go @@ -145,29 +145,33 @@ func (channel *Channel) IsRegistered() bool { return channel.registeredFounder != "" } -func (channel *Channel) regenerateMembersCache() { +func (channel *Channel) regenerateMembersCache(noLocksNeeded bool) { // this is eventually consistent even without holding stateMutex.Lock() // throughout the update; all updates to `members` while holding Lock() // have a serial order, so the call to `regenerateMembersCache` that // happens-after the last one will see *all* the updates. then, // `membersCacheMutex` ensures that this final read is correctly paired // with the final write to `membersCache`. - channel.membersCacheMutex.Lock() - defer channel.membersCacheMutex.Unlock() + if !noLocksNeeded { + channel.membersCacheMutex.Lock() + defer channel.membersCacheMutex.Unlock() + channel.stateMutex.RLock() + } - channel.stateMutex.RLock() result := make([]*Client, len(channel.members)) i := 0 for client := range channel.members { result[i] = client i++ } - channel.stateMutex.RUnlock() - channel.stateMutex.Lock() + if !noLocksNeeded { + channel.stateMutex.RUnlock() + channel.stateMutex.Lock() + } channel.membersCache = result - channel.stateMutex.Unlock() - return - + if !noLocksNeeded { + channel.stateMutex.Unlock() + } } // Names sends the list of users joined to the channel to the given client. @@ -413,7 +417,7 @@ func (channel *Channel) Join(client *Client, key string) { channel.members.Add(client) firstJoin := len(channel.members) == 1 channel.stateMutex.Unlock() - channel.regenerateMembersCache() + channel.regenerateMembersCache(false) client.addChannel(channel) @@ -722,7 +726,7 @@ func (channel *Channel) Quit(client *Client) { channel.stateMutex.Lock() channel.members.Remove(client) channel.stateMutex.Unlock() - channel.regenerateMembersCache() + channel.regenerateMembersCache(false) client.removeChannel(channel) } diff --git a/irc/client.go b/irc/client.go index 056212e9..ce55f92b 100644 --- a/irc/client.go +++ b/irc/client.go @@ -380,10 +380,12 @@ func (client *Client) TryResume() { for channel := range oldClient.channels { channel.stateMutex.Lock() + client.resumeDetails.SendFakeJoinsFor = append(client.resumeDetails.SendFakeJoinsFor, channel.name) + oldModeSet := channel.members[oldClient] channel.members.Remove(oldClient) channel.members[client] = oldModeSet - channel.regenerateMembersCache() + channel.regenerateMembersCache(true) // send join for old clients for member := range channel.members { @@ -397,7 +399,7 @@ func (client *Client) TryResume() { member.Send(nil, client.nickMaskString, "JOIN", channel.name) } - //TODO(dan): send priv modes + //TODO(dan): send priv modes for fake new join } channel.stateMutex.Unlock() @@ -652,26 +654,18 @@ func (client *Client) destroy(beingResumed bool) { } } - fmt.Println("2") - // clean up server if !beingResumed { client.server.clients.Remove(client) } - fmt.Println("3") - // clean up self if client.idletimer != nil { client.idletimer.Stop() } - fmt.Println("4") - client.socket.Close() - fmt.Println("5") - // send quit messages to friends if !beingResumed { for friend := range friends { diff --git a/irc/server.go b/irc/server.go index 9ab0dc1f..16a7d537 100644 --- a/irc/server.go +++ b/irc/server.go @@ -447,6 +447,24 @@ func (server *Server) tryRegister(c *Client) { if server.logger.IsLoggingRawIO() { c.Notice(rawIONotice) } + + // if resumed, send fake channel joins + if c.resumeDetails != nil { + for _, name := range c.resumeDetails.SendFakeJoinsFor { + channel := server.channels.Get(name) + if channel == nil { + continue + } + + if c.capabilities.Has(caps.ExtendedJoin) { + c.Send(nil, c.nickMaskString, "JOIN", channel.name, c.account.Name, c.realname) + } else { + c.Send(nil, c.nickMaskString, "JOIN", channel.name) + } + channel.SendTopic(c) + channel.Names(c) + } + } } // t returns the translated version of the given string, based on the languages configured by the client. @@ -2077,8 +2095,9 @@ func lusersHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool { // ResumeDetails are the details that we use to resume connections. type ResumeDetails struct { - OldNick string - Timestamp *time.Time + OldNick string + Timestamp *time.Time + SendFakeJoinsFor []string } // RESUME [timestamp]