mirror of
https://github.com/m1k1o/neko.git
synced 2024-07-24 14:40:50 +12:00
642 lines
14 KiB
Go
642 lines
14 KiB
Go
package legacy
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
|
|
"github.com/pion/webrtc/v3"
|
|
|
|
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/demodesk/neko/internal/plugins/chat"
|
|
"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 sessionDataToMember(id string, session message.SessionData) (*oldTypes.Member, error) {
|
|
settings := chat.Settings{
|
|
CanSend: true, // defaults to true
|
|
CanReceive: true, // defaults to true
|
|
}
|
|
|
|
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 (s *session) sendControlHost(request message.ControlHost) error {
|
|
lastHostID := s.lastHostID
|
|
|
|
if request.HasHost {
|
|
s.lastHostID = request.ID
|
|
|
|
if request.ID == request.HostID {
|
|
if request.ID == lastHostID || lastHostID == "" {
|
|
return s.toClient(&oldMessage.Control{
|
|
Event: oldEvent.CONTROL_LOCKED,
|
|
ID: request.HostID,
|
|
})
|
|
} else {
|
|
return s.toClient(&oldMessage.AdminTarget{
|
|
Event: oldEvent.ADMIN_CONTROL,
|
|
ID: request.ID,
|
|
Target: lastHostID,
|
|
})
|
|
}
|
|
} else {
|
|
return s.toClient(&oldMessage.ControlTarget{
|
|
Event: oldEvent.CONTROL_GIVE,
|
|
ID: request.HostID,
|
|
Target: request.ID,
|
|
})
|
|
}
|
|
}
|
|
|
|
if request.ID != "" {
|
|
s.lastHostID = ""
|
|
|
|
if request.ID == lastHostID {
|
|
return s.toClient(&oldMessage.Control{
|
|
Event: oldEvent.CONTROL_RELEASE,
|
|
ID: request.ID,
|
|
})
|
|
} else {
|
|
return s.toClient(&oldMessage.Control{
|
|
Event: oldEvent.ADMIN_RELEASE,
|
|
ID: request.ID,
|
|
})
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (s *session) wsToClient(msg []byte) error {
|
|
data := types.WebSocketMessage{}
|
|
err := json.Unmarshal(msg, &data)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
switch data.Event {
|
|
// System Events
|
|
case event.SYSTEM_DISCONNECT:
|
|
request := &message.SystemDisconnect{}
|
|
err := json.Unmarshal(data.Payload, request)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return s.toClient(&oldMessage.SystemMessage{
|
|
Event: oldEvent.SYSTEM_DISCONNECT,
|
|
Message: request.Message,
|
|
})
|
|
|
|
case event.SYSTEM_INIT:
|
|
request := &message.SystemInit{}
|
|
err := json.Unmarshal(data.Payload, request)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
//
|
|
// MembersList
|
|
//
|
|
|
|
membersList := []*oldTypes.Member{}
|
|
s.sessions = map[string]*oldTypes.Member{}
|
|
for id, session := range request.Sessions {
|
|
if !session.State.IsConnected {
|
|
continue
|
|
}
|
|
member, err := sessionDataToMember(id, session)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
membersList = append(membersList, member)
|
|
s.sessions[id] = member
|
|
}
|
|
|
|
err = s.toClient(&oldMessage.MembersList{
|
|
Event: oldEvent.MEMBER_LIST,
|
|
Members: membersList,
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
//
|
|
// ScreenSize
|
|
//
|
|
|
|
err = s.toClient(&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 = s.sendControlHost(request.ControlHost)
|
|
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.
|
|
s.lockedLogins = true
|
|
}
|
|
if request.Settings.LockedControls {
|
|
locks["control"] = "" // TODO: We don't know who locked the control.
|
|
s.lockedControls = true
|
|
}
|
|
if !filetransferSettings.Enabled {
|
|
locks["file_transfer"] = "" // TODO: We don't know who locked the file transfer.
|
|
s.lockedFileTransfer = true
|
|
}
|
|
|
|
return s.toClient(&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 = s.toClient(&oldMessage.ScreenConfigurations{
|
|
Event: oldEvent.SCREEN_CONFIGURATIONS,
|
|
Configurations: screenSizesList,
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
//
|
|
// BroadcastStatus
|
|
//
|
|
|
|
return s.toClient(&oldMessage.BroadcastStatus{
|
|
Event: oldEvent.BROADCAST_STATUS,
|
|
URL: request.BroadcastStatus.URL,
|
|
IsActive: request.BroadcastStatus.IsActive,
|
|
})
|
|
|
|
// Member Events
|
|
|
|
case event.SESSION_CREATED:
|
|
request := &message.SessionData{}
|
|
err := json.Unmarshal(data.Payload, request)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
member, err := sessionDataToMember(request.ID, *request)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// only save session - will be notified on connect
|
|
s.sessions[request.ID] = member
|
|
|
|
return nil
|
|
|
|
case event.SESSION_DELETED:
|
|
request := &message.SessionID{}
|
|
err := json.Unmarshal(data.Payload, request)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// only continue if session is in the list - should have been already removed
|
|
if _, ok := s.sessions[request.ID]; !ok {
|
|
return nil
|
|
}
|
|
|
|
delete(s.sessions, request.ID)
|
|
|
|
return s.toClient(&oldMessage.MemberDisconnected{
|
|
Event: oldEvent.MEMBER_DISCONNECTED,
|
|
ID: request.ID,
|
|
})
|
|
|
|
case event.SESSION_STATE:
|
|
request := &message.SessionState{}
|
|
err := json.Unmarshal(data.Payload, request)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
member, ok := s.sessions[request.ID]
|
|
if !ok {
|
|
return nil
|
|
}
|
|
|
|
if request.IsConnected && member != nil {
|
|
s.sessions[request.ID] = nil
|
|
|
|
// oldEvent.MEMBER_CONNECTED if not sent already
|
|
return s.toClient(&oldMessage.Member{
|
|
Event: oldEvent.MEMBER_CONNECTED,
|
|
Member: member,
|
|
})
|
|
}
|
|
|
|
if !request.IsConnected {
|
|
delete(s.sessions, request.ID)
|
|
|
|
// oldEvent.MEMBER_DISCONNECTED if nor sent already
|
|
return s.toClient(&oldMessage.MemberDisconnected{
|
|
Event: oldEvent.MEMBER_DISCONNECTED,
|
|
ID: request.ID,
|
|
})
|
|
}
|
|
|
|
return nil
|
|
|
|
// Signal Events
|
|
case event.SIGNAL_OFFER:
|
|
request := &message.SignalDescription{}
|
|
err := json.Unmarshal(data.Payload, request)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return s.toClient(&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 s.toClient(&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 s.toClient(&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 s.toClient(&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 event.CLIPBOARD_UPDATED:
|
|
request := &message.ClipboardData{}
|
|
err := json.Unmarshal(data.Payload, request)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return s.toClient(&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
|
|
}
|
|
|
|
return s.sendControlHost(*request)
|
|
|
|
case event.CONTROL_REQUEST:
|
|
request := &message.SessionID{}
|
|
err := json.Unmarshal(data.Payload, request)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if s.id == request.ID {
|
|
// if i am the one that is requesting, send CONTROL_REQUEST to me
|
|
return s.toClient(&oldMessage.Control{
|
|
Event: oldEvent.CONTROL_REQUEST,
|
|
ID: request.ID,
|
|
})
|
|
} else {
|
|
// if not, let me know someone else is requesting
|
|
return s.toClient(&oldMessage.Control{
|
|
Event: oldEvent.CONTROL_REQUESTING,
|
|
ID: request.ID,
|
|
})
|
|
}
|
|
|
|
// Chat Events
|
|
case chat.CHAT_MESSAGE:
|
|
request := &chat.Message{}
|
|
err := json.Unmarshal(data.Payload, request)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return s.toClient(&oldMessage.ChatSend{
|
|
Event: oldEvent.CHAT_MESSAGE,
|
|
ID: request.ID,
|
|
Content: request.Content.Text,
|
|
})
|
|
|
|
case event.SEND_BROADCAST:
|
|
request := &message.SendBroadcast{}
|
|
err := json.Unmarshal(data.Payload, request)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if request.Subject == "emote" {
|
|
return s.toClient(&oldMessage.EmoteSend{
|
|
Event: oldEvent.CHAT_EMOTE,
|
|
ID: request.Sender,
|
|
Emote: request.Body.(string),
|
|
})
|
|
}
|
|
|
|
return nil
|
|
|
|
// File Transfer Events
|
|
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 s.toClient(&oldMessage.FileTransferList{
|
|
Event: oldEvent.FILETRANSFER_LIST,
|
|
Cwd: request.RootDir,
|
|
Files: files,
|
|
})
|
|
|
|
// Screen Events
|
|
case event.SCREEN_UPDATED:
|
|
request := &message.ScreenSizeUpdate{}
|
|
err := json.Unmarshal(data.Payload, request)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return s.toClient(&oldMessage.ScreenResolution{
|
|
Event: oldEvent.SCREEN_RESOLUTION,
|
|
ID: request.ID,
|
|
Width: request.ScreenSize.Width,
|
|
Height: request.ScreenSize.Height,
|
|
Rate: request.ScreenSize.Rate,
|
|
})
|
|
|
|
// Broadcast Events
|
|
case event.BROADCAST_STATUS:
|
|
request := &message.BroadcastStatus{}
|
|
err := json.Unmarshal(data.Payload, request)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return s.toClient(&oldMessage.BroadcastStatus{
|
|
Event: oldEvent.BROADCAST_STATUS,
|
|
URL: request.URL,
|
|
IsActive: request.IsActive,
|
|
})
|
|
|
|
// Admin Events
|
|
case event.SYSTEM_SETTINGS:
|
|
request := &message.SystemSettingsUpdate{}
|
|
err := json.Unmarshal(data.Payload, request)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if s.lockedControls != request.LockedControls {
|
|
s.lockedControls = request.LockedControls
|
|
|
|
if request.LockedControls {
|
|
err = s.toClient(&oldMessage.AdminLock{
|
|
Event: oldEvent.ADMIN_LOCK,
|
|
Resource: "control",
|
|
})
|
|
} else {
|
|
err = s.toClient(&oldMessage.AdminLock{
|
|
Event: oldEvent.ADMIN_UNLOCK,
|
|
Resource: "control",
|
|
})
|
|
}
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if s.lockedLogins != request.LockedLogins {
|
|
s.lockedLogins = request.LockedLogins
|
|
|
|
if request.LockedLogins {
|
|
err = s.toClient(&oldMessage.AdminLock{
|
|
Event: oldEvent.ADMIN_LOCK,
|
|
Resource: "login",
|
|
})
|
|
} else {
|
|
err = s.toClient(&oldMessage.AdminLock{
|
|
Event: oldEvent.ADMIN_UNLOCK,
|
|
Resource: "login",
|
|
})
|
|
}
|
|
|
|
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)
|
|
}
|
|
|
|
if s.lockedFileTransfer != !filetransferSettings.Enabled {
|
|
s.lockedFileTransfer = !filetransferSettings.Enabled
|
|
|
|
if !filetransferSettings.Enabled {
|
|
err = s.toClient(&oldMessage.AdminLock{
|
|
Event: oldEvent.ADMIN_LOCK,
|
|
Resource: "file_transfer",
|
|
})
|
|
} else {
|
|
err = s.toClient(&oldMessage.AdminLock{
|
|
Event: oldEvent.ADMIN_UNLOCK,
|
|
Resource: "file_transfer",
|
|
})
|
|
}
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
|
|
/*
|
|
case:
|
|
s.toClient(&oldMessage.AdminTarget{
|
|
Event: oldEvent.ADMIN_BAN,
|
|
})
|
|
case:
|
|
s.toClient(&oldMessage.AdminTarget{
|
|
Event: oldEvent.ADMIN_KICK,
|
|
})
|
|
case:
|
|
s.toClient(&oldMessage.AdminTarget{
|
|
Event: oldEvent.ADMIN_MUTE,
|
|
})
|
|
case:
|
|
s.toClient(&oldMessage.AdminTarget{
|
|
Event: oldEvent.ADMIN_UNMUTE,
|
|
})
|
|
*/
|
|
|
|
case event.SYSTEM_HEARTBEAT:
|
|
return nil
|
|
|
|
default:
|
|
return fmt.Errorf("unknown event type: %s", data.Event)
|
|
}
|
|
}
|