diff --git a/server/internal/session/session.go b/server/internal/session/session.go index 2a858f68..76d7eff6 100644 --- a/server/internal/session/session.go +++ b/server/internal/session/session.go @@ -68,6 +68,12 @@ func (session *SessionCtx) IsHost() bool { return session.manager.isHost(session) } +// only needed for legacy webrtc handler +func (session *SessionCtx) LegacyIsHost() bool { + implicitHosting := session.manager.Settings().ImplicitHosting + return !(!implicitHosting && !session.manager.isHost(session)) || (implicitHosting && !session.profile.CanHost) +} + func (session *SessionCtx) SetAsHost() { session.manager.setHost(session, session) } diff --git a/server/internal/webrtc/legacyhandler.go b/server/internal/webrtc/legacyhandler.go new file mode 100644 index 00000000..c085b3e6 --- /dev/null +++ b/server/internal/webrtc/legacyhandler.go @@ -0,0 +1,139 @@ +package webrtc + +import ( + "bytes" + "encoding/binary" + "strconv" + + "github.com/demodesk/neko/pkg/types" + "github.com/rs/zerolog" +) + +const ( + OP_MOVE = 0x01 + OP_SCROLL = 0x02 + OP_KEY_DOWN = 0x03 + OP_KEY_UP = 0x04 + OP_KEY_CLK = 0x05 +) + +type PayloadHeader struct { + Event uint8 + Length uint16 +} + +type PayloadMove struct { + PayloadHeader + X uint16 + Y uint16 +} + +type PayloadScroll struct { + PayloadHeader + X int16 + Y int16 +} + +type PayloadKey struct { + PayloadHeader + Key uint64 // TODO: uint32 +} + +func (manager *WebRTCManagerCtx) handleLegacy( + logger zerolog.Logger, data []byte, + session types.Session, +) error { + // continue only if session is host + if !session.LegacyIsHost() { + return nil + } + + buffer := bytes.NewBuffer(data) + header := &PayloadHeader{} + hbytes := make([]byte, 3) + + if _, err := buffer.Read(hbytes); err != nil { + return err + } + + if err := binary.Read(bytes.NewBuffer(hbytes), binary.LittleEndian, header); err != nil { + return err + } + + buffer = bytes.NewBuffer(data) + + switch header.Event { + case OP_MOVE: + payload := &PayloadMove{} + if err := binary.Read(buffer, binary.LittleEndian, payload); err != nil { + return err + } + + manager.desktop.Move(int(payload.X), int(payload.Y)) + case OP_SCROLL: + payload := &PayloadScroll{} + if err := binary.Read(buffer, binary.LittleEndian, payload); err != nil { + return err + } + + logger. + Debug(). + Str("x", strconv.Itoa(int(payload.X))). + Str("y", strconv.Itoa(int(payload.Y))). + Msg("scroll") + + manager.desktop.Scroll(int(payload.X), int(payload.Y), false) + case OP_KEY_DOWN: + payload := &PayloadKey{} + if err := binary.Read(buffer, binary.LittleEndian, payload); err != nil { + return err + } + + if payload.Key < 8 { + err := manager.desktop.ButtonDown(uint32(payload.Key)) + if err != nil { + logger.Warn().Err(err).Msg("button down failed") + return nil + } + + logger.Debug().Msgf("button down %d", payload.Key) + } else { + err := manager.desktop.KeyDown(uint32(payload.Key)) + if err != nil { + logger.Warn().Err(err).Msg("key down failed") + return nil + } + + logger.Debug().Msgf("key down %d", payload.Key) + } + case OP_KEY_UP: + payload := &PayloadKey{} + err := binary.Read(buffer, binary.LittleEndian, payload) + if err != nil { + return err + } + + if payload.Key < 8 { + err := manager.desktop.ButtonUp(uint32(payload.Key)) + if err != nil { + logger.Warn().Err(err).Msg("button up failed") + return nil + } + + logger.Debug().Msgf("button up %d", payload.Key) + } else { + err := manager.desktop.KeyUp(uint32(payload.Key)) + if err != nil { + logger.Warn().Err(err).Msg("key up failed") + return nil + } + + logger.Debug().Msgf("key up %d", payload.Key) + } + case OP_KEY_CLK: + // unused + break + } + + return nil +} diff --git a/server/internal/webrtc/manager.go b/server/internal/webrtc/manager.go index 5412c9e7..af4161a5 100644 --- a/server/internal/webrtc/manager.go +++ b/server/internal/webrtc/manager.go @@ -470,6 +470,21 @@ func (manager *WebRTCManagerCtx) CreatePeer(session types.Session) (*webrtc.Sess connection.OnDataChannel(func(dc *webrtc.DataChannel) { logger.Info().Interface("data_channel", dc).Msg("got remote data channel") + + // + // old implementation created a new data channel on client side + // new implementation creates a new data channel on server side + // + + // handle legacy data channel + dc.OnMessage(func(message webrtc.DataChannelMessage) { + if err := manager.handleLegacy(logger, message.Data, session); err != nil { + logger.Err(err).Msg("data handle failed") + } + }) + + // handle legacy data channel + peer.dataChannel = dc }) var once sync.Once diff --git a/server/pkg/types/session.go b/server/pkg/types/session.go index 4ec4085c..e03ef235 100644 --- a/server/pkg/types/session.go +++ b/server/pkg/types/session.go @@ -57,6 +57,7 @@ type Session interface { Profile() MemberProfile State() SessionState IsHost() bool + LegacyIsHost() bool SetAsHost() SetAsHostBy(session Session) ClearHost()