go-ssb-room/muxrpc/handlers/tunnel/server/attendants.go

111 lines
2.5 KiB
Go

// SPDX-FileCopyrightText: 2021 The NGI Pointer Secure-Scuttlebutt Team of 2020/2021
//
// SPDX-License-Identifier: MIT
package server
import (
"context"
"encoding/json"
"fmt"
"sync"
"github.com/ssbc/go-muxrpc/v2"
refs "github.com/ssbc/go-ssb-refs"
"github.com/ssbc/go-ssb-room/v2/internal/network"
"github.com/ssbc/go-ssb-room/v2/roomdb"
)
// AttendantsUpdate is emitted if a single member joins or leaves.
// Type is either 'joined' or 'left'.
type AttendantsUpdate struct {
Type string `json:"type"`
ID refs.FeedRef `json:"id"`
}
// AttendantsInitialState is emitted the first time the stream is opened
type AttendantsInitialState struct {
Type string `json:"type"`
IDs []refs.FeedRef `json:"ids"`
}
func (h *Handler) attendants(ctx context.Context, req *muxrpc.Request, snk *muxrpc.ByteSink) error {
// get public key from the calling peer
peer, err := network.GetFeedRefFromAddr(req.RemoteAddr())
if err != nil {
return err
}
pm, err := h.config.GetPrivacyMode(ctx)
if err != nil {
return fmt.Errorf("running with unknown privacy mode")
}
if pm == roomdb.ModeCommunity || pm == roomdb.ModeRestricted {
_, err := h.membersdb.GetByFeed(ctx, peer)
if err != nil {
return fmt.Errorf("external user are not allowed to enumerate members")
}
}
// add peer to the state
h.state.AddEndpoint(peer, req.Endpoint())
// send the current state
snk.SetEncoding(muxrpc.TypeJSON)
err = json.NewEncoder(snk).Encode(AttendantsInitialState{
Type: "state",
IDs: h.state.ListAsRefs(),
})
if err != nil {
return err
}
// register for future updates
toPeer := newAttendantsEncoder(snk)
h.state.RegisterAttendantsUpdates(toPeer)
return nil
}
// a muxrpc json encoder for endpoints broadcasts
type attendantsJSONEncoder struct {
mu sync.Mutex // only one caller to forwarder at a time
snk *muxrpc.ByteSink
enc *json.Encoder
}
func newAttendantsEncoder(snk *muxrpc.ByteSink) *attendantsJSONEncoder {
enc := json.NewEncoder(snk)
snk.SetEncoding(muxrpc.TypeJSON)
return &attendantsJSONEncoder{
snk: snk,
enc: enc,
}
}
func (uf *attendantsJSONEncoder) Joined(member refs.FeedRef) error {
uf.mu.Lock()
defer uf.mu.Unlock()
return uf.enc.Encode(AttendantsUpdate{
Type: "joined",
ID: member,
})
}
func (uf *attendantsJSONEncoder) Left(member refs.FeedRef) error {
uf.mu.Lock()
defer uf.mu.Unlock()
return uf.enc.Encode(AttendantsUpdate{
Type: "left",
ID: member,
})
}
func (uf *attendantsJSONEncoder) Close() error {
uf.mu.Lock()
defer uf.mu.Unlock()
return uf.snk.Close()
}