Send fake channel joins on RESUME

This commit is contained in:
Daniel Oaks 2018-01-21 13:13:20 +10:00
parent d13b2ee095
commit f8b39cfe61
3 changed files with 40 additions and 23 deletions

View File

@ -145,29 +145,33 @@ func (channel *Channel) IsRegistered() bool {
return channel.registeredFounder != "" return channel.registeredFounder != ""
} }
func (channel *Channel) regenerateMembersCache() { func (channel *Channel) regenerateMembersCache(noLocksNeeded bool) {
// this is eventually consistent even without holding stateMutex.Lock() // this is eventually consistent even without holding stateMutex.Lock()
// throughout the update; all updates to `members` while holding Lock() // throughout the update; all updates to `members` while holding Lock()
// have a serial order, so the call to `regenerateMembersCache` that // have a serial order, so the call to `regenerateMembersCache` that
// happens-after the last one will see *all* the updates. then, // happens-after the last one will see *all* the updates. then,
// `membersCacheMutex` ensures that this final read is correctly paired // `membersCacheMutex` ensures that this final read is correctly paired
// with the final write to `membersCache`. // with the final write to `membersCache`.
channel.membersCacheMutex.Lock() if !noLocksNeeded {
defer channel.membersCacheMutex.Unlock() channel.membersCacheMutex.Lock()
defer channel.membersCacheMutex.Unlock()
channel.stateMutex.RLock()
}
channel.stateMutex.RLock()
result := make([]*Client, len(channel.members)) result := make([]*Client, len(channel.members))
i := 0 i := 0
for client := range channel.members { for client := range channel.members {
result[i] = client result[i] = client
i++ i++
} }
channel.stateMutex.RUnlock() if !noLocksNeeded {
channel.stateMutex.Lock() channel.stateMutex.RUnlock()
channel.stateMutex.Lock()
}
channel.membersCache = result channel.membersCache = result
channel.stateMutex.Unlock() if !noLocksNeeded {
return channel.stateMutex.Unlock()
}
} }
// Names sends the list of users joined to the channel to the given client. // 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) channel.members.Add(client)
firstJoin := len(channel.members) == 1 firstJoin := len(channel.members) == 1
channel.stateMutex.Unlock() channel.stateMutex.Unlock()
channel.regenerateMembersCache() channel.regenerateMembersCache(false)
client.addChannel(channel) client.addChannel(channel)
@ -722,7 +726,7 @@ func (channel *Channel) Quit(client *Client) {
channel.stateMutex.Lock() channel.stateMutex.Lock()
channel.members.Remove(client) channel.members.Remove(client)
channel.stateMutex.Unlock() channel.stateMutex.Unlock()
channel.regenerateMembersCache() channel.regenerateMembersCache(false)
client.removeChannel(channel) client.removeChannel(channel)
} }

View File

@ -380,10 +380,12 @@ func (client *Client) TryResume() {
for channel := range oldClient.channels { for channel := range oldClient.channels {
channel.stateMutex.Lock() channel.stateMutex.Lock()
client.resumeDetails.SendFakeJoinsFor = append(client.resumeDetails.SendFakeJoinsFor, channel.name)
oldModeSet := channel.members[oldClient] oldModeSet := channel.members[oldClient]
channel.members.Remove(oldClient) channel.members.Remove(oldClient)
channel.members[client] = oldModeSet channel.members[client] = oldModeSet
channel.regenerateMembersCache() channel.regenerateMembersCache(true)
// send join for old clients // send join for old clients
for member := range channel.members { for member := range channel.members {
@ -397,7 +399,7 @@ func (client *Client) TryResume() {
member.Send(nil, client.nickMaskString, "JOIN", channel.name) 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() channel.stateMutex.Unlock()
@ -652,26 +654,18 @@ func (client *Client) destroy(beingResumed bool) {
} }
} }
fmt.Println("2")
// clean up server // clean up server
if !beingResumed { if !beingResumed {
client.server.clients.Remove(client) client.server.clients.Remove(client)
} }
fmt.Println("3")
// clean up self // clean up self
if client.idletimer != nil { if client.idletimer != nil {
client.idletimer.Stop() client.idletimer.Stop()
} }
fmt.Println("4")
client.socket.Close() client.socket.Close()
fmt.Println("5")
// send quit messages to friends // send quit messages to friends
if !beingResumed { if !beingResumed {
for friend := range friends { for friend := range friends {

View File

@ -447,6 +447,24 @@ func (server *Server) tryRegister(c *Client) {
if server.logger.IsLoggingRawIO() { if server.logger.IsLoggingRawIO() {
c.Notice(rawIONotice) 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. // 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. // ResumeDetails are the details that we use to resume connections.
type ResumeDetails struct { type ResumeDetails struct {
OldNick string OldNick string
Timestamp *time.Time Timestamp *time.Time
SendFakeJoinsFor []string
} }
// RESUME <oldnick> [timestamp] // RESUME <oldnick> [timestamp]