fix bug with capabilities breaking after reconnect
This commit is contained in:
parent
0648fdc4f7
commit
683485f34c
116
cap.go
116
cap.go
@ -9,7 +9,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var possibleCap = []string{
|
var possibleCap = []string{
|
||||||
@ -19,89 +18,68 @@ var possibleCap = []string{
|
|||||||
"message-tags",
|
"message-tags",
|
||||||
}
|
}
|
||||||
|
|
||||||
// capHandler attempts to find out what IRCv3 capabilities the server supports.
|
// handleCAP attempts to find out what IRCv3 capabilities the server supports.
|
||||||
// This will lock further registration until we have acknowledged the
|
// This will lock further registration until we have acknowledged the
|
||||||
// capabilities.
|
// capabilities.
|
||||||
func (c *Client) capHandler() {
|
func handleCAP(c *Client, e Event) {
|
||||||
// testnet.inspircd.org may potentially be used for testing.
|
// testnet.inspircd.org may potentially be used for testing.
|
||||||
capDone := make(chan struct{})
|
|
||||||
var caps []string
|
|
||||||
|
|
||||||
possible := c.Config.SupportedCaps
|
possible := c.Config.SupportedCaps
|
||||||
possible = append(possible, possibleCap...)
|
possible = append(possible, possibleCap...)
|
||||||
|
|
||||||
cuid := c.Callbacks.sregister(true, CAP, CallbackFunc(func(client *Client, e Event) {
|
// We can assume there was a failure attempting to enable a capability.
|
||||||
// We can assume there was a failure attempting to enable a capability.
|
if len(e.Params) == 2 && e.Params[1] == CAP_NAK {
|
||||||
if len(e.Params) == 2 && e.Params[1] == CAP_NAK {
|
// Let the server know that we're done.
|
||||||
// Let the server know that we're done.
|
c.Send(&Event{Command: CAP, Params: []string{CAP_END}})
|
||||||
client.Send(&Event{Command: CAP, Params: []string{CAP_END}})
|
return
|
||||||
|
}
|
||||||
|
|
||||||
capDone <- struct{}{}
|
if len(e.Params) >= 2 && len(e.Trailing) > 1 && e.Params[1] == CAP_LS {
|
||||||
return
|
c.state.mu.Lock()
|
||||||
}
|
// Loop through and check if it's one we support.
|
||||||
|
for _, cap := range strings.Split(e.Trailing, " ") {
|
||||||
if len(e.Params) >= 2 && len(e.Trailing) > 1 && e.Params[1] == CAP_LS {
|
for i := 0; i < len(possible); i++ {
|
||||||
|
// Check if it's one that we support.
|
||||||
client.state.mu.Lock()
|
if possibleCap[i] == cap {
|
||||||
client.state.supportedCap = strings.Split(e.Trailing, " ")
|
// Ensure that there are no duplicates.
|
||||||
|
var isin bool
|
||||||
for i := 0; i < len(client.state.supportedCap); i++ {
|
for j := 0; j < len(c.state.tmpCap); j++ {
|
||||||
for j := 0; j < len(possible); j++ {
|
if c.state.tmpCap[j] == cap {
|
||||||
if client.state.supportedCap[i] == possibleCap[j] {
|
isin = true
|
||||||
// It's one for which we support.
|
break
|
||||||
caps = append(caps, client.state.supportedCap[i])
|
}
|
||||||
break
|
|
||||||
}
|
}
|
||||||
|
if !isin {
|
||||||
|
c.state.tmpCap = append(c.state.tmpCap, cap)
|
||||||
|
}
|
||||||
|
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
client.state.mu.Unlock()
|
}
|
||||||
|
c.state.mu.Unlock()
|
||||||
|
|
||||||
// Indicates if this is a multi-line LS. (2 args means it's the
|
// Indicates if this is a multi-line LS. (2 args means it's the
|
||||||
// last LS)
|
// last LS).
|
||||||
if len(e.Params) == 2 {
|
if len(e.Params) == 2 {
|
||||||
// If we support no caps, just ack the CAP message and END.
|
// If we support no caps, just ack the CAP message and END.
|
||||||
if len(caps) == 0 {
|
if len(c.state.tmpCap) == 0 {
|
||||||
client.Send(&Event{Command: CAP, Params: []string{CAP_END}})
|
c.Send(&Event{Command: CAP, Params: []string{CAP_END}})
|
||||||
capDone <- struct{}{}
|
return
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Let them know which ones we'd like to enable.
|
|
||||||
client.Send(&Event{Command: CAP, Params: []string{CAP_REQ}, Trailing: strings.Join(caps, " ")})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Let them know which ones we'd like to enable.
|
||||||
|
c.Send(&Event{Command: CAP, Params: []string{CAP_REQ}, Trailing: strings.Join(c.state.tmpCap, " ")})
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if len(e.Params) == 2 && len(e.Trailing) > 1 && e.Params[1] == CAP_ACK {
|
if len(e.Params) == 2 && len(e.Trailing) > 1 && e.Params[1] == CAP_ACK {
|
||||||
client.state.mu.Lock()
|
c.state.mu.Lock()
|
||||||
client.state.enabledCap = strings.Split(e.Trailing, " ")
|
c.state.enabledCap = strings.Split(e.Trailing, " ")
|
||||||
client.state.mu.Unlock()
|
c.state.mu.Unlock()
|
||||||
|
|
||||||
// Let the server know that we're done.
|
// Let the server know that we're done.
|
||||||
client.Send(&Event{Command: CAP, Params: []string{CAP_END}})
|
c.Send(&Event{Command: CAP, Params: []string{CAP_END}})
|
||||||
|
return
|
||||||
capDone <- struct{}{}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
|
|
||||||
unknCuid := c.Callbacks.sregister(true, ERR_UNKNOWNCOMMAND, CallbackFunc(func(client *Client, e Event) {
|
|
||||||
capDone <- struct{}{}
|
|
||||||
}))
|
|
||||||
|
|
||||||
// Ensure that the callbacks are removed when done.
|
|
||||||
defer c.Callbacks.Remove(cuid)
|
|
||||||
defer c.Callbacks.Remove(unknCuid)
|
|
||||||
|
|
||||||
// List the capabilities, specifically with the max protocol we support.
|
|
||||||
c.Send(&Event{Command: CAP, Params: []string{CAP_LS, "302"}})
|
|
||||||
|
|
||||||
select {
|
|
||||||
case <-capDone:
|
|
||||||
close(capDone)
|
|
||||||
case <-time.After(time.Second * 15):
|
|
||||||
// Wait 15 seconds, only because the server HAS YET to tell us that
|
|
||||||
// it's an unknown command, meaning it's likely doing things behind
|
|
||||||
// the scenes.
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -215,9 +215,12 @@ func (c *Client) Connect() error {
|
|||||||
// Start read loop to process messages from the server.
|
// Start read loop to process messages from the server.
|
||||||
go c.readLoop()
|
go c.readLoop()
|
||||||
|
|
||||||
// Process potential IRCv3 capabilities.
|
// List the IRCv3 capabilities, specifically with the max protocol we
|
||||||
if !c.Config.DisableCapTracking && !c.Config.DisableTracking {
|
// support.
|
||||||
go c.capHandler()
|
if !c.Config.DisableTracking && !c.Config.DisableCapTracking {
|
||||||
|
if err := c.Send(&Event{Command: CAP, Params: []string{CAP_LS, "302"}}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Consider the connection a success at this point.
|
// Consider the connection a success at this point.
|
||||||
|
@ -43,6 +43,7 @@ func (c *Client) registerHandlers() {
|
|||||||
|
|
||||||
// CAP IRCv3-specific tracking and functionality.
|
// CAP IRCv3-specific tracking and functionality.
|
||||||
if !c.Config.DisableTracking && !c.Config.DisableCapTracking {
|
if !c.Config.DisableTracking && !c.Config.DisableCapTracking {
|
||||||
|
c.Callbacks.register(true, CAP, CallbackFunc(handleCAP))
|
||||||
c.Callbacks.register(true, CAP_CHGHOST, CallbackFunc(handleCHGHOST))
|
c.Callbacks.register(true, CAP_CHGHOST, CallbackFunc(handleCHGHOST))
|
||||||
c.Callbacks.register(true, CAP_AWAY, CallbackFunc(handleAWAY))
|
c.Callbacks.register(true, CAP_AWAY, CallbackFunc(handleAWAY))
|
||||||
c.Callbacks.register(true, CAP_ACCOUNT, CallbackFunc(handleACCOUNT))
|
c.Callbacks.register(true, CAP_ACCOUNT, CallbackFunc(handleACCOUNT))
|
||||||
|
7
state.go
7
state.go
@ -40,9 +40,10 @@ type state struct {
|
|||||||
channels map[string]*Channel
|
channels map[string]*Channel
|
||||||
// enabledCap are the capabilities which are enabled for this connection.
|
// enabledCap are the capabilities which are enabled for this connection.
|
||||||
enabledCap []string
|
enabledCap []string
|
||||||
// supportedCap are the capabilties which are supporteed by the server
|
// tmpCap are the capabilties which we share with the server during the
|
||||||
// during the last capability check.
|
// last capability check. These will get sent once we have received the
|
||||||
supportedCap []string
|
// last capability list command from the server.
|
||||||
|
tmpCap []string
|
||||||
}
|
}
|
||||||
|
|
||||||
// User represents an IRC user and the state attached to them.
|
// User represents an IRC user and the state attached to them.
|
||||||
|
Loading…
Reference in New Issue
Block a user