Files
Panama/config/dot/quickshell/modules/settings/SharingPage.qml
T
Gabriel Brown f6b970da21 Turn the rows that only reported things into controls
Autostart entries showed "Enabled" or "Disabled" as plain text. The row did
toggle on click the whole time, so this is an affordance rather than a missing
capability -- but a control that reads as static text is one nobody knows they
have. It is a switch now, with removal alongside it behind a confirmation:
disabling writes Hidden=true and can be undone, deleting the file cannot.

remove-autostart is confined to files the autostart directory owns. It resolves
the path and compares the parent, so a name like "../../.bashrc" cannot escape,
and it refuses symlinks rather than following them -- deleting through one would
remove whatever it points at, which is somewhere else and not ours. Each refusal
was tested against a fixture directory, including a symlink aimed at
/etc/hostname, which survived.

Sharing says who is signed in from another machine: user, origin and since when.
An empty list on this machine proves nothing, so the parser was checked against
sample `who` output -- it picks out remote sessions and leaves out local seats
and the :0 display, which would otherwise report the person at the keyboard as a
remote login.

Media sharing was "Available" and nothing else: rygel installed, rygel.service
disabled, no way to change that from here. It is a switch now, and it says what
it does before you touch it rather than afterwards -- turning it on publishes
media folders to every device on the network with no password in front of them.

Per-application camera and microphone permissions come from the portal's
permission store, which is where an application that asked through the portal
has its answer recorded. The page states the limit plainly instead of implying a
protection that does not exist: a program installed outside the portal opens the
device directly and nothing here stands in its way. Anything that is not an
explicit "yes" is treated as withheld, because guessing generously about a
camera is the wrong way to be wrong.

The first version of the write silently did nothing -- SetPermission takes an
array of strings and was being handed one string -- and the test did not notice,
because it discarded the helper's output and only checked that state was
unchanged afterwards, which was trivially true. The contract now requires the
value to move, and was proven to fail by putting that exact bug back.

Claude-Session: https://claude.ai/code/session_01BRvzt4H8XXLPVH5MyYdk9L
2026-08-19 23:10:48 -04:00

210 lines
7.7 KiB
QML

