diff --git a/internal/api/room/handler.go b/internal/api/room/handler.go index ef9cc525..45ba0598 100644 --- a/internal/api/room/handler.go +++ b/internal/api/room/handler.go @@ -31,22 +31,24 @@ func New( } // generate fallback image for private mode when needed - sessions.OnPrivateModeChanged(func(isPrivateMode bool) { - if !isPrivateMode { + sessions.OnSettingsChanged(func(new types.Settings, old types.Settings) { + if old.PrivateMode && !new.PrivateMode { log.Debug().Msg("clearing private mode fallback image") h.privateModeImage = nil return } - img := h.desktop.GetScreenshotImage() - bytes, err := utils.CreateJPGImage(img, 90) - if err != nil { - log.Err(err).Msg("could not generate private mode fallback image") - return - } + if !old.PrivateMode && new.PrivateMode { + img := h.desktop.GetScreenshotImage() + bytes, err := utils.CreateJPGImage(img, 90) + if err != nil { + log.Err(err).Msg("could not generate private mode fallback image") + return + } - log.Debug().Msg("using private mode fallback image") - h.privateModeImage = bytes + log.Debug().Msg("using private mode fallback image") + h.privateModeImage = bytes + } }) return h @@ -110,7 +112,7 @@ func (h *RoomHandler) Route(r types.Router) { func (h *RoomHandler) uploadMiddleware(w http.ResponseWriter, r *http.Request) (context.Context, error) { session, ok := auth.GetSession(r) - if !ok || (!session.IsHost() && (!session.Profile().CanHost || !h.sessions.ImplicitHosting())) { + if !ok || (!session.IsHost() && (!session.Profile().CanHost || !h.sessions.Settings().ImplicitHosting)) { return nil, utils.HttpForbidden("without implicit hosting, only host can upload files") } diff --git a/internal/session/manager.go b/internal/session/manager.go index b0eed57a..d1017de4 100644 --- a/internal/session/manager.go +++ b/internal/session/manager.go @@ -15,8 +15,14 @@ import ( func New(config *config.Session) *SessionManagerCtx { manager := &SessionManagerCtx{ - logger: log.With().Str("module", "session").Logger(), - config: config, + logger: log.With().Str("module", "session").Logger(), + config: config, + settings: types.Settings{ + PrivateMode: false, // By default disabled. + ImplicitHosting: config.ImplicitHosting, + InactiveCursors: config.InactiveCursors, + MercifulReconnect: config.MercifulReconnect, + }, tokens: make(map[string]string), sessions: make(map[string]*SessionCtx), cursors: make(map[types.Session][]types.Cursor), @@ -49,6 +55,9 @@ type SessionManagerCtx struct { logger zerolog.Logger config *config.Session + settings types.Settings + settingsMu sync.Mutex + tokens map[string]string sessions map[string]*SessionCtx sessionsMu sync.Mutex @@ -56,9 +65,6 @@ type SessionManagerCtx struct { host types.Session hostMu sync.Mutex - privateMode bool - privateModeMu sync.Mutex - cursors map[types.Session][]types.Cursor cursorsMu sync.Mutex @@ -200,39 +206,6 @@ func (manager *SessionManagerCtx) ClearHost() { manager.SetHost(nil) } -// --- -// private mode -// --- - -func (manager *SessionManagerCtx) SetPrivateMode(isPrivateMode bool) { - manager.privateModeMu.Lock() - - // only if value changed - if manager.privateMode == isPrivateMode { - manager.privateModeMu.Unlock() - return - } - - // update webrtc paused state for all sessions - for _, session := range manager.List() { - if webrtcPeer := session.GetWebRTCPeer(); webrtcPeer != nil { - webrtcPeer.SetPaused(isPrivateMode && !session.Profile().IsAdmin) - } - } - - manager.privateMode = isPrivateMode - manager.privateModeMu.Unlock() - - manager.emmiter.Emit("private_mode_changed", isPrivateMode) -} - -func (manager *SessionManagerCtx) PrivateMode() bool { - manager.privateModeMu.Lock() - defer manager.privateModeMu.Unlock() - - return manager.privateMode -} - // --- // cursors // --- @@ -362,28 +335,42 @@ func (manager *SessionManagerCtx) OnHostChanged(listener func(session types.Sess }) } -func (manager *SessionManagerCtx) OnPrivateModeChanged(listener func(isPrivateMode bool)) { - manager.emmiter.On("private_mode_changed", func(payload ...interface{}) { - listener(payload[0].(bool)) +func (manager *SessionManagerCtx) OnSettingsChanged(listener func(new types.Settings, old types.Settings)) { + manager.emmiter.On("settings_changed", func(payload ...interface{}) { + listener(payload[0].(types.Settings), payload[1].(types.Settings)) }) } // --- -// config +// settings // --- -func (manager *SessionManagerCtx) ImplicitHosting() bool { - return manager.config.ImplicitHosting +func (manager *SessionManagerCtx) UpdateSettings(new types.Settings) { + manager.settingsMu.Lock() + old := manager.settings + manager.settings = new + manager.settingsMu.Unlock() + + // if private mode changed + if old.PrivateMode != new.PrivateMode { + // update webrtc paused state for all sessions + for _, session := range manager.List() { + if webrtcPeer := session.GetWebRTCPeer(); webrtcPeer != nil { + webrtcPeer.SetPaused(session.PrivateModeEnabled()) + } + } + } + + manager.emmiter.Emit("settings_changed", new, old) } -func (manager *SessionManagerCtx) InactiveCursors() bool { - return manager.config.InactiveCursors +func (manager *SessionManagerCtx) Settings() types.Settings { + manager.settingsMu.Lock() + defer manager.settingsMu.Unlock() + + return manager.settings } func (manager *SessionManagerCtx) CookieEnabled() bool { return manager.config.CookieEnabled } - -func (manager *SessionManagerCtx) MercifulReconnect() bool { - return manager.config.MercifulReconnect -} diff --git a/internal/session/session.go b/internal/session/session.go index 4e46c917..445391ef 100644 --- a/internal/session/session.go +++ b/internal/session/session.go @@ -60,11 +60,11 @@ func (session *SessionCtx) IsHost() bool { } func (session *SessionCtx) PrivateModeEnabled() bool { - return session.manager.PrivateMode() && !session.profile.IsAdmin + return session.manager.Settings().PrivateMode && !session.profile.IsAdmin } func (session *SessionCtx) SetCursor(cursor types.Cursor) { - if session.manager.InactiveCursors() && session.profile.SendsInactiveCursor { + if session.manager.Settings().InactiveCursors && session.profile.SendsInactiveCursor { session.manager.SetCursor(cursor, session) } } diff --git a/internal/websocket/handler/control.go b/internal/websocket/handler/control.go index 364852ea..d7e041cf 100644 --- a/internal/websocket/handler/control.go +++ b/internal/websocket/handler/control.go @@ -40,7 +40,7 @@ func (h *MessageHandlerCtx) controlRequest(session types.Session) error { return ErrIsAlreadyTheHost } - if !h.sessions.ImplicitHosting() { + if !h.sessions.Settings().ImplicitHosting { // tell session if there is a host if host := h.sessions.GetHost(); host != nil { session.Send( diff --git a/internal/websocket/handler/system.go b/internal/websocket/handler/system.go index f4ae2dd8..a2e0f273 100644 --- a/internal/websocket/handler/system.go +++ b/internal/websocket/handler/system.go @@ -44,12 +44,15 @@ func (h *MessageHandlerCtx) systemInit(session types.Session) error { ControlHost: controlHost, ScreenSize: message.ScreenSize(*size), Sessions: sessions, - ImplicitHosting: h.sessions.ImplicitHosting(), - InactiveCursors: h.sessions.InactiveCursors(), + Settings: h.sessions.Settings(), ScreencastEnabled: h.capture.Screencast().Enabled(), WebRTC: message.SystemWebRTC{ Videos: h.capture.VideoIDs(), }, + + // TODO: Left for compatibility with old client, remove. + ImplicitHosting: h.sessions.Settings().ImplicitHosting, + InactiveCursors: h.sessions.Settings().InactiveCursors, }) return nil diff --git a/internal/websocket/manager.go b/internal/websocket/manager.go index 067a3fc3..47caed03 100644 --- a/internal/websocket/manager.go +++ b/internal/websocket/manager.go @@ -49,6 +49,8 @@ type WebSocketManagerCtx struct { desktop types.DesktopManager handler *handler.MessageHandlerCtx handlers []types.WebSocketHandler + + shutdownInactiveCursors chan struct{} } func (manager *WebSocketManagerCtx) Start() { @@ -111,6 +113,24 @@ func (manager *WebSocketManagerCtx) Start() { Msg("session host changed") }) + manager.sessions.OnSettingsChanged(func(new types.Settings, old types.Settings) { + // start inactive cursors + if new.InactiveCursors && !old.InactiveCursors { + manager.startInactiveCursors() + } + + // stop inactive cursors + if !new.InactiveCursors && old.InactiveCursors { + manager.stopInactiveCursors() + } + + manager.sessions.Broadcast(event.SYSTEM_SETTINGS, new, nil) + manager.logger.Info(). + Interface("new", new). + Interface("old", old). + Msg("settings changed") + }) + manager.desktop.OnClipboardUpdated(func() { session := manager.sessions.GetHost() if session == nil || !session.Profile().CanAccessClipboard { @@ -135,8 +155,8 @@ func (manager *WebSocketManagerCtx) Start() { manager.fileChooserDialogEvents() - if manager.sessions.InactiveCursors() { - manager.inactiveCursors() + if manager.sessions.Settings().InactiveCursors { + manager.startInactiveCursors() } manager.logger.Info().Msg("websocket starting") @@ -145,6 +165,7 @@ func (manager *WebSocketManagerCtx) Start() { func (manager *WebSocketManagerCtx) Shutdown() error { manager.logger.Info().Msg("shutdown") close(manager.shutdown) + manager.stopInactiveCursors() manager.wg.Wait() return nil } @@ -196,7 +217,7 @@ func (manager *WebSocketManagerCtx) connect(connection *websocket.Conn, r *http. if session.State().IsConnected { logger.Warn().Msg("already connected") - if !manager.sessions.MercifulReconnect() { + if !manager.sessions.Settings().MercifulReconnect { peer.Destroy("already connected") return } @@ -299,8 +320,14 @@ func (manager *WebSocketManagerCtx) handle(connection *websocket.Conn, peer type } } -func (manager *WebSocketManagerCtx) inactiveCursors() { +func (manager *WebSocketManagerCtx) startInactiveCursors() { + if manager.shutdownInactiveCursors != nil { + manager.logger.Warn().Msg("inactive cursors handler already running") + return + } + manager.logger.Info().Msg("starting inactive cursors handler") + manager.shutdownInactiveCursors = make(chan struct{}) manager.wg.Add(1) go func() { @@ -314,8 +341,9 @@ func (manager *WebSocketManagerCtx) inactiveCursors() { for { select { - case <-manager.shutdown: + case <-manager.shutdownInactiveCursors: manager.logger.Info().Msg("stopping inactive cursors handler") + manager.shutdownInactiveCursors = nil return case <-ticker.C: cursorsMap := manager.sessions.PopCursors() @@ -342,3 +370,9 @@ func (manager *WebSocketManagerCtx) inactiveCursors() { } }() } + +func (manager *WebSocketManagerCtx) stopInactiveCursors() { + if manager.shutdownInactiveCursors != nil { + close(manager.shutdownInactiveCursors) + } +} diff --git a/pkg/types/event/events.go b/pkg/types/event/events.go index b9c37202..60973e40 100644 --- a/pkg/types/event/events.go +++ b/pkg/types/event/events.go @@ -3,6 +3,7 @@ package event const ( SYSTEM_INIT = "system/init" SYSTEM_ADMIN = "system/admin" + SYSTEM_SETTINGS = "system/settings" SYSTEM_LOGS = "system/logs" SYSTEM_DISCONNECT = "system/disconnect" ) diff --git a/pkg/types/message/messages.go b/pkg/types/message/messages.go index a46d67b4..e5de8bfb 100644 --- a/pkg/types/message/messages.go +++ b/pkg/types/message/messages.go @@ -19,10 +19,13 @@ type SystemInit struct { ControlHost ControlHost `json:"control_host"` ScreenSize ScreenSize `json:"screen_size"` Sessions map[string]SessionData `json:"sessions"` - ImplicitHosting bool `json:"implicit_hosting"` - InactiveCursors bool `json:"inactive_cursors"` + Settings types.Settings `json:"settings"` ScreencastEnabled bool `json:"screencast_enabled"` WebRTC SystemWebRTC `json:"webrtc"` + + // TODO: Left for compatibility with old client, remove. + ImplicitHosting bool `json:"implicit_hosting"` + InactiveCursors bool `json:"inactive_cursors"` } type SystemAdmin struct { diff --git a/pkg/types/session.go b/pkg/types/session.go index f3c3a39d..841b1854 100644 --- a/pkg/types/session.go +++ b/pkg/types/session.go @@ -22,6 +22,13 @@ type SessionState struct { IsWatching bool `json:"is_watching"` } +type Settings struct { + PrivateMode bool `json:"private_mode"` + ImplicitHosting bool `json:"implicit_hosting"` + InactiveCursors bool `json:"inactive_cursors"` + MercifulReconnect bool `json:"merciful_reconnect"` +} + type Session interface { ID() string Profile() MemberProfile @@ -56,9 +63,6 @@ type SessionManager interface { GetHost() Session ClearHost() - SetPrivateMode(isPrivateMode bool) - PrivateMode() bool - SetCursor(cursor Cursor, session Session) PopCursors() map[Session][]Cursor @@ -73,12 +77,11 @@ type SessionManager interface { OnProfileChanged(listener func(session Session)) OnStateChanged(listener func(session Session)) OnHostChanged(listener func(session Session)) - OnPrivateModeChanged(listener func(isPrivateMode bool)) + OnSettingsChanged(listener func(new Settings, old Settings)) - ImplicitHosting() bool - InactiveCursors() bool + UpdateSettings(Settings) + Settings() Settings CookieEnabled() bool - MercifulReconnect() bool CookieSetToken(w http.ResponseWriter, token string) CookieClearToken(w http.ResponseWriter, r *http.Request)