// set sets the given items using the given conflict resolution policy. // The returned slice will have the same length as the input slice. // If value is not nil, each element should correspond to an item. func set(c appengine.Context, item []*Item, value [][]byte, policy int32) []os.Error { req := &pb.MemcacheSetRequest{ Item: make([]*pb.MemcacheSetRequest_Item, len(item)), } for i, t := range item { p := &pb.MemcacheSetRequest_Item{ Key: []byte(t.Key), } if value == nil { p.Value = t.Value } else { p.Value = value[i] } if t.Flags != 0 { p.Flags = proto.Uint32(t.Flags) } if t.Expiration != 0 { // In the .proto file, MemcacheSetRequest_Item uses a fixed32 (i.e. unsigned) // for expiration time, while MemcacheGetRequest_Item uses int32 (i.e. signed). // Throughout this .go file, we use int32. p.ExpirationTime = proto.Uint32(uint32(t.Expiration)) } if t.casID != 0 { p.CasId = proto.Uint64(t.casID) p.ForCas = proto.Bool(true) } p.SetPolicy = pb.NewMemcacheSetRequest_SetPolicy(policy) req.Item[i] = p } res := &pb.MemcacheSetResponse{} e := make([]os.Error, len(item)) if err := c.Call("memcache", "Set", req, res); err != nil { for i := range e { e[i] = err } return e } if len(e) != len(res.SetStatus) { for i := range e { e[i] = ErrServerError } return e } for i := range e { switch res.SetStatus[i] { case pb.MemcacheSetResponse_STORED: e[i] = nil case pb.MemcacheSetResponse_NOT_STORED: e[i] = ErrNotStored case pb.MemcacheSetResponse_EXISTS: e[i] = ErrCASConflict default: e[i] = ErrServerError } } return e }
func interface2value(data interface{}) *TransactionValue { switch f := data.(type) { case string: return &TransactionValue{ Type: proto.Uint32(val_string), StringValue: proto.String(f), } } return &TransactionValue{ Type: proto.Uint32(val_null), } }
// Send permission denied by who, what, where func (c *Client) sendPermissionDenied(who *Client, where *Channel, what Permission) { d, err := proto.Marshal(&mumbleproto.PermissionDenied{ Permission: proto.Uint32(uint32(what)), ChannelId: proto.Uint32(uint32(where.Id)), Session: proto.Uint32(who.Session), Type: mumbleproto.NewPermissionDenied_DenyType(mumbleproto.PermissionDenied_Permission), }) if err != nil { c.Panic(err.String()) } c.msgchan <- &Message{ buf: d, kind: MessagePermissionDenied, } }
// // Get // func (op *TransactionOperation_Get) executeTransaction(t *Transaction, b *TransactionBlock, vs *viewState) (ret *TransactionReturn) { // TODO: Walk! var value *TransactionValue if op.Source.Variable != nil { sourceVar := b.getRealVar(op.Source.Variable.Variable) value = sourceVar.Value } else if op.Source.Object != nil { // make sure we don't use variables anymore for _, ac := range op.Accessors { ac.MakeAbsoluteValue(b) } obj, osErr := vs.getObject(op.Source.Object.Container.Value().(string), op.Source.Object.Key.Value().(string), true) if osErr != nil { return &TransactionReturn{ Error: &TransactionError{ Id: proto.Uint32(0), // TODO: ERRNO Message: proto.String(osErr.String()), }, } } value = interface2value(obj.data) } destVar := b.getRealVar(op.Destination) destVar.Value = value return }
func (b *TransactionBlock) Return(data ...interface{}) { op := &TransactionOperation{ Type: proto.Uint32(op_return), Return: &TransactionOperation_Return{}, } op.Return.Returns = make([]*TransactionObject, len(data)) for i, obj := range data { if vr, ok := obj.(*TransactionVariable); ok { op.Return.Returns[i] = &TransactionObject{ Variable: vr, } } else { val := interface2value(obj) if val == nil { panic("Unknown return object") } op.Return.Returns[i] = &TransactionObject{ Value: val, } } } b.addOperation(op) }
// // Set // func (op *TransactionOperation_Set) executeTransaction(t *Transaction, b *TransactionBlock, vs *viewState) (ret *TransactionReturn) { // TODO: Walk! // set into a variable if op.Destination.Variable != nil { v := b.getRealVar(op.Destination.Variable.Variable) if op.Value.Value != nil { v.Value = op.Value.Value } else { valvar := b.getRealVar(op.Value.Variable) v.Value = valvar.Value } // set into a database object } else if op.Destination.Object != nil { // make sure we don't use variables anymore for _, ac := range op.Accessors { ac.MakeAbsoluteValue(b) } op.Value.MakeAbsoluteValue(b) partial := false // TODO: if walk, partial = true osErr := vs.mutateObject(op, partial) if osErr != nil { return &TransactionReturn{ Error: &TransactionError{ Id: proto.Uint32(0), // TODO: ERRNO Message: proto.String(osErr.String()), }, } } } return }
// Broadcast text messages func (server *Server) handleTextMessage(client *Client, msg *Message) { txtmsg := &mumbleproto.TextMessage{} err := proto.Unmarshal(msg.buf, txtmsg) if err != nil { client.Panic(err.String()) return } // fixme(mkrautz): Check text message length. // fixme(mkrautz): Sanitize text as well. clients := make(map[uint32]*Client) // Tree for _, chanid := range txtmsg.TreeId { if channel, ok := server.Channels[int(chanid)]; ok { if !server.HasPermission(client, channel, TextMessagePermission) { client.sendPermissionDenied(client, channel, TextMessagePermission) } for _, target := range channel.clients { clients[target.Session] = target } } } // Direct-to-channel for _, chanid := range txtmsg.ChannelId { if channel, ok := server.Channels[int(chanid)]; ok { if !server.HasPermission(client, channel, TextMessagePermission) { client.sendPermissionDenied(client, channel, TextMessagePermission) return } for _, target := range channel.clients { clients[target.Session] = target } } } // Direct-to-clients for _, session := range txtmsg.Session { if target, ok := server.clients[session]; ok { if !server.HasPermission(client, target.Channel, TextMessagePermission) { client.sendPermissionDenied(client, target.Channel, TextMessagePermission) return } clients[session] = target } } // Remove ourselves clients[client.Session] = nil, false for _, target := range clients { target.sendProtoMessage(MessageTextMessage, &mumbleproto.TextMessage{ Actor: proto.Uint32(client.Session), Message: txtmsg.Message, }) } }
func makeConnect() (msg *protocol.Message) { connect := &protocol.Connect{Version: proto.Uint32(ProtocolVersion)} return &protocol.Message{ Connect: connect, Type: protocol.NewMessage_Type(protocol.Message_CONNECT), } }
func (b *TransactionBlock) NewVar() *TransactionVariable { v := new(TransactionVariable) // TODO: we should only add variables that have values v.Block = b.Id v.Id = proto.Uint32(uint32(len(b.Variables))) b.Variables = append(b.Variables, v) return v }
func (server *Server) handlePingMessage(client *Client, msg *Message) { ping := &mumbleproto.Ping{} err := proto.Unmarshal(msg.buf, ping) if err != nil { client.Panic(err.String()) return } // Phony response for ping messages. We don't keep stats // for this yet. client.sendProtoMessage(MessagePing, &mumbleproto.Ping{ Timestamp: ping.Timestamp, Good: proto.Uint32(uint32(client.crypt.Good)), Late: proto.Uint32(uint32(client.crypt.Late)), Lost: proto.Uint32(uint32(client.crypt.Lost)), Resync: proto.Uint32(uint32(client.crypt.Resync)), }) }
// Send a client its permissions for channel. func (server *Server) sendClientPermissions(client *Client, channel *Channel) { // No caching for SuperUser if client.IsSuperUser() { return } // Update cache server.HasPermission(client, channel, EnterPermission) perm := server.aclcache.GetPermission(client, channel) log.Printf("Permissions = 0x%x", perm) // fixme(mkrautz): Cache which permissions we've already sent. client.sendProtoMessage(MessagePermissionQuery, &mumbleproto.PermissionQuery{ ChannelId: proto.Uint32(uint32(channel.Id)), Permissions: proto.Uint32(uint32(perm)), }) }
func (client *Client) sendChannelTree(channel *Channel) { chanstate := &mumbleproto.ChannelState{ ChannelId: proto.Uint32(uint32(channel.Id)), Name: proto.String(channel.Name), } if channel.parent != nil { chanstate.Parent = proto.Uint32(uint32(channel.parent.Id)) } if channel.HasDescription() { if client.Version >= 0x10202 { chanstate.DescriptionHash = channel.DescriptionBlobHashBytes() } else { buf, err := globalBlobstore.Get(channel.DescriptionBlob) if err != nil { panic("Blobstore error.") } chanstate.Description = proto.String(string(buf)) } } if channel.Temporary { chanstate.Temporary = proto.Bool(true) } chanstate.Position = proto.Int32(int32(channel.Position)) links := []uint32{} for cid, _ := range channel.Links { links = append(links, uint32(cid)) } chanstate.Links = links err := client.sendProtoMessage(MessageChannelState, chanstate) if err != nil { client.Panic(err.String()) } for _, subchannel := range channel.children { client.sendChannelTree(subchannel) } }
func makeLogin(name string, authToken string, permissions uint32) (msg *protocol.Message) { login := &protocol.Login{ Name: proto.String(name), Authtoken: proto.String(authToken), Permissions: proto.Uint32(permissions), } return &protocol.Message{ Login: login, Type: protocol.NewMessage_Type(protocol.Message_LOGIN), } }
// Handle a user remove packet. This can either be a client disconnecting, or a // user kicking or kick-banning another player. func (server *Server) handleUserRemoveMessage(client *Client, msg *Message) { userremove := &mumbleproto.UserRemove{} err := proto.Unmarshal(msg.buf, userremove) if err != nil { client.Panic(err.String()) } // Get the client to be removed. removeClient, ok := server.clients[*userremove.Session] if !ok { client.Panic("Invalid session in UserRemove message") return } ban := false if userremove.Ban != nil { ban = *userremove.Ban } // Check client's permissions perm := Permission(KickPermission) if ban { perm = Permission(BanPermission) } if removeClient.IsSuperUser() || !server.HasPermission(client, server.root, perm) { client.sendPermissionDenied(client, server.root, perm) return } if ban { // fixme(mkrautz): Implement banning. log.Printf("handleUserRemove: Banning is not yet implemented.") } userremove.Actor = proto.Uint32(uint32(client.Session)) if err = server.broadcastProtoMessage(MessageUserRemove, userremove); err != nil { log.Panic("Unable to broadcast UserRemove message") return } removeClient.ForceDisconnect() }
// Send permission denied by type (and user) func (c *Client) sendPermissionDeniedTypeUser(kind string, user *Client) { val, ok := mumbleproto.PermissionDenied_DenyType_value[kind] if ok { pd := &mumbleproto.PermissionDenied{} pd.Type = mumbleproto.NewPermissionDenied_DenyType(val) if user != nil { pd.Session = proto.Uint32(uint32(user.Session)) } d, err := proto.Marshal(pd) if err != nil { c.Panic(err.String()) return } c.msgchan <- &Message{ buf: d, kind: MessagePermissionDenied, } } else { log.Panic("Unknown permission denied type.") } }
//type TransactionBlock struct { // Id *uint32 "PB(varint,1,req,name=id)" // Parent *TransactionBlock "PB(bytes,2,opt,name=parent)" // Operations []*TransactionOperation "PB(bytes,5,rep,name=operations)" // VariableCount *uint32 "PB(varint,6,opt,name=variable_count)" // Variables []*TransactionVariable "PB(bytes,7,rep,name=variables)" // XXX_unrecognized []byte //} func (b *TransactionBlock) init(t *Transaction, ti *transactionInfo) (err os.Error) { // initialize variables if b.VariableCount != nil && len(b.Variables) != int(*b.VariableCount) { newVars := make([]*TransactionVariable, int(*b.VariableCount)) for _, vr := range b.Variables { newVars[*vr.Id] = vr } for i := uint32(0); i < *b.VariableCount; i++ { if newVars[i] == nil { newVars[i] = &TransactionVariable{ Block: b.Id, Id: proto.Uint32(i), } } } } // initialize operations for _, op := range b.Operations { switch *op.Type { case op_return: err = op.Return.init(t, ti) case op_set: err = op.Set.init(t, ti) case op_get: err = op.Get.init(t, ti) } if err != nil { return } } return }
func (b *TransactionBlock) GetInto(dest *TransactionVariable, data ...interface{}) { if len(data) < 1 { panic("You must at least specify source") } source, rest := data2destination(data) acs := make([]*TransactionObject, len(rest)) for i := 0; i < len(rest); i++ { acs[i] = interface2object(rest[i]) } op := &TransactionOperation{ Type: proto.Uint32(op_get), Get: &TransactionOperation_Get{ Destination: dest, Source: source, Accessors: acs, }, } b.addOperation(op) }
// Remove a disconnected client from the server's // internal representation. func (server *Server) RemoveClient(client *Client, kicked bool) { server.hmutex.Lock() if client.udpaddr != nil { host := client.udpaddr.IP.String() oldclients := server.hclients[host] newclients := []*Client{} for _, hostclient := range oldclients { if hostclient != client { newclients = append(newclients, hostclient) } } server.hclients[host] = newclients server.hpclients[client.udpaddr.String()] = nil, false } server.hmutex.Unlock() server.clients[client.Session] = nil, false // Remove client from channel channel := client.Channel if channel != nil { channel.RemoveClient(client) } // If the user was not kicked, broadcast a UserRemove message. // If the user is disconnect via a kick, the UserRemove message has already been sent // at this point. if !kicked && client.state > StateClientAuthenticated { err := server.broadcastProtoMessage(MessageUserRemove, &mumbleproto.UserRemove{ Session: proto.Uint32(client.Session), }) if err != nil { log.Panic("Unable to broadcast UserRemove message for disconnected client.") } } }
func (b *TransactionBlock) Set(data ...interface{}) { if len(data) < 2 { panic("You must at least specify destination and value") } dest, rest := data2destination(data) acs := make([]*TransactionObject, len(rest)-1) for i := 0; i < len(rest)-1; i++ { acs[i] = interface2object(rest[i]) } val := interface2object(rest[len(rest)-1]) op := &TransactionOperation{ Type: proto.Uint32(op_set), Set: &TransactionOperation_Set{ Destination: dest, Accessors: acs, Value: val, }, } b.addOperation(op) }
// Handle channel state change. func (server *Server) handleChannelStateMessage(client *Client, msg *Message) { chanstate := &mumbleproto.ChannelState{} err := proto.Unmarshal(msg.buf, chanstate) if err != nil { client.Panic(err.String()) return } var channel *Channel var parent *Channel var ok bool // Lookup channel for channel ID if chanstate.ChannelId != nil { channel, ok = server.Channels[int(*chanstate.ChannelId)] if !ok { client.Panic("Invalid channel specified in ChannelState message") return } } // Lookup parent if chanstate.Parent != nil { parent, ok = server.Channels[int(*chanstate.Parent)] if !ok { client.Panic("Invalid parent channel specified in ChannelState message") return } } // The server can't receive links through the links field in the ChannelState message, // because clients are supposed to send modifications to a channel's link state through // the links_add and links_remove fields. // Make sure the links field is clear so we can transmit the channel's link state in our reply. chanstate.Links = nil var name string var description string // Extract the description and perform sanity checks. if chanstate.Description != nil { description = *chanstate.Description // fixme(mkrautz): Check length } // Extract the the name of channel and check whether it's valid. // A valid channel name is a name that: // a) Isn't already used by a channel at the same level as the channel itself (that is, channels // that have a common parent can't have the same name. // b) A name must be a valid name on the server (it must pass the channel name regexp) if chanstate.Name != nil { name = *chanstate.Name // We don't allow renames for the root channel. if channel != nil && channel.Id != 0 { // Pick a parent. If the name change is part of a re-parent (a channel move), // we must evaluate the parent variable. Since we're explicitly exlcuding the root // channel from renames, channels that are the target of renames are guaranteed to have // a parent. evalp := parent if evalp == nil { evalp = channel.parent } for _, iter := range evalp.children { if iter.Name == name { client.sendPermissionDeniedType("ChannelName") return } } } } // If the channel does not exist already, the ChannelState message is a create operation. if channel == nil { if parent == nil || len(name) == 0 { return } // Check whether the client has permission to create the channel in parent. perm := Permission(NonePermission) if *chanstate.Temporary { perm = Permission(TempChannelPermission) } else { perm = Permission(MakeChannelPermission) } if !server.HasPermission(client, parent, perm) { client.sendPermissionDenied(client, parent, perm) return } // Only registered users can create channels. if !client.IsRegistered() && !client.HasCertificate() { client.sendPermissionDeniedTypeUser("MissingCertificate", client) return } // We can't add channels to a temporary channel if parent.Temporary { client.sendPermissionDeniedType("TemporaryChannel") return } key := "" if len(description) > 0 { key, err = globalBlobstore.Put([]byte(description)) if err != nil { log.Panicf("Blobstore error: %v", err.String()) } } // Add the new channel channel = server.AddChannel(name) channel.DescriptionBlob = key channel.Temporary = *chanstate.Temporary channel.Position = int(*chanstate.Position) parent.AddChild(channel) // Add the creator to the channel's admin group if client.IsRegistered() { grp := NewGroup(channel, "admin") grp.Add[client.UserId()] = true channel.Groups["admin"] = grp } // If the client wouldn't have WritePermission in the just-created channel, // add a +write ACL for the user's hash. if !server.HasPermission(client, channel, WritePermission) { acl := NewChannelACL(channel) acl.ApplyHere = true acl.ApplySubs = true if client.IsRegistered() { acl.UserId = client.UserId() } else { acl.Group = "$" + client.CertHash } acl.Deny = Permission(NonePermission) acl.Allow = Permission(WritePermission | TraversePermission) channel.ACL = append(channel.ACL, acl) server.ClearACLCache() } chanstate.ChannelId = proto.Uint32(uint32(channel.Id)) // Broadcast channel add server.broadcastProtoMessageWithPredicate(MessageChannelState, chanstate, func(client *Client) bool { return client.Version < 0x10202 }) // Remove description if client knows how to handle blobs. if chanstate.Description != nil && channel.HasDescription() { chanstate.Description = nil chanstate.DescriptionHash = channel.DescriptionBlobHashBytes() } server.broadcastProtoMessageWithPredicate(MessageChannelState, chanstate, func(client *Client) bool { return client.Version >= 0x10202 }) // If it's a temporary channel, move the creator in there. if channel.Temporary { userstate := &mumbleproto.UserState{} userstate.Session = proto.Uint32(client.Session) userstate.ChannelId = proto.Uint32(uint32(channel.Id)) server.userEnterChannel(client, channel, userstate) server.broadcastProtoMessage(MessageUserState, userstate) } } else { // Edit existing channel. // First, check whether the actor has the neccessary permissions. // Name change. if chanstate.Name != nil { // The client can only rename the channel if it has WritePermission in the channel. // Also, clients cannot change the name of the root channel. if !server.HasPermission(client, channel, WritePermission) || channel.Id == 0 { client.sendPermissionDenied(client, channel, WritePermission) return } } // Description change if chanstate.Description != nil { if !server.HasPermission(client, channel, WritePermission) { client.sendPermissionDenied(client, channel, WritePermission) return } } // Position change if chanstate.Position != nil { if !server.HasPermission(client, channel, WritePermission) { client.sendPermissionDenied(client, channel, WritePermission) return } } // Parent change (channel move) if parent != nil { // No-op? if parent == channel.parent { return } // Make sure that channel we're operating on is not a parent of the new parent. iter := parent for iter != nil { if iter == channel { client.Panic("Illegal channel reparent") return } iter = iter.parent } // A temporary channel must not have any subchannels, so deny it. if parent.Temporary { client.sendPermissionDeniedType("TemporaryChannel") return } // To move a channel, the user must have WritePermission in the channel if !server.HasPermission(client, channel, WritePermission) { client.sendPermissionDenied(client, channel, WritePermission) return } // And the user must also have MakeChannel permission in the new parent if !server.HasPermission(client, parent, MakeChannelPermission) { client.sendPermissionDenied(client, parent, MakeChannelPermission) return } // If a sibling of parent already has this name, don't allow it. for _, iter := range parent.children { if iter.Name == channel.Name { client.sendPermissionDeniedType("ChannelName") return } } } // Links linkadd := []*Channel{} linkremove := []*Channel{} if len(chanstate.LinksAdd) > 0 || len(chanstate.LinksRemove) > 0 { // Client must have permission to link if !server.HasPermission(client, channel, LinkChannelPermission) { client.sendPermissionDenied(client, channel, LinkChannelPermission) return } // Add any valid channels to linkremove slice for _, cid := range chanstate.LinksRemove { if iter, ok := server.Channels[int(cid)]; ok { linkremove = append(linkremove, iter) } } // Add any valid channels to linkadd slice for _, cid := range chanstate.LinksAdd { if iter, ok := server.Channels[int(cid)]; ok { if !server.HasPermission(client, iter, LinkChannelPermission) { client.sendPermissionDenied(client, iter, LinkChannelPermission) return } linkadd = append(linkadd, iter) } } } // Permission checks done! // Channel move if parent != nil { channel.parent.RemoveChild(channel) parent.AddChild(channel) } // Rename if chanstate.Name != nil { channel.Name = *chanstate.Name } // Description change if chanstate.Description != nil { key, err := globalBlobstore.Put([]byte(*chanstate.Description)) if err != nil { log.Panicf("Blobstore error: %v", err.String()) } channel.DescriptionBlob = key } // Position change if chanstate.Position != nil { channel.Position = int(*chanstate.Position) } // Add links for _, iter := range linkadd { server.LinkChannels(channel, iter) } // Remove links for _, iter := range linkremove { server.UnlinkChannels(channel, iter) } // Broadcast the update server.broadcastProtoMessageWithPredicate(MessageChannelState, chanstate, func(client *Client) bool { return client.Version < 0x10202 }) // Remove description blob when sending to 1.2.2 >= users. Only send the blob hash. if channel.HasDescription() { chanstate.Description = nil chanstate.DescriptionHash = channel.DescriptionBlobHashBytes() } chanstate.DescriptionHash = channel.DescriptionBlobHashBytes() server.broadcastProtoMessageWithPredicate(MessageChannelState, chanstate, func(client *Client) bool { return client.Version >= 0x10202 }) } }
// Handle user state changes func (server *Server) handleUserStateMessage(client *Client, msg *Message) { userstate := &mumbleproto.UserState{} err := proto.Unmarshal(msg.buf, userstate) if err != nil { client.Panic(err.String()) } actor, ok := server.clients[client.Session] if !ok { log.Panic("Client not found in server's client map.") return } target := actor if userstate.Session != nil { target, ok = server.clients[*userstate.Session] if !ok { client.Panic("Invalid session in UserState message") return } } userstate.Session = proto.Uint32(target.Session) userstate.Actor = proto.Uint32(actor.Session) // Does it have a channel ID? if userstate.ChannelId != nil { // Destination channel dstChan, ok := server.Channels[int(*userstate.ChannelId)] if !ok { return } // If the user and the actor aren't the same, check whether the actor has MovePermission on // the user's curent channel. if actor != target && !server.HasPermission(actor, target.Channel, MovePermission) { client.sendPermissionDenied(actor, target.Channel, MovePermission) return } // Check whether the actor has MovePermission on dstChan. Check whether user has EnterPermission // on dstChan. if !server.HasPermission(actor, dstChan, MovePermission) && !server.HasPermission(target, dstChan, EnterPermission) { client.sendPermissionDenied(target, dstChan, EnterPermission) return } // fixme(mkrautz): Check whether the channel is full. } if userstate.Mute != nil || userstate.Deaf != nil || userstate.Suppress != nil || userstate.PrioritySpeaker != nil { // Disallow for SuperUser if target.IsSuperUser() { client.sendPermissionDeniedType("SuperUser") return } // Check whether the actor has 'mutedeafen' permission on user's channel. if !server.HasPermission(actor, target.Channel, MuteDeafenPermission) { client.sendPermissionDenied(actor, target.Channel, MuteDeafenPermission) return } // Check if this was a suppress operation. Only the server can suppress users. if userstate.Suppress != nil { client.sendPermissionDenied(actor, target.Channel, MuteDeafenPermission) return } } // Comment set/clear if userstate.Comment != nil { comment := *userstate.Comment // Clearing another user's comment. if target != actor { // Check if actor has 'move' permissions on the root channel. It is needed // to clear another user's comment. if !server.HasPermission(actor, server.root, MovePermission) { client.sendPermissionDenied(actor, server.root, MovePermission) return } // Only allow empty text. if len(comment) > 0 { client.Panic("Cannot clear another user's comment") return } } // todo(mkrautz): Check if the text is allowed. } // Texture change if userstate.Texture != nil { // Check the length of the texture } // Registration if userstate.UserId != nil { // If user == actor, check for SelfRegisterPermission on root channel. // If user != actor, check for RegisterPermission permission on root channel. permCheck := Permission(NonePermission) uid := *userstate.UserId if target == actor { permCheck = SelfRegisterPermission } else { permCheck = RegisterPermission } if uid >= 0 || !server.HasPermission(actor, server.root, SelfRegisterPermission) { client.sendPermissionDenied(actor, server.root, permCheck) return } // We can't register a user with an empty hash. if len(target.CertHash) == 0 { client.sendPermissionDeniedTypeUser("MissingCertificate", target) } } // Prevent self-targetting state changes to be applied to other users // That is, if actor != user, then: // Discard message if it has any of the following things set: // - SelfDeaf // - SelfMute // - Texture // - PluginContext // - PluginIdentity // - Recording if actor != target && (userstate.SelfDeaf != nil || userstate.SelfMute != nil || userstate.Texture != nil || userstate.PluginContext != nil || userstate.PluginIdentity != nil || userstate.Recording != nil) { client.Panic("Invalid UserState") return } broadcast := false if userstate.Texture != nil && target.user != nil { key, err := globalBlobstore.Put(userstate.Texture) if err != nil { log.Panicf("Blobstore error: %v", err.String()) } if target.user.TextureBlob != key { target.user.TextureBlob = key } else { userstate.Texture = nil } broadcast = true } if userstate.SelfDeaf != nil { target.SelfDeaf = *userstate.SelfDeaf if target.SelfDeaf { userstate.SelfDeaf = proto.Bool(true) target.SelfMute = true } broadcast = true } if userstate.SelfMute != nil { target.SelfMute = *userstate.SelfMute if !target.SelfMute { userstate.SelfDeaf = proto.Bool(false) target.SelfDeaf = false } } if userstate.PluginContext != nil { target.PluginContext = userstate.PluginContext } if userstate.PluginIdentity != nil { target.PluginIdentity = *userstate.PluginIdentity } if userstate.Comment != nil && target.user != nil { key, err := globalBlobstore.Put([]byte(*userstate.Comment)) if err != nil { log.Panicf("Blobstore error: %v", err.String()) } if target.user.CommentBlob != key { target.user.CommentBlob = key } else { userstate.Comment = nil } broadcast = true } if userstate.Mute != nil || userstate.Deaf != nil || userstate.Suppress != nil || userstate.PrioritySpeaker != nil { if userstate.Deaf != nil { target.Deaf = *userstate.Deaf if target.Deaf { userstate.Mute = proto.Bool(true) } } if userstate.Mute != nil { target.Mute = *userstate.Mute if !target.Mute { userstate.Deaf = proto.Bool(false) target.Deaf = false } } if userstate.Suppress != nil { target.Suppress = *userstate.Suppress } if userstate.PrioritySpeaker != nil { target.PrioritySpeaker = *userstate.PrioritySpeaker } broadcast = true } if userstate.Recording != nil && *userstate.Recording != target.Recording { target.Recording = *userstate.Recording txtmsg := &mumbleproto.TextMessage{} txtmsg.TreeId = append(txtmsg.TreeId, uint32(0)) if target.Recording { txtmsg.Message = proto.String(fmt.Sprintf("User '%s' started recording", target.ShownName())) } else { txtmsg.Message = proto.String(fmt.Sprintf("User '%s' stopped recording", target.ShownName())) } server.broadcastProtoMessageWithPredicate(MessageTextMessage, txtmsg, func(client *Client) bool { return client.Version < 0x10203 }) broadcast = true } if userstate.UserId != nil { // fixme(mkrautz): Registration is currently unhandled. log.Printf("handleUserState: (Self)Register not implemented yet!") userstate.UserId = nil } if userstate.ChannelId != nil { channel, ok := server.Channels[int(*userstate.ChannelId)] if ok { server.userEnterChannel(target, channel, userstate) broadcast = true } } if broadcast { // This variable denotes the length of a zlib-encoded "old-style" texture. // Mumble and Murmur used qCompress and qUncompress from Qt to compress // textures that were sent over the wire. We can use this to determine // whether a texture is a "new style" or an "old style" texture. texture := userstate.Texture texlen := uint32(0) if texture != nil && len(texture) > 4 { texlen = uint32(texture[0])<<24 | uint32(texture[1])<<16 | uint32(texture[2])<<8 | uint32(texture[3]) } if texture != nil && len(texture) > 4 && texlen != 600*60*4 { // The sent texture is a new-style texture. Strip it from the message // we send to pre-1.2.2 clients. userstate.Texture = nil err := server.broadcastProtoMessageWithPredicate(MessageUserState, userstate, func(client *Client) bool { return client.Version < 0x10202 }) if err != nil { log.Panic("Unable to broadcast UserState") } // Re-add it to the message, so that 1.2.2+ clients *do* get the new-style texture. userstate.Texture = texture } else { // Old style texture. We can send the message as-is. err := server.broadcastProtoMessageWithPredicate(MessageUserState, userstate, func(client *Client) bool { return client.Version < 0x10202 }) if err != nil { log.Panic("Unable to broadcast UserState") } } // If a texture hash is set on user, we transmit that instead of // the texture itself. This allows the client to intelligently fetch // the blobs that it does not already have in its local storage. if userstate.Texture != nil && target.user != nil && target.user.HasTexture() { userstate.Texture = nil userstate.TextureHash = target.user.TextureBlobHashBytes() } else if target.user == nil { userstate.Texture = nil userstate.TextureHash = nil } // Ditto for comments. if userstate.Comment != nil && target.user.HasComment() { userstate.Comment = nil userstate.CommentHash = target.user.CommentBlobHashBytes() } else if target.user == nil { userstate.Comment = nil userstate.CommentHash = nil } err := server.broadcastProtoMessageWithPredicate(MessageUserState, userstate, func(client *Client) bool { return client.Version >= 0x10203 }) if err != nil { log.Panic("Unable to broadcast UserState") } } }
func (server *Server) sendUserList(client *Client) { for _, connectedClient := range server.clients { if connectedClient.state != StateClientReady { continue } if connectedClient == client { continue } userstate := &mumbleproto.UserState{ Session: proto.Uint32(connectedClient.Session), Name: proto.String(connectedClient.ShownName()), ChannelId: proto.Uint32(uint32(connectedClient.Channel.Id)), } if connectedClient.IsRegistered() { userstate.UserId = proto.Uint32(uint32(connectedClient.UserId())) if connectedClient.user.HasTexture() { // Does the client support blobs? if client.Version >= 0x10203 { userstate.TextureHash = connectedClient.user.TextureBlobHashBytes() } else { buf, err := globalBlobstore.Get(connectedClient.user.TextureBlob) if err != nil { log.Panicf("Blobstore error: %v", err.String()) } userstate.Texture = buf } } if connectedClient.user.HasComment() { // Does the client support blobs? if client.Version >= 0x10203 { userstate.CommentHash = connectedClient.user.CommentBlobHashBytes() } else { buf, err := globalBlobstore.Get(connectedClient.user.CommentBlob) if err != nil { log.Panicf("Blobstore error: %v", err.String()) } userstate.Comment = proto.String(string(buf)) } } if len(connectedClient.user.CertHash) > 0 { userstate.Hash = proto.String(connectedClient.user.CertHash) } } if connectedClient.Mute { userstate.Mute = proto.Bool(true) } if connectedClient.Suppress { userstate.Suppress = proto.Bool(true) } if connectedClient.SelfMute { userstate.SelfMute = proto.Bool(true) } if connectedClient.SelfDeaf { userstate.SelfDeaf = proto.Bool(true) } if connectedClient.PrioritySpeaker { userstate.PrioritySpeaker = proto.Bool(true) } if connectedClient.Recording { userstate.Recording = proto.Bool(true) } if connectedClient.PluginContext != nil || len(connectedClient.PluginContext) > 0 { userstate.PluginContext = connectedClient.PluginContext } if len(connectedClient.PluginIdentity) > 0 { userstate.PluginIdentity = proto.String(connectedClient.PluginIdentity) } err := client.sendProtoMessage(MessageUserState, userstate) if err != nil { // Server panic? continue } } }
// The last part of authentication runs in the server's synchronous handler. func (server *Server) finishAuthenticate(client *Client) { // If the client succeeded in proving to the server that it should be granted // the credentials of a registered user, do some sanity checking to make sure // that user isn't already connected. // // If the user is already connected, try to check whether this new client is // connecting from the same IP address. If that's the case, disconnect the // previous client and let the new guy in. if client.user != nil { found := false for _, connectedClient := range server.clients { if connectedClient.UserId() == client.UserId() { found = true break } } // The user is already present on the server. if found { // todo(mkrautz): Do the address checking. client.RejectAuth("UsernameInUse", "A client is already connected using those credentials.") return } // No, that user isn't already connected. Move along. } // Add the client to the connected list client.Session = server.GenSessionId() server.clients[client.Session] = client // First, check whether we need to tell the other connected // clients to switch to a codec so the new guy can actually speak. server.updateCodecVersions() client.sendChannelList() // Add the client to the host slice for its host address. host := client.tcpaddr.IP.String() server.hmutex.Lock() server.hclients[host] = append(server.hclients[host], client) server.hmutex.Unlock() userstate := &mumbleproto.UserState{ Session: proto.Uint32(client.Session), Name: proto.String(client.ShownName()), ChannelId: proto.Uint32(0), } if client.IsRegistered() { userstate.UserId = proto.Uint32(uint32(client.UserId())) if client.user.HasTexture() { // Does the client support blobs? if client.Version >= 0x10203 { userstate.TextureHash = client.user.TextureBlobHashBytes() } else { buf, err := globalBlobstore.Get(client.user.TextureBlob) if err != nil { log.Panicf("Blobstore error: %v", err.String()) } userstate.Texture = buf } } if client.user.HasComment() { // Does the client support blobs? if client.Version >= 0x10203 { userstate.CommentHash = client.user.CommentBlobHashBytes() } else { buf, err := globalBlobstore.Get(client.user.CommentBlob) if err != nil { log.Panicf("Blobstore error: %v", err.String()) } userstate.Comment = proto.String(string(buf)) } } } server.userEnterChannel(client, server.root, userstate) if err := server.broadcastProtoMessage(MessageUserState, userstate); err != nil { // Server panic? } server.sendUserList(client) sync := &mumbleproto.ServerSync{} sync.Session = proto.Uint32(client.Session) sync.MaxBandwidth = proto.Uint32(server.MaxBandwidth) if client.IsSuperUser() { sync.Permissions = proto.Uint64(uint64(AllPermissions)) } else { server.HasPermission(client, server.root, EnterPermission) perm := server.aclcache.GetPermission(client, server.root) if !perm.IsCached() { client.Panic("Corrupt ACL cache") return } perm.ClearCacheBit() sync.Permissions = proto.Uint64(uint64(perm)) } if err := client.sendProtoMessage(MessageServerSync, sync); err != nil { client.Panic(err.String()) return } err := client.sendProtoMessage(MessageServerConfig, &mumbleproto.ServerConfig{ AllowHtml: proto.Bool(true), MessageLength: proto.Uint32(1000), ImageMessageLength: proto.Uint32(1000), }) if err != nil { client.Panic(err.String()) return } client.state = StateClientReady client.clientReady <- true }
// ACL set/query func (server *Server) handleAclMessage(client *Client, msg *Message) { acl := &mumbleproto.ACL{} err := proto.Unmarshal(msg.buf, acl) if err != nil { client.Panic(err.String()) } // Look up the channel this ACL message operates on. channel, ok := server.Channels[int(*acl.ChannelId)] if !ok { return } // Does the user have permission to update or look at ACLs? if !server.HasPermission(client, channel, WritePermission) && !(channel.parent != nil && server.HasPermission(client, channel.parent, WritePermission)) { client.sendPermissionDenied(client, channel, WritePermission) return } reply := &mumbleproto.ACL{} reply.ChannelId = proto.Uint32(uint32(channel.Id)) channels := []*Channel{} users := map[int]bool{} // Query the current ACL state for the channel if acl.Query != nil && *acl.Query != false { reply.InheritAcls = proto.Bool(channel.InheritACL) // Walk the channel tree to get all relevant channels. // (Stop if we reach a channel that doesn't have the InheritACL flag set) iter := channel for iter != nil { channels = append([]*Channel{iter}, channels...) if iter == channel || iter.InheritACL { iter = iter.parent } else { iter = nil } } // Construct the protobuf ChanACL objects corresponding to the ACLs defined // in our channel list. reply.Acls = []*mumbleproto.ACL_ChanACL{} for _, iter := range channels { for _, chanacl := range iter.ACL { if iter == channel || chanacl.ApplySubs { mpacl := &mumbleproto.ACL_ChanACL{} mpacl.Inherited = proto.Bool(iter != channel) mpacl.ApplyHere = proto.Bool(chanacl.ApplyHere) mpacl.ApplySubs = proto.Bool(chanacl.ApplySubs) if chanacl.UserId >= 0 { mpacl.UserId = proto.Uint32(uint32(chanacl.UserId)) users[chanacl.UserId] = true } else { mpacl.Group = proto.String(chanacl.Group) } mpacl.Grant = proto.Uint32(uint32(chanacl.Allow)) mpacl.Deny = proto.Uint32(uint32(chanacl.Deny)) reply.Acls = append(reply.Acls, mpacl) } } } parent := channel.parent allnames := channel.GroupNames() // Construct the protobuf ChanGroups that we send back to the client. // Also constructs a usermap that is a set user ids from the channel's groups. reply.Groups = []*mumbleproto.ACL_ChanGroup{} for name, _ := range allnames { var ( group *Group pgroup *Group ) group = channel.Groups[name] if parent != nil { pgroup = parent.Groups[name] } mpgroup := &mumbleproto.ACL_ChanGroup{} mpgroup.Name = proto.String(name) mpgroup.Inherit = proto.Bool(true) if group != nil { mpgroup.Inherit = proto.Bool(group.Inherit) } mpgroup.Inheritable = proto.Bool(true) if group != nil { mpgroup.Inheritable = proto.Bool(group.Inheritable) } mpgroup.Inherited = proto.Bool(pgroup != nil && pgroup.Inheritable) // Add the set of user ids that this group affects to the user map. // This is used later on in this function to send the client a QueryUsers // message that maps user ids to usernames. if group != nil { toadd := map[int]bool{} for uid, _ := range group.Add { users[uid] = true toadd[uid] = true } for uid, _ := range group.Remove { users[uid] = true toadd[uid] = false, false } for uid, _ := range toadd { mpgroup.Add = append(mpgroup.Add, uint32(uid)) } } if pgroup != nil { for uid, _ := range pgroup.Members() { users[uid] = true mpgroup.InheritedMembers = append(mpgroup.InheritedMembers, uint32(uid)) } } reply.Groups = append(reply.Groups, mpgroup) } if err := client.sendProtoMessage(MessageACL, reply); err != nil { client.Panic(err.String()) } // Map the user ids in the user map to usernames of users. // fixme(mkrautz): This requires a persistent datastore, because it retrieves registered users. queryusers := &mumbleproto.QueryUsers{} for uid, _ := range users { queryusers.Ids = append(queryusers.Ids, uint32(uid)) queryusers.Names = append(queryusers.Names, "Unknown") } if len(queryusers.Ids) > 0 { client.sendProtoMessage(MessageQueryUsers, reply) } // Set new groups and ACLs } else { // Get old temporary members oldtmp := map[string]map[int]bool{} for name, grp := range channel.Groups { oldtmp[name] = grp.Temporary } // Clear current ACLs and groups channel.ACL = []*ChannelACL{} channel.Groups = map[string]*Group{} // Add the received groups to the channel. channel.InheritACL = *acl.InheritAcls for _, pbgrp := range acl.Groups { changroup := NewGroup(channel, *pbgrp.Name) changroup.Inherit = *pbgrp.Inherit changroup.Inheritable = *pbgrp.Inheritable for _, uid := range pbgrp.Add { changroup.Add[int(uid)] = true } for _, uid := range pbgrp.Remove { changroup.Remove[int(uid)] = true } if temp, ok := oldtmp[*pbgrp.Name]; ok { changroup.Temporary = temp } channel.Groups[changroup.Name] = changroup } // Add the received ACLs to the channel. for _, pbacl := range acl.Acls { chanacl := NewChannelACL(channel) chanacl.ApplyHere = *pbacl.ApplyHere chanacl.ApplySubs = *pbacl.ApplySubs if pbacl.UserId != nil { chanacl.UserId = int(*pbacl.UserId) } else { chanacl.Group = *pbacl.Group } chanacl.Deny = Permission(*pbacl.Deny & AllPermissions) chanacl.Allow = Permission(*pbacl.Grant & AllPermissions) channel.ACL = append(channel.ACL, chanacl) } // Clear the server's ACL cache server.ClearACLCache() // Regular user? if !server.HasPermission(client, channel, WritePermission) && client.IsRegistered() || client.HasCertificate() { chanacl := NewChannelACL(channel) chanacl.ApplyHere = true chanacl.ApplySubs = false if client.IsRegistered() { chanacl.UserId = client.UserId() } else if client.HasCertificate() { chanacl.Group = "$" + client.CertHash } chanacl.Deny = Permission(NonePermission) chanacl.Allow = Permission(WritePermission | TraversePermission) channel.ACL = append(channel.ACL, chanacl) server.ClearACLCache() } // fixme(mkrautz): Sync channel to datastore } }
func (t *Transaction) newBlock() (b *TransactionBlock) { b = new(TransactionBlock) b.Id = proto.Uint32(uint32(len(t.Blocks))) t.Blocks = append(t.Blocks, b) return }
// Request big blobs from the server func (server *Server) handleRequestBlob(client *Client, msg *Message) { blobreq := &mumbleproto.RequestBlob{} err := proto.Unmarshal(msg.buf, blobreq) if err != nil { client.Panic(err.String()) return } userstate := &mumbleproto.UserState{} // Request for user textures if len(blobreq.SessionTexture) > 0 { for _, sid := range blobreq.SessionTexture { if target, ok := server.clients[sid]; ok { if target.user == nil { continue } if target.user.HasTexture() { buf, err := globalBlobstore.Get(target.user.TextureBlob) if err != nil { log.Panicf("Blobstore error: %v", err.String()) } userstate.Reset() userstate.Session = proto.Uint32(uint32(target.Session)) userstate.Texture = buf if err := client.sendProtoMessage(MessageUserState, userstate); err != nil { client.Panic(err.String()) return } } } } } // Request for user comments if len(blobreq.SessionComment) > 0 { for _, sid := range blobreq.SessionComment { if target, ok := server.clients[sid]; ok { if target.user == nil { continue } if target.user.HasComment() { buf, err := globalBlobstore.Get(target.user.CommentBlob) if err != nil { log.Panicf("Blobstore error: %v", err.String()) } userstate.Reset() userstate.Session = proto.Uint32(uint32(target.Session)) userstate.Comment = proto.String(string(buf)) if err := client.sendProtoMessage(MessageUserState, userstate); err != nil { client.Panic(err.String()) return } } } } } chanstate := &mumbleproto.ChannelState{} // Request for channel descriptions if len(blobreq.ChannelDescription) > 0 { for _, cid := range blobreq.ChannelDescription { if channel, ok := server.Channels[int(cid)]; ok { if channel.HasDescription() { chanstate.Reset() buf, err := globalBlobstore.Get(channel.DescriptionBlob) if err != nil { log.Panicf("Blobstore error: %v", err.String()) } chanstate.ChannelId = proto.Uint32(uint32(channel.Id)) chanstate.Description = proto.String(string(buf)) if err := client.sendProtoMessage(MessageChannelState, chanstate); err != nil { client.Panic(err.String()) return } } } } } }
// Receiver Goroutine func (client *Client) receiver() { for { // The version handshake is done, the client has been authenticated and it has received // all necessary information regarding the server. Now we're ready to roll! if client.state == StateClientReady { // Try to read the next message in the pool msg, err := client.readProtoMessage() if err != nil { if err == os.EOF { log.Printf("Client disconnected.") client.Disconnect() } else { log.Printf("Client error.") } return } // Special case UDPTunnel messages. They're high priority and shouldn't // go through our synchronous path. if msg.kind == MessageUDPTunnel { client.udp = false client.udprecv <- msg.buf } else { client.server.incoming <- msg } } // The client has responded to our version query. It will try to authenticate. if client.state == StateClientSentVersion { // Try to read the next message in the pool msg, err := client.readProtoMessage() if err != nil { client.Panic(err.String()) return } client.clientReady = make(chan bool) go client.server.handleAuthenticate(client, msg) <-client.clientReady // It's possible that the client has disconnected in the meantime. // In that case, step out of the receiver, since there's nothing left // to receive. if client.disconnected { return } close(client.clientReady) client.clientReady = nil } // The client has just connected. Before it sends its authentication // information we must send it our version information so it knows // what version of the protocol it should speak. if client.state == StateClientConnected { client.sendProtoMessage(MessageVersion, &mumbleproto.Version{ Version: proto.Uint32(0x10203), Release: proto.String("Grumble"), }) // fixme(mkrautz): Re-add OS information... Does it break anything? It seems like // the client discards the version message if there is no OS information in it. client.state = StateServerSentVersion continue } else if client.state == StateServerSentVersion { msg, err := client.readProtoMessage() if err != nil { if err == os.EOF { log.Printf("Client disconnected.") client.Disconnect() } else { log.Printf("Client error.") } return } version := &mumbleproto.Version{} err = proto.Unmarshal(msg.buf, version) if err != nil { client.Panic("Unable to unmarshal client version packet.") return } if version.Version != nil { client.Version = *version.Version } else { client.Version = 0x10200 } if version.Release != nil { client.ClientName = *version.Release } if version.Os != nil { client.OSName = *version.Os } if version.OsVersion != nil { client.OSVersion = *version.OsVersion } log.Printf("version = 0x%x", client.Version) log.Printf("os = %s %s", client.OSName, client.OSVersion) log.Printf("client = %s", client.ClientName) client.state = StateClientSentVersion } } }
func (db *Db) Execute(trans *Transaction) (ret *TransactionReturn) { var err os.Error /* bytes, err := proto.Marshal(trans) if err != nil { panic(fmt.Sprintf("Got an error marshalling: %s", err)) } newtrans := NewEmptyTransaction() err = proto.Unmarshal(bytes, newtrans) if err != nil { panic(fmt.Sprintf("Got an error unmarshalling: %s", err)) } newtrans.Id = proto.Uint64(db.getNextTransactionId()) token := Token(0) // TODO: Use real token!! vs := db.viewstateManager.createViewState(token, true, 0) err = newtrans.init() if err != nil { panic(fmt.Sprintf("Transaction initialisation error: %s", err)) } ret = newtrans.execute(vs) bytes, err = proto.Marshal(ret) if err != nil { panic(fmt.Sprintf("Got an error marshalling: %s", err)) } newret := new(TransactionReturn) err = proto.Unmarshal(bytes, newret) if err != nil { panic(fmt.Sprintf("Got an error unmarshalling: %s", err)) } //*/ //* trans.Id = proto.Uint64(db.getNextTransactionId()) token := Token(0) // TODO: Use real token!! ti := &transactionInfo{ token: Token(0), readOnly: true, } trans.init(ti) vs := db.viewstateManager.createViewState(token, ti.readOnly, true, 0) newret := trans.execute(vs) //*/ if newret.Error == nil { err = vs.prepareCommit() if err != nil { vs.rollback() newret.Error = &TransactionError{ Id: proto.Uint32(0), // TODO: ERRNO Message: proto.String(err.String()), } return newret } err = vs.commit() if err != nil { newret.Error = &TransactionError{ Id: proto.Uint32(0), // TODO: ERRNO Message: proto.String(err.String()), } return newret } } return newret }