// What this machine offers to other machines on the network.
//
// Every row states what is true right now, including "the software for this is
// not installed" -- which is the honest answer for file sharing here, and is
// what the panel this replaces hides behind a switch that silently does
// nothing.
//
// Turning remote login on changes the whole system and prompts. Remote desktop
// is a user service and does not.
import Quickshell
import QtQuick
import qs.config
import qs.services
SettingsPage {
id: root
objectName: "sharing"
title: "Sharing"
lede: "What this machine offers to other machines on the network."
Component.onCompleted: Sharing.refresh()
TextRow {
visible: Sharing.lastError !== ""
label: "Sharing needs attention"
detail: Sharing.lastError
value: ""
divider: false
}
SettingsCard {
title: "This machine"
subtitle: "The name other machines see."
TextFieldRow {
label: "Network name"
detail: "Used for ssh and for anything else that finds this machine by name"
text: Sharing.hostname
placeholder: "desktop"
enabled: !Sharing.busy
divider: false
onAccepted: value => Sharing.setHostname(value)
}
}
SettingsCard {
title: "Remote login"
subtitle: "Sign in to a terminal on this machine over SSH."
SwitchRow {
label: "Allow remote login"
detail: Sharing.remoteLogin?.installed === true
? (Sharing.remoteLoginOn
? "Running, and starts automatically at boot"
: "Not running")
: "OpenSSH server is not installed"
checked: Sharing.remoteLoginOn
enabled: !Sharing.busy && Sharing.remoteLogin?.installed === true
onToggled: value => Sharing.setRemoteLogin(value)
}
TextRow {
visible: Sharing.remoteLoginOn
label: "Connect with"
detail: "From another machine on your network"
value: "ssh " + Sharing.networkName
}
TextRow {
visible: Sharing.remoteLoginOn && Sharing.remoteSessions.length === 0
label: "Nobody is signed in"
detail: "Remote login is on, and no one is connected from another machine."
value: ""
}
Repeater {
model: Sharing.remoteSessions
delegate: TextRow {
required property var modelData
width: parent.width
label: String(modelData.user ?? "") + " is signed in from " + String(modelData.from ?? "")
detail: "Since " + String(modelData.since ?? "") + " · " + String(modelData.line ?? "")
value: ""
}
}
TextRow {
visible: Sharing.remoteLoginOn
label: "Port"
detail: "Where the SSH server is listening"
value: String(Sharing.remoteLogin?.port ?? "22")
}
// Reported from the configuration rather than assumed. Saying "keys
// only" on a machine that actually accepts passwords would be a
// security claim this page cannot back up.
TextRow {
visible: Sharing.remoteLoginOn
label: "Password sign-in"
detail: Sharing.passwordLoginSummary()
value: ""
divider: false
}
}
SettingsCard {
title: "Remote desktop"
subtitle: "See and control this desktop from another machine."
SwitchRow {
label: "Allow remote desktop"
detail: Sharing.remoteDesktop?.available === true
? (Sharing.remoteDesktopOn
? "Running for your session"
: (Sharing.remoteDesktop?.hasCredentials === true
? "Not running"
: "Set a username and password before turning this on"))
: "Remote desktop support is not installed"
checked: Sharing.remoteDesktopOn
enabled: !Sharing.busy
&& Sharing.remoteDesktop?.available === true
&& Sharing.remoteDesktop?.hasCredentials === true
onToggled: value => Sharing.setRemoteDesktop(value)
}
TextFieldRow {
visible: Sharing.remoteDesktop?.available === true
label: "Port"
detail: "The RDP port other machines connect to"
text: String(Sharing.remoteDesktop?.port ?? "")
placeholder: "3389"
enabled: !Sharing.busy
onAccepted: value => Sharing.setRdpPort(value)
}
SwitchRow {
visible: Sharing.remoteDesktop?.available === true
label: "View only"
detail: "Let someone watch this desktop without controlling the pointer or keyboard"
checked: Sharing.remoteDesktop?.viewOnly === true
enabled: !Sharing.busy
onToggled: value => Sharing.setRdpViewOnly(value)
}
// The password is typed into gnome-remote-desktop's own tool in a
// terminal, never into this page. grdctl prompts for it on a terminal
// and crashes without one, and passing it as an argument would publish
// it through /proc to every process on this machine.
ActionRow {
visible: Sharing.remoteDesktop?.available === true
label: "Credentials"
detail: Sharing.remoteDesktop?.hasCredentials === true
? "Stored in the login keyring · setting new ones opens a terminal to type into"
: "None stored yet · remote desktop cannot be turned on without them"
action: "Set…"
enabled: !Sharing.busy
onTriggered: Sharing.setRdpCredentials(Quickshell.env("USER") || "")
}
ActionRow {
visible: Sharing.remoteDesktop?.available === true
&& Sharing.remoteDesktop?.hasCredentials === true
label: "Forget the stored credentials"
detail: "Remote desktop cannot be turned on again until new ones are set"
action: "Clear"
enabled: !Sharing.busy
divider: false
onTriggered: Sharing.clearRdpCredentials()
}
}
SettingsCard {
title: "File and media sharing"
subtitle: "Sharing folders and media needs software this machine does not necessarily have."
TextRow {
label: "Share folders on the network"
detail: Sharing.fileSharing?.installed === true
? "Samba is installed"
: "Needs Samba, which is not installed. Settings does not install software."
value: Sharing.fileSharing?.installed === true ? "Available" : "Not installed"
}
SwitchRow {
visible: Sharing.mediaSharing?.installed === true
label: "Share music and video to devices"
// Said before it happens, not after: this advertises on the network
// to anything that speaks DLNA, with no password in front of it.
detail: Sharing.mediaSharing?.active === true
? "Rygel is serving your media to devices on the network"
: "Publishes your media folders to every device on the network. No password is asked for."
checked: Sharing.mediaSharing?.active === true
enabled: !Sharing.busy
divider: false
onToggled: value => Sharing.setMediaSharing(value)
}
TextRow {
visible: Sharing.mediaSharing?.installed !== true
label: "Share music and video to devices"
detail: "Needs Rygel, which is not installed."
value: "Not installed"
divider: false
}
}
}