Skip to content

Commit

Permalink
fix #1531
Browse files Browse the repository at this point in the history
AWAY status should be tracked per-session:

1. With auto-away enabled, away status is aggregated across sessions
   (if any session is not away, the client is not away, else use
   the away status that was set most recently)
2. With auto-away disabled, we get the legacy behavior where AWAY
   applies directly to the client
  • Loading branch information
slingamn committed Mar 18, 2021
1 parent 507d53c commit 70b2075
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 36 deletions.
4 changes: 0 additions & 4 deletions docs/MANUAL.md
Original file line number Diff line number Diff line change
Expand Up @@ -621,10 +621,6 @@ In this section, we give an overview of the modes Oragono supports.

These are the modes which can be set on you when you're connected.

### +a - Away

If this mode is set, you're marked as being away. This mode is set with the /AWAY command.

### +i - Invisible

If this mode is set, you're marked as 'invisible'. This means that your channels won't be shown when users `/WHOIS` you (except for IRC operators, they can see all the channels you're in).
Expand Down
25 changes: 11 additions & 14 deletions irc/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,6 @@ type Client struct {
accountName string // display name of the account: uncasefolded, '*' if not logged in
accountRegDate time.Time
accountSettings AccountSettings
away bool
autoAway bool
awayMessage string
brbTimer BrbTimer
channels ChannelSet
Expand Down Expand Up @@ -177,6 +175,9 @@ type Session struct {

quitMessage string

awayMessage string
awayAt time.Time

capabilities caps.Set
capState caps.State
capVersion caps.Version
Expand Down Expand Up @@ -486,8 +487,6 @@ func (server *Server) AddAlwaysOnClient(account ClientAccount, channelToStatus m
}

if persistenceEnabled(config.Accounts.Multiclient.AutoAway, client.accountSettings.AutoAway) {
client.autoAway = true
client.away = true
client.awayMessage = client.t("User is currently disconnected")
}
}
Expand Down Expand Up @@ -675,7 +674,7 @@ func (client *Client) run(session *Session) {
session.playResume()
session.resumeDetails = nil
client.brbTimer.Disable()
client.SetAway(false, "") // clear BRB message if any
session.SetAway("") // clear BRB message if any
} else {
client.playReattachMessages(session)
}
Expand Down Expand Up @@ -1458,15 +1457,13 @@ func (client *Client) destroy(session *Session) {
client.dirtyBits |= IncludeLastSeen
}

autoAway := false
becameAutoAway := false
var awayMessage string
if alwaysOn && !client.away && remainingSessions == 0 &&
persistenceEnabled(config.Accounts.Multiclient.AutoAway, client.accountSettings.AutoAway) {
autoAway = true
client.autoAway = true
client.away = true
awayMessage = config.languageManager.Translate(client.languages, `User is currently disconnected`)
client.awayMessage = awayMessage
if alwaysOn && persistenceEnabled(config.Accounts.Multiclient.AutoAway, client.accountSettings.AutoAway) {
wasAway := client.awayMessage != ""
client.setAutoAwayNoMutex(config)
awayMessage = client.awayMessage
becameAutoAway = !wasAway && awayMessage != ""
}

if client.registrationTimer != nil {
Expand Down Expand Up @@ -1523,7 +1520,7 @@ func (client *Client) destroy(session *Session) {
client.server.stats.Remove(registered, invisible, operator)
}

if autoAway {
if becameAutoAway {
dispatchAwayNotify(client, true, awayMessage)
}

Expand Down
62 changes: 46 additions & 16 deletions irc/getters.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ func (client *Client) AllSessionData(currentSession *Session, hasPrivs bool) (da
}

func (client *Client) AddSession(session *Session) (success bool, numSessions int, lastSeen time.Time, back bool) {
config := client.server.Config()
client.stateMutex.Lock()
defer client.stateMutex.Unlock()

Expand All @@ -126,11 +127,12 @@ func (client *Client) AddSession(session *Session) (success bool, numSessions in
client.setLastSeen(time.Now().UTC(), session.deviceID)
}
client.sessions = newSessions
if client.autoAway {
back = true
client.autoAway = false
client.away = false
// TODO(#1551) there should be a cap to opt out of this behavior on a session
if persistenceEnabled(config.Accounts.Multiclient.AutoAway, client.accountSettings.AutoAway) {
client.awayMessage = ""
if len(client.sessions) == 1 {
back = true
}
}
return true, len(client.sessions), lastSeen, back
}
Expand Down Expand Up @@ -196,20 +198,54 @@ func (client *Client) Hostname() string {

func (client *Client) Away() (result bool, message string) {
client.stateMutex.Lock()
result, message = client.away, client.awayMessage
message = client.awayMessage
client.stateMutex.Unlock()
result = client.awayMessage != ""
return
}

func (client *Client) SetAway(away bool, awayMessage string) (changed bool) {
func (session *Session) SetAway(awayMessage string) {
client := session.client
config := client.server.Config()

client.stateMutex.Lock()
changed = away != client.away
client.away = away
client.awayMessage = awayMessage
client.stateMutex.Unlock()
defer client.stateMutex.Unlock()

session.awayMessage = awayMessage
session.awayAt = time.Now().UTC()

autoAway := client.registered && client.alwaysOn && persistenceEnabled(config.Accounts.Multiclient.AutoAway, client.accountSettings.AutoAway)
if autoAway {
client.setAutoAwayNoMutex(config)
} else {
client.awayMessage = awayMessage
}
return
}

func (client *Client) setAutoAwayNoMutex(config *Config) {
// aggregate the away statuses of the individual sessions:
var globalAwayState string
var awaySetAt time.Time
for _, cSession := range client.sessions {
if cSession.awayMessage == "" {
// a session is active, we are not auto-away
client.awayMessage = ""
return
} else if cSession.awayAt.After(awaySetAt) {
// choose the latest available away message from any session
globalAwayState = cSession.awayMessage
awaySetAt = cSession.awayAt
}
}
if awaySetAt.IsZero() {
// no sessions, enable auto-away
client.awayMessage = config.languageManager.Translate(client.languages, `User is currently disconnected`)
} else {
client.awayMessage = globalAwayState
}
}

func (client *Client) AlwaysOn() (alwaysOn bool) {
client.stateMutex.RLock()
alwaysOn = client.registered && client.alwaysOn
Expand Down Expand Up @@ -269,12 +305,6 @@ func (client *Client) AwayMessage() (result string) {
return
}

func (client *Client) SetAwayMessage(message string) {
client.stateMutex.Lock()
client.awayMessage = message
client.stateMutex.Unlock()
}

func (client *Client) Account() string {
client.stateMutex.RLock()
defer client.stateMutex.RUnlock()
Expand Down
4 changes: 2 additions & 2 deletions irc/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -352,7 +352,7 @@ func awayHandler(server *Server, client *Client, msg ircmsg.Message, rb *Respons
}
}

client.SetAway(isAway, awayMessage)
rb.session.SetAway(awayMessage)

if isAway {
rb.Add(nil, server.name, RPL_NOWAWAY, client.nick, client.t("You have been marked as being away"))
Expand Down Expand Up @@ -439,7 +439,7 @@ func brbHandler(server *Server, client *Client, msg ircmsg.Message, rb *Response

if len(client.Sessions()) == 1 {
// true BRB
client.SetAway(true, message)
rb.session.SetAway(message)
}

return true
Expand Down

0 comments on commit 70b2075

Please sign in to comment.