report configuration failures instead of crashing

Return errors from the save path rather than panicking.
Every write panicked on failure, so a read-only or missing
configuration directory killed a running client. The parent directory
is created when absent, and callers now show the failure in the output
window and carry on.

Write through an unpredictable temporary file.
The old fixed ".tmp" name next to the configuration was a symlink
target an attacker could plant in advance.

Serialize configuration reads and writes.
Hotkey handlers, the audio thread, and the connection callbacks all
touch the same structure, so saves could interleave with updates.

Parse addresses with SplitHostPort.
Splitting on every colon broke IPv6 addresses and panicked outright on
an address with no port. Both now fall back to Mumble's default port.

Fail immediately when an explicitly requested config file is missing
or is not a regular file.
Silently falling back to defaults hid a mistyped -config path.

Supply defaults for the clear-output and scroll-to-top and -bottom
hotkeys.
The UI registered listeners for them but the configuration never
filled the keys in, so the bindings were nil and the keys did nothing.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Brandon McGinty
2026-08-20 14:42:52 -04:00
co-authored by Claude Opus 5
parent 7e1faba06b
commit 17173b779b
7 changed files with 235 additions and 32 deletions
+7 -5
View File
@@ -115,10 +115,10 @@ func (b *Barnard) TreeItemKeyPress(ui *uiterm.Ui, tree *uiterm.Tree, item uiterm
users := makeUsersArray(treeItem.Channel.Users)
for _, u := range users {
// Explicitly set user mute state to match channel state
if channelWillBeMuted && !u.LocallyMuted() {
b.UserConfig.ToggleMute(u)
} else if !channelWillBeMuted && u.LocallyMuted() {
b.UserConfig.ToggleMute(u)
if channelWillBeMuted != u.LocallyMuted() {
if err := b.UserConfig.ToggleMute(u); err != nil {
b.AddOutputLine("Mute: could not save setting: " + err.Error())
}
}
if source := u.AudioSource(); source != nil {
@@ -158,7 +158,9 @@ func (b *Barnard) TreeItemKeyPress(ui *uiterm.Ui, tree *uiterm.Tree, item uiterm
if treeItem.User != nil {
if key == *b.Hotkeys.MuteToggle {
// Toggle mute for single user
b.UserConfig.ToggleMute(treeItem.User)
if err := b.UserConfig.ToggleMute(treeItem.User); err != nil {
b.AddOutputLine("Mute: could not save setting: " + err.Error())
}
if source := treeItem.User.AudioSource(); source != nil {
if treeItem.User.LocallyMuted() {
source.SetGain(0)