2021-10-08 12:39:31 +00:00
|
|
|
// SPDX-FileCopyrightText: 2021 The NGI Pointer Secure-Scuttlebutt Team of 2020/2021
|
|
|
|
//
|
2021-05-17 14:13:00 +00:00
|
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
|
2021-05-17 14:05:51 +00:00
|
|
|
package server
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"sync"
|
|
|
|
|
2021-05-31 12:50:44 +00:00
|
|
|
"github.com/ssb-ngi-pointer/go-ssb-room/v2/internal/network"
|
|
|
|
"github.com/ssb-ngi-pointer/go-ssb-room/v2/roomdb"
|
2021-05-17 14:05:51 +00:00
|
|
|
"go.cryptoscope.co/muxrpc/v2"
|
|
|
|
refs "go.mindeco.de/ssb-refs"
|
|
|
|
)
|
|
|
|
|
|
|
|
// 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 {
|
2022-10-31 15:42:32 +00:00
|
|
|
_, err := h.membersdb.GetByFeed(ctx, *peer)
|
2021-05-17 14:05:51 +00:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("external user are not allowed to enumerate members")
|
|
|
|
}
|
|
|
|
}
|
2021-05-21 13:52:04 +00:00
|
|
|
|
|
|
|
// add peer to the state
|
|
|
|
h.state.AddEndpoint(*peer, req.Endpoint())
|
|
|
|
|
|
|
|
// send the current state
|
2021-05-24 14:18:59 +00:00
|
|
|
snk.SetEncoding(muxrpc.TypeJSON)
|
2021-05-17 14:05:51 +00:00
|
|
|
err = json.NewEncoder(snk).Encode(AttendantsInitialState{
|
|
|
|
Type: "state",
|
|
|
|
IDs: h.state.ListAsRefs(),
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2021-05-21 13:52:04 +00:00
|
|
|
// register for future updates
|
|
|
|
toPeer := newAttendantsEncoder(snk)
|
|
|
|
h.state.RegisterAttendantsUpdates(toPeer)
|
|
|
|
|
2021-05-17 14:05:51 +00:00
|
|
|
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()
|
|
|
|
}
|