mirror of
https://github.com/m1k1o/neko.git
synced 2024-07-24 14:40:50 +12:00
WIP legacy adapter.
This commit is contained in:
parent
e4c0c68d79
commit
260a1973f5
@ -38,14 +38,9 @@ func (h *LegacyHandler) Route(r types.Router) {
|
||||
log.Println("legacy handler route")
|
||||
|
||||
r.Get("/ws", func(w http.ResponseWriter, r *http.Request) error {
|
||||
connBackend, _, err := DefaultDialer.Dial("ws://127.0.0.1:8080/api/ws?token="+r.URL.Query().Get("token"), nil)
|
||||
if err != nil {
|
||||
return utils.HttpError(http.StatusServiceUnavailable).
|
||||
WithInternalErr(err).
|
||||
Msg("couldn't dial to remote backend url")
|
||||
}
|
||||
defer connBackend.Close()
|
||||
s := newSession("http://127.0.0.1:8080")
|
||||
|
||||
// create a new websocket connection
|
||||
connClient, err := DefaultUpgrader.Upgrade(w, r, nil)
|
||||
if err != nil {
|
||||
return utils.HttpError(http.StatusInternalServerError).
|
||||
@ -53,10 +48,36 @@ func (h *LegacyHandler) Route(r types.Router) {
|
||||
Msg("couldn't upgrade connection to websocket")
|
||||
}
|
||||
defer connClient.Close()
|
||||
s.connClient = connClient
|
||||
|
||||
// create a new session
|
||||
password := r.URL.Query().Get("password")
|
||||
token, err := s.create(password)
|
||||
if err != nil {
|
||||
log.Printf("couldn't create a new session: %v", err)
|
||||
return nil
|
||||
}
|
||||
defer s.destroy()
|
||||
|
||||
// dial to the remote backend
|
||||
connBackend, _, err := DefaultDialer.Dial("ws://127.0.0.1:8080/api/ws?token="+token, nil)
|
||||
if err != nil {
|
||||
log.Printf("couldn't dial to the remote backend: %v", err)
|
||||
return nil
|
||||
}
|
||||
defer connBackend.Close()
|
||||
s.connBackend = connBackend
|
||||
|
||||
// request signal
|
||||
if err = connBackend.WriteMessage(websocket.TextMessage, []byte(`{"event":"signal/request", "payload":{}}`)); err != nil {
|
||||
log.Printf("couldn't request signal: %v", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
// copy messages between the client and the backend
|
||||
errClient := make(chan error, 1)
|
||||
errBackend := make(chan error, 1)
|
||||
replicateWebsocketConn := func(dst, src *websocket.Conn, errc chan error, rewriteTextMessage func([]byte) ([]byte, error)) {
|
||||
replicateWebsocketConn := func(dst, src *websocket.Conn, errc chan error, rewriteTextMessage func([]byte, func([]byte) error) error) {
|
||||
for {
|
||||
msgType, msg, err := src.ReadMessage()
|
||||
if err != nil {
|
||||
@ -71,25 +92,33 @@ func (h *LegacyHandler) Route(r types.Router) {
|
||||
break
|
||||
}
|
||||
if msgType == websocket.TextMessage {
|
||||
msg, err = rewriteTextMessage(msg)
|
||||
doBreak := false
|
||||
err = rewriteTextMessage(msg, func(msg []byte) error {
|
||||
err := dst.WriteMessage(msgType, msg)
|
||||
if err != nil {
|
||||
doBreak = true
|
||||
}
|
||||
return err
|
||||
})
|
||||
|
||||
if doBreak {
|
||||
errc <- err
|
||||
break
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Printf("websocketproxy: Error when rewriting message: %v", err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
err = dst.WriteMessage(msgType, msg)
|
||||
if err != nil {
|
||||
errc <- err
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// client -> backend
|
||||
go replicateWebsocketConn(connClient, connBackend, errClient, h.wsToBackend)
|
||||
|
||||
// backend -> client
|
||||
go replicateWebsocketConn(connBackend, connClient, errBackend, h.wsToClient)
|
||||
go replicateWebsocketConn(connClient, connBackend, errClient, s.wsToClient)
|
||||
|
||||
// client -> backend
|
||||
go replicateWebsocketConn(connBackend, connClient, errBackend, s.wsToBackend)
|
||||
|
||||
var message string
|
||||
select {
|
||||
@ -97,7 +126,6 @@ func (h *LegacyHandler) Route(r types.Router) {
|
||||
message = "websocketproxy: Error when copying from backend to client: %v"
|
||||
case err = <-errBackend:
|
||||
message = "websocketproxy: Error when copying from client to backend: %v"
|
||||
|
||||
}
|
||||
|
||||
if e, ok := err.(*websocket.CloseError); !ok || e.Code == websocket.CloseAbnormalClosure {
|
||||
|
96
server/internal/http/legacy/session.go
Normal file
96
server/internal/http/legacy/session.go
Normal file
@ -0,0 +1,96 @@
|
||||
package legacy
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"github.com/demodesk/neko/internal/api"
|
||||
"github.com/demodesk/neko/pkg/types"
|
||||
"github.com/gorilla/websocket"
|
||||
)
|
||||
|
||||
type session struct {
|
||||
url string
|
||||
id string
|
||||
token string
|
||||
profile types.MemberProfile
|
||||
client *http.Client
|
||||
|
||||
connClient *websocket.Conn
|
||||
connBackend *websocket.Conn
|
||||
}
|
||||
|
||||
func newSession(url string) *session {
|
||||
return &session{
|
||||
url: url,
|
||||
client: http.DefaultClient,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *session) apiReq(method, path string, request, response any) error {
|
||||
body, err := json.Marshal(request)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(method, s.url+path, bytes.NewReader(body))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
if s.token != "" {
|
||||
req.Header.Set("Authorization", "Bearer "+s.token)
|
||||
}
|
||||
|
||||
res, err := s.client.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
|
||||
if res.StatusCode != http.StatusOK {
|
||||
body, _ := io.ReadAll(res.Body)
|
||||
return fmt.Errorf("unexpected status code: %d, body: %s", res.StatusCode, body)
|
||||
}
|
||||
|
||||
return json.NewDecoder(res.Body).Decode(response)
|
||||
}
|
||||
|
||||
func (s *session) create(password string) (string, error) {
|
||||
data := api.SessionDataPayload{}
|
||||
|
||||
// pefrom login with arbitrary username that will be changed later
|
||||
err := s.apiReq(http.MethodPost, "/api/login", api.SessionLoginPayload{
|
||||
Username: "admin",
|
||||
Password: password,
|
||||
}, &data)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
s.id = data.ID
|
||||
s.token = data.Token
|
||||
s.profile = data.Profile
|
||||
|
||||
if s.token == "" {
|
||||
return "", fmt.Errorf("token not found")
|
||||
}
|
||||
|
||||
return data.Token, nil
|
||||
}
|
||||
|
||||
func (s *session) destroy() {
|
||||
defer s.client.CloseIdleConnections()
|
||||
|
||||
// logout session
|
||||
err := s.apiReq(http.MethodPost, "/api/logout", nil, nil)
|
||||
if err != nil {
|
||||
log.Println("failed to logout session:", err)
|
||||
}
|
||||
}
|
@ -4,158 +4,293 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/demodesk/neko/internal/http/legacy/event"
|
||||
"github.com/demodesk/neko/internal/http/legacy/message"
|
||||
oldEvent "github.com/demodesk/neko/internal/http/legacy/event"
|
||||
oldMessage "github.com/demodesk/neko/internal/http/legacy/message"
|
||||
"github.com/pion/webrtc/v3"
|
||||
|
||||
"github.com/demodesk/neko/pkg/types"
|
||||
"github.com/demodesk/neko/pkg/types/event"
|
||||
"github.com/demodesk/neko/pkg/types/message"
|
||||
|
||||
chat "github.com/demodesk/neko/internal/plugins/chat"
|
||||
filetransfer "github.com/demodesk/neko/internal/plugins/filetransfer"
|
||||
)
|
||||
|
||||
func (h *LegacyHandler) wsToBackend(msg []byte) ([]byte, error) {
|
||||
header := message.Message{}
|
||||
func (s *session) wsToBackend(msg []byte, sendMsg func([]byte) error) error {
|
||||
header := oldMessage.Message{}
|
||||
err := json.Unmarshal(msg, &header)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
|
||||
send := func(event string, payload any) error {
|
||||
rawPayload, err := json.Marshal(payload)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
msg, err := json.Marshal(&types.WebSocketMessage{
|
||||
Event: event,
|
||||
Payload: rawPayload,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return sendMsg(msg)
|
||||
}
|
||||
|
||||
var response any
|
||||
switch header.Event {
|
||||
// Signal Events
|
||||
case event.SIGNAL_OFFER:
|
||||
request := &message.SignalOffer{}
|
||||
case oldEvent.SIGNAL_OFFER:
|
||||
request := &oldMessage.SignalOffer{}
|
||||
err := json.Unmarshal(msg, request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
|
||||
case event.SIGNAL_ANSWER:
|
||||
request := &message.SignalAnswer{}
|
||||
return send(event.SIGNAL_OFFER, &message.SignalDescription{
|
||||
SDP: request.SDP,
|
||||
})
|
||||
|
||||
case oldEvent.SIGNAL_ANSWER:
|
||||
request := &oldMessage.SignalAnswer{}
|
||||
err := json.Unmarshal(msg, request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
|
||||
case event.SIGNAL_CANDIDATE:
|
||||
request := &message.SignalCandidate{}
|
||||
// TODO: Set Display Name here.
|
||||
|
||||
return send(event.SIGNAL_ANSWER, &message.SignalDescription{
|
||||
SDP: request.SDP,
|
||||
})
|
||||
|
||||
case oldEvent.SIGNAL_CANDIDATE:
|
||||
request := &oldMessage.SignalCandidate{}
|
||||
err := json.Unmarshal(msg, request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
|
||||
var candidate webrtc.ICECandidateInit
|
||||
err = json.Unmarshal([]byte(request.Data), &candidate)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return send(event.SIGNAL_CANDIDATE, &message.SignalCandidate{
|
||||
ICECandidateInit: candidate,
|
||||
})
|
||||
|
||||
// Control Events
|
||||
case event.CONTROL_RELEASE:
|
||||
case event.CONTROL_REQUEST:
|
||||
case event.CONTROL_GIVE:
|
||||
request := &message.Control{}
|
||||
case oldEvent.CONTROL_RELEASE:
|
||||
return send(event.CONTROL_RELEASE, nil)
|
||||
|
||||
case oldEvent.CONTROL_REQUEST:
|
||||
return send(event.CONTROL_REQUEST, nil)
|
||||
|
||||
case oldEvent.CONTROL_GIVE:
|
||||
request := &oldMessage.Control{}
|
||||
err := json.Unmarshal(msg, request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
|
||||
case event.CONTROL_CLIPBOARD:
|
||||
request := &message.Clipboard{}
|
||||
// TODO: No WS equivalent, call HTTP API.
|
||||
return fmt.Errorf("event not implemented: %s", header.Event)
|
||||
|
||||
case oldEvent.CONTROL_CLIPBOARD:
|
||||
request := &oldMessage.Clipboard{}
|
||||
err := json.Unmarshal(msg, request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
|
||||
case event.CONTROL_KEYBOARD:
|
||||
request := &message.Keyboard{}
|
||||
return send(event.CLIPBOARD_SET, &message.ClipboardData{
|
||||
Text: request.Text,
|
||||
})
|
||||
|
||||
case oldEvent.CONTROL_KEYBOARD:
|
||||
request := &oldMessage.Keyboard{}
|
||||
err := json.Unmarshal(msg, request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
|
||||
if request.Layout != nil {
|
||||
err = send(event.KEYBOARD_MAP, &message.KeyboardMap{
|
||||
KeyboardMap: types.KeyboardMap{
|
||||
Layout: *request.Layout,
|
||||
},
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if request.CapsLock != nil || request.NumLock != nil || request.ScrollLock != nil {
|
||||
err = send(event.KEYBOARD_MODIFIERS, &message.KeyboardModifiers{
|
||||
KeyboardModifiers: types.KeyboardModifiers{
|
||||
CapsLock: request.CapsLock,
|
||||
NumLock: request.NumLock,
|
||||
// ScrollLock: request.ScrollLock, // ScrollLock is deprecated.
|
||||
},
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
// Chat Events
|
||||
case event.CHAT_MESSAGE:
|
||||
request := &message.ChatReceive{}
|
||||
case oldEvent.CHAT_MESSAGE:
|
||||
request := &oldMessage.ChatReceive{}
|
||||
err := json.Unmarshal(msg, request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
|
||||
case event.CHAT_EMOTE:
|
||||
request := &message.EmoteReceive{}
|
||||
return send(chat.CHAT_MESSAGE, &chat.Content{
|
||||
Text: request.Content,
|
||||
})
|
||||
|
||||
case oldEvent.CHAT_EMOTE:
|
||||
request := &oldMessage.EmoteReceive{}
|
||||
err := json.Unmarshal(msg, request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO: Emote plugin not implemented.
|
||||
return fmt.Errorf("event not implemented: %s", header.Event)
|
||||
|
||||
// File Transfer Events
|
||||
case event.FILETRANSFER_REFRESH:
|
||||
case oldEvent.FILETRANSFER_REFRESH:
|
||||
return send(filetransfer.FILETRANSFER_UPDATE, nil)
|
||||
|
||||
// Screen Events
|
||||
case event.SCREEN_RESOLUTION:
|
||||
case event.SCREEN_CONFIGURATIONS:
|
||||
case event.SCREEN_SET:
|
||||
request := &message.ScreenResolution{}
|
||||
case oldEvent.SCREEN_RESOLUTION:
|
||||
// No WS equivalent, call HTTP API and return screen resolution.
|
||||
return fmt.Errorf("event not implemented: %s", header.Event)
|
||||
|
||||
case oldEvent.SCREEN_CONFIGURATIONS:
|
||||
// No WS equivalent, call HTTP API and return screen configurations.
|
||||
return fmt.Errorf("event not implemented: %s", header.Event)
|
||||
|
||||
case oldEvent.SCREEN_SET:
|
||||
request := &oldMessage.ScreenResolution{}
|
||||
err := json.Unmarshal(msg, request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
|
||||
return send(event.SCREEN_SET, &message.ScreenSize{
|
||||
ScreenSize: types.ScreenSize{
|
||||
Width: request.Width,
|
||||
Height: request.Height,
|
||||
Rate: request.Rate,
|
||||
},
|
||||
})
|
||||
|
||||
// Broadcast Events
|
||||
case event.BROADCAST_CREATE:
|
||||
request := &message.BroadcastCreate{}
|
||||
case oldEvent.BROADCAST_CREATE:
|
||||
request := &oldMessage.BroadcastCreate{}
|
||||
err := json.Unmarshal(msg, request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
|
||||
case event.BROADCAST_DESTROY:
|
||||
// TODO: No WS equivalent, call HTTP API.
|
||||
return fmt.Errorf("event not implemented: %s", header.Event)
|
||||
|
||||
case oldEvent.BROADCAST_DESTROY:
|
||||
// TODO: No WS equivalent, call HTTP API.
|
||||
return fmt.Errorf("event not implemented: %s", header.Event)
|
||||
|
||||
// Admin Events
|
||||
case event.ADMIN_LOCK:
|
||||
request := &message.AdminLock{}
|
||||
case oldEvent.ADMIN_LOCK:
|
||||
request := &oldMessage.AdminLock{}
|
||||
err := json.Unmarshal(msg, request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
|
||||
case event.ADMIN_UNLOCK:
|
||||
request := &message.AdminLock{}
|
||||
// TODO: No WS equivalent, call HTTP API.
|
||||
return fmt.Errorf("event not implemented: %s", header.Event)
|
||||
|
||||
case oldEvent.ADMIN_UNLOCK:
|
||||
request := &oldMessage.AdminLock{}
|
||||
err := json.Unmarshal(msg, request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
|
||||
case event.ADMIN_CONTROL:
|
||||
case event.ADMIN_RELEASE:
|
||||
case event.ADMIN_GIVE:
|
||||
request := &message.Admin{}
|
||||
// TODO: No WS equivalent, call HTTP API.
|
||||
return fmt.Errorf("event not implemented: %s", header.Event)
|
||||
|
||||
case oldEvent.ADMIN_CONTROL:
|
||||
// TODO: No WS equivalent, call HTTP API.
|
||||
return fmt.Errorf("event not implemented: %s", header.Event)
|
||||
|
||||
case oldEvent.ADMIN_RELEASE:
|
||||
// TODO: No WS equivalent, call HTTP API.
|
||||
return fmt.Errorf("event not implemented: %s", header.Event)
|
||||
|
||||
case oldEvent.ADMIN_GIVE:
|
||||
request := &oldMessage.Admin{}
|
||||
err := json.Unmarshal(msg, request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
|
||||
case event.ADMIN_BAN:
|
||||
request := &message.Admin{}
|
||||
// TODO: No WS equivalent, call HTTP API.
|
||||
return fmt.Errorf("event not implemented: %s", header.Event)
|
||||
|
||||
case oldEvent.ADMIN_BAN:
|
||||
request := &oldMessage.Admin{}
|
||||
err := json.Unmarshal(msg, request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
|
||||
case event.ADMIN_KICK:
|
||||
request := &message.Admin{}
|
||||
// TODO: No WS equivalent, call HTTP API.
|
||||
return fmt.Errorf("event not implemented: %s", header.Event)
|
||||
|
||||
case oldEvent.ADMIN_KICK:
|
||||
request := &oldMessage.Admin{}
|
||||
err := json.Unmarshal(msg, request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
|
||||
case event.ADMIN_MUTE:
|
||||
request := &message.Admin{}
|
||||
// TODO: No WS equivalent, call HTTP API.
|
||||
return fmt.Errorf("event not implemented: %s", header.Event)
|
||||
|
||||
case oldEvent.ADMIN_MUTE:
|
||||
request := &oldMessage.Admin{}
|
||||
err := json.Unmarshal(msg, request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
|
||||
case event.ADMIN_UNMUTE:
|
||||
request := &message.Admin{}
|
||||
// TODO: No WS equivalent, call HTTP API.
|
||||
return fmt.Errorf("event not implemented: %s", header.Event)
|
||||
|
||||
case oldEvent.ADMIN_UNMUTE:
|
||||
request := &oldMessage.Admin{}
|
||||
err := json.Unmarshal(msg, request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO: No WS equivalent, call HTTP API.
|
||||
return fmt.Errorf("event not implemented: %s", header.Event)
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown event type: %s", header.Event)
|
||||
return fmt.Errorf("unknown event type: %s", header.Event)
|
||||
}
|
||||
|
||||
return json.Marshal(request)
|
||||
}
|
||||
|
@ -2,161 +2,520 @@ package legacy
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/demodesk/neko/internal/http/legacy/event"
|
||||
"github.com/demodesk/neko/internal/http/legacy/message"
|
||||
oldEvent "github.com/demodesk/neko/internal/http/legacy/event"
|
||||
oldMessage "github.com/demodesk/neko/internal/http/legacy/message"
|
||||
oldTypes "github.com/demodesk/neko/internal/http/legacy/types"
|
||||
"github.com/pion/webrtc/v3"
|
||||
|
||||
chat "github.com/demodesk/neko/internal/plugins/chat"
|
||||
filetransfer "github.com/demodesk/neko/internal/plugins/filetransfer"
|
||||
"github.com/demodesk/neko/pkg/types"
|
||||
"github.com/demodesk/neko/pkg/types/event"
|
||||
"github.com/demodesk/neko/pkg/types/message"
|
||||
)
|
||||
|
||||
func (h *LegacyHandler) wsToClient(msg []byte) ([]byte, error) {
|
||||
header := message.Message{}
|
||||
err := json.Unmarshal(msg, &header)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
func sessionDataToMember(id string, session message.SessionData) (*oldTypes.Member, error) {
|
||||
settings := chat.Settings{
|
||||
CanSend: true, // defaults to true
|
||||
CanReceive: true, // defaults to true
|
||||
}
|
||||
|
||||
var payload any
|
||||
switch header.Event {
|
||||
err := session.Profile.Plugins.Unmarshal(chat.PluginName, &settings)
|
||||
if err != nil && !errors.Is(err, types.ErrPluginSettingsNotFound) {
|
||||
return nil, fmt.Errorf("unable to unmarshal %s plugin settings from global settings: %w", chat.PluginName, err)
|
||||
}
|
||||
|
||||
return &oldTypes.Member{
|
||||
ID: id,
|
||||
Name: session.Profile.Name,
|
||||
Admin: session.Profile.IsAdmin,
|
||||
Muted: !settings.CanSend,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func sendControlHost(request message.ControlHost, send func(payload any) error) error {
|
||||
if request.HasHost {
|
||||
if request.ID == request.HostID {
|
||||
return send(&oldMessage.Control{
|
||||
Event: oldEvent.CONTROL_LOCKED,
|
||||
ID: request.ID,
|
||||
})
|
||||
}
|
||||
|
||||
return send(&oldMessage.ControlTarget{
|
||||
Event: oldEvent.CONTROL_GIVE,
|
||||
ID: request.ID,
|
||||
Target: request.HostID,
|
||||
})
|
||||
}
|
||||
|
||||
if request.ID != "" {
|
||||
return send(&oldMessage.Control{
|
||||
Event: oldEvent.CONTROL_RELEASE,
|
||||
ID: request.ID,
|
||||
})
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *session) wsToClient(msg []byte, sendMsg func([]byte) error) error {
|
||||
data := types.WebSocketMessage{}
|
||||
err := json.Unmarshal(msg, &data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
send := func(payload any) error {
|
||||
msg, err := json.Marshal(payload)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return sendMsg(msg)
|
||||
}
|
||||
|
||||
switch data.Event {
|
||||
// System Events
|
||||
case:
|
||||
payload = &message.SystemMessage{
|
||||
Event: event.SYSTEM_DISCONNECT,
|
||||
case event.SYSTEM_DISCONNECT:
|
||||
request := &message.SystemDisconnect{}
|
||||
err := json.Unmarshal(data.Payload, request)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case:
|
||||
payload = &message.SystemMessage{
|
||||
Event: event.SYSTEM_ERROR,
|
||||
|
||||
return send(&oldMessage.SystemMessage{
|
||||
Event: oldEvent.SYSTEM_DISCONNECT,
|
||||
Message: request.Message,
|
||||
})
|
||||
|
||||
// case:
|
||||
// send(&oldMessage.SystemMessage{
|
||||
// Event: oldEvent.SYSTEM_ERROR,
|
||||
// })
|
||||
case event.SYSTEM_INIT:
|
||||
request := &message.SystemInit{}
|
||||
err := json.Unmarshal(data.Payload, request)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case:
|
||||
payload = &message.SystemInit{
|
||||
Event: event.SYSTEM_INIT,
|
||||
|
||||
//
|
||||
// MembersList
|
||||
//
|
||||
|
||||
membersList := []*oldTypes.Member{}
|
||||
for id, session := range request.Sessions {
|
||||
member, err := sessionDataToMember(id, session)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
membersList = append(membersList, member)
|
||||
}
|
||||
|
||||
err = send(&oldMessage.MembersList{
|
||||
Event: oldEvent.MEMBER_LIST,
|
||||
Members: membersList,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
//
|
||||
// ScreenSize
|
||||
//
|
||||
|
||||
err = send(&oldMessage.ScreenResolution{
|
||||
Event: oldEvent.SCREEN_RESOLUTION,
|
||||
Width: request.ScreenSize.Width,
|
||||
Height: request.ScreenSize.Height,
|
||||
Rate: request.ScreenSize.Rate,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// actually its already set when we create the session
|
||||
s.id = request.SessionId
|
||||
|
||||
//
|
||||
// ControlHost
|
||||
//
|
||||
|
||||
err = sendControlHost(request.ControlHost, send)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
//
|
||||
// FileTransfer
|
||||
//
|
||||
|
||||
filetransferSettings := filetransfer.Settings{
|
||||
Enabled: true, // defaults to true
|
||||
}
|
||||
|
||||
err = request.Settings.Plugins.Unmarshal(filetransfer.PluginName, &filetransferSettings)
|
||||
if err != nil && !errors.Is(err, types.ErrPluginSettingsNotFound) {
|
||||
return fmt.Errorf("unable to unmarshal %s plugin settings from global settings: %w", filetransfer.PluginName, err)
|
||||
}
|
||||
|
||||
//
|
||||
// Locks
|
||||
//
|
||||
locks := map[string]string{}
|
||||
if request.Settings.LockedLogins {
|
||||
locks["login"] = "" // TODO: We don't know who locked the login.
|
||||
}
|
||||
if request.Settings.LockedControls {
|
||||
locks["control"] = "" // TODO: We don't know who locked the control.
|
||||
}
|
||||
if !filetransferSettings.Enabled {
|
||||
locks["filetransfer"] = "" // TODO: We don't know who locked the file transfer.
|
||||
}
|
||||
|
||||
return send(&oldMessage.SystemInit{
|
||||
Event: oldEvent.SYSTEM_INIT,
|
||||
ImplicitHosting: request.Settings.ImplicitHosting,
|
||||
Locks: locks,
|
||||
FileTransfer: true, // TODO: We don't know if file transfer is enabled, we would need to check the global config somehow.
|
||||
})
|
||||
|
||||
case event.SYSTEM_ADMIN:
|
||||
request := &message.SystemAdmin{}
|
||||
err := json.Unmarshal(data.Payload, request)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
//
|
||||
// ScreenSizesList
|
||||
//
|
||||
|
||||
rates := map[string][]int16{}
|
||||
for _, size := range request.ScreenSizesList {
|
||||
key := fmt.Sprintf("%dx%d", size.Width, size.Height)
|
||||
rates[key] = append(rates[key], size.Rate)
|
||||
}
|
||||
|
||||
usedScreenSizes := map[string]struct{}{}
|
||||
screenSizesList := map[int]oldTypes.ScreenConfiguration{}
|
||||
for i, size := range request.ScreenSizesList {
|
||||
key := fmt.Sprintf("%dx%d", size.Width, size.Height)
|
||||
if _, ok := usedScreenSizes[key]; ok {
|
||||
continue
|
||||
}
|
||||
|
||||
ratesMap := map[int]int16{}
|
||||
for i, rate := range rates[key] {
|
||||
ratesMap[i] = rate
|
||||
}
|
||||
|
||||
screenSizesList[i] = oldTypes.ScreenConfiguration{
|
||||
Width: size.Width,
|
||||
Height: size.Height,
|
||||
Rates: ratesMap,
|
||||
}
|
||||
}
|
||||
|
||||
err = send(&oldMessage.ScreenConfigurations{
|
||||
Event: oldEvent.SCREEN_CONFIGURATIONS,
|
||||
Configurations: screenSizesList,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
//
|
||||
// BroadcastStatus
|
||||
//
|
||||
|
||||
return send(&oldMessage.BroadcastStatus{
|
||||
Event: oldEvent.BROADCAST_STATUS,
|
||||
URL: request.BroadcastStatus.URL,
|
||||
IsActive: request.BroadcastStatus.IsActive,
|
||||
})
|
||||
|
||||
// Member Events
|
||||
case:
|
||||
payload = &message.MembersList{
|
||||
Event: event.MEMBER_LIST,
|
||||
|
||||
// TODO: This is on Created but old API wants OnConnected.
|
||||
case event.SESSION_CREATED:
|
||||
request := &message.SessionData{}
|
||||
err := json.Unmarshal(data.Payload, request)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case:
|
||||
payload = &message.Member{
|
||||
Event: event.MEMBER_CONNECTED,
|
||||
|
||||
member, err := sessionDataToMember(request.ID, *request)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case:
|
||||
payload = &message.MemberDisconnected{
|
||||
Event: event.MEMBER_DISCONNECTED,
|
||||
|
||||
return send(&oldMessage.Member{
|
||||
Event: oldEvent.MEMBER_CONNECTED,
|
||||
Member: member,
|
||||
})
|
||||
|
||||
// TODO: This is on Deleted but old API wants OnDisconnected.
|
||||
case event.SESSION_DELETED:
|
||||
request := &message.SessionID{}
|
||||
err := json.Unmarshal(data.Payload, request)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return send(&oldMessage.MemberDisconnected{
|
||||
Event: oldEvent.MEMBER_DISCONNECTED,
|
||||
ID: request.ID,
|
||||
})
|
||||
|
||||
// TODO: This would need some context to know it message has been sent already/to get session data on connect.
|
||||
//case event.SESSION_STATE:
|
||||
// request := &message.SessionState{}
|
||||
// err := json.Unmarshal(data.Payload, request)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// if request.IsConnected {
|
||||
// // oldEvent.MEMBER_CONNECTED if not sent already
|
||||
// } else {
|
||||
// // oldEvent.MEMBER_DISCONNECTED if nor sent already
|
||||
// }
|
||||
|
||||
// Signal Events
|
||||
case:
|
||||
payload = &message.SignalOffer{
|
||||
Event: event.SIGNAL_OFFER,
|
||||
}
|
||||
case:
|
||||
payload = &message.SignalAnswer{
|
||||
Event: event.SIGNAL_ANSWER,
|
||||
}
|
||||
case:
|
||||
payload = &message.SignalCandidate{
|
||||
Event: event.SIGNAL_CANDIDATE,
|
||||
}
|
||||
case:
|
||||
payload = &message.SignalProvide{
|
||||
Event: event.SIGNAL_PROVIDE,
|
||||
case event.SIGNAL_OFFER:
|
||||
request := &message.SignalDescription{}
|
||||
err := json.Unmarshal(data.Payload, request)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return send(&oldMessage.SignalOffer{
|
||||
Event: oldEvent.SIGNAL_OFFER,
|
||||
SDP: request.SDP,
|
||||
})
|
||||
|
||||
case event.SIGNAL_ANSWER:
|
||||
request := &message.SignalDescription{}
|
||||
err := json.Unmarshal(data.Payload, request)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return send(&oldMessage.SignalAnswer{
|
||||
Event: oldEvent.SIGNAL_ANSWER,
|
||||
DisplayName: s.profile.Name, // DisplayName
|
||||
SDP: request.SDP,
|
||||
})
|
||||
|
||||
case event.SIGNAL_CANDIDATE:
|
||||
request := &message.SignalCandidate{}
|
||||
err := json.Unmarshal(data.Payload, request)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
json, err := json.Marshal(request.ICECandidateInit)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return send(&oldMessage.SignalCandidate{
|
||||
Event: oldEvent.SIGNAL_CANDIDATE,
|
||||
Data: string(json),
|
||||
})
|
||||
|
||||
case event.SIGNAL_PROVIDE:
|
||||
request := &message.SignalProvide{}
|
||||
err := json.Unmarshal(data.Payload, request)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
iceServers := []webrtc.ICEServer{}
|
||||
for _, ice := range request.ICEServers {
|
||||
iceServers = append(iceServers, webrtc.ICEServer{
|
||||
URLs: ice.URLs,
|
||||
Username: ice.Username,
|
||||
Credential: ice.Credential,
|
||||
CredentialType: webrtc.ICECredentialTypePassword,
|
||||
})
|
||||
}
|
||||
|
||||
return send(&oldMessage.SignalProvide{
|
||||
Event: oldEvent.SIGNAL_PROVIDE,
|
||||
ID: s.id, // SessionId
|
||||
SDP: request.SDP,
|
||||
Lite: len(iceServers) == 0, // if no ICE servers are provided, it's a lite offer
|
||||
ICE: iceServers,
|
||||
})
|
||||
|
||||
// Control Events
|
||||
case:
|
||||
payload = &message.Clipboard{
|
||||
Event: event.CONTROL_CLIPBOARD,
|
||||
case event.CLIPBOARD_UPDATED:
|
||||
request := &message.ClipboardData{}
|
||||
err := json.Unmarshal(data.Payload, request)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case:
|
||||
payload = &message.Control{
|
||||
Event: event.CONTROL_REQUEST,
|
||||
|
||||
return send(&oldMessage.Clipboard{
|
||||
Event: oldEvent.CONTROL_CLIPBOARD,
|
||||
Text: request.Text,
|
||||
})
|
||||
|
||||
case event.CONTROL_HOST:
|
||||
request := &message.ControlHost{}
|
||||
err := json.Unmarshal(data.Payload, request)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case:
|
||||
payload = &message.Control{
|
||||
Event: event.CONTROL_REQUESTING,
|
||||
|
||||
return sendControlHost(*request, send)
|
||||
|
||||
case event.CONTROL_REQUEST:
|
||||
request := &message.SessionID{}
|
||||
err := json.Unmarshal(data.Payload, request)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case:
|
||||
payload = &message.ControlTarget{
|
||||
Event: event.CONTROL_GIVE,
|
||||
} // message.AdminTarget
|
||||
case:
|
||||
payload = &message.Control{
|
||||
Event: event.CONTROL_RELEASE,
|
||||
}
|
||||
case:
|
||||
payload = &message.Control{
|
||||
Event: event.CONTROL_LOCKED,
|
||||
|
||||
if s.id == request.ID {
|
||||
// if i am the one that is requesting, send CONTROL_REQUEST to me
|
||||
return send(&oldMessage.Control{
|
||||
Event: oldEvent.CONTROL_REQUEST,
|
||||
ID: request.ID,
|
||||
})
|
||||
} else {
|
||||
// if not, let me know someone else is requesting
|
||||
return send(&oldMessage.Control{
|
||||
Event: oldEvent.CONTROL_REQUESTING,
|
||||
ID: request.ID,
|
||||
})
|
||||
}
|
||||
|
||||
// Chat Events
|
||||
case:
|
||||
payload = &message.ChatSend{
|
||||
Event: event.CHAT_MESSAGE,
|
||||
}
|
||||
case:
|
||||
payload = &message.EmoteSend{
|
||||
Event: event.CHAT_EMOTE,
|
||||
case chat.CHAT_MESSAGE:
|
||||
request := &chat.Message{}
|
||||
err := json.Unmarshal(data.Payload, request)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return send(&oldMessage.ChatSend{
|
||||
Event: oldEvent.CHAT_MESSAGE,
|
||||
ID: request.ID,
|
||||
Content: request.Content.Text,
|
||||
})
|
||||
|
||||
// TODO: emotes.
|
||||
//case:
|
||||
// send(&oldMessage.EmoteSend{
|
||||
// Event: oldEvent.CHAT_EMOTE,
|
||||
// })
|
||||
|
||||
// File Transfer Events
|
||||
case:
|
||||
payload = &message.FileTransferList{
|
||||
Event: event.FILETRANSFER_LIST,
|
||||
case filetransfer.FILETRANSFER_UPDATE:
|
||||
request := &filetransfer.Message{}
|
||||
err := json.Unmarshal(data.Payload, request)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
files := []oldTypes.FileListItem{}
|
||||
for _, file := range request.Files {
|
||||
var itemType string
|
||||
switch file.Type {
|
||||
case filetransfer.ItemTypeFile:
|
||||
itemType = "file"
|
||||
case filetransfer.ItemTypeDir:
|
||||
itemType = "dir"
|
||||
}
|
||||
files = append(files, oldTypes.FileListItem{
|
||||
Filename: file.Name,
|
||||
Type: itemType,
|
||||
Size: file.Size,
|
||||
})
|
||||
}
|
||||
|
||||
return send(&oldMessage.FileTransferList{
|
||||
Event: oldEvent.FILETRANSFER_LIST,
|
||||
Cwd: request.RootDir,
|
||||
Files: files,
|
||||
})
|
||||
|
||||
// Screen Events
|
||||
case:
|
||||
payload = &message.ScreenResolution{
|
||||
Event: event.SCREEN_RESOLUTION,
|
||||
}
|
||||
case:
|
||||
payload = &message.ScreenConfigurations{
|
||||
Event: event.SCREEN_CONFIGURATIONS,
|
||||
case event.SCREEN_UPDATED:
|
||||
request := &message.ScreenSizeUpdate{}
|
||||
err := json.Unmarshal(data.Payload, request)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return send(&oldMessage.ScreenResolution{
|
||||
Event: oldEvent.SCREEN_RESOLUTION,
|
||||
ID: request.ID,
|
||||
Width: request.ScreenSize.Width,
|
||||
Height: request.ScreenSize.Height,
|
||||
Rate: request.ScreenSize.Rate,
|
||||
})
|
||||
|
||||
// Broadcast Events
|
||||
case:
|
||||
payload = &message.BroadcastStatus{
|
||||
Event: event.BROADCAST_STATUS,
|
||||
case event.BROADCAST_STATUS:
|
||||
request := &message.BroadcastStatus{}
|
||||
err := json.Unmarshal(data.Payload, request)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Admin Events
|
||||
case:
|
||||
payload = &message.AdminLock{
|
||||
Event: event.ADMIN_UNLOCK,
|
||||
}
|
||||
case:
|
||||
payload = &message.AdminLock{
|
||||
Event: event.ADMIN_LOCK,
|
||||
}
|
||||
case:
|
||||
payload = &message.AdminTarget{
|
||||
Event: event.ADMIN_CONTROL,
|
||||
} // message.Admin
|
||||
case:
|
||||
payload = &message.AdminTarget{
|
||||
Event: event.ADMIN_RELEASE,
|
||||
} // message.Admin
|
||||
case:
|
||||
payload = &message.AdminTarget{
|
||||
Event: event.ADMIN_MUTE,
|
||||
}
|
||||
case:
|
||||
payload = &message.AdminTarget{
|
||||
Event: event.ADMIN_UNMUTE,
|
||||
}
|
||||
case:
|
||||
payload = &message.AdminTarget{
|
||||
Event: event.ADMIN_KICK,
|
||||
}
|
||||
case:
|
||||
payload = &message.AdminTarget{
|
||||
Event: event.ADMIN_BAN,
|
||||
}
|
||||
return send(&oldMessage.BroadcastStatus{
|
||||
Event: oldEvent.BROADCAST_STATUS,
|
||||
URL: request.URL,
|
||||
IsActive: request.IsActive,
|
||||
})
|
||||
|
||||
/*
|
||||
// Admin Events
|
||||
case:
|
||||
send(&oldMessage.AdminLock{
|
||||
Event: oldEvent.ADMIN_UNLOCK,
|
||||
})
|
||||
case:
|
||||
send(&oldMessage.AdminLock{
|
||||
Event: oldEvent.ADMIN_LOCK,
|
||||
})
|
||||
case:
|
||||
send(&oldMessage.AdminTarget{
|
||||
Event: oldEvent.ADMIN_CONTROL,
|
||||
} // )oldMessage.Admin
|
||||
case:
|
||||
send(&oldMessage.AdminTarget{
|
||||
Event: oldEvent.ADMIN_RELEASE,
|
||||
} // )oldMessage.Admin
|
||||
case:
|
||||
send(&oldMessage.AdminTarget{
|
||||
Event: oldEvent.ADMIN_MUTE,
|
||||
})
|
||||
case:
|
||||
send(&oldMessage.AdminTarget{
|
||||
Event: oldEvent.ADMIN_UNMUTE,
|
||||
})
|
||||
case:
|
||||
send(&oldMessage.AdminTarget{
|
||||
Event: oldEvent.ADMIN_KICK,
|
||||
})
|
||||
case:
|
||||
send(&oldMessage.AdminTarget{
|
||||
Event: oldEvent.ADMIN_BAN,
|
||||
})
|
||||
*/
|
||||
|
||||
case event.SYSTEM_HEARTBEAT:
|
||||
return nil
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown event type: %s", header.Event)
|
||||
return fmt.Errorf("unknown event type: %s", data.Event)
|
||||
}
|
||||
|
||||
return json.Marshal(payload)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user