172 lines
3.7 KiB
Go
172 lines
3.7 KiB
Go
// SPDX-FileCopyrightText: 2021 The NGI Pointer Secure-Scuttlebutt Team of 2020/2021
|
|
//
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package server
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/ssbc/go-ssb-room/v2/internal/network"
|
|
"github.com/ssbc/go-ssb-room/v2/roomdb"
|
|
"github.com/ssbc/go-ssb-room/v2/roomstate"
|
|
|
|
"github.com/ssbc/go-muxrpc/v2"
|
|
kitlog "go.mindeco.de/log"
|
|
)
|
|
|
|
type Handler struct {
|
|
logger kitlog.Logger
|
|
|
|
netInfo network.ServerEndpointDetails
|
|
state *roomstate.Manager
|
|
membersdb roomdb.MembersService
|
|
config roomdb.RoomConfig
|
|
}
|
|
|
|
type MetadataReply struct {
|
|
Name string `json:"name"`
|
|
Membership bool `json:"membership"`
|
|
Features []string `json:"features"`
|
|
}
|
|
|
|
func (h *Handler) metadata(ctx context.Context, req *muxrpc.Request) (interface{}, error) {
|
|
ref, err := network.GetFeedRefFromAddr(req.RemoteAddr())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
pm, err := h.config.GetPrivacyMode(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var reply MetadataReply
|
|
reply.Name = h.netInfo.Domain
|
|
|
|
// check if caller is a member
|
|
if _, err := h.membersdb.GetByFeed(ctx, ref); err != nil {
|
|
if !errors.Is(err, roomdb.ErrNotFound) {
|
|
return nil, err
|
|
}
|
|
// already initialized as false, just to be clear
|
|
reply.Membership = false
|
|
} else {
|
|
reply.Membership = true
|
|
}
|
|
|
|
// always-on features
|
|
reply.Features = []string{
|
|
"tunnel",
|
|
"httpAuth",
|
|
"httpInvite",
|
|
// TODO: add "room2" once implemented
|
|
}
|
|
|
|
if pm == roomdb.ModeOpen {
|
|
reply.Features = append(reply.Features, "room1")
|
|
}
|
|
|
|
if pm == roomdb.ModeOpen || pm == roomdb.ModeCommunity {
|
|
reply.Features = append(reply.Features, "alias")
|
|
}
|
|
|
|
return reply, nil
|
|
}
|
|
|
|
func (h *Handler) ping(context.Context, *muxrpc.Request) (interface{}, error) {
|
|
now := time.Now().UnixNano() / 1000
|
|
return now, nil
|
|
}
|
|
|
|
func (h *Handler) announce(_ context.Context, req *muxrpc.Request) (interface{}, error) {
|
|
ref, err := network.GetFeedRefFromAddr(req.RemoteAddr())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
h.state.AddEndpoint(ref, req.Endpoint())
|
|
|
|
return true, nil
|
|
}
|
|
|
|
func (h *Handler) leave(_ context.Context, req *muxrpc.Request) (interface{}, error) {
|
|
ref, err := network.GetFeedRefFromAddr(req.RemoteAddr())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
h.state.Remove(ref)
|
|
|
|
return true, nil
|
|
}
|
|
|
|
func (h *Handler) endpoints(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")
|
|
}
|
|
|
|
switch pm {
|
|
case roomdb.ModeCommunity:
|
|
fallthrough
|
|
case roomdb.ModeRestricted:
|
|
_, err := h.membersdb.GetByFeed(ctx, peer)
|
|
if err != nil {
|
|
return fmt.Errorf("external user are not allowed to enumerate members")
|
|
}
|
|
}
|
|
|
|
// for future updates
|
|
toPeer := newEndpointsForwarder(snk)
|
|
h.state.RegisterLegacyEndpoints(toPeer)
|
|
|
|
// add the peer to the room state if they arent already
|
|
h.state.AlreadyAdded(peer, req.Endpoint())
|
|
|
|
// update the peer with
|
|
toPeer.Update(h.state.List())
|
|
|
|
return nil
|
|
}
|
|
|
|
// a muxrpc json encoder for endpoints broadcasts
|
|
type endpointsJSONEncoder struct {
|
|
mu sync.Mutex // only one caller to forwarder at a time
|
|
snk *muxrpc.ByteSink
|
|
enc *json.Encoder
|
|
}
|
|
|
|
func newEndpointsForwarder(snk *muxrpc.ByteSink) *endpointsJSONEncoder {
|
|
enc := json.NewEncoder(snk)
|
|
snk.SetEncoding(muxrpc.TypeJSON)
|
|
return &endpointsJSONEncoder{
|
|
snk: snk,
|
|
enc: enc,
|
|
}
|
|
}
|
|
|
|
func (uf *endpointsJSONEncoder) Update(members []string) error {
|
|
uf.mu.Lock()
|
|
defer uf.mu.Unlock()
|
|
return uf.enc.Encode(members)
|
|
}
|
|
|
|
func (uf *endpointsJSONEncoder) Close() error {
|
|
uf.mu.Lock()
|
|
defer uf.mu.Unlock()
|
|
return uf.snk.Close()
|
|
}
|