From cb0951dee862613ea978e16a43a0161b78525c72 Mon Sep 17 00:00:00 2001 From: "Brandon McGinty (chatgpt)" Date: Sun, 9 Aug 2026 14:21:35 -0400 Subject: [PATCH] Remove stale reverse channel links --- .../gumble/channel_links_regression_test.go | 27 +++++++++++++++++++ gumble/gumble/handlers.go | 11 +++++--- 2 files changed, 35 insertions(+), 3 deletions(-) create mode 100644 gumble/gumble/channel_links_regression_test.go diff --git a/gumble/gumble/channel_links_regression_test.go b/gumble/gumble/channel_links_regression_test.go new file mode 100644 index 0000000..49ba088 --- /dev/null +++ b/gumble/gumble/channel_links_regression_test.go @@ -0,0 +1,27 @@ +package gumble + +import ( + "testing" + + "git.stormux.org/storm/barnard/gumble/gumble/MumbleProto" + "google.golang.org/protobuf/proto" +) + +// Regression: a full channel-link update used to leave the removed peer's +// reverse link behind, making the client report a link that no longer exists. +func TestChannelStateFullLinksRemovesReverseLinks(t *testing.T) { + c := &Client{Config: NewConfig(), Channels: make(Channels)} + a, b, replacement := c.Channels.create(1), c.Channels.create(2), c.Channels.create(3) + a.Links[b.ID], b.Links[a.ID] = b, a + id := a.ID + data, _ := proto.Marshal(&MumbleProto.ChannelState{ChannelId: &id, Links: []uint32{replacement.ID}}) + if err := c.handleChannelState(data); err != nil { + t.Fatal(err) + } + if _, ok := b.Links[a.ID]; ok { + t.Fatal("stale reciprocal link remains") + } + if replacement.Links[a.ID] != a { + t.Fatal("replacement reciprocal link missing") + } +} diff --git a/gumble/gumble/handlers.go b/gumble/gumble/handlers.go index 5f33c37..9445314 100644 --- a/gumble/gumble/handlers.go +++ b/gumble/gumble/handlers.go @@ -463,11 +463,16 @@ func (c *Client) handleChannelState(buffer []byte) error { channel.Name = *packet.Name } if packet.Links != nil { - channel.Links = make(Channels) + // A full replacement must also remove our old reciprocal links. + for oldID, old := range channel.Links { + delete(old.Links, channel.ID) + delete(channel.Links, oldID) + } event.Type |= ChannelChangeLinks for _, channelID := range packet.Links { - if c := c.Channels[channelID]; c != nil { - channel.Links[channelID] = c + if linked := c.Channels[channelID]; linked != nil { + channel.Links[channelID] = linked + linked.Links[channel.ID] = channel } } }