package hub import ( "context" "errors" ) // ErrNotFanout is a Fanout of a type that is targeted or server-raised. var ErrNotFanout = errors.New("hub: not a fan-out type") // Fanout is §4.3's chat and share: an admitted peer to every other // admitted peer, the envelope rebuilt from the sender's participation so // from and name can never be forged, nothing ringed or stored (§6). // The sender's authority is §2.5's last row, exactly as Relay's: a // member-kind participation re-derives standing first (§15.12), and one // whose standing is gone is ended here with lost{standing} and answered // ErrNotYours; a guest never consults standing at all (ruling 14). func (h *Hub) Fanout(ctx context.Context, sqid, session string, from PeerID, m Message) error { if m.Type != TypeChat && m.Type != TypeShare { return ErrNotFanout } gone, err := h.memberStandingGone(ctx, sqid, session, from, true) if err != nil { return err } if gone { return ErrNotYours } h.mu.Lock() defer h.mu.Unlock() r, ok := h.rooms[sqid] if !ok { return ErrUnknownPeer } p, found := h.findLocked(sqid, session, from) if !found { return ErrUnknownPeer } if !p.Admitted { return ErrNotAdmitted } out := Message{Type: m.Type, From: p.Peer, Name: p.Name, Payload: m.Payload} h.broadcastLocked(r, out, func(q *Participation) bool { return q.Admitted && q.Peer != p.Peer }) return nil }