mirror of
https://github.com/ergochat/ergo.git
synced 2024-12-22 18:52:41 +01:00
fix several session destruction bugs
This commit is contained in:
parent
da656c07c8
commit
60c8f286e8
@ -323,8 +323,10 @@ func (client *Client) run(session *Session) {
|
|||||||
session.resetFakelag()
|
session.resetFakelag()
|
||||||
|
|
||||||
isReattach := client.Registered()
|
isReattach := client.Registered()
|
||||||
|
if isReattach {
|
||||||
|
client.playReattachMessages(session)
|
||||||
|
} else {
|
||||||
// don't reset the nick timer during a reattach
|
// don't reset the nick timer during a reattach
|
||||||
if !isReattach {
|
|
||||||
client.nickTimer.Initialize(client)
|
client.nickTimer.Initialize(client)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -386,14 +388,14 @@ func (client *Client) run(session *Session) {
|
|||||||
break
|
break
|
||||||
} else if session.client != client {
|
} else if session.client != client {
|
||||||
// bouncer reattach
|
// bouncer reattach
|
||||||
session.playReattachMessages()
|
|
||||||
go session.client.run(session)
|
go session.client.run(session)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (session *Session) playReattachMessages() {
|
func (client *Client) playReattachMessages(session *Session) {
|
||||||
|
client.server.playRegistrationBurst(session)
|
||||||
for _, channel := range session.client.Channels() {
|
for _, channel := range session.client.Channels() {
|
||||||
channel.playJoinForSession(session)
|
channel.playJoinForSession(session)
|
||||||
}
|
}
|
||||||
@ -922,14 +924,11 @@ func (client *Client) destroy(beingResumed bool, session *Session) {
|
|||||||
|
|
||||||
// allow destroy() to execute at most once
|
// allow destroy() to execute at most once
|
||||||
client.stateMutex.Lock()
|
client.stateMutex.Lock()
|
||||||
nickMaskString := client.nickMaskString
|
details := client.detailsNoMutex()
|
||||||
accountName := client.accountName
|
wasReattach := session != nil && session.client != client
|
||||||
|
|
||||||
alreadyDestroyed := len(client.sessions) == 0
|
|
||||||
sessionRemoved := false
|
sessionRemoved := false
|
||||||
var remainingSessions int
|
var remainingSessions int
|
||||||
if session == nil {
|
if session == nil {
|
||||||
sessionRemoved = !alreadyDestroyed
|
|
||||||
sessionsToDestroy = client.sessions
|
sessionsToDestroy = client.sessions
|
||||||
client.sessions = nil
|
client.sessions = nil
|
||||||
remainingSessions = 0
|
remainingSessions = 0
|
||||||
@ -939,33 +938,46 @@ func (client *Client) destroy(beingResumed bool, session *Session) {
|
|||||||
sessionsToDestroy = []*Session{session}
|
sessionsToDestroy = []*Session{session}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var quitMessage string
|
|
||||||
if 0 < len(sessionsToDestroy) {
|
|
||||||
quitMessage = sessionsToDestroy[0].quitMessage
|
|
||||||
}
|
|
||||||
client.stateMutex.Unlock()
|
client.stateMutex.Unlock()
|
||||||
|
|
||||||
if alreadyDestroyed || !sessionRemoved {
|
if len(sessionsToDestroy) == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// destroy all applicable sessions:
|
||||||
|
var quitMessage string
|
||||||
for _, session := range sessionsToDestroy {
|
for _, session := range sessionsToDestroy {
|
||||||
if session.client != client {
|
if session.client != client {
|
||||||
// session has been attached to a new client; do not destroy it
|
// session has been attached to a new client; do not destroy it
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
session.idletimer.Stop()
|
session.idletimer.Stop()
|
||||||
session.socket.Close()
|
|
||||||
// send quit/error message to client if they haven't been sent already
|
// send quit/error message to client if they haven't been sent already
|
||||||
client.Quit("", session)
|
client.Quit("", session)
|
||||||
|
quitMessage = session.quitMessage
|
||||||
|
session.socket.Close()
|
||||||
|
|
||||||
|
// remove from connection limits
|
||||||
|
var source string
|
||||||
|
if client.isTor {
|
||||||
|
client.server.torLimiter.RemoveClient()
|
||||||
|
source = "tor"
|
||||||
|
} else {
|
||||||
|
ip := session.realIP
|
||||||
|
if session.proxiedIP != nil {
|
||||||
|
ip = session.proxiedIP
|
||||||
|
}
|
||||||
|
client.server.connectionLimiter.RemoveClient(ip)
|
||||||
|
source = ip.String()
|
||||||
|
}
|
||||||
|
client.server.logger.Info("localconnect-ip", fmt.Sprintf("disconnecting session of %s from %s", details.nick, source))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ok, now destroy the client, unless it still has sessions:
|
||||||
if remainingSessions != 0 {
|
if remainingSessions != 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
details := client.Details()
|
|
||||||
|
|
||||||
// see #235: deduplicating the list of PART recipients uses (comparatively speaking)
|
// see #235: deduplicating the list of PART recipients uses (comparatively speaking)
|
||||||
// a lot of RAM, so limit concurrency to avoid thrashing
|
// a lot of RAM, so limit concurrency to avoid thrashing
|
||||||
client.server.semaphores.ClientDestroy.Acquire()
|
client.server.semaphores.ClientDestroy.Acquire()
|
||||||
@ -973,38 +985,35 @@ func (client *Client) destroy(beingResumed bool, session *Session) {
|
|||||||
|
|
||||||
if beingResumed {
|
if beingResumed {
|
||||||
client.server.logger.Debug("quit", fmt.Sprintf("%s is being resumed", details.nick))
|
client.server.logger.Debug("quit", fmt.Sprintf("%s is being resumed", details.nick))
|
||||||
} else {
|
} else if !wasReattach {
|
||||||
client.server.logger.Debug("quit", fmt.Sprintf("%s is no longer on the server", details.nick))
|
client.server.logger.Debug("quit", fmt.Sprintf("%s is no longer on the server", details.nick))
|
||||||
}
|
}
|
||||||
|
|
||||||
if !beingResumed {
|
registered := client.Registered()
|
||||||
client.server.whoWas.Append(details.WhoWas)
|
if !beingResumed && registered {
|
||||||
}
|
client.server.whoWas.Append(client.WhoWas())
|
||||||
|
|
||||||
// remove from connection limits
|
|
||||||
if client.isTor {
|
|
||||||
client.server.torLimiter.RemoveClient()
|
|
||||||
} else {
|
|
||||||
client.server.connectionLimiter.RemoveClient(client.IP())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
client.server.resumeManager.Delete(client)
|
client.server.resumeManager.Delete(client)
|
||||||
|
|
||||||
// alert monitors
|
// alert monitors
|
||||||
|
if registered {
|
||||||
client.server.monitorManager.AlertAbout(client, false)
|
client.server.monitorManager.AlertAbout(client, false)
|
||||||
|
}
|
||||||
// clean up monitor state
|
// clean up monitor state
|
||||||
client.server.monitorManager.RemoveAll(client)
|
client.server.monitorManager.RemoveAll(client)
|
||||||
|
|
||||||
splitQuitMessage := utils.MakeSplitMessage(quitMessage, true)
|
splitQuitMessage := utils.MakeSplitMessage(quitMessage, true)
|
||||||
// clean up channels
|
// clean up channels
|
||||||
|
// (note that if this is a reattach, client has no channels and therefore no friends)
|
||||||
friends := make(ClientSet)
|
friends := make(ClientSet)
|
||||||
for _, channel := range client.Channels() {
|
for _, channel := range client.Channels() {
|
||||||
if !beingResumed {
|
if !beingResumed {
|
||||||
channel.Quit(client)
|
channel.Quit(client)
|
||||||
channel.history.Add(history.Item{
|
channel.history.Add(history.Item{
|
||||||
Type: history.Quit,
|
Type: history.Quit,
|
||||||
Nick: nickMaskString,
|
Nick: details.nickMask,
|
||||||
AccountName: accountName,
|
AccountName: details.accountName,
|
||||||
Message: splitQuitMessage,
|
Message: splitQuitMessage,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -160,9 +160,7 @@ func (clients *ClientManager) SetNick(client *Client, session *Session, newNick
|
|||||||
if !currentClient.AddSession(session) {
|
if !currentClient.AddSession(session) {
|
||||||
return errNicknameInUse
|
return errNicknameInUse
|
||||||
}
|
}
|
||||||
// successful reattach. temporarily assign them the nick they'll have going forward
|
// successful reattach!
|
||||||
// (the current `client` will be discarded at the end of command execution)
|
|
||||||
client.updateNick(currentClient.Nick(), newcfnick, newSkeleton)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
// analogous checks for skeletons
|
// analogous checks for skeletons
|
||||||
|
@ -329,7 +329,10 @@ func (client *Client) WhoWas() (result WhoWas) {
|
|||||||
func (client *Client) Details() (result ClientDetails) {
|
func (client *Client) Details() (result ClientDetails) {
|
||||||
client.stateMutex.RLock()
|
client.stateMutex.RLock()
|
||||||
defer client.stateMutex.RUnlock()
|
defer client.stateMutex.RUnlock()
|
||||||
|
return client.detailsNoMutex()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (client *Client) detailsNoMutex() (result ClientDetails) {
|
||||||
result.nick = client.nick
|
result.nick = client.nick
|
||||||
result.nickCasefolded = client.nickCasefolded
|
result.nickCasefolded = client.nickCasefolded
|
||||||
result.username = client.username
|
result.username = client.username
|
||||||
|
@ -413,20 +413,30 @@ func (server *Server) tryRegister(c *Client, session *Session) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
reattached := session.client != c
|
if session.client != c {
|
||||||
|
// reattached, bail out.
|
||||||
|
// we'll play the reg burst later, on the new goroutine associated with
|
||||||
|
// (thisSession, otherClient). This is to avoid having to transfer state
|
||||||
|
// like nickname, hostname, etc. to show the correct values in the reg burst.
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if !reattached {
|
|
||||||
// registration has succeeded:
|
// registration has succeeded:
|
||||||
c.SetRegistered()
|
c.SetRegistered()
|
||||||
|
|
||||||
// count new user in statistics
|
// count new user in statistics
|
||||||
server.stats.ChangeTotal(1)
|
server.stats.ChangeTotal(1)
|
||||||
|
|
||||||
if !resumed {
|
server.playRegistrationBurst(session)
|
||||||
|
|
||||||
|
if resumed {
|
||||||
|
c.tryResumeChannels()
|
||||||
|
} else {
|
||||||
server.monitorManager.AlertAbout(c, true)
|
server.monitorManager.AlertAbout(c, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (server *Server) playRegistrationBurst(session *Session) {
|
||||||
|
c := session.client
|
||||||
// continue registration
|
// continue registration
|
||||||
d := c.Details()
|
d := c.Details()
|
||||||
server.logger.Info("localconnect", fmt.Sprintf("Client connected [%s] [u:%s] [r:%s]", d.nick, d.username, d.realname))
|
server.logger.Info("localconnect", fmt.Sprintf("Client connected [%s] [u:%s] [r:%s]", d.nick, d.username, d.realname))
|
||||||
@ -435,11 +445,11 @@ func (server *Server) tryRegister(c *Client, session *Session) {
|
|||||||
// send welcome text
|
// send welcome text
|
||||||
//NOTE(dan): we specifically use the NICK here instead of the nickmask
|
//NOTE(dan): we specifically use the NICK here instead of the nickmask
|
||||||
// see http://modern.ircdocs.horse/#rplwelcome-001 for details on why we avoid using the nickmask
|
// see http://modern.ircdocs.horse/#rplwelcome-001 for details on why we avoid using the nickmask
|
||||||
c.Send(nil, server.name, RPL_WELCOME, d.nick, fmt.Sprintf(c.t("Welcome to the Internet Relay Network %s"), d.nick))
|
session.Send(nil, server.name, RPL_WELCOME, d.nick, fmt.Sprintf(c.t("Welcome to the Internet Relay Network %s"), d.nick))
|
||||||
c.Send(nil, server.name, RPL_YOURHOST, d.nick, fmt.Sprintf(c.t("Your host is %[1]s, running version %[2]s"), server.name, Ver))
|
session.Send(nil, server.name, RPL_YOURHOST, d.nick, fmt.Sprintf(c.t("Your host is %[1]s, running version %[2]s"), server.name, Ver))
|
||||||
c.Send(nil, server.name, RPL_CREATED, d.nick, fmt.Sprintf(c.t("This server was created %s"), server.ctime.Format(time.RFC1123)))
|
session.Send(nil, server.name, RPL_CREATED, d.nick, fmt.Sprintf(c.t("This server was created %s"), server.ctime.Format(time.RFC1123)))
|
||||||
//TODO(dan): Look at adding last optional [<channel modes with a parameter>] parameter
|
//TODO(dan): Look at adding last optional [<channel modes with a parameter>] parameter
|
||||||
c.Send(nil, server.name, RPL_MYINFO, d.nick, server.name, Ver, supportedUserModesString, supportedChannelModesString)
|
session.Send(nil, server.name, RPL_MYINFO, d.nick, server.name, Ver, supportedUserModesString, supportedChannelModesString)
|
||||||
|
|
||||||
rb := NewResponseBuffer(session)
|
rb := NewResponseBuffer(session)
|
||||||
c.RplISupport(rb)
|
c.RplISupport(rb)
|
||||||
@ -448,14 +458,10 @@ func (server *Server) tryRegister(c *Client, session *Session) {
|
|||||||
|
|
||||||
modestring := c.ModeString()
|
modestring := c.ModeString()
|
||||||
if modestring != "+" {
|
if modestring != "+" {
|
||||||
c.Send(nil, d.nickMask, RPL_UMODEIS, d.nick, c.ModeString())
|
session.Send(nil, d.nickMask, RPL_UMODEIS, d.nick, modestring)
|
||||||
}
|
}
|
||||||
if server.logger.IsLoggingRawIO() {
|
if server.logger.IsLoggingRawIO() {
|
||||||
c.Notice(c.t("This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect."))
|
session.Send(nil, c.server.name, "NOTICE", d.nick, c.t("This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect."))
|
||||||
}
|
|
||||||
|
|
||||||
if resumed {
|
|
||||||
c.tryResumeChannels()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user