5
0
mirror of https://github.com/cwinfo/matterbridge.git synced 2024-11-26 12:21:35 +00:00
matterbridge/vendor/layeh.com/gumble/gumble/handlers.go
2020-10-01 22:50:56 +02:00

1262 lines
29 KiB
Go

package gumble
import (
"crypto/x509"
"encoding/binary"
"errors"
"math"
"net"
"sync/atomic"
"time"
"github.com/golang/protobuf/proto"
"layeh.com/gumble/gumble/MumbleProto"
"layeh.com/gumble/gumble/varint"
)
var (
errUnimplementedHandler = errors.New("gumble: the handler has not been implemented")
errIncompleteProtobuf = errors.New("gumble: protobuf message is missing a required field")
errInvalidProtobuf = errors.New("gumble: protobuf message has an invalid field")
errUnsupportedAudio = errors.New("gumble: unsupported audio codec")
errNoCodec = errors.New("gumble: no audio codec")
)
var handlers = [...]func(*Client, []byte) error{
(*Client).handleVersion,
(*Client).handleUDPTunnel,
(*Client).handleAuthenticate,
(*Client).handlePing,
(*Client).handleReject,
(*Client).handleServerSync,
(*Client).handleChannelRemove,
(*Client).handleChannelState,
(*Client).handleUserRemove,
(*Client).handleUserState,
(*Client).handleBanList,
(*Client).handleTextMessage,
(*Client).handlePermissionDenied,
(*Client).handleACL,
(*Client).handleQueryUsers,
(*Client).handleCryptSetup,
(*Client).handleContextActionModify,
(*Client).handleContextAction,
(*Client).handleUserList,
(*Client).handleVoiceTarget,
(*Client).handlePermissionQuery,
(*Client).handleCodecVersion,
(*Client).handleUserStats,
(*Client).handleRequestBlob,
(*Client).handleServerConfig,
(*Client).handleSuggestConfig,
}
func parseVersion(packet *MumbleProto.Version) Version {
var version Version
if packet.Version != nil {
version.Version = *packet.Version
}
if packet.Release != nil {
version.Release = *packet.Release
}
if packet.Os != nil {
version.OS = *packet.Os
}
if packet.OsVersion != nil {
version.OSVersion = *packet.OsVersion
}
return version
}
func (c *Client) handleVersion(buffer []byte) error {
var packet MumbleProto.Version
if err := proto.Unmarshal(buffer, &packet); err != nil {
return err
}
return nil
}
func (c *Client) handleUDPTunnel(buffer []byte) error {
if len(buffer) < 1 {
return errInvalidProtobuf
}
audioType := (buffer[0] >> 5) & 0x7
audioTarget := buffer[0] & 0x1F
// Opus only
// TODO: add handling for other packet types
if audioType != audioCodecIDOpus {
return errUnsupportedAudio
}
// Session
buffer = buffer[1:]
session, n := varint.Decode(buffer)
if n <= 0 {
return errInvalidProtobuf
}
buffer = buffer[n:]
user := c.Users[uint32(session)]
if user == nil {
return errInvalidProtobuf
}
decoder := user.decoder
if decoder == nil {
// TODO: decoder pool
// TODO: de-reference after stream is done
codec := c.audioCodec
if codec == nil {
return errNoCodec
}
decoder = codec.NewDecoder()
user.decoder = decoder
}
// Sequence
// TODO: use in jitter buffer
_, n = varint.Decode(buffer)
if n <= 0 {
return errInvalidProtobuf
}
buffer = buffer[n:]
// Length
length, n := varint.Decode(buffer)
if n <= 0 {
return errInvalidProtobuf
}
buffer = buffer[n:]
// Opus audio packets set the 13th bit in the size field as the terminator.
audioLength := int(length) &^ 0x2000
if audioLength > len(buffer) {
return errInvalidProtobuf
}
pcm, err := decoder.Decode(buffer[:audioLength], AudioMaximumFrameSize)
if err != nil {
return err
}
event := AudioPacket{
Client: c,
Sender: user,
Target: &VoiceTarget{
ID: uint32(audioTarget),
},
AudioBuffer: AudioBuffer(pcm),
}
if len(buffer)-audioLength == 3*4 {
// the packet has positional audio data; 3x float32
buffer = buffer[audioLength:]
event.X = math.Float32frombits(binary.LittleEndian.Uint32(buffer))
event.Y = math.Float32frombits(binary.LittleEndian.Uint32(buffer[4:]))
event.Z = math.Float32frombits(binary.LittleEndian.Uint32(buffer[8:]))
event.HasPosition = true
}
c.volatile.Lock()
for item := c.Config.AudioListeners.head; item != nil; item = item.next {
c.volatile.Unlock()
ch := item.streams[user]
if ch == nil {
ch = make(chan *AudioPacket)
item.streams[user] = ch
event := AudioStreamEvent{
Client: c,
User: user,
C: ch,
}
item.listener.OnAudioStream(&event)
}
ch <- &event
c.volatile.Lock()
}
c.volatile.Unlock()
return nil
}
func (c *Client) handleAuthenticate(buffer []byte) error {
return errUnimplementedHandler
}
func (c *Client) handlePing(buffer []byte) error {
var packet MumbleProto.Ping
if err := proto.Unmarshal(buffer, &packet); err != nil {
return err
}
atomic.AddUint32(&c.tcpPacketsReceived, 1)
if packet.Timestamp != nil {
diff := time.Since(time.Unix(0, int64(*packet.Timestamp)))
index := int(c.tcpPacketsReceived) - 1
if index >= len(c.tcpPingTimes) {
for i := 1; i < len(c.tcpPingTimes); i++ {
c.tcpPingTimes[i-1] = c.tcpPingTimes[i]
}
index = len(c.tcpPingTimes) - 1
}
// average is in milliseconds
ping := float32(diff.Seconds() * 1000)
c.tcpPingTimes[index] = ping
var sum float32
for i := 0; i <= index; i++ {
sum += c.tcpPingTimes[i]
}
avg := sum / float32(index+1)
sum = 0
for i := 0; i <= index; i++ {
sum += (avg - c.tcpPingTimes[i]) * (avg - c.tcpPingTimes[i])
}
variance := sum / float32(index+1)
atomic.StoreUint32(&c.tcpPingAvg, math.Float32bits(avg))
atomic.StoreUint32(&c.tcpPingVar, math.Float32bits(variance))
}
return nil
}
func (c *Client) handleReject(buffer []byte) error {
var packet MumbleProto.Reject
if err := proto.Unmarshal(buffer, &packet); err != nil {
return err
}
if c.State() != StateConnected {
return errInvalidProtobuf
}
err := &RejectError{}
if packet.Type != nil {
err.Type = RejectType(*packet.Type)
}
if packet.Reason != nil {
err.Reason = *packet.Reason
}
c.connect <- err
c.Conn.Close()
return nil
}
func (c *Client) handleServerSync(buffer []byte) error {
var packet MumbleProto.ServerSync
if err := proto.Unmarshal(buffer, &packet); err != nil {
return err
}
event := ConnectEvent{
Client: c,
}
if packet.Session != nil {
{
c.volatile.Lock()
c.Self = c.Users[*packet.Session]
c.volatile.Unlock()
}
}
if packet.WelcomeText != nil {
event.WelcomeMessage = packet.WelcomeText
}
if packet.MaxBandwidth != nil {
val := int(*packet.MaxBandwidth)
event.MaximumBitrate = &val
}
atomic.StoreUint32(&c.state, uint32(StateSynced))
c.Config.Listeners.onConnect(&event)
close(c.connect)
return nil
}
func (c *Client) handleChannelRemove(buffer []byte) error {
var packet MumbleProto.ChannelRemove
if err := proto.Unmarshal(buffer, &packet); err != nil {
return err
}
if packet.ChannelId == nil {
return errIncompleteProtobuf
}
var channel *Channel
{
c.volatile.Lock()
channelID := *packet.ChannelId
channel = c.Channels[channelID]
if channel == nil {
c.volatile.Unlock()
return errInvalidProtobuf
}
channel.client = nil
delete(c.Channels, channelID)
delete(c.permissions, channelID)
if parent := channel.Parent; parent != nil {
delete(parent.Children, channel.ID)
}
for _, link := range channel.Links {
delete(link.Links, channelID)
}
c.volatile.Unlock()
}
if c.State() == StateSynced {
event := ChannelChangeEvent{
Client: c,
Type: ChannelChangeRemoved,
Channel: channel,
}
c.Config.Listeners.onChannelChange(&event)
}
return nil
}
func (c *Client) handleChannelState(buffer []byte) error {
var packet MumbleProto.ChannelState
if err := proto.Unmarshal(buffer, &packet); err != nil {
return err
}
if packet.ChannelId == nil {
return errIncompleteProtobuf
}
event := ChannelChangeEvent{
Client: c,
}
{
c.volatile.Lock()
channelID := *packet.ChannelId
channel := c.Channels[channelID]
if channel == nil {
channel = c.Channels.create(channelID)
channel.client = c
event.Type |= ChannelChangeCreated
}
event.Channel = channel
if packet.Parent != nil {
if channel.Parent != nil {
delete(channel.Parent.Children, channelID)
}
newParent := c.Channels[*packet.Parent]
if newParent != channel.Parent {
event.Type |= ChannelChangeMoved
}
channel.Parent = newParent
if channel.Parent != nil {
channel.Parent.Children[channel.ID] = channel
}
}
if packet.Name != nil {
if *packet.Name != channel.Name {
event.Type |= ChannelChangeName
}
channel.Name = *packet.Name
}
if packet.Links != nil {
channel.Links = make(Channels)
event.Type |= ChannelChangeLinks
for _, channelID := range packet.Links {
if c := c.Channels[channelID]; c != nil {
channel.Links[channelID] = c
}
}
}
for _, channelID := range packet.LinksAdd {
if c := c.Channels[channelID]; c != nil {
event.Type |= ChannelChangeLinks
channel.Links[channelID] = c
c.Links[channel.ID] = channel
}
}
for _, channelID := range packet.LinksRemove {
if c := c.Channels[channelID]; c != nil {
event.Type |= ChannelChangeLinks
delete(channel.Links, channelID)
delete(c.Links, channel.ID)
}
}
if packet.Description != nil {
if *packet.Description != channel.Description {
event.Type |= ChannelChangeDescription
}
channel.Description = *packet.Description
channel.DescriptionHash = nil
}
if packet.Temporary != nil {
channel.Temporary = *packet.Temporary
}
if packet.Position != nil {
if *packet.Position != channel.Position {
event.Type |= ChannelChangePosition
}
channel.Position = *packet.Position
}
if packet.DescriptionHash != nil {
event.Type |= ChannelChangeDescription
channel.DescriptionHash = packet.DescriptionHash
channel.Description = ""
}
if packet.MaxUsers != nil {
event.Type |= ChannelChangeMaxUsers
channel.MaxUsers = *packet.MaxUsers
}
c.volatile.Unlock()
}
if c.State() == StateSynced {
c.Config.Listeners.onChannelChange(&event)
}
return nil
}
func (c *Client) handleUserRemove(buffer []byte) error {
var packet MumbleProto.UserRemove
if err := proto.Unmarshal(buffer, &packet); err != nil {
return err
}
if packet.Session == nil {
return errIncompleteProtobuf
}
event := UserChangeEvent{
Client: c,
Type: UserChangeDisconnected,
}
{
c.volatile.Lock()
session := *packet.Session
event.User = c.Users[session]
if event.User == nil {
c.volatile.Unlock()
return errInvalidProtobuf
}
if packet.Actor != nil {
event.Actor = c.Users[*packet.Actor]
if event.Actor == nil {
c.volatile.Unlock()
return errInvalidProtobuf
}
event.Type |= UserChangeKicked
}
event.User.client = nil
if event.User.Channel != nil {
delete(event.User.Channel.Users, session)
}
delete(c.Users, session)
if packet.Reason != nil {
event.String = *packet.Reason
}
if packet.Ban != nil && *packet.Ban {
event.Type |= UserChangeBanned
}
if event.User == c.Self {
if packet.Ban != nil && *packet.Ban {
c.disconnectEvent.Type = DisconnectBanned
} else {
c.disconnectEvent.Type = DisconnectKicked
}
}
c.volatile.Unlock()
}
if c.State() == StateSynced {
c.Config.Listeners.onUserChange(&event)
}
return nil
}
func (c *Client) handleUserState(buffer []byte) error {
var packet MumbleProto.UserState
if err := proto.Unmarshal(buffer, &packet); err != nil {
return err
}
if packet.Session == nil {
return errIncompleteProtobuf
}
event := UserChangeEvent{
Client: c,
}
var user, actor *User
{
c.volatile.Lock()
session := *packet.Session
user = c.Users[session]
if user == nil {
user = c.Users.create(session)
user.Channel = c.Channels[0]
user.client = c
event.Type |= UserChangeConnected
if user.Channel == nil {
c.volatile.Unlock()
return errInvalidProtobuf
}
event.Type |= UserChangeChannel
user.Channel.Users[session] = user
}
event.User = user
if packet.Actor != nil {
actor = c.Users[*packet.Actor]
if actor == nil {
c.volatile.Unlock()
return errInvalidProtobuf
}
event.Actor = actor
}
if packet.Name != nil {
if *packet.Name != user.Name {
event.Type |= UserChangeName
}
user.Name = *packet.Name
}
if packet.UserId != nil {
if *packet.UserId != user.UserID && !event.Type.Has(UserChangeConnected) {
if *packet.UserId != math.MaxUint32 {
event.Type |= UserChangeRegistered
user.UserID = *packet.UserId
} else {
event.Type |= UserChangeUnregistered
user.UserID = 0
}
} else {
user.UserID = *packet.UserId
}
}
if packet.ChannelId != nil {
if user.Channel != nil {
delete(user.Channel.Users, user.Session)
}
newChannel := c.Channels[*packet.ChannelId]
if newChannel == nil {
c.volatile.Lock()
return errInvalidProtobuf
}
if newChannel != user.Channel {
event.Type |= UserChangeChannel
user.Channel = newChannel
}
user.Channel.Users[user.Session] = user
}
if packet.Mute != nil {
if *packet.Mute != user.Muted {
event.Type |= UserChangeAudio
}
user.Muted = *packet.Mute
}
if packet.Deaf != nil {
if *packet.Deaf != user.Deafened {
event.Type |= UserChangeAudio
}
user.Deafened = *packet.Deaf
}
if packet.Suppress != nil {
if *packet.Suppress != user.Suppressed {
event.Type |= UserChangeAudio
}
user.Suppressed = *packet.Suppress
}
if packet.SelfMute != nil {
if *packet.SelfMute != user.SelfMuted {
event.Type |= UserChangeAudio
}
user.SelfMuted = *packet.SelfMute
}
if packet.SelfDeaf != nil {
if *packet.SelfDeaf != user.SelfDeafened {
event.Type |= UserChangeAudio
}
user.SelfDeafened = *packet.SelfDeaf
}
if packet.Texture != nil {
event.Type |= UserChangeTexture
user.Texture = packet.Texture
user.TextureHash = nil
}
if packet.Comment != nil {
if *packet.Comment != user.Comment {
event.Type |= UserChangeComment
}
user.Comment = *packet.Comment
user.CommentHash = nil
}
if packet.Hash != nil {
user.Hash = *packet.Hash
}
if packet.CommentHash != nil {
event.Type |= UserChangeComment
user.CommentHash = packet.CommentHash
user.Comment = ""
}
if packet.TextureHash != nil {
event.Type |= UserChangeTexture
user.TextureHash = packet.TextureHash
user.Texture = nil
}
if packet.PrioritySpeaker != nil {
if *packet.PrioritySpeaker != user.PrioritySpeaker {
event.Type |= UserChangePrioritySpeaker
}
user.PrioritySpeaker = *packet.PrioritySpeaker
}
if packet.Recording != nil {
if *packet.Recording != user.Recording {
event.Type |= UserChangeRecording
}
user.Recording = *packet.Recording
}
c.volatile.Unlock()
}
if c.State() == StateSynced {
c.Config.Listeners.onUserChange(&event)
}
return nil
}
func (c *Client) handleBanList(buffer []byte) error {
var packet MumbleProto.BanList
if err := proto.Unmarshal(buffer, &packet); err != nil {
return err
}
event := BanListEvent{
Client: c,
BanList: make(BanList, 0, len(packet.Bans)),
}
for _, banPacket := range packet.Bans {
ban := &Ban{
Address: net.IP(banPacket.Address),
}
if banPacket.Mask != nil {
size := net.IPv4len * 8
if len(ban.Address) == net.IPv6len {
size = net.IPv6len * 8
}
ban.Mask = net.CIDRMask(int(*banPacket.Mask), size)
}
if banPacket.Name != nil {
ban.Name = *banPacket.Name
}
if banPacket.Hash != nil {
ban.Hash = *banPacket.Hash
}
if banPacket.Reason != nil {
ban.Reason = *banPacket.Reason
}
if banPacket.Start != nil {
ban.Start, _ = time.Parse(time.RFC3339, *banPacket.Start)
}
if banPacket.Duration != nil {
ban.Duration = time.Duration(*banPacket.Duration) * time.Second
}
event.BanList = append(event.BanList, ban)
}
c.Config.Listeners.onBanList(&event)
return nil
}
func (c *Client) handleTextMessage(buffer []byte) error {
var packet MumbleProto.TextMessage
if err := proto.Unmarshal(buffer, &packet); err != nil {
return err
}
event := TextMessageEvent{
Client: c,
}
if packet.Actor != nil {
event.Sender = c.Users[*packet.Actor]
}
if packet.Session != nil {
event.Users = make([]*User, 0, len(packet.Session))
for _, session := range packet.Session {
if user := c.Users[session]; user != nil {
event.Users = append(event.Users, user)
}
}
}
if packet.ChannelId != nil {
event.Channels = make([]*Channel, 0, len(packet.ChannelId))
for _, id := range packet.ChannelId {
if channel := c.Channels[id]; channel != nil {
event.Channels = append(event.Channels, channel)
}
}
}
if packet.TreeId != nil {
event.Trees = make([]*Channel, 0, len(packet.TreeId))
for _, id := range packet.TreeId {
if channel := c.Channels[id]; channel != nil {
event.Trees = append(event.Trees, channel)
}
}
}
if packet.Message != nil {
event.Message = *packet.Message
}
c.Config.Listeners.onTextMessage(&event)
return nil
}
func (c *Client) handlePermissionDenied(buffer []byte) error {
var packet MumbleProto.PermissionDenied
if err := proto.Unmarshal(buffer, &packet); err != nil {
return err
}
if packet.Type == nil || *packet.Type == MumbleProto.PermissionDenied_H9K {
return errInvalidProtobuf
}
event := PermissionDeniedEvent{
Client: c,
Type: PermissionDeniedType(*packet.Type),
}
if packet.Reason != nil {
event.String = *packet.Reason
}
if packet.Name != nil {
event.String = *packet.Name
}
if packet.Session != nil {
event.User = c.Users[*packet.Session]
if event.User == nil {
return errInvalidProtobuf
}
}
if packet.ChannelId != nil {
event.Channel = c.Channels[*packet.ChannelId]
if event.Channel == nil {
return errInvalidProtobuf
}
}
if packet.Permission != nil {
event.Permission = Permission(*packet.Permission)
}
c.Config.Listeners.onPermissionDenied(&event)
return nil
}
func (c *Client) handleACL(buffer []byte) error {
var packet MumbleProto.ACL
if err := proto.Unmarshal(buffer, &packet); err != nil {
return err
}
acl := &ACL{
Inherits: packet.GetInheritAcls(),
}
if packet.ChannelId == nil {
return errInvalidProtobuf
}
acl.Channel = c.Channels[*packet.ChannelId]
if acl.Channel == nil {
return errInvalidProtobuf
}
if packet.Groups != nil {
acl.Groups = make([]*ACLGroup, 0, len(packet.Groups))
for _, group := range packet.Groups {
aclGroup := &ACLGroup{
Name: *group.Name,
Inherited: group.GetInherited(),
InheritUsers: group.GetInherit(),
Inheritable: group.GetInheritable(),
}
if group.Add != nil {
aclGroup.UsersAdd = make(map[uint32]*ACLUser)
for _, userID := range group.Add {
aclGroup.UsersAdd[userID] = &ACLUser{
UserID: userID,
}
}
}
if group.Remove != nil {
aclGroup.UsersRemove = make(map[uint32]*ACLUser)
for _, userID := range group.Remove {
aclGroup.UsersRemove[userID] = &ACLUser{
UserID: userID,
}
}
}
if group.InheritedMembers != nil {
aclGroup.UsersInherited = make(map[uint32]*ACLUser)
for _, userID := range group.InheritedMembers {
aclGroup.UsersInherited[userID] = &ACLUser{
UserID: userID,
}
}
}
acl.Groups = append(acl.Groups, aclGroup)
}
}
if packet.Acls != nil {
acl.Rules = make([]*ACLRule, 0, len(packet.Acls))
for _, rule := range packet.Acls {
aclRule := &ACLRule{
AppliesCurrent: rule.GetApplyHere(),
AppliesChildren: rule.GetApplySubs(),
Inherited: rule.GetInherited(),
Granted: Permission(rule.GetGrant()),
Denied: Permission(rule.GetDeny()),
}
if rule.UserId != nil {
aclRule.User = &ACLUser{
UserID: *rule.UserId,
}
} else if rule.Group != nil {
var group *ACLGroup
for _, g := range acl.Groups {
if g.Name == *rule.Group {
group = g
break
}
}
if group == nil {
group = &ACLGroup{
Name: *rule.Group,
}
}
aclRule.Group = group
}
acl.Rules = append(acl.Rules, aclRule)
}
}
c.tmpACL = acl
return nil
}
func (c *Client) handleQueryUsers(buffer []byte) error {
var packet MumbleProto.QueryUsers
if err := proto.Unmarshal(buffer, &packet); err != nil {
return err
}
acl := c.tmpACL
if acl == nil {
return errIncompleteProtobuf
}
c.tmpACL = nil
userMap := make(map[uint32]string)
for i := 0; i < len(packet.Ids) && i < len(packet.Names); i++ {
userMap[packet.Ids[i]] = packet.Names[i]
}
for _, group := range acl.Groups {
for _, user := range group.UsersAdd {
user.Name = userMap[user.UserID]
}
for _, user := range group.UsersRemove {
user.Name = userMap[user.UserID]
}
for _, user := range group.UsersInherited {
user.Name = userMap[user.UserID]
}
}
for _, rule := range acl.Rules {
if rule.User != nil {
rule.User.Name = userMap[rule.User.UserID]
}
}
event := ACLEvent{
Client: c,
ACL: acl,
}
c.Config.Listeners.onACL(&event)
return nil
}
func (c *Client) handleCryptSetup(buffer []byte) error {
return errUnimplementedHandler
}
func (c *Client) handleContextActionModify(buffer []byte) error {
var packet MumbleProto.ContextActionModify
if err := proto.Unmarshal(buffer, &packet); err != nil {
return err
}
if packet.Action == nil || packet.Operation == nil {
return errInvalidProtobuf
}
event := ContextActionChangeEvent{
Client: c,
}
{
c.volatile.Lock()
switch *packet.Operation {
case MumbleProto.ContextActionModify_Add:
if ca := c.ContextActions[*packet.Action]; ca != nil {
c.volatile.Unlock()
return nil
}
event.Type = ContextActionAdd
contextAction := c.ContextActions.create(*packet.Action)
if packet.Text != nil {
contextAction.Label = *packet.Text
}
if packet.Context != nil {
contextAction.Type = ContextActionType(*packet.Context)
}
event.ContextAction = contextAction
case MumbleProto.ContextActionModify_Remove:
contextAction := c.ContextActions[*packet.Action]
if contextAction == nil {
c.volatile.Unlock()
return nil
}
event.Type = ContextActionRemove
delete(c.ContextActions, *packet.Action)
event.ContextAction = contextAction
default:
c.volatile.Unlock()
return errInvalidProtobuf
}
c.volatile.Unlock()
}
c.Config.Listeners.onContextActionChange(&event)
return nil
}
func (c *Client) handleContextAction(buffer []byte) error {
return errUnimplementedHandler
}
func (c *Client) handleUserList(buffer []byte) error {
var packet MumbleProto.UserList
if err := proto.Unmarshal(buffer, &packet); err != nil {
return err
}
event := UserListEvent{
Client: c,
UserList: make(RegisteredUsers, 0, len(packet.Users)),
}
for _, user := range packet.Users {
registeredUser := &RegisteredUser{
UserID: *user.UserId,
}
if user.Name != nil {
registeredUser.Name = *user.Name
}
if user.LastSeen != nil {
registeredUser.LastSeen, _ = time.ParseInLocation(time.RFC3339, *user.LastSeen, nil)
}
if user.LastChannel != nil {
if lastChannel := c.Channels[*user.LastChannel]; lastChannel != nil {
registeredUser.LastChannel = lastChannel
}
}
event.UserList = append(event.UserList, registeredUser)
}
c.Config.Listeners.onUserList(&event)
return nil
}
func (c *Client) handleVoiceTarget(buffer []byte) error {
return errUnimplementedHandler
}
func (c *Client) handlePermissionQuery(buffer []byte) error {
var packet MumbleProto.PermissionQuery
if err := proto.Unmarshal(buffer, &packet); err != nil {
return err
}
var singleChannel *Channel
if packet.ChannelId != nil && packet.Permissions != nil {
singleChannel = c.Channels[*packet.ChannelId]
if singleChannel == nil {
return errInvalidProtobuf
}
}
var changedChannels []*Channel
{
c.volatile.Lock()
if packet.GetFlush() {
oldPermissions := c.permissions
c.permissions = make(map[uint32]*Permission)
changedChannels = make([]*Channel, 0, len(oldPermissions))
for channelID := range oldPermissions {
changedChannels = append(changedChannels, c.Channels[channelID])
}
}
if singleChannel != nil {
p := Permission(*packet.Permissions)
c.permissions[singleChannel.ID] = &p
changedChannels = append(changedChannels, singleChannel)
}
c.volatile.Unlock()
}
for _, channel := range changedChannels {
event := ChannelChangeEvent{
Client: c,
Type: ChannelChangePermission,
Channel: channel,
}
c.Config.Listeners.onChannelChange(&event)
}
return nil
}
func (c *Client) handleCodecVersion(buffer []byte) error {
var packet MumbleProto.CodecVersion
if err := proto.Unmarshal(buffer, &packet); err != nil {
return err
}
event := ServerConfigEvent{
Client: c,
}
event.CodecAlpha = packet.Alpha
event.CodecBeta = packet.Beta
{
val := packet.GetPreferAlpha()
event.CodecPreferAlpha = &val
}
{
val := packet.GetOpus()
event.CodecOpus = &val
}
var codec AudioCodec
switch {
case *event.CodecOpus:
codec = getAudioCodec(audioCodecIDOpus)
}
if codec != nil {
c.audioCodec = codec
{
c.volatile.Lock()
c.AudioEncoder = codec.NewEncoder()
c.volatile.Unlock()
}
}
c.Config.Listeners.onServerConfig(&event)
return nil
}
func (c *Client) handleUserStats(buffer []byte) error {
var packet MumbleProto.UserStats
if err := proto.Unmarshal(buffer, &packet); err != nil {
return err
}
if packet.Session == nil {
return errIncompleteProtobuf
}
user := c.Users[*packet.Session]
if user == nil {
return errInvalidProtobuf
}
{
c.volatile.Lock()
if user.Stats == nil {
user.Stats = &UserStats{}
}
*user.Stats = UserStats{
User: user,
}
stats := user.Stats
if packet.FromClient != nil {
if packet.FromClient.Good != nil {
stats.FromClient.Good = *packet.FromClient.Good
}
if packet.FromClient.Late != nil {
stats.FromClient.Late = *packet.FromClient.Late
}
if packet.FromClient.Lost != nil {
stats.FromClient.Lost = *packet.FromClient.Lost
}
if packet.FromClient.Resync != nil {
stats.FromClient.Resync = *packet.FromClient.Resync
}
}
if packet.FromServer != nil {
if packet.FromServer.Good != nil {
stats.FromServer.Good = *packet.FromServer.Good
}
if packet.FromClient.Late != nil {
stats.FromServer.Late = *packet.FromServer.Late
}
if packet.FromClient.Lost != nil {
stats.FromServer.Lost = *packet.FromServer.Lost
}
if packet.FromClient.Resync != nil {
stats.FromServer.Resync = *packet.FromServer.Resync
}
}
if packet.UdpPackets != nil {
stats.UDPPackets = *packet.UdpPackets
}
if packet.UdpPingAvg != nil {
stats.UDPPingAverage = *packet.UdpPingAvg
}
if packet.UdpPingVar != nil {
stats.UDPPingVariance = *packet.UdpPingVar
}
if packet.TcpPackets != nil {
stats.TCPPackets = *packet.TcpPackets
}
if packet.TcpPingAvg != nil {
stats.TCPPingAverage = *packet.TcpPingAvg
}
if packet.TcpPingVar != nil {
stats.TCPPingVariance = *packet.TcpPingVar
}
if packet.Version != nil {
stats.Version = parseVersion(packet.Version)
}
if packet.Onlinesecs != nil {
stats.Connected = time.Now().Add(time.Duration(*packet.Onlinesecs) * -time.Second)
}
if packet.Idlesecs != nil {
stats.Idle = time.Duration(*packet.Idlesecs) * time.Second
}
if packet.Bandwidth != nil {
stats.Bandwidth = int(*packet.Bandwidth)
}
if packet.Address != nil {
stats.IP = net.IP(packet.Address)
}
if packet.Certificates != nil {
stats.Certificates = make([]*x509.Certificate, 0, len(packet.Certificates))
for _, data := range packet.Certificates {
if data != nil {
if cert, err := x509.ParseCertificate(data); err == nil {
stats.Certificates = append(stats.Certificates, cert)
}
}
}
}
stats.StrongCertificate = packet.GetStrongCertificate()
stats.CELTVersions = packet.GetCeltVersions()
if packet.Opus != nil {
stats.Opus = *packet.Opus
}
c.volatile.Unlock()
}
event := UserChangeEvent{
Client: c,
Type: UserChangeStats,
User: user,
}
c.Config.Listeners.onUserChange(&event)
return nil
}
func (c *Client) handleRequestBlob(buffer []byte) error {
return errUnimplementedHandler
}
func (c *Client) handleServerConfig(buffer []byte) error {
var packet MumbleProto.ServerConfig
if err := proto.Unmarshal(buffer, &packet); err != nil {
return err
}
event := ServerConfigEvent{
Client: c,
}
if packet.MaxBandwidth != nil {
val := int(*packet.MaxBandwidth)
event.MaximumBitrate = &val
}
if packet.WelcomeText != nil {
event.WelcomeMessage = packet.WelcomeText
}
if packet.AllowHtml != nil {
event.AllowHTML = packet.AllowHtml
}
if packet.MessageLength != nil {
val := int(*packet.MessageLength)
event.MaximumMessageLength = &val
}
if packet.ImageMessageLength != nil {
val := int(*packet.ImageMessageLength)
event.MaximumImageMessageLength = &val
}
if packet.MaxUsers != nil {
val := int(*packet.MaxUsers)
event.MaximumUsers = &val
}
c.Config.Listeners.onServerConfig(&event)
return nil
}
func (c *Client) handleSuggestConfig(buffer []byte) error {
var packet MumbleProto.SuggestConfig
if err := proto.Unmarshal(buffer, &packet); err != nil {
return err
}
event := ServerConfigEvent{
Client: c,
}
if packet.Version != nil {
event.SuggestVersion = &Version{
Version: packet.GetVersion(),
}
}
if packet.Positional != nil {
event.SuggestPositional = packet.Positional
}
if packet.PushToTalk != nil {
event.SuggestPushToTalk = packet.PushToTalk
}
c.Config.Listeners.onServerConfig(&event)
return nil
